Compare commits
173 Commits
14a3b23e43
...
main
Author | SHA1 | Date | |
---|---|---|---|
b40a33f69a
|
|||
22b1450a72
|
|||
abb5646959
|
|||
1ee1c7215d
|
|||
d45f8d2a57
|
|||
cd40ba58da
|
|||
d110d688c6
|
|||
6cc16233fa
|
|||
00ce17cb21
|
|||
3a4ad875e2
|
|||
e25f23f40b
|
|||
f794f9848b
|
|||
f2b60ca6b0
|
|||
a68b68a0e3
|
|||
33604db8a0
|
|||
a31321e662
|
|||
db74d07155
|
|||
d578db6fb2
|
|||
296fe1efc2
|
|||
2c95dfa660
|
|||
fa1aefe425
|
|||
dca1482526
|
|||
9aa248e7f2
|
|||
2b62599485
|
|||
f588d3bc72
|
|||
c6edaac2e1
|
|||
e18b4d78fb
|
|||
68c90c8f8a
|
|||
f3be1aae48
|
|||
40ed66d76e
|
|||
07455f2091
|
|||
3ac5f3c9ec
|
|||
bf3d1ba574
|
|||
9aa47128c7
|
|||
0fceb397d1
|
|||
6a5068deee
|
|||
527d5ec15d
|
|||
467a22af35
|
|||
4d5559a27d
|
|||
df6a14f2b1
|
|||
89841beeb7
|
|||
7ca6689bad
|
|||
3ce9081400
|
|||
1bd5965ba1
|
|||
eb772916dc
|
|||
421f80c9a1
|
|||
fd7766b38c
|
|||
b574d8a3d4
|
|||
96c48fa0db
|
|||
fe5eeb0364
|
|||
b183e99b3c
|
|||
bdccd0c7f3
|
|||
726a0337c5
|
|||
0aee57947d
|
|||
336cab7de4
|
|||
1d2d9ce4d8
|
|||
bbef6cb342
|
|||
314957eeb5
|
|||
79b11498c1
|
|||
730f6b9781
|
|||
d5c9eb6b41
|
|||
2f7c9a883a
|
|||
e917807adc
|
|||
023017a829
|
|||
2e868528f7
|
|||
c67c6da32e
|
|||
519060ca23
|
|||
249d060075
|
|||
7fcd199b9f
|
|||
a575f183b4
|
|||
46c304931d
|
|||
d2c1380b82
|
|||
f31969f4e5
|
|||
382ab9b0e2
|
|||
e87d6f7d92
|
|||
08b138dc30
|
|||
d01f7b642e
|
|||
673ee61d78
|
|||
f5fd65aee8
|
|||
2e9fd2f05d
|
|||
a5bece993a
|
|||
245c5d2490
|
|||
ca0f7b4856
|
|||
b0a5d8c86c
|
|||
027082d177
|
|||
69449c8e66
|
|||
dfa5627ddd
|
|||
3cab891af0
|
|||
34ae57e259
|
|||
73923b9586
|
|||
d2b159ade2
|
|||
8eb5a75051
|
|||
efade64c0f
|
|||
cc360be7c1
|
|||
6dddd60405
|
|||
ca87f6704c
|
|||
cb9102be74
|
|||
c162186f1a
|
|||
11c232228a
|
|||
a48b793b7e
|
|||
ad66862620
|
|||
d077704c05
|
|||
dc4a9764cd
|
|||
c290984202
|
|||
849a88bbf6
|
|||
5beb4d977f
|
|||
e70697fa6a
|
|||
2089a966e4
|
|||
03861f193b
|
|||
e60de5a03a
|
|||
294a5291b1
|
|||
bc64a13d9c
|
|||
bbfb3f9b9a
|
|||
27ba95e312
|
|||
30187548bb
|
|||
fa16d2172f
|
|||
f9e6ffacc1
|
|||
df4ad180d7
|
|||
590cfe4f2a
|
|||
33e206f9fe
|
|||
77c87216f7
|
|||
4356793f52
|
|||
bc2f250bae
|
|||
5a41bf0665
|
|||
968c885dba
|
|||
68b8c73896
|
|||
1841e71ff8
|
|||
680d4d76e1
|
|||
d88f110fe5
|
|||
899eac954e
|
|||
e7aa4f4c30
|
|||
7308a478b8
|
|||
c80859d643
|
|||
82de489963
|
|||
60304f5c14
|
|||
0c678ed6f9
|
|||
27e45defad
|
|||
f97a9f6689
|
|||
c3db1ccad8
|
|||
efaa5a6e33
|
|||
b70688f182
|
|||
d1cc15e096
|
|||
33ad5138ea
|
|||
c39615bea9
|
|||
024e097d1e
|
|||
ba3f962905
|
|||
f6381345ae
|
|||
7f49b0a84f
|
|||
e3cdc60d22
|
|||
81853396cd
|
|||
e15a099678
|
|||
718bcd1c4f
|
|||
bc8c4425a6
|
|||
decb0d3921
|
|||
442f536b26
|
|||
ed6776f9df
|
|||
906263cb2b
|
|||
929474ff19
|
|||
ea50514b8f
|
|||
617c46950e
|
|||
f5b1372693
|
|||
16a2728ac1
|
|||
cb9ae35260
|
|||
94be2a823f
|
|||
175659bcdc
|
|||
e13d5ff178
|
|||
f2ceaf176c
|
|||
88dcf52a57
|
|||
b1f44ff2db
|
|||
f451a4b868
|
|||
e7ed8f44de
|
|||
82c6da8ae6
|
|||
5e1661cff3
|
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
build
|
build
|
||||||
.gradle
|
.gradle
|
||||||
forge/run
|
*/run
|
||||||
.idea
|
.idea
|
||||||
|
4
Makefile
@@ -1,6 +1,6 @@
|
|||||||
common/src/main/resources/data/quaedam/projected-person-names:
|
common/src/main/resources/data/quaedam/projected-person-names:
|
||||||
curl -Ls https://github.com/wainshine/Chinese-Names-Corpus/raw/master/English_Names_Corpus/English_Names_Corpus%EF%BC%882W%EF%BC%89.txt | tail -n +4 | shuf | head -n 2500 > $@.tmp
|
curl -Ls https://github.com/wainshine/Chinese-Names-Corpus/raw/master/English_Names_Corpus/English_Names_Corpus%EF%BC%882W%EF%BC%89.txt | tail -n +4 | shuf | head -n 5000 > $@.tmp
|
||||||
curl -Ls https://github.com/wainshine/Chinese-Names-Corpus/raw/master/Chinese_Names_Corpus/Chinese_Names_Corpus%EF%BC%88120W%EF%BC%89.txt | tail -n +4 | shuf | head -n 2500 >> $@.tmp
|
#curl -Ls https://github.com/wainshine/Chinese-Names-Corpus/raw/master/Chinese_Names_Corpus/Chinese_Names_Corpus%EF%BC%88120W%EF%BC%89.txt | tail -n +4 | shuf | head -n 2500 >> $@.tmp
|
||||||
cat $@.tmp | tr -d '\015' > $@
|
cat $@.tmp | tr -d '\015' > $@
|
||||||
rm $@.tmp
|
rm $@.tmp
|
||||||
|
|
||||||
|
@@ -2,11 +2,12 @@ import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
kotlin("jvm") version "1.8.22"
|
kotlin("jvm") version "1.9.0"
|
||||||
|
kotlin("plugin.serialization") version "1.9.0"
|
||||||
id("architectury-plugin") version "3.4-SNAPSHOT"
|
id("architectury-plugin") version "3.4-SNAPSHOT"
|
||||||
id("dev.architectury.loom") version "1.2-SNAPSHOT" apply false
|
id("dev.architectury.loom") version "1.3-SNAPSHOT" apply false
|
||||||
id("com.github.johnrengelman.shadow") version "8.1.1" apply false
|
id("com.github.johnrengelman.shadow") version "8.1.1" apply false
|
||||||
id("io.github.juuxel.loom-quiltflower") version ("1.10.0") apply false
|
id("io.github.juuxel.loom-vineflower") version "1.11.0" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
architectury {
|
architectury {
|
||||||
@@ -15,19 +16,23 @@ architectury {
|
|||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
apply(plugin = "dev.architectury.loom")
|
apply(plugin = "dev.architectury.loom")
|
||||||
apply(plugin = "io.github.juuxel.loom-quiltflower")
|
apply(plugin = "io.github.juuxel.loom-vineflower")
|
||||||
|
|
||||||
val loom = project.extensions.getByName<LoomGradleExtensionAPI>("loom")
|
val loom = project.extensions.getByName<LoomGradleExtensionAPI>("loom")
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
"minecraft"("com.mojang:minecraft:${project.property("minecraft_version")}")
|
"minecraft"("com.mojang:minecraft:${project.property("minecraft_version")}")
|
||||||
"mappings"(loom.officialMojangMappings())
|
"mappings"(loom.layered {
|
||||||
|
officialMojangMappings()
|
||||||
|
parchment("org.parchmentmc.data:parchment-${project.property("minecraft_version")}:${project.property("parchment_version")}@zip")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
apply(plugin = "java")
|
apply(plugin = "java")
|
||||||
apply(plugin = "kotlin")
|
apply(plugin = "kotlin")
|
||||||
|
apply(plugin = "kotlinx-serialization")
|
||||||
apply(plugin = "architectury-plugin")
|
apply(plugin = "architectury-plugin")
|
||||||
apply(plugin = "maven-publish")
|
apply(plugin = "maven-publish")
|
||||||
|
|
||||||
@@ -35,8 +40,20 @@ allprojects {
|
|||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
group = "quaedam"
|
group = "quaedam"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = "ParchmentMC"
|
||||||
|
setUrl("https://maven.parchmentmc.org")
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
name = "QuiltMC"
|
||||||
|
setUrl("https://maven.quiltmc.org/repository/release/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
||||||
|
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<JavaCompile> {
|
tasks.withType<JavaCompile> {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
architectury {
|
architectury {
|
||||||
common("forge")
|
common("forge", "fabric", "quilt")
|
||||||
}
|
}
|
||||||
|
|
||||||
loom {
|
loom {
|
||||||
|
30
common/src/main/java/quaedam/mixin/MixinBedBlock.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package quaedam.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BedBlock;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
import quaedam.projection.swarm.ProjectedPersonEntity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mixin(BedBlock.class)
|
||||||
|
public class MixinBedBlock {
|
||||||
|
|
||||||
|
@Inject(at = @At("RETURN"), method = "kickVillagerOutOfBed(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)Z", cancellable = true)
|
||||||
|
private void kickVillagerOutOfBed(Level level, BlockPos blockPos, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
if (!cir.getReturnValueZ()) {
|
||||||
|
List<ProjectedPersonEntity> list = level.getEntitiesOfClass(ProjectedPersonEntity.class, new AABB(blockPos), LivingEntity::isSleeping);
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
list.get(0).stopSleeping();
|
||||||
|
cir.setReturnValue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -8,8 +8,8 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
import quaedam.projection.SkylightProjection;
|
import quaedam.projection.misc.SkylightProjection;
|
||||||
import quaedam.projection.SkylightProjectionEffect;
|
import quaedam.projection.misc.SkylightProjectionEffect;
|
||||||
import quaedam.projector.Projector;
|
import quaedam.projector.Projector;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
24
common/src/main/java/quaedam/mixin/MixinMinecraftServer.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package quaedam.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.core.GlobalPos;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import quaedam.mixininterface.ProjectionShellMutexAccessor;
|
||||||
|
import quaedam.shell.ProjectionShellMutex;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
@Mixin(MinecraftServer.class)
|
||||||
|
public class MixinMinecraftServer implements ProjectionShellMutexAccessor {
|
||||||
|
|
||||||
|
private LinkedHashMap<GlobalPos, ProjectionShellMutex.Lock> quaedam$projectionShellMutex;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedHashMap<GlobalPos, ProjectionShellMutex.Lock> quaedam$getProjectionShellMutex() {
|
||||||
|
if (quaedam$projectionShellMutex == null) {
|
||||||
|
quaedam$projectionShellMutex = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
return quaedam$projectionShellMutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
common/src/main/java/quaedam/mixin/MixinPlayerList.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package quaedam.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.network.Connection;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.players.PlayerList;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import quaedam.config.SimpleQuaedamConfigPush;
|
||||||
|
|
||||||
|
@Mixin(PlayerList.class)
|
||||||
|
public class MixinPlayerList {
|
||||||
|
|
||||||
|
@Inject(at = @At("RETURN"), method = "placeNewPlayer(Lnet/minecraft/network/Connection;Lnet/minecraft/server/level/ServerPlayer;)V")
|
||||||
|
public void placeNewPlayer(Connection netManager, ServerPlayer player, CallbackInfo ci) {
|
||||||
|
SimpleQuaedamConfigPush.INSTANCE.sendCurrent(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
package quaedam.mixininterface;
|
||||||
|
|
||||||
|
import net.minecraft.core.GlobalPos;
|
||||||
|
import quaedam.shell.ProjectionShellMutex;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
public interface ProjectionShellMutexAccessor {
|
||||||
|
|
||||||
|
LinkedHashMap<GlobalPos, ProjectionShellMutex.Lock> quaedam$getProjectionShellMutex();
|
||||||
|
|
||||||
|
}
|
@@ -5,14 +5,25 @@ import dev.architectury.registry.registries.DeferredRegister
|
|||||||
import dev.architectury.registry.registries.RegistrySupplier
|
import dev.architectury.registry.registries.RegistrySupplier
|
||||||
import net.minecraft.core.registries.Registries
|
import net.minecraft.core.registries.Registries
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.minecraft.world.item.CreativeModeTab
|
import net.minecraft.world.item.CreativeModeTab
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.item.Items
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import quaedam.config.QuaedamConfig
|
||||||
|
import quaedam.misc.CraftingMaterials
|
||||||
|
import quaedam.misc.causality.CausalityAnchor
|
||||||
|
import quaedam.misc.reality.RealityStabler
|
||||||
|
import quaedam.projection.ProjectionCommand
|
||||||
import quaedam.projection.ProjectionEffectType
|
import quaedam.projection.ProjectionEffectType
|
||||||
import quaedam.projection.SkylightProjection
|
import quaedam.projection.SimpleProjectionUpdate
|
||||||
|
import quaedam.projection.misc.NoiseProjection
|
||||||
|
import quaedam.projection.misc.SkylightProjection
|
||||||
|
import quaedam.projection.misc.SoundProjection
|
||||||
|
import quaedam.projection.music.MusicProjection
|
||||||
|
import quaedam.projection.swarm.ProjectedPersonEntity
|
||||||
import quaedam.projection.swarm.SwarmProjection
|
import quaedam.projection.swarm.SwarmProjection
|
||||||
import quaedam.projector.Projector
|
import quaedam.projector.Projector
|
||||||
|
import quaedam.shell.ProjectionShell
|
||||||
|
|
||||||
object Quaedam {
|
object Quaedam {
|
||||||
|
|
||||||
@@ -25,28 +36,53 @@ object Quaedam {
|
|||||||
val blocks = DeferredRegister.create(ID, Registries.BLOCK)!!
|
val blocks = DeferredRegister.create(ID, Registries.BLOCK)!!
|
||||||
val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!!
|
val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!!
|
||||||
val entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!!
|
val entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!!
|
||||||
val schedule = DeferredRegister.create(ID, Registries.SCHEDULE)!!
|
val schedules = DeferredRegister.create(ID, Registries.SCHEDULE)!!
|
||||||
val projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!!
|
val memoryTypes = DeferredRegister.create(ID, Registries.MEMORY_MODULE_TYPE)!!
|
||||||
|
val sensors = DeferredRegister.create(ID, Registries.SENSOR_TYPE)!!
|
||||||
|
val soundEvents = DeferredRegister.create(ID, Registries.SOUND_EVENT)!!
|
||||||
|
val poiTypes = DeferredRegister.create(ID, Registries.POINT_OF_INTEREST_TYPE)!!
|
||||||
|
val projectionEffects by lazy { DeferredRegister.create(ID, ProjectionEffectType.registryKey)!! }
|
||||||
|
|
||||||
val creativeModeTab: RegistrySupplier<CreativeModeTab> = creativeModeTabs.register("quaedam") {
|
val creativeModeTab: RegistrySupplier<CreativeModeTab> = creativeModeTabs.register("quaedam") {
|
||||||
CreativeTabRegistry.create(Component.translatable("category.quaedam")) {
|
CreativeTabRegistry.create(Component.translatable("category.quaedam")) {
|
||||||
ItemStack(Items.TORCH)
|
ItemStack(Projector.item.get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val lateinit = mutableListOf<() -> Unit>()
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
|
QuaedamConfig
|
||||||
Projector
|
Projector
|
||||||
ProjectionEffectType
|
ProjectionEffectType
|
||||||
SkylightProjection
|
SkylightProjection
|
||||||
SwarmProjection
|
SwarmProjection
|
||||||
|
SoundProjection
|
||||||
|
NoiseProjection
|
||||||
|
MusicProjection
|
||||||
|
ProjectionCommand
|
||||||
|
SimpleProjectionUpdate
|
||||||
|
ProjectionShell
|
||||||
|
CausalityAnchor
|
||||||
|
RealityStabler
|
||||||
|
CraftingMaterials
|
||||||
|
|
||||||
creativeModeTabs.register()
|
creativeModeTabs.register()
|
||||||
items.register()
|
items.register()
|
||||||
blocks.register()
|
blocks.register()
|
||||||
blockEntities.register()
|
blockEntities.register()
|
||||||
entities.register()
|
entities.register()
|
||||||
schedule.register()
|
schedules.register()
|
||||||
|
memoryTypes.register()
|
||||||
|
sensors.register()
|
||||||
|
soundEvents.register()
|
||||||
|
poiTypes.register()
|
||||||
projectionEffects.register()
|
projectionEffects.register()
|
||||||
|
|
||||||
|
lateinit.forEach { it() }
|
||||||
|
lateinit.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resource(path: String) = ResourceLocation(ID, path)
|
||||||
|
|
||||||
}
|
}
|
105
common/src/main/kotlin/quaedam/config/QuaedamConfig.kt
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package quaedam.config
|
||||||
|
|
||||||
|
import dev.architectury.event.events.client.ClientPlayerEvent
|
||||||
|
import dev.architectury.platform.Platform
|
||||||
|
import dev.architectury.utils.GameInstance
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import net.fabricmc.api.EnvType
|
||||||
|
import net.minecraft.nbt.*
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
import kotlin.io.path.notExists
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class QuaedamConfig(
|
||||||
|
val valuesInt: Map<String, Int> = mapOf(),
|
||||||
|
val valuesFloat: Map<String, Float> = mapOf(),
|
||||||
|
val valuesDouble: Map<String, Double> = mapOf(),
|
||||||
|
val valuesBoolean: Map<String, Boolean> = mapOf(),
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val localJson = Json {
|
||||||
|
isLenient = true
|
||||||
|
prettyPrint = true
|
||||||
|
encodeDefaults = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val pushJson = Json {
|
||||||
|
encodeDefaults = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val localFile: Path = Platform.getConfigFolder().resolve("quaedam.json")
|
||||||
|
private var local0 = loadLocalConfig()
|
||||||
|
var local
|
||||||
|
get() = local0
|
||||||
|
set(value) {
|
||||||
|
local0 = value
|
||||||
|
writeLocalConfig()
|
||||||
|
}
|
||||||
|
private var remote: QuaedamConfig? = null
|
||||||
|
val current get() = remote ?: local0
|
||||||
|
|
||||||
|
init {
|
||||||
|
SimpleQuaedamConfigPush
|
||||||
|
|
||||||
|
if (Platform.getEnv() == EnvType.CLIENT) {
|
||||||
|
ClientPlayerEvent.CLIENT_PLAYER_QUIT.register { player ->
|
||||||
|
if (player == GameInstance.getClient().player) {
|
||||||
|
applyRemoteConfig(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (localFile.notExists()) {
|
||||||
|
writeLocalConfig()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadLocalConfig(): QuaedamConfig = if (localFile.exists()) {
|
||||||
|
localJson.decodeFromString(localFile.readText())
|
||||||
|
} else {
|
||||||
|
QuaedamConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeLocalConfig() {
|
||||||
|
localFile.writeText(localJson.encodeToString(local0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyRemoteConfig(config: QuaedamConfig?) {
|
||||||
|
Quaedam.logger.info("Received remote config push: $config")
|
||||||
|
remote = config
|
||||||
|
}
|
||||||
|
|
||||||
|
const val TAG_VALUES_INT = "ValuesInt"
|
||||||
|
const val TAG_VALUES_FLOAT = "ValuesFloat"
|
||||||
|
const val TAG_VALUES_DOUBLE = "ValuesDouble"
|
||||||
|
const val TAG_VALUES_BOOLEAN = "ValuesBoolean"
|
||||||
|
|
||||||
|
fun fromPushNbt(tag: CompoundTag): QuaedamConfig {
|
||||||
|
return QuaedamConfig(
|
||||||
|
valuesInt = pushJson.decodeFromString(tag.getString(TAG_VALUES_INT)),
|
||||||
|
valuesFloat = pushJson.decodeFromString(tag.getString(TAG_VALUES_FLOAT)),
|
||||||
|
valuesDouble = pushJson.decodeFromString(tag.getString(TAG_VALUES_DOUBLE)),
|
||||||
|
valuesBoolean = pushJson.decodeFromString(tag.getString(TAG_VALUES_BOOLEAN)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toPushNbt(tag: CompoundTag) {
|
||||||
|
tag.putString(TAG_VALUES_INT, pushJson.encodeToString(valuesInt))
|
||||||
|
tag.putString(TAG_VALUES_FLOAT, pushJson.encodeToString(valuesFloat))
|
||||||
|
tag.putString(TAG_VALUES_DOUBLE, pushJson.encodeToString(valuesDouble))
|
||||||
|
tag.putString(TAG_VALUES_BOOLEAN, pushJson.encodeToString(valuesBoolean))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toPushNbt() = CompoundTag().also { toPushNbt(it) }
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
package quaedam.config
|
||||||
|
|
||||||
|
import dev.architectury.networking.NetworkManager
|
||||||
|
import dev.architectury.platform.Platform
|
||||||
|
import io.netty.buffer.Unpooled
|
||||||
|
import net.fabricmc.api.EnvType
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import quaedam.Quaedam
|
||||||
|
|
||||||
|
object SimpleQuaedamConfigPush {
|
||||||
|
|
||||||
|
val id = Quaedam.resource("simple_config_push")
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (Platform.getEnv() == EnvType.CLIENT) {
|
||||||
|
NetworkManager.registerReceiver(NetworkManager.Side.S2C, id, ::handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handle(buf: FriendlyByteBuf, ctx: NetworkManager.PacketContext) {
|
||||||
|
val data = buf.readNbt()!!
|
||||||
|
val config = QuaedamConfig.fromPushNbt(data)
|
||||||
|
QuaedamConfig.applyRemoteConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendCurrent(player: ServerPlayer) = send(player, QuaedamConfig.current)
|
||||||
|
|
||||||
|
fun send(player: ServerPlayer, config: QuaedamConfig) = send(player, config.toPushNbt())
|
||||||
|
|
||||||
|
private fun send(player: ServerPlayer, data: CompoundTag) {
|
||||||
|
val buf = FriendlyByteBuf(Unpooled.buffer())
|
||||||
|
buf.writeNbt(data)
|
||||||
|
NetworkManager.sendToPlayer(player, id, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
common/src/main/kotlin/quaedam/misc/CraftingMaterials.kt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package quaedam.misc
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import quaedam.Quaedam
|
||||||
|
|
||||||
|
object CraftingMaterials {
|
||||||
|
|
||||||
|
val ironCopperMetal = Quaedam.items.register("iron_copper_metal") {
|
||||||
|
Item(Item.Properties().`arch$tab`(Quaedam.creativeModeTab))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val projectionMetal = Quaedam.items.register("projection_metal") {
|
||||||
|
Item(Item.Properties().`arch$tab`(Quaedam.creativeModeTab))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
}
|
109
common/src/main/kotlin/quaedam/misc/causality/CABlock.kt
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package quaedam.misc.causality
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext
|
||||||
|
import net.minecraft.world.level.BlockGetter
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.LevelAccessor
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.EntityBlock
|
||||||
|
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||||
|
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||||
|
import net.minecraft.world.level.material.FluidState
|
||||||
|
import net.minecraft.world.level.material.Fluids
|
||||||
|
import net.minecraft.world.level.material.MapColor
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape
|
||||||
|
|
||||||
|
object CABlock : HorizontalDirectionalBlock(
|
||||||
|
Properties.of()
|
||||||
|
.noOcclusion()
|
||||||
|
.strength(2f)
|
||||||
|
.requiresCorrectToolForDrops()
|
||||||
|
.mapColor(MapColor.COLOR_CYAN)
|
||||||
|
), EntityBlock, SimpleWaterloggedBlock {
|
||||||
|
|
||||||
|
val shapes = getShapeForEachState(::createVoxelShape)
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerDefaultState(
|
||||||
|
defaultBlockState()
|
||||||
|
.setValue(FACING, Direction.EAST)
|
||||||
|
.setValue(BlockStateProperties.WATERLOGGED, false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newBlockEntity(pos: BlockPos, state: BlockState) = CABlockEntity(pos, state)
|
||||||
|
|
||||||
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||||
|
super.createBlockStateDefinition(builder)
|
||||||
|
builder.add(FACING, BlockStateProperties.WATERLOGGED)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||||
|
if (!context.level.getBlockState(context.clickedPos.below()).canOcclude()) return null
|
||||||
|
return super.defaultBlockState().setValue(FACING, context.horizontalDirection)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION")
|
||||||
|
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext) =
|
||||||
|
shapes[state]!!
|
||||||
|
|
||||||
|
private fun createVoxelShape(state: BlockState): VoxelShape =
|
||||||
|
when (state.getValue(FACING)) {
|
||||||
|
Direction.WEST, Direction.EAST -> Shapes.or(
|
||||||
|
box(0.0, 0.0, 0.0, 16.0, 12.0, 16.0),
|
||||||
|
box(7.0, 14.0, 6.0, 9.0, 16.0, 10.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
Direction.SOUTH, Direction.NORTH -> Shapes.or(
|
||||||
|
box(0.0, 0.0, 0.0, 16.0, 12.0, 16.0),
|
||||||
|
box(6.0, 14.0, 7.0, 10.0, 16.0, 9.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> throw IllegalStateException(state.getValue(FACING).name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun updateShape(
|
||||||
|
state: BlockState,
|
||||||
|
direction: Direction,
|
||||||
|
neighborState: BlockState,
|
||||||
|
level: LevelAccessor,
|
||||||
|
pos: BlockPos,
|
||||||
|
neighborPos: BlockPos
|
||||||
|
): BlockState {
|
||||||
|
if (state.getValue(BlockStateProperties.WATERLOGGED)) {
|
||||||
|
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level))
|
||||||
|
}
|
||||||
|
return super.updateShape(state, direction, neighborState, level, pos, neighborPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun getFluidState(state: BlockState): FluidState = if (state.getValue(BlockStateProperties.WATERLOGGED)) {
|
||||||
|
Fluids.WATER.getSource(false)
|
||||||
|
} else {
|
||||||
|
super.getFluidState(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun neighborChanged(
|
||||||
|
state: BlockState,
|
||||||
|
level: Level,
|
||||||
|
pos: BlockPos,
|
||||||
|
neighborBlock: Block,
|
||||||
|
neighborPos: BlockPos,
|
||||||
|
movedByPiston: Boolean
|
||||||
|
) {
|
||||||
|
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston)
|
||||||
|
if (!level.getBlockState(pos.below()).canOcclude()) {
|
||||||
|
level.destroyBlock(pos, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
package quaedam.misc.causality
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.network.protocol.Packet
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
|
||||||
|
class CABlockEntity(pos: BlockPos, state: BlockState) :
|
||||||
|
BlockEntity(CausalityAnchor.blockEntity.get(), pos, state) {
|
||||||
|
|
||||||
|
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
||||||
|
|
||||||
|
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,33 @@
|
|||||||
|
package quaedam.misc.causality
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.world.item.BlockItem
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||||
|
import quaedam.Quaedam
|
||||||
|
|
||||||
|
object CausalityAnchor {
|
||||||
|
|
||||||
|
const val ID = "causality_anchor"
|
||||||
|
|
||||||
|
val block = Quaedam.blocks.register(ID) { CABlock }!!
|
||||||
|
|
||||||
|
val item = Quaedam.items.register(ID) {
|
||||||
|
BlockItem(
|
||||||
|
CABlock, Item.Properties()
|
||||||
|
.stacksTo(1)
|
||||||
|
.`arch$tab`(Quaedam.creativeModeTab)
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val blockEntity = Quaedam.blockEntities.register(ID) {
|
||||||
|
BlockEntityType.Builder.of(::CABlockEntity, block.get()).build(null)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
fun checkEffect(level: Level, pos: BlockPos) = level.getChunkAt(pos)
|
||||||
|
.blockEntities
|
||||||
|
.any { (k, v) -> v is CABlockEntity && !level.getBlockState(k).getValue(BlockStateProperties.WATERLOGGED) }
|
||||||
|
|
||||||
|
}
|
96
common/src/main/kotlin/quaedam/misc/reality/RSBlock.kt
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package quaedam.misc.reality
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext
|
||||||
|
import net.minecraft.world.level.BlockGetter
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.LevelAccessor
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.EntityBlock
|
||||||
|
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||||
|
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||||
|
import net.minecraft.world.level.material.FluidState
|
||||||
|
import net.minecraft.world.level.material.Fluids
|
||||||
|
import net.minecraft.world.level.material.MapColor
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes
|
||||||
|
|
||||||
|
object RSBlock : HorizontalDirectionalBlock(
|
||||||
|
Properties.of()
|
||||||
|
.noOcclusion()
|
||||||
|
.strength(3f)
|
||||||
|
.requiresCorrectToolForDrops()
|
||||||
|
.mapColor(MapColor.COLOR_BLUE)
|
||||||
|
), EntityBlock, SimpleWaterloggedBlock {
|
||||||
|
|
||||||
|
val shape = Shapes.or(
|
||||||
|
box(1.0, 0.0, 1.0, 15.0, 1.0, 15.0),
|
||||||
|
box(0.0, 1.0, 0.0, 16.0, 14.0, 16.0),
|
||||||
|
box(1.0, 14.0, 1.0, 15.0, 15.0, 15.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerDefaultState(
|
||||||
|
defaultBlockState()
|
||||||
|
.setValue(FACING, Direction.EAST)
|
||||||
|
.setValue(BlockStateProperties.WATERLOGGED, false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newBlockEntity(pos: BlockPos, state: BlockState) = RSBlockEntity(pos, state)
|
||||||
|
|
||||||
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||||
|
super.createBlockStateDefinition(builder)
|
||||||
|
builder.add(FACING, BlockStateProperties.WATERLOGGED)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||||
|
if (!context.level.getBlockState(context.clickedPos.below()).canOcclude()) return null
|
||||||
|
return super.defaultBlockState().setValue(FACING, context.horizontalDirection)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext) = shape
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun updateShape(
|
||||||
|
state: BlockState,
|
||||||
|
direction: Direction,
|
||||||
|
neighborState: BlockState,
|
||||||
|
level: LevelAccessor,
|
||||||
|
pos: BlockPos,
|
||||||
|
neighborPos: BlockPos
|
||||||
|
): BlockState {
|
||||||
|
if (state.getValue(BlockStateProperties.WATERLOGGED)) {
|
||||||
|
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level))
|
||||||
|
}
|
||||||
|
return super.updateShape(state, direction, neighborState, level, pos, neighborPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun getFluidState(state: BlockState): FluidState = if (state.getValue(BlockStateProperties.WATERLOGGED)) {
|
||||||
|
Fluids.WATER.getSource(false)
|
||||||
|
} else {
|
||||||
|
super.getFluidState(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun neighborChanged(
|
||||||
|
state: BlockState,
|
||||||
|
level: Level,
|
||||||
|
pos: BlockPos,
|
||||||
|
neighborBlock: Block,
|
||||||
|
neighborPos: BlockPos,
|
||||||
|
movedByPiston: Boolean
|
||||||
|
) {
|
||||||
|
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston)
|
||||||
|
if (!level.getBlockState(pos.below()).canOcclude()) {
|
||||||
|
level.destroyBlock(pos, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
common/src/main/kotlin/quaedam/misc/reality/RSBlockEntity.kt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package quaedam.misc.reality
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.network.protocol.Packet
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
|
||||||
|
class RSBlockEntity(pos: BlockPos, state: BlockState) :
|
||||||
|
BlockEntity(RealityStabler.blockEntity.get(), pos, state) {
|
||||||
|
|
||||||
|
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
||||||
|
|
||||||
|
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
package quaedam.misc.reality
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.world.item.BlockItem
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
|
import quaedam.Quaedam
|
||||||
|
|
||||||
|
object RealityStabler {
|
||||||
|
|
||||||
|
const val ID = "reality_stabler"
|
||||||
|
|
||||||
|
val block = Quaedam.blocks.register(ID) { RSBlock }!!
|
||||||
|
|
||||||
|
val item = Quaedam.items.register(ID) {
|
||||||
|
BlockItem(
|
||||||
|
RSBlock, Item.Properties()
|
||||||
|
.stacksTo(1)
|
||||||
|
.`arch$tab`(Quaedam.creativeModeTab)
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val blockEntity = Quaedam.blockEntities.register(ID) {
|
||||||
|
BlockEntityType.Builder.of(::RSBlockEntity, block.get()).build(null)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
fun checkEffect(level: Level, pos: BlockPos) = level.getChunkAt(pos)
|
||||||
|
.blockEntities
|
||||||
|
.any { (_, v) -> v is RSBlockEntity }
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,52 @@
|
|||||||
|
package quaedam.projection
|
||||||
|
|
||||||
|
import dev.architectury.registry.registries.DeferredSupplier
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.EntityBlock
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import quaedam.shell.ProjectionEffectShell
|
||||||
|
import quaedam.shell.ProjectionShellBlock
|
||||||
|
import quaedam.utils.sendBlockUpdated
|
||||||
|
|
||||||
|
abstract class EntityProjectionBlock<P : ProjectionEffect>(properties: Properties = createProperties()) :
|
||||||
|
ProjectionBlock<P>(properties), EntityBlock, ProjectionShellBlock {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createProperties(): Properties = ProjectionBlock.createProperties()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract val blockEntity: DeferredSupplier<BlockEntityType<SimpleProjectionEntity<P>>>
|
||||||
|
|
||||||
|
override fun newBlockEntity(pos: BlockPos, state: BlockState) = blockEntity.get().create(pos, state)!!
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun getBlockEntity(level: Level, pos: BlockPos) = (level.getBlockEntity(pos) as SimpleProjectionEntity<P>)
|
||||||
|
|
||||||
|
override fun applyProjectionEffect(level: ServerLevel, state: BlockState, pos: BlockPos) =
|
||||||
|
getBlockEntity(level, pos).cloneProjection()
|
||||||
|
|
||||||
|
fun applyChange(level: Level, pos: BlockPos, func: P.() -> Unit) {
|
||||||
|
val entity = getBlockEntity(level, pos)
|
||||||
|
val projection = entity.projection
|
||||||
|
projection.apply(func)
|
||||||
|
if (level.isClientSide) {
|
||||||
|
check(level == Minecraft.getInstance().player!!.level())
|
||||||
|
SimpleProjectionUpdate.send(pos, projection.toNbt())
|
||||||
|
} else {
|
||||||
|
getBlockEntity(level, pos).sendBlockUpdated()
|
||||||
|
sendUpdateToProjectors(level, pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getProjectionEffectForShell(level: Level, pos: BlockPos) =
|
||||||
|
(getBlockEntity(level, pos).cloneProjection() as ProjectionEffectShell.Provider).createShell()
|
||||||
|
|
||||||
|
override fun applyFromShell(level: Level, pos: BlockPos, shell: ProjectionEffectShell) = applyChange(level, pos) {
|
||||||
|
fromNbt(shell.effect.toNbt())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -29,6 +29,13 @@ abstract class ProjectionBlock<P : ProjectionEffect>(properties: Properties = cr
|
|||||||
}
|
}
|
||||||
.toSet()
|
.toSet()
|
||||||
|
|
||||||
|
fun sendUpdateToProjectors(level: Level, pos: BlockPos) {
|
||||||
|
if (!level.isClientSide) {
|
||||||
|
findNearbyProjectors(level, pos)
|
||||||
|
.forEach { (level.getBlockEntity(it) as ProjectorBlockEntity).checkUpdate() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("OVERRIDE_DEPRECATION")
|
@Suppress("OVERRIDE_DEPRECATION")
|
||||||
@@ -42,17 +49,13 @@ abstract class ProjectionBlock<P : ProjectionEffect>(properties: Properties = cr
|
|||||||
itemStack: ItemStack
|
itemStack: ItemStack
|
||||||
) {
|
) {
|
||||||
super.setPlacedBy(level, pos, state, placer, itemStack)
|
super.setPlacedBy(level, pos, state, placer, itemStack)
|
||||||
if (!level.isClientSide) {
|
sendUpdateToProjectors(level, pos)
|
||||||
findNearbyProjectors(level, pos)
|
|
||||||
.forEach { (level.getBlockEntity(it) as ProjectorBlockEntity).checkUpdate() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destroy(level: LevelAccessor, pos: BlockPos, state: BlockState) {
|
override fun destroy(level: LevelAccessor, pos: BlockPos, state: BlockState) {
|
||||||
super.destroy(level, pos, state)
|
super.destroy(level, pos, state)
|
||||||
if (level is Level && !level.isClientSide) {
|
if (level is Level) {
|
||||||
findNearbyProjectors(level, pos)
|
sendUpdateToProjectors(level, pos)
|
||||||
.forEach { (level.getBlockEntity(it) as ProjectorBlockEntity).checkUpdate() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,70 @@
|
|||||||
|
package quaedam.projection
|
||||||
|
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder.literal
|
||||||
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder.argument
|
||||||
|
import com.mojang.brigadier.context.CommandContext
|
||||||
|
import dev.architectury.event.events.common.CommandRegistrationEvent
|
||||||
|
import net.minecraft.commands.CommandSourceStack
|
||||||
|
import net.minecraft.commands.arguments.ResourceArgument.resource
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Holder
|
||||||
|
import net.minecraft.nbt.ListTag
|
||||||
|
import net.minecraft.nbt.NbtUtils
|
||||||
|
import quaedam.projector.Projector
|
||||||
|
|
||||||
|
object ProjectionCommand {
|
||||||
|
|
||||||
|
init {
|
||||||
|
CommandRegistrationEvent.EVENT.register { dispatcher, ctx, _ ->
|
||||||
|
dispatcher.register(
|
||||||
|
literal<CommandSourceStack>("quaedam_projection")
|
||||||
|
.then(
|
||||||
|
literal<CommandSourceStack>("dump")
|
||||||
|
.requires { it.hasPermission(2) }
|
||||||
|
.executes(::dump)
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
literal<CommandSourceStack>("get")
|
||||||
|
.requires { it.hasPermission(2) }
|
||||||
|
.then(
|
||||||
|
argument<CommandSourceStack, Holder.Reference<ProjectionEffectType<*>>>(
|
||||||
|
"type",
|
||||||
|
resource(ctx, ProjectionEffectType.registryKey)
|
||||||
|
)
|
||||||
|
.executes(::get)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dump(ctx: CommandContext<CommandSourceStack>): Int {
|
||||||
|
val pos = BlockPos(
|
||||||
|
ctx.source.position.x.toInt(),
|
||||||
|
ctx.source.position.y.toInt(),
|
||||||
|
ctx.source.position.z.toInt()
|
||||||
|
)
|
||||||
|
val data = Projector.findNearbyProjectors(ctx.source.level, pos)
|
||||||
|
.map { ctx.source.level.getBlockEntity(it)!!.saveWithFullMetadata() }
|
||||||
|
val tag = ListTag()
|
||||||
|
tag.addAll(data)
|
||||||
|
ctx.source.sendSystemMessage(NbtUtils.toPrettyComponent(tag))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun get(ctx: CommandContext<CommandSourceStack>): Int {
|
||||||
|
val pos = BlockPos(
|
||||||
|
ctx.source.position.x.toInt(),
|
||||||
|
ctx.source.position.y.toInt(),
|
||||||
|
ctx.source.position.z.toInt()
|
||||||
|
)
|
||||||
|
val type = ctx.getArgument("type", Holder.Reference::class.java).value() as ProjectionEffectType<*>
|
||||||
|
val data = Projector.findNearbyProjections(ctx.source.level, pos, type)
|
||||||
|
.map { it.toNbt() }
|
||||||
|
val tag = ListTag()
|
||||||
|
tag.addAll(data)
|
||||||
|
ctx.source.sendSystemMessage(NbtUtils.toPrettyComponent(tag))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -9,18 +9,19 @@ import net.minecraft.resources.ResourceLocation
|
|||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import quaedam.Quaedam
|
||||||
|
|
||||||
abstract class ProjectionEffect {
|
abstract class ProjectionEffect : Cloneable {
|
||||||
|
|
||||||
abstract val type: ProjectionEffectType<*>
|
abstract val type: ProjectionEffectType<*>
|
||||||
|
|
||||||
abstract fun toNbt(tag: CompoundTag)
|
abstract fun toNbt(tag: CompoundTag)
|
||||||
|
|
||||||
abstract fun fromNbt(tag: CompoundTag)
|
abstract fun fromNbt(tag: CompoundTag, trusted: Boolean = true)
|
||||||
|
|
||||||
fun toNbt() = CompoundTag().apply { toNbt(this) }
|
fun toNbt() = CompoundTag().also { toNbt(it) }
|
||||||
|
|
||||||
override fun equals(other: Any?) = other === this
|
override fun equals(other: Any?): Boolean = other === this
|
||||||
|
|
||||||
override fun hashCode() = type.hashCode()
|
override fun hashCode() = type.hashCode()
|
||||||
|
|
||||||
@@ -37,11 +38,11 @@ data class ProjectionEffectType<T : ProjectionEffect>(val constructor: () -> T)
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val registryKey: ResourceKey<Registry<ProjectionEffectType<*>>> =
|
val registryKey: ResourceKey<Registry<ProjectionEffectType<*>>> =
|
||||||
ResourceKey.createRegistryKey(ResourceLocation("quaedam", "projection_effect"))
|
ResourceKey.createRegistryKey(Quaedam.resource("projection_effect"))
|
||||||
val registry: Registry<ProjectionEffectType<*>> = BuiltInRegistries.registerSimple(registryKey) { null }
|
val registry: Registry<ProjectionEffectType<*>> = BuiltInRegistries.registerSimple(registryKey) { nopEffect }
|
||||||
|
|
||||||
val nopEffect: ProjectionEffectType<NopEffect> =
|
val nopEffect: ProjectionEffectType<NopEffect> =
|
||||||
Registry.register(registry, ResourceLocation("quaedam", "nop"), ProjectionEffectType { NopEffect })
|
Registry.register(registry, Quaedam.resource("nop"), ProjectionEffectType { NopEffect })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,11 +52,11 @@ data class ProjectionEffectType<T : ProjectionEffect>(val constructor: () -> T)
|
|||||||
object NopEffect : ProjectionEffect() {
|
object NopEffect : ProjectionEffect() {
|
||||||
override val type get() = nopEffect
|
override val type get() = nopEffect
|
||||||
override fun toNbt(tag: CompoundTag) {}
|
override fun toNbt(tag: CompoundTag) {}
|
||||||
override fun fromNbt(tag: CompoundTag) {}
|
override fun fromNbt(tag: CompoundTag, trusted: Boolean) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProjectionProvider<P : ProjectionEffect> {
|
interface ProjectionProvider<P : ProjectionEffect> {
|
||||||
fun createProjectionEffect(level: ServerLevel, state: BlockState, pos: BlockPos): P?
|
fun applyProjectionEffect(level: ServerLevel, state: BlockState, pos: BlockPos): P?
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,56 @@
|
|||||||
|
package quaedam.projection
|
||||||
|
|
||||||
|
import dev.architectury.registry.registries.RegistrySupplier
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.network.protocol.Packet
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
|
||||||
|
class SimpleProjectionEntity<P : ProjectionEffect>(
|
||||||
|
type: BlockEntityType<SimpleProjectionEntity<P>>,
|
||||||
|
pos: BlockPos,
|
||||||
|
state: BlockState,
|
||||||
|
var projection: P,
|
||||||
|
val default: () -> P,
|
||||||
|
) : BlockEntity(type, pos, state) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_PROJECTION_EFFECT = "ProjectionEffect"
|
||||||
|
|
||||||
|
fun <P : ProjectionEffect, B : ProjectionBlock<P>> createBlockEntityType(
|
||||||
|
block: RegistrySupplier<B>,
|
||||||
|
default: () -> P,
|
||||||
|
): BlockEntityType<SimpleProjectionEntity<P>> {
|
||||||
|
val type = ValueContainer<BlockEntityType<SimpleProjectionEntity<P>>>()
|
||||||
|
type.inner = BlockEntityType.Builder.of({ pos, state ->
|
||||||
|
SimpleProjectionEntity(type.inner!!, pos, state, default(), default)
|
||||||
|
}, block.get()).build(null)
|
||||||
|
return type.inner!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ValueContainer<E>(var inner: E? = null)
|
||||||
|
|
||||||
|
override fun saveAdditional(tag: CompoundTag) {
|
||||||
|
super.saveAdditional(tag)
|
||||||
|
tag.put(TAG_PROJECTION_EFFECT, projection.toNbt())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun load(tag: CompoundTag) {
|
||||||
|
super.load(tag)
|
||||||
|
if (TAG_PROJECTION_EFFECT in tag) {
|
||||||
|
projection.fromNbt(tag.getCompound(TAG_PROJECTION_EFFECT))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
||||||
|
|
||||||
|
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
|
||||||
|
|
||||||
|
fun cloneProjection() = default().apply { fromNbt(projection.toNbt()) }
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,65 @@
|
|||||||
|
package quaedam.projection
|
||||||
|
|
||||||
|
import dev.architectury.networking.NetworkManager
|
||||||
|
import dev.architectury.networking.NetworkManager.PacketContext
|
||||||
|
import io.netty.buffer.Unpooled
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.utils.sendBlockUpdated
|
||||||
|
|
||||||
|
object SimpleProjectionUpdate {
|
||||||
|
|
||||||
|
val id = Quaedam.resource("simple_projection_update")
|
||||||
|
|
||||||
|
init {
|
||||||
|
NetworkManager.registerReceiver(NetworkManager.Side.C2S, id, ::handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handle(buf: FriendlyByteBuf, ctx: PacketContext) = runCatching {
|
||||||
|
val player = ctx.player!! as ServerPlayer
|
||||||
|
val level = player.level()
|
||||||
|
|
||||||
|
val pos = buf.readBlockPos()
|
||||||
|
val data = buf.readNbt()!!
|
||||||
|
|
||||||
|
if (player.blockPosition().distSqr(pos) > 10 * 10) {
|
||||||
|
Quaedam.logger.info("Player ${player.name} tried to update a projection block far away")
|
||||||
|
if (player.blockPosition().distSqr(pos) > 50 * 50) {
|
||||||
|
player.connection.disconnect(Component.literal("[Quaedam] wth r u doing? why not waiting for server?"))
|
||||||
|
}
|
||||||
|
return@runCatching
|
||||||
|
}
|
||||||
|
|
||||||
|
level.server!!.execute {
|
||||||
|
val entity = level.getBlockEntity(pos) ?: return@execute
|
||||||
|
val blockEntity = entity as SimpleProjectionEntity<*>
|
||||||
|
try {
|
||||||
|
blockEntity.projection.fromNbt(data, trusted = false)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Quaedam.logger.error(
|
||||||
|
"Player ${player.name} tried to update projection " +
|
||||||
|
"at $pos but caused error: $data", e
|
||||||
|
)
|
||||||
|
player.connection.disconnect(Component.literal("[Quaedam] ? wait what did you send to the server?"))
|
||||||
|
return@execute
|
||||||
|
}
|
||||||
|
entity.setChanged()
|
||||||
|
blockEntity.sendBlockUpdated()
|
||||||
|
ProjectionBlock.sendUpdateToProjectors(level, pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onFailure { Quaedam.logger.error("Error handling simple projection update packet", it) }
|
||||||
|
.getOrThrow()
|
||||||
|
|
||||||
|
fun send(pos: BlockPos, data: CompoundTag) {
|
||||||
|
val buf = FriendlyByteBuf(Unpooled.buffer())
|
||||||
|
buf.writeBlockPos(pos)
|
||||||
|
buf.writeNbt(data)
|
||||||
|
NetworkManager.sendToServer(id, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,58 +0,0 @@
|
|||||||
package quaedam.projection
|
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
|
||||||
import net.minecraft.nbt.CompoundTag
|
|
||||||
import net.minecraft.server.level.ServerLevel
|
|
||||||
import net.minecraft.world.item.BlockItem
|
|
||||||
import net.minecraft.world.item.Item
|
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
|
||||||
import quaedam.Quaedam
|
|
||||||
|
|
||||||
object SkylightProjection {
|
|
||||||
|
|
||||||
const val ID = "skylight_projection"
|
|
||||||
const val SHORT_ID = "skylight"
|
|
||||||
|
|
||||||
val block = Quaedam.blocks.register(ID) { SkylightProjectionBlock }!!
|
|
||||||
|
|
||||||
val item = Quaedam.items.register(ID) {
|
|
||||||
BlockItem(
|
|
||||||
SkylightProjectionBlock, Item.Properties()
|
|
||||||
.`arch$tab`(Quaedam.creativeModeTab)
|
|
||||||
)
|
|
||||||
}!!
|
|
||||||
|
|
||||||
val effect = Quaedam.projectionEffects.register(SHORT_ID) {
|
|
||||||
ProjectionEffectType { SkylightProjectionEffect() }
|
|
||||||
}!!
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object SkylightProjectionBlock : ProjectionBlock<SkylightProjectionEffect>(createProperties().lightLevel { 3 }) {
|
|
||||||
|
|
||||||
override fun createProjectionEffect(
|
|
||||||
level: ServerLevel,
|
|
||||||
state: BlockState,
|
|
||||||
pos: BlockPos
|
|
||||||
) = SkylightProjectionEffect()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
data class SkylightProjectionEffect(var factor: Double = 2.0) : ProjectionEffect() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG_FACTOR = "Factor"
|
|
||||||
}
|
|
||||||
|
|
||||||
override val type
|
|
||||||
get() = SkylightProjection.effect.get()!!
|
|
||||||
|
|
||||||
override fun toNbt(tag: CompoundTag) {
|
|
||||||
tag.putDouble(TAG_FACTOR, factor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun fromNbt(tag: CompoundTag) {
|
|
||||||
factor = tag.getDouble(TAG_FACTOR)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -0,0 +1,144 @@
|
|||||||
|
package quaedam.projection.misc
|
||||||
|
|
||||||
|
import dev.architectury.event.events.client.ClientTickEvent
|
||||||
|
import dev.architectury.platform.Platform
|
||||||
|
import net.fabricmc.api.EnvType
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||||
|
import net.minecraft.client.resources.sounds.SoundInstance
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.sounds.SoundEvent
|
||||||
|
import net.minecraft.sounds.SoundSource
|
||||||
|
import net.minecraft.util.RandomSource
|
||||||
|
import net.minecraft.world.item.BlockItem
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.config.QuaedamConfig
|
||||||
|
import quaedam.projection.EntityProjectionBlock
|
||||||
|
import quaedam.projection.ProjectionEffect
|
||||||
|
import quaedam.projection.ProjectionEffectType
|
||||||
|
import quaedam.projection.SimpleProjectionEntity
|
||||||
|
import quaedam.projector.Projector
|
||||||
|
import quaedam.shell.ProjectionEffectShell
|
||||||
|
import quaedam.shell.buildProjectionEffectShell
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
object NoiseProjection {
|
||||||
|
|
||||||
|
const val ID = "noise_projection"
|
||||||
|
const val SHORT_ID = "noise"
|
||||||
|
|
||||||
|
val block = Quaedam.blocks.register(ID) { NoiseProjectionBlock }!!
|
||||||
|
|
||||||
|
val item = Quaedam.items.register(ID) {
|
||||||
|
BlockItem(
|
||||||
|
NoiseProjectionBlock, Item.Properties()
|
||||||
|
.`arch$tab`(Quaedam.creativeModeTab)
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val effect = Quaedam.projectionEffects.register(SHORT_ID) {
|
||||||
|
ProjectionEffectType { NoiseProjectionEffect() }
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val blockEntity = Quaedam.blockEntities.register(ID) {
|
||||||
|
SimpleProjectionEntity.createBlockEntityType(block, ::NoiseProjectionEffect)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
const val SOUND_NOISE_ID = "quaedam.projection.noise"
|
||||||
|
val soundEvent = Quaedam.soundEvents.register(SOUND_NOISE_ID) {
|
||||||
|
SoundEvent.createVariableRangeEvent(Quaedam.resource(SOUND_NOISE_ID))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (Platform.getEnv() == EnvType.CLIENT) {
|
||||||
|
ClientTickEvent.CLIENT_POST.register { game ->
|
||||||
|
val player = game.player ?: return@register
|
||||||
|
val random = (game.level ?: return@register).random
|
||||||
|
val projections = Projector.findNearbyProjections(player.level(), player.blockPosition(), effect.get())
|
||||||
|
if (projections.isNotEmpty()) {
|
||||||
|
val rate = projections.maxOf { it.rate }
|
||||||
|
val amount = min(projections.sumOf { it.amount }, 12)
|
||||||
|
val volume = projections.fold(1.0f) { v, p -> v * p.volume }
|
||||||
|
if (amount != 0 && random.nextInt(1000 / rate) == 1) {
|
||||||
|
for (i in 0 until random.nextInt(amount)) {
|
||||||
|
// play random noise
|
||||||
|
playRandomNoise(random, game, volume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playRandomNoise(random: RandomSource, game: Minecraft, volume: Float) {
|
||||||
|
val volumeFactor = random.nextInt(100)
|
||||||
|
val sound = SimpleSoundInstance(
|
||||||
|
soundEvent.get().location,
|
||||||
|
SoundSource.AMBIENT,
|
||||||
|
when (volumeFactor) {
|
||||||
|
in 0..8 -> random.nextFloat() * 0.65f
|
||||||
|
in 10..15 -> random.nextFloat() * 0.5f + 0.5f
|
||||||
|
in 21..50 -> random.nextFloat() * 0.3f
|
||||||
|
else -> random.nextFloat() * 0.2f
|
||||||
|
} * volume,
|
||||||
|
random.nextFloat() + 0.4f,
|
||||||
|
RandomSource.create(random.nextLong()),
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
SoundInstance.Attenuation.NONE,
|
||||||
|
random.nextFloat() * 28.0 - 14,
|
||||||
|
random.nextFloat() * 12.0 - 2,
|
||||||
|
random.nextFloat() * 28.0 - 14,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
game.soundManager.playDelayed(sound, random.nextInt(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object NoiseProjectionBlock : EntityProjectionBlock<NoiseProjectionEffect>(createProperties().lightLevel { 3 }) {
|
||||||
|
|
||||||
|
override val blockEntity = NoiseProjection.blockEntity
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class NoiseProjectionEffect(var rate: Int = 250, var amount: Int = 3, var volume: Float = 1.0f) :
|
||||||
|
ProjectionEffect(),
|
||||||
|
ProjectionEffectShell.Provider {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_RATE = "Rate"
|
||||||
|
const val TAG_AMOUNT = "Amount"
|
||||||
|
const val TAG_VOLUME = "Volume"
|
||||||
|
|
||||||
|
val maxAmount get() = QuaedamConfig.current.valuesInt["projection.noise.max_amount"] ?: 8
|
||||||
|
val maxRate get() = QuaedamConfig.current.valuesInt["projection.noise.max_rate"] ?: 300
|
||||||
|
}
|
||||||
|
|
||||||
|
override val type
|
||||||
|
get() = NoiseProjection.effect.get()!!
|
||||||
|
|
||||||
|
override fun toNbt(tag: CompoundTag) {
|
||||||
|
tag.putInt(TAG_RATE, rate)
|
||||||
|
tag.putInt(TAG_AMOUNT, amount)
|
||||||
|
tag.putFloat(TAG_VOLUME, volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromNbt(tag: CompoundTag, trusted: Boolean) {
|
||||||
|
rate = tag.getInt(TAG_RATE)
|
||||||
|
amount = tag.getInt(TAG_AMOUNT)
|
||||||
|
volume = tag.getFloat(TAG_VOLUME)
|
||||||
|
if (!trusted) {
|
||||||
|
amount = min(amount, maxAmount)
|
||||||
|
rate = min(rate, maxRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createShell() = buildProjectionEffectShell(this) {
|
||||||
|
intSlider("quaedam.shell.noise.rate", ::rate, 0..maxRate step 5)
|
||||||
|
intSlider("quaedam.shell.noise.amount", ::amount, 0..maxAmount)
|
||||||
|
floatSlider("quaedam.shell.noise.volume", ::volume, 0.0f..1.0f, 0.1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,72 @@
|
|||||||
|
package quaedam.projection.misc
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.world.item.BlockItem
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.config.QuaedamConfig
|
||||||
|
import quaedam.projection.EntityProjectionBlock
|
||||||
|
import quaedam.projection.ProjectionEffect
|
||||||
|
import quaedam.projection.ProjectionEffectType
|
||||||
|
import quaedam.projection.SimpleProjectionEntity
|
||||||
|
import quaedam.shell.ProjectionEffectShell
|
||||||
|
import quaedam.shell.buildProjectionEffectShell
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
object SkylightProjection {
|
||||||
|
|
||||||
|
const val ID = "skylight_projection"
|
||||||
|
const val SHORT_ID = "skylight"
|
||||||
|
|
||||||
|
val block = Quaedam.blocks.register(ID) { SkylightProjectionBlock }!!
|
||||||
|
|
||||||
|
val item = Quaedam.items.register(ID) {
|
||||||
|
BlockItem(
|
||||||
|
SkylightProjectionBlock, Item.Properties()
|
||||||
|
.`arch$tab`(Quaedam.creativeModeTab)
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val effect = Quaedam.projectionEffects.register(SHORT_ID) {
|
||||||
|
ProjectionEffectType { SkylightProjectionEffect() }
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val blockEntity = Quaedam.blockEntities.register(ID) {
|
||||||
|
SimpleProjectionEntity.createBlockEntityType(block, ::SkylightProjectionEffect)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object SkylightProjectionBlock : EntityProjectionBlock<SkylightProjectionEffect>(createProperties().lightLevel { 3 }) {
|
||||||
|
|
||||||
|
override val blockEntity = SkylightProjection.blockEntity
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SkylightProjectionEffect(var factor: Double = 2.0) : ProjectionEffect(), ProjectionEffectShell.Provider {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_FACTOR = "Factor"
|
||||||
|
|
||||||
|
val maxFactor get() = QuaedamConfig.current.valuesDouble["projection.skylight.max_factor"] ?: 5.0
|
||||||
|
}
|
||||||
|
|
||||||
|
override val type
|
||||||
|
get() = SkylightProjection.effect.get()!!
|
||||||
|
|
||||||
|
override fun toNbt(tag: CompoundTag) {
|
||||||
|
tag.putDouble(TAG_FACTOR, factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromNbt(tag: CompoundTag, trusted: Boolean) {
|
||||||
|
factor = tag.getDouble(TAG_FACTOR)
|
||||||
|
if (!trusted) {
|
||||||
|
factor = min(factor, maxFactor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createShell() = buildProjectionEffectShell(this) {
|
||||||
|
doubleSlider("quaedam.shell.skylight.factor", ::factor, 0.0..maxFactor, 0.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,77 @@
|
|||||||
|
package quaedam.projection.misc
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.world.item.BlockItem
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.config.QuaedamConfig
|
||||||
|
import quaedam.projection.EntityProjectionBlock
|
||||||
|
import quaedam.projection.ProjectionEffect
|
||||||
|
import quaedam.projection.ProjectionEffectType
|
||||||
|
import quaedam.projection.SimpleProjectionEntity
|
||||||
|
import quaedam.shell.ProjectionEffectShell
|
||||||
|
import quaedam.shell.buildProjectionEffectShell
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
object SoundProjection {
|
||||||
|
|
||||||
|
const val ID = "sound_projection"
|
||||||
|
const val SHORT_ID = "sound"
|
||||||
|
|
||||||
|
val block = Quaedam.blocks.register(ID) { SoundProjectionBlock }!!
|
||||||
|
|
||||||
|
val item = Quaedam.items.register(ID) {
|
||||||
|
BlockItem(
|
||||||
|
SoundProjectionBlock, Item.Properties()
|
||||||
|
.`arch$tab`(Quaedam.creativeModeTab)
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val effect = Quaedam.projectionEffects.register(SHORT_ID) {
|
||||||
|
ProjectionEffectType { SoundProjectionEffect() }
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val blockEntity = Quaedam.blockEntities.register(ID) {
|
||||||
|
SimpleProjectionEntity.createBlockEntityType(block) { SoundProjectionEffect() }
|
||||||
|
}!!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object SoundProjectionBlock : EntityProjectionBlock<SoundProjectionEffect>(createProperties().lightLevel { 3 }) {
|
||||||
|
|
||||||
|
override val blockEntity = SoundProjection.blockEntity
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SoundProjectionEffect(var rate: Int = 60, var volume: Float = 1.0f) : ProjectionEffect(),
|
||||||
|
ProjectionEffectShell.Provider {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_RATE = "Rate"
|
||||||
|
const val TAG_VOLUME = "Volume"
|
||||||
|
|
||||||
|
val maxRate get() = QuaedamConfig.current.valuesInt["projection.sound.max_rate"] ?: 210
|
||||||
|
}
|
||||||
|
|
||||||
|
override val type
|
||||||
|
get() = SoundProjection.effect.get()!!
|
||||||
|
|
||||||
|
override fun toNbt(tag: CompoundTag) {
|
||||||
|
tag.putInt(TAG_RATE, rate)
|
||||||
|
tag.putFloat(TAG_VOLUME, volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromNbt(tag: CompoundTag, trusted: Boolean) {
|
||||||
|
rate = tag.getInt(TAG_RATE)
|
||||||
|
volume = tag.getFloat(TAG_VOLUME)
|
||||||
|
if (!trusted) {
|
||||||
|
rate = min(rate, maxRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createShell() = buildProjectionEffectShell(this) {
|
||||||
|
intSlider("quaedam.shell.sound.rate", ::rate, 0..maxRate)
|
||||||
|
floatSlider("quaedam.shell.sound.volume", ::volume, 0.0f..1.0f, 0.1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
common/src/main/kotlin/quaedam/projection/music/Composer.kt
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package quaedam.projection.music
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.random.Random
|
||||||
|
import kotlin.random.nextInt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The composer for music.
|
||||||
|
* rhythmRandom is used for a better rhythm sync between different instruments.
|
||||||
|
*/
|
||||||
|
class Composer(val noteRandom: Random, val rhythmRandom: Random, val instrument: NoteBlockInstrument) {
|
||||||
|
|
||||||
|
data class Note(val note: Int, val volume: Float, val time: Int)
|
||||||
|
|
||||||
|
val baseTime = arrayOf(5, 5, 3, 3, 4, 4, 2, 2, 8).random(rhythmRandom)
|
||||||
|
val baseNote = noteRandom.nextInt(5..19)
|
||||||
|
|
||||||
|
val mayDropOut = instrument in arrayOf(
|
||||||
|
NoteBlockInstrument.BASEDRUM,
|
||||||
|
NoteBlockInstrument.HAT,
|
||||||
|
NoteBlockInstrument.SNARE,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun composeMusic(): List<Note> {
|
||||||
|
var note = (0..rhythmRandom.nextInt(4)).flatMap { composeSection() }
|
||||||
|
note = decorate(note)
|
||||||
|
if (mayDropOut && rhythmRandom.nextInt(6) != 0) {
|
||||||
|
val dropRate = arrayOf(2, 3, 3, 4, 4, 4, 4, 6).random(rhythmRandom)
|
||||||
|
note = note.chunked(dropRate).map {
|
||||||
|
val first = it.first()
|
||||||
|
Note(first.note, first.volume, it.sumOf { note -> note.time })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return note
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decorate(notes: List<Note>) = notes.map {
|
||||||
|
if (noteRandom.nextInt(4) == 0) {
|
||||||
|
doDecorate(it)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doDecorate(note: Note): Note {
|
||||||
|
var noteVal = note.note
|
||||||
|
if (noteRandom.nextInt(4) == 0) {
|
||||||
|
if (noteRandom.nextBoolean()) {
|
||||||
|
noteVal += 1
|
||||||
|
} else {
|
||||||
|
noteVal -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var volume = note.volume
|
||||||
|
if (noteRandom.nextInt(4) == 0) {
|
||||||
|
volume *= noteRandom.nextFloat() * 0.8f + 0.6f
|
||||||
|
}
|
||||||
|
return Note(noteVal, volume, note.time)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun composeSection(depth: Int = 0): List<Note> {
|
||||||
|
if (depth < 3 && rhythmRandom.nextBoolean()) {
|
||||||
|
val notes = (0..rhythmRandom.nextInt(3 - depth)).flatMap { composeSection(depth + 1) }
|
||||||
|
if (depth == 2) {
|
||||||
|
return (0..rhythmRandom.nextInt(3)).flatMap { notes }
|
||||||
|
} else {
|
||||||
|
return notes
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var notePointer = baseNote + noteRandom.nextInt(-3..3)
|
||||||
|
var direction = -1
|
||||||
|
var directionCounter = 0
|
||||||
|
return (0..rhythmRandom.nextInt(4..16)).map {
|
||||||
|
if (directionCounter == 0) {
|
||||||
|
// start new direction
|
||||||
|
directionCounter = rhythmRandom.nextInt(2..6)
|
||||||
|
direction = if (directionCounter % 2 == 0) {
|
||||||
|
rhythmRandom.nextInt(-2..2)
|
||||||
|
} else {
|
||||||
|
noteRandom.nextInt(-3..3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notePointer = abs(notePointer + direction) % 25
|
||||||
|
directionCounter--
|
||||||
|
Note(notePointer, 1.0f, baseTime + rhythmRandom.nextInt(-1..1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
128
common/src/main/kotlin/quaedam/projection/music/MusicPlayer.kt
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package quaedam.projection.music
|
||||||
|
|
||||||
|
import dev.architectury.utils.GameInstance
|
||||||
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||||
|
import net.minecraft.client.resources.sounds.SoundInstance
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Holder
|
||||||
|
import net.minecraft.core.particles.ParticleTypes
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.sounds.SoundEvent
|
||||||
|
import net.minecraft.sounds.SoundSource
|
||||||
|
import net.minecraft.util.RandomSource
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.NoteBlock
|
||||||
|
import net.minecraft.world.level.block.entity.SkullBlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||||
|
import quaedam.projector.Projector
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class MusicPlayer(
|
||||||
|
val noteSeed: Long,
|
||||||
|
val rhythmSeed: Long,
|
||||||
|
val level: Level,
|
||||||
|
val pos: BlockPos,
|
||||||
|
val startedAt: Long = level.gameTime
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_NOTE_SEED = "NoteSeed"
|
||||||
|
const val TAG_RHYTHM_SEED = "RhythmSeed"
|
||||||
|
const val TAG_STARTED_AT = "StartedAt"
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(tag: CompoundTag, level: Level, pos: BlockPos) : this(
|
||||||
|
tag.getLong(TAG_NOTE_SEED),
|
||||||
|
tag.getLong(TAG_RHYTHM_SEED),
|
||||||
|
level,
|
||||||
|
pos,
|
||||||
|
tag.getLong(TAG_STARTED_AT)
|
||||||
|
)
|
||||||
|
|
||||||
|
var notes = Composer(
|
||||||
|
noteRandom = Random(noteSeed),
|
||||||
|
rhythmRandom = Random(rhythmSeed),
|
||||||
|
instrument = level.getBlockState(pos).getValue(BlockStateProperties.NOTEBLOCK_INSTRUMENT)
|
||||||
|
).composeMusic().toMutableList()
|
||||||
|
val totalTime = notes.sumOf { it.time }.toLong()
|
||||||
|
var remainingTime = totalTime
|
||||||
|
val isEnd get() = remainingTime <= 0 || notes.isEmpty()
|
||||||
|
var noteTime = 0
|
||||||
|
|
||||||
|
init {
|
||||||
|
val currentRemaining = totalTime - (level.gameTime - startedAt)
|
||||||
|
while (remainingTime > currentRemaining && !isEnd) {
|
||||||
|
// seek to current position
|
||||||
|
remainingTime -= fetchNote().time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchNote() = notes.removeFirst()
|
||||||
|
|
||||||
|
fun tick() {
|
||||||
|
if (isEnd)
|
||||||
|
return
|
||||||
|
if (noteTime <= 0) {
|
||||||
|
// start new note
|
||||||
|
val note = fetchNote()
|
||||||
|
remainingTime -= note.time
|
||||||
|
noteTime = note.time
|
||||||
|
if (level.isClientSide) {
|
||||||
|
// play note
|
||||||
|
val projections = Projector.findNearbyProjections(level, pos, MusicProjection.effect.get())
|
||||||
|
.takeIf { it.isNotEmpty() } ?: listOf(MusicProjectionEffect())
|
||||||
|
val volume = projections.maxOf { it.volumeFactor } * note.volume
|
||||||
|
val particle = projections.any { it.particle }
|
||||||
|
val instrument = level.getBlockState(pos).getValue(BlockStateProperties.NOTEBLOCK_INSTRUMENT)
|
||||||
|
val pitch = if (instrument.isTunable) {
|
||||||
|
NoteBlock.getPitchFromNote(note.note)
|
||||||
|
} else {
|
||||||
|
1.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
val holder = if (instrument.hasCustomSound()) {
|
||||||
|
val entity = level.getBlockEntity(pos.below())
|
||||||
|
(entity as? SkullBlockEntity)?.noteBlockSound?.let {
|
||||||
|
Holder.direct(SoundEvent.createVariableRangeEvent(it))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
} ?: instrument.soundEvent
|
||||||
|
|
||||||
|
if (particle) {
|
||||||
|
level.addParticle(
|
||||||
|
ParticleTypes.NOTE,
|
||||||
|
pos.x.toDouble() + 0.5,
|
||||||
|
pos.y.toDouble() + 1.2,
|
||||||
|
pos.z.toDouble() + 0.5,
|
||||||
|
note.time.toDouble() / 24.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val instance = SimpleSoundInstance(
|
||||||
|
holder.value().location,
|
||||||
|
SoundSource.RECORDS,
|
||||||
|
volume,
|
||||||
|
pitch,
|
||||||
|
RandomSource.create(level.random.nextLong()),
|
||||||
|
false, 0, SoundInstance.Attenuation.LINEAR,
|
||||||
|
pos.x.toDouble() + 0.5,
|
||||||
|
pos.y.toDouble() + 0.5,
|
||||||
|
pos.z.toDouble() + 0.5,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
GameInstance.getClient().soundManager.play(instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noteTime--
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toTag() = CompoundTag().apply {
|
||||||
|
putLong(TAG_NOTE_SEED, noteSeed)
|
||||||
|
putLong(TAG_RHYTHM_SEED, rhythmSeed)
|
||||||
|
putLong(TAG_STARTED_AT, startedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,85 @@
|
|||||||
|
package quaedam.projection.music
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.world.item.BlockItem
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.config.QuaedamConfig
|
||||||
|
import quaedam.projection.EntityProjectionBlock
|
||||||
|
import quaedam.projection.ProjectionEffect
|
||||||
|
import quaedam.projection.ProjectionEffectType
|
||||||
|
import quaedam.projection.SimpleProjectionEntity
|
||||||
|
import quaedam.shell.ProjectionEffectShell
|
||||||
|
import quaedam.shell.buildProjectionEffectShell
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
object MusicProjection {
|
||||||
|
|
||||||
|
const val ID = "music_projection"
|
||||||
|
const val SHORT_ID = "music"
|
||||||
|
|
||||||
|
val block = Quaedam.blocks.register(ID) { MusicProjectionBlock }!!
|
||||||
|
|
||||||
|
val item = Quaedam.items.register(ID) {
|
||||||
|
BlockItem(
|
||||||
|
MusicProjectionBlock, Item.Properties()
|
||||||
|
.`arch$tab`(Quaedam.creativeModeTab)
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val effect = Quaedam.projectionEffects.register(SHORT_ID) {
|
||||||
|
ProjectionEffectType { MusicProjectionEffect() }
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val blockEntity = Quaedam.blockEntities.register(ID) {
|
||||||
|
SimpleProjectionEntity.createBlockEntityType(block) { MusicProjectionEffect() }
|
||||||
|
}!!
|
||||||
|
|
||||||
|
init {
|
||||||
|
SmartInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object MusicProjectionBlock : EntityProjectionBlock<MusicProjectionEffect>(createProperties().lightLevel { 3 }) {
|
||||||
|
|
||||||
|
override val blockEntity = MusicProjection.blockEntity
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class MusicProjectionEffect(var volumeFactor: Float = 1.0f, var particle: Boolean = true) : ProjectionEffect(),
|
||||||
|
ProjectionEffectShell.Provider {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_VOLUME_FACTOR = "VolumeFactor"
|
||||||
|
const val TAG_PARTICLE = "Particle"
|
||||||
|
|
||||||
|
val maxVolumeFactor get() = QuaedamConfig.current.valuesFloat["projection.music.max_volume_factor"] ?: 5.0f
|
||||||
|
val enforceParticle get() = QuaedamConfig.current.valuesBoolean["projection.music.enforce_particle"]
|
||||||
|
}
|
||||||
|
|
||||||
|
override val type
|
||||||
|
get() = MusicProjection.effect.get()!!
|
||||||
|
|
||||||
|
override fun toNbt(tag: CompoundTag) {
|
||||||
|
tag.putFloat(TAG_VOLUME_FACTOR, volumeFactor)
|
||||||
|
tag.putBoolean(TAG_PARTICLE, particle)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromNbt(tag: CompoundTag, trusted: Boolean) {
|
||||||
|
volumeFactor = tag.getFloat(TAG_VOLUME_FACTOR)
|
||||||
|
particle = tag.getBoolean(TAG_PARTICLE)
|
||||||
|
if (!trusted) {
|
||||||
|
volumeFactor = min(volumeFactor, maxVolumeFactor)
|
||||||
|
particle = enforceParticle ?: particle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createShell() = buildProjectionEffectShell(this) {
|
||||||
|
floatSlider("quaedam.shell.music.volume_factor", ::volumeFactor, 0.0f..maxVolumeFactor, 0.1f)
|
||||||
|
if (enforceParticle == null) {
|
||||||
|
boolean("quaedam.shell.music.particle", ::particle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,236 @@
|
|||||||
|
package quaedam.projection.music
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.network.protocol.Packet
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.util.RandomSource
|
||||||
|
import net.minecraft.world.InteractionHand
|
||||||
|
import net.minecraft.world.InteractionResult
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.item.BlockItem
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.EntityBlock
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||||
|
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
|
||||||
|
import net.minecraft.world.level.material.MapColor
|
||||||
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.misc.causality.CausalityAnchor
|
||||||
|
import quaedam.projector.Projector
|
||||||
|
import quaedam.utils.getChunksNearby
|
||||||
|
import quaedam.utils.sendBlockUpdated
|
||||||
|
|
||||||
|
object SmartInstrument {
|
||||||
|
|
||||||
|
const val ID = "smart_instrument"
|
||||||
|
|
||||||
|
val block = Quaedam.blocks.register(ID) { SmartInstrumentBlock }!!
|
||||||
|
|
||||||
|
val item = Quaedam.items.register(ID) {
|
||||||
|
BlockItem(
|
||||||
|
SmartInstrumentBlock, Item.Properties()
|
||||||
|
.`arch$tab`(Quaedam.creativeModeTab)
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val blockEntity = Quaedam.blockEntities.register(ID) {
|
||||||
|
BlockEntityType.Builder.of(::SmartInstrumentBlockEntity, block.get()).build(null)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object SmartInstrumentBlock : Block(
|
||||||
|
Properties.of()
|
||||||
|
.strength(2.7f)
|
||||||
|
.requiresCorrectToolForDrops()
|
||||||
|
.mapColor(MapColor.COLOR_BROWN)
|
||||||
|
.randomTicks()
|
||||||
|
), EntityBlock {
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerDefaultState(
|
||||||
|
defaultBlockState()
|
||||||
|
.setValue(BlockStateProperties.NOTEBLOCK_INSTRUMENT, NoteBlockInstrument.HARP)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newBlockEntity(pos: BlockPos, state: BlockState) = SmartInstrumentBlockEntity(pos, state)
|
||||||
|
|
||||||
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||||
|
super.createBlockStateDefinition(builder)
|
||||||
|
builder.add(BlockStateProperties.NOTEBLOCK_INSTRUMENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun neighborChanged(
|
||||||
|
state: BlockState,
|
||||||
|
level: Level,
|
||||||
|
pos: BlockPos,
|
||||||
|
neighborBlock: Block,
|
||||||
|
neighborPos: BlockPos,
|
||||||
|
movedByPiston: Boolean
|
||||||
|
) {
|
||||||
|
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston)
|
||||||
|
level.setBlock(
|
||||||
|
pos,
|
||||||
|
state.setValue(BlockStateProperties.NOTEBLOCK_INSTRUMENT, level.getBlockState(pos.below()).instrument()),
|
||||||
|
UPDATE_ALL
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun onPlace(state: BlockState, level: Level, pos: BlockPos, oldState: BlockState, movedByPiston: Boolean) {
|
||||||
|
super.onPlace(state, level, pos, oldState, movedByPiston)
|
||||||
|
level.setBlock(
|
||||||
|
pos,
|
||||||
|
state.setValue(BlockStateProperties.NOTEBLOCK_INSTRUMENT, level.getBlockState(pos.below()).instrument()),
|
||||||
|
UPDATE_ALL
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION")
|
||||||
|
override fun randomTick(
|
||||||
|
state: BlockState,
|
||||||
|
level: ServerLevel,
|
||||||
|
pos: BlockPos,
|
||||||
|
random: RandomSource
|
||||||
|
) {
|
||||||
|
if (Projector.findNearbyProjections(level, pos, MusicProjection.effect.get()).isNotEmpty()) {
|
||||||
|
val entity = level.getBlockEntity(pos) as SmartInstrumentBlockEntity
|
||||||
|
if (entity.player == null) {
|
||||||
|
entity.startMusic()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
|
||||||
|
override fun use(
|
||||||
|
state: BlockState,
|
||||||
|
level: Level,
|
||||||
|
pos: BlockPos,
|
||||||
|
player: Player,
|
||||||
|
hand: InteractionHand,
|
||||||
|
hit: BlockHitResult
|
||||||
|
): InteractionResult {
|
||||||
|
if (Projector.findNearbyProjections(level, pos, MusicProjection.effect.get()).isNotEmpty()
|
||||||
|
|| CausalityAnchor.checkEffect(level, pos)
|
||||||
|
) {
|
||||||
|
val entity = level.getBlockEntity(pos) as SmartInstrumentBlockEntity
|
||||||
|
if (entity.player == null && !level.isClientSide) {
|
||||||
|
entity.startMusic()
|
||||||
|
}
|
||||||
|
return InteractionResult.sidedSuccess(level.isClientSide)
|
||||||
|
}
|
||||||
|
return super.use(state, level, pos, player, hand, hit)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : BlockEntity?> getTicker(
|
||||||
|
level: Level,
|
||||||
|
state: BlockState,
|
||||||
|
blockEntityType: BlockEntityType<T>
|
||||||
|
): BlockEntityTicker<T> {
|
||||||
|
return BlockEntityTicker { _, _, _, entity ->
|
||||||
|
(entity as? SmartInstrumentBlockEntity)?.tick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SmartInstrumentBlockEntity(pos: BlockPos, state: BlockState) :
|
||||||
|
BlockEntity(SmartInstrument.blockEntity.get(), pos, state) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_MUSIC = "Music"
|
||||||
|
}
|
||||||
|
|
||||||
|
// delay MusicPlayer initialization until level is available
|
||||||
|
var playerData: CompoundTag? = null
|
||||||
|
var player: MusicPlayer? = null
|
||||||
|
|
||||||
|
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
||||||
|
|
||||||
|
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
|
||||||
|
|
||||||
|
override fun load(tag: CompoundTag) {
|
||||||
|
super.load(tag)
|
||||||
|
if (TAG_MUSIC in tag) {
|
||||||
|
try {
|
||||||
|
player = MusicPlayer(tag.getCompound(TAG_MUSIC), level!!, blockPos)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
playerData = tag.getCompound(TAG_MUSIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveAdditional(tag: CompoundTag) {
|
||||||
|
super.saveAdditional(tag)
|
||||||
|
if (playerData != null) {
|
||||||
|
tag.put(TAG_MUSIC, playerData!!)
|
||||||
|
}
|
||||||
|
if (player != null) {
|
||||||
|
tag.put(TAG_MUSIC, player!!.toTag())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkProjections() =
|
||||||
|
Projector.findNearbyProjections(level!!, blockPos, MusicProjection.effect.get()).isNotEmpty()
|
||||||
|
|| CausalityAnchor.checkEffect(level!!, blockPos)
|
||||||
|
|
||||||
|
fun startMusic(force: Boolean = false, synced: Boolean = false) {
|
||||||
|
if ((player == null || force) && !level!!.isClientSide && checkProjections()) {
|
||||||
|
player = MusicPlayer(level!!.random.nextLong(), level!!.gameTime / 20, level!!, blockPos)
|
||||||
|
setChanged()
|
||||||
|
sendBlockUpdated()
|
||||||
|
if (!synced) {
|
||||||
|
// sync start to other instruments
|
||||||
|
level!!.getChunksNearby(blockPos, 1)
|
||||||
|
.flatMap {
|
||||||
|
it.blockEntities
|
||||||
|
.filterValues { entity -> entity is SmartInstrumentBlockEntity }
|
||||||
|
.filterKeys { pos -> pos.distSqr(blockPos) < 100 }
|
||||||
|
.values
|
||||||
|
}
|
||||||
|
.filterNot { it == this }
|
||||||
|
.filterIsInstance<SmartInstrumentBlockEntity>()
|
||||||
|
.forEach { it.startMusic(force = true, synced = true) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tick() {
|
||||||
|
if (playerData != null) {
|
||||||
|
player = MusicPlayer(playerData!!, level!!, blockPos)
|
||||||
|
playerData = null
|
||||||
|
}
|
||||||
|
if (player != null) {
|
||||||
|
if (checkProjections()) {
|
||||||
|
player!!.tick()
|
||||||
|
if (!level!!.isClientSide) {
|
||||||
|
if (player!!.isEnd) {
|
||||||
|
player = null
|
||||||
|
setChanged()
|
||||||
|
sendBlockUpdated()
|
||||||
|
if (CausalityAnchor.checkEffect(level!!, blockPos) || level!!.random.nextInt(7) != 0) {
|
||||||
|
startMusic()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player = null
|
||||||
|
setChanged()
|
||||||
|
sendBlockUpdated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,142 +0,0 @@
|
|||||||
package quaedam.projection.swarm
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import net.minecraft.world.entity.ai.Brain
|
|
||||||
import net.minecraft.world.entity.ai.behavior.*
|
|
||||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
|
||||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
|
||||||
import net.minecraft.world.entity.schedule.Activity
|
|
||||||
import net.minecraft.world.entity.schedule.Schedule
|
|
||||||
import net.minecraft.world.entity.schedule.ScheduleBuilder
|
|
||||||
import quaedam.Quaedam
|
|
||||||
import quaedam.utils.weight
|
|
||||||
import quaedam.utils.weightR
|
|
||||||
|
|
||||||
object ProjectedPersonAI {
|
|
||||||
|
|
||||||
private val memoryTypes = listOf(
|
|
||||||
MemoryModuleType.PATH,
|
|
||||||
MemoryModuleType.LOOK_TARGET,
|
|
||||||
MemoryModuleType.WALK_TARGET,
|
|
||||||
MemoryModuleType.ATTACK_TARGET,
|
|
||||||
MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
|
|
||||||
MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM,
|
|
||||||
MemoryModuleType.HURT_BY,
|
|
||||||
MemoryModuleType.ATTACK_COOLING_DOWN,
|
|
||||||
MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE,
|
|
||||||
)
|
|
||||||
|
|
||||||
private val sensorTypes = listOf(
|
|
||||||
SensorType.NEAREST_LIVING_ENTITIES,
|
|
||||||
SensorType.NEAREST_PLAYERS,
|
|
||||||
SensorType.HURT_BY,
|
|
||||||
SensorType.NEAREST_ITEMS,
|
|
||||||
)
|
|
||||||
|
|
||||||
val defaultSchedule = Quaedam.schedule.register("projected_person_default") {
|
|
||||||
ScheduleBuilder(Schedule()).changeActivityAt(10, Activity.IDLE)
|
|
||||||
.changeActivityAt(10, Activity.IDLE)
|
|
||||||
.changeActivityAt(2000, Activity.WORK)
|
|
||||||
.changeActivityAt(7300, Activity.IDLE)
|
|
||||||
.changeActivityAt(9000, Activity.WORK)
|
|
||||||
.changeActivityAt(10700, Activity.IDLE)
|
|
||||||
.changeActivityAt(11000, Activity.PLAY)
|
|
||||||
.changeActivityAt(11500, Activity.IDLE)
|
|
||||||
.changeActivityAt(12000, Activity.REST)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
val babySchedule = Quaedam.schedule.register("projected_person_baby") {
|
|
||||||
ScheduleBuilder(Schedule()).changeActivityAt(10, Activity.IDLE)
|
|
||||||
.changeActivityAt(10, Activity.IDLE)
|
|
||||||
.changeActivityAt(3200, Activity.PLAY)
|
|
||||||
.changeActivityAt(7000, Activity.IDLE)
|
|
||||||
.changeActivityAt(9000, Activity.PLAY)
|
|
||||||
.changeActivityAt(11000, Activity.REST)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun provider(): Brain.Provider<out ProjectedPersonEntity> = Brain.provider(memoryTypes, sensorTypes)
|
|
||||||
|
|
||||||
fun initBrain(entity: ProjectedPersonEntity, brain: Brain<ProjectedPersonEntity>) {
|
|
||||||
initCoreActivity(brain)
|
|
||||||
initIdleActivity(brain)
|
|
||||||
initPlayActivity(brain)
|
|
||||||
initWorkActivity(brain)
|
|
||||||
initRestActivity(brain)
|
|
||||||
brain.setCoreActivities(setOf(Activity.CORE))
|
|
||||||
brain.setDefaultActivity(Activity.IDLE)
|
|
||||||
updateSchedule(entity, brain, baby = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateSchedule(entity: ProjectedPersonEntity, brain: Brain<ProjectedPersonEntity>, baby: Boolean) {
|
|
||||||
if (baby) {
|
|
||||||
brain.schedule = babySchedule.get()
|
|
||||||
} else {
|
|
||||||
brain.schedule = defaultSchedule.get()
|
|
||||||
}
|
|
||||||
brain.updateActivityFromSchedule(entity.level().dayTime, entity.level().gameTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateSchedule(entity: ProjectedPersonEntity) = updateSchedule(entity, entity.brain, entity.shape.baby)
|
|
||||||
|
|
||||||
private fun initCoreActivity(brain: Brain<ProjectedPersonEntity>) {
|
|
||||||
brain.addActivity(
|
|
||||||
Activity.CORE, ImmutableList.of(
|
|
||||||
0 weight Swim(0.8f),
|
|
||||||
0 weight InteractWithDoor.create(),
|
|
||||||
0 weight LookAtTargetSink(40, 70),
|
|
||||||
0 weight MoveToTargetSink(),
|
|
||||||
0 weight WakeUp.create(),
|
|
||||||
3 weight GoToWantedItem.create(1.2f, false, 7),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initIdleActivity(brain: Brain<ProjectedPersonEntity>) {
|
|
||||||
brain.addActivity(
|
|
||||||
Activity.IDLE, ImmutableList.of(
|
|
||||||
3 weight createStrollBehavior(),
|
|
||||||
99 weight UpdateActivityFromSchedule.create(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initPlayActivity(brain: Brain<ProjectedPersonEntity>) {
|
|
||||||
brain.addActivity(
|
|
||||||
Activity.PLAY, ImmutableList.of(
|
|
||||||
3 weight GoToWantedItem.create(1.75f, true, 32),
|
|
||||||
5 weight JumpOnBed(1.0f),
|
|
||||||
5 weight createStrollBehavior(),
|
|
||||||
99 weight UpdateActivityFromSchedule.create(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initWorkActivity(brain: Brain<ProjectedPersonEntity>) {
|
|
||||||
brain.addActivity(
|
|
||||||
Activity.WORK, ImmutableList.of(
|
|
||||||
3 weight createStrollBehavior(),
|
|
||||||
99 weight UpdateActivityFromSchedule.create(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initRestActivity(brain: Brain<ProjectedPersonEntity>) {
|
|
||||||
brain.addActivity(
|
|
||||||
Activity.REST, ImmutableList.of(
|
|
||||||
3 weight createStrollBehavior(),
|
|
||||||
99 weight UpdateActivityFromSchedule.create(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createStrollBehavior() = RunOne(
|
|
||||||
listOf(
|
|
||||||
2 weightR RandomStroll.stroll(1.0f),
|
|
||||||
2 weightR SetWalkTargetFromLookTarget.create(1.0f, 5),
|
|
||||||
1 weightR DoNothing(30, 60)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
@@ -4,6 +4,7 @@ import com.mojang.serialization.Dynamic
|
|||||||
import dev.architectury.platform.Platform
|
import dev.architectury.platform.Platform
|
||||||
import dev.architectury.registry.level.entity.EntityAttributeRegistry
|
import dev.architectury.registry.level.entity.EntityAttributeRegistry
|
||||||
import net.fabricmc.api.EnvType
|
import net.fabricmc.api.EnvType
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.network.protocol.game.DebugPackets
|
import net.minecraft.network.protocol.game.DebugPackets
|
||||||
@@ -11,10 +12,12 @@ import net.minecraft.network.syncher.EntityDataAccessor
|
|||||||
import net.minecraft.network.syncher.EntityDataSerializers
|
import net.minecraft.network.syncher.EntityDataSerializers
|
||||||
import net.minecraft.network.syncher.SynchedEntityData
|
import net.minecraft.network.syncher.SynchedEntityData
|
||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.sounds.SoundEvent
|
||||||
import net.minecraft.world.DifficultyInstance
|
import net.minecraft.world.DifficultyInstance
|
||||||
import net.minecraft.world.SimpleContainer
|
import net.minecraft.world.SimpleContainer
|
||||||
import net.minecraft.world.entity.*
|
import net.minecraft.world.entity.*
|
||||||
import net.minecraft.world.entity.ai.Brain
|
import net.minecraft.world.entity.ai.Brain
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeModifier
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes
|
import net.minecraft.world.entity.ai.attributes.Attributes
|
||||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||||
@@ -23,8 +26,12 @@ import net.minecraft.world.entity.npc.InventoryCarrier
|
|||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.level.ServerLevelAccessor
|
import net.minecraft.world.level.ServerLevelAccessor
|
||||||
import quaedam.Quaedam
|
import quaedam.Quaedam
|
||||||
|
import quaedam.misc.causality.CausalityAnchor
|
||||||
|
import quaedam.projection.misc.SoundProjection
|
||||||
|
import quaedam.projection.swarm.ai.ProjectedPersonAI
|
||||||
|
import quaedam.projection.swarm.ai.ProjectedPersonNavigation
|
||||||
import quaedam.projector.Projector
|
import quaedam.projector.Projector
|
||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.random.Random
|
||||||
|
|
||||||
class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Level) : PathfinderMob(entityType, level),
|
class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Level) : PathfinderMob(entityType, level),
|
||||||
InventoryCarrier {
|
InventoryCarrier {
|
||||||
@@ -37,6 +44,7 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
|
|||||||
|
|
||||||
const val BOUNDING_WIDTH = 0.6f
|
const val BOUNDING_WIDTH = 0.6f
|
||||||
const val BOUNDING_HEIGHT = 1.8f
|
const val BOUNDING_HEIGHT = 1.8f
|
||||||
|
const val INV_DIFF_NAME = "quaedam:Random Individual Differences"
|
||||||
|
|
||||||
val entity = Quaedam.entities.register(ID) {
|
val entity = Quaedam.entities.register(ID) {
|
||||||
EntityType.Builder.of(::ProjectedPersonEntity, MobCategory.CREATURE).canSpawnFarFromPlayer()
|
EntityType.Builder.of(::ProjectedPersonEntity, MobCategory.CREATURE).canSpawnFarFromPlayer()
|
||||||
@@ -46,20 +54,28 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
|
|||||||
val dataShape =
|
val dataShape =
|
||||||
SynchedEntityData.defineId(ProjectedPersonEntity::class.java, EntityDataSerializers.COMPOUND_TAG)
|
SynchedEntityData.defineId(ProjectedPersonEntity::class.java, EntityDataSerializers.COMPOUND_TAG)
|
||||||
|
|
||||||
|
const val SOUND_NOISE_ID = "entity.projected_person.noise"
|
||||||
|
val soundNoise = Quaedam.soundEvents.register(SOUND_NOISE_ID) {
|
||||||
|
SoundEvent.createVariableRangeEvent(Quaedam.resource(SOUND_NOISE_ID))
|
||||||
|
}!!
|
||||||
|
|
||||||
init {
|
init {
|
||||||
EntityAttributeRegistry.register(entity, ::createAttributes)
|
Quaedam.lateinit += { EntityAttributeRegistry.register(entity, ::createAttributes) }
|
||||||
if (Platform.getEnv() == EnvType.CLIENT) ProjectedPersonRenderer
|
if (Platform.getEnv() == EnvType.CLIENT) ProjectedPersonRenderer
|
||||||
ProjectedPersonShape
|
ProjectedPersonShape
|
||||||
ProjectedPersonAI
|
ProjectedPersonAI
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAttributes(): AttributeSupplier.Builder =
|
private fun createAttributes(): AttributeSupplier.Builder =
|
||||||
Mob.createMobAttributes().add(Attributes.ATTACK_DAMAGE, 1.5)
|
Mob.createMobAttributes().add(Attributes.ATTACK_DAMAGE, 1.5).add(Attributes.MOVEMENT_SPEED, 0.2)
|
||||||
.add(Attributes.MOVEMENT_SPEED, 0.2)
|
|
||||||
.add(Attributes.ATTACK_SPEED)
|
.add(Attributes.ATTACK_SPEED)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
setCanPickUpLoot(true)
|
||||||
|
}
|
||||||
|
|
||||||
override fun finalizeSpawn(
|
override fun finalizeSpawn(
|
||||||
serverLevelAccessor: ServerLevelAccessor,
|
serverLevelAccessor: ServerLevelAccessor,
|
||||||
difficultyInstance: DifficultyInstance,
|
difficultyInstance: DifficultyInstance,
|
||||||
@@ -67,7 +83,38 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
|
|||||||
spawnGroupData: SpawnGroupData?,
|
spawnGroupData: SpawnGroupData?,
|
||||||
compoundTag: CompoundTag?
|
compoundTag: CompoundTag?
|
||||||
): SpawnGroupData? {
|
): SpawnGroupData? {
|
||||||
shape = ProjectedPersonShape.create(serverLevelAccessor.random.nextLong())
|
val rand = Random(serverLevelAccessor.random.nextLong())
|
||||||
|
// random shape
|
||||||
|
shape = ProjectedPersonShape.create(rand.nextLong())
|
||||||
|
// random attributes
|
||||||
|
getAttribute(Attributes.MOVEMENT_SPEED)!!.addPermanentModifier(
|
||||||
|
AttributeModifier(
|
||||||
|
INV_DIFF_NAME,
|
||||||
|
rand.nextFloat() * 0.1,
|
||||||
|
AttributeModifier.Operation.ADDITION
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getAttribute(Attributes.ATTACK_DAMAGE)!!.addPermanentModifier(
|
||||||
|
AttributeModifier(
|
||||||
|
INV_DIFF_NAME,
|
||||||
|
rand.nextFloat() * 1.5,
|
||||||
|
AttributeModifier.Operation.ADDITION
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getAttribute(Attributes.ATTACK_SPEED)!!.addPermanentModifier(
|
||||||
|
AttributeModifier(
|
||||||
|
INV_DIFF_NAME,
|
||||||
|
rand.nextFloat() * -2.0,
|
||||||
|
AttributeModifier.Operation.ADDITION
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getAttribute(Attributes.MAX_HEALTH)!!.addPermanentModifier(
|
||||||
|
AttributeModifier(
|
||||||
|
INV_DIFF_NAME,
|
||||||
|
rand.nextFloat() * 5.0,
|
||||||
|
AttributeModifier.Operation.ADDITION
|
||||||
|
)
|
||||||
|
)
|
||||||
return super.finalizeSpawn(serverLevelAccessor, difficultyInstance, mobSpawnType, spawnGroupData, compoundTag)
|
return super.finalizeSpawn(serverLevelAccessor, difficultyInstance, mobSpawnType, spawnGroupData, compoundTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +151,7 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
|
|||||||
super.readAdditionalSaveData(tag)
|
super.readAdditionalSaveData(tag)
|
||||||
shapeTag = tag.getCompound(KEY_ENTITY_SHAPE)
|
shapeTag = tag.getCompound(KEY_ENTITY_SHAPE)
|
||||||
readInventoryFromTag(tag)
|
readInventoryFromTag(tag)
|
||||||
|
setCanPickUpLoot(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldShowName() = true
|
override fun shouldShowName() = true
|
||||||
@@ -111,14 +159,17 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
|
|||||||
override fun getTypeName(): Component =
|
override fun getTypeName(): Component =
|
||||||
shape.name.takeIf { it.isNotEmpty() }?.let { Component.literal(it) } ?: super.getTypeName()
|
shape.name.takeIf { it.isNotEmpty() }?.let { Component.literal(it) } ?: super.getTypeName()
|
||||||
|
|
||||||
override fun getNameTagOffsetY() = super.getNameTagOffsetY() - (BOUNDING_HEIGHT * (1.3f - shape.scaleY))
|
override fun getNameTagOffsetY() = super.getNameTagOffsetY() - (bbHeight * (1f - shape.scaleY))
|
||||||
|
|
||||||
override fun createNavigation(level: Level) = ProjectedPersonNavigation(this, level)
|
override fun createNavigation(level: Level) = ProjectedPersonNavigation(this, level)
|
||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
super.tick()
|
super.tick()
|
||||||
if (tickCount % 20 == 0) {
|
if (tickCount % 20 == 0) {
|
||||||
if (!checkProjectionEffect()) remove(RemovalReason.KILLED)
|
if (!checkProjectionEffect() && !CausalityAnchor.checkEffect(level(), blockPosition())) {
|
||||||
|
dropEquipment()
|
||||||
|
remove(RemovalReason.KILLED)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,8 +177,12 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
|
|||||||
Projector.findNearbyProjections(level(), blockPosition(), SwarmProjection.effect.get()).isNotEmpty()
|
Projector.findNearbyProjections(level(), blockPosition(), SwarmProjection.effect.get()).isNotEmpty()
|
||||||
|
|
||||||
override fun checkDespawn() {
|
override fun checkDespawn() {
|
||||||
super.checkDespawn()
|
// no despawn
|
||||||
if (!checkProjectionEffect()) discard()
|
// super.checkDespawn()
|
||||||
|
if (!checkProjectionEffect() && !CausalityAnchor.checkEffect(level(), blockPosition())) {
|
||||||
|
dropEquipment()
|
||||||
|
remove(RemovalReason.KILLED)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val inventory = SimpleContainer(10)
|
private val inventory = SimpleContainer(10)
|
||||||
@@ -163,4 +218,48 @@ class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Le
|
|||||||
|
|
||||||
override fun isBaby() = shape.baby
|
override fun isBaby() = shape.baby
|
||||||
|
|
||||||
|
override fun startSleeping(blockPos: BlockPos) {
|
||||||
|
super.startSleeping(blockPos)
|
||||||
|
brain.eraseMemory(MemoryModuleType.WALK_TARGET)
|
||||||
|
brain.eraseMemory(MemoryModuleType.LOOK_TARGET)
|
||||||
|
brain.eraseMemory(MemoryModuleType.NEAREST_BED)
|
||||||
|
brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stopSleeping() {
|
||||||
|
super.stopSleeping()
|
||||||
|
brain.setMemory(MemoryModuleType.LAST_WOKEN, level().gameTime)
|
||||||
|
brain.eraseMemory(MemoryModuleType.HOME)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dropEquipment() {
|
||||||
|
super.dropEquipment()
|
||||||
|
inventory.removeAllItems().forEach(::spawnAtLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findNearbySoundProjection() =
|
||||||
|
Projector.findNearbyProjections(level(), blockPosition(), SoundProjection.effect.get())
|
||||||
|
|
||||||
|
override fun isSilent() =
|
||||||
|
super.isSilent() && findNearbySoundProjection().isEmpty()
|
||||||
|
|
||||||
|
override fun getAmbientSound(): SoundEvent? {
|
||||||
|
if (findNearbySoundProjection().isNotEmpty()) {
|
||||||
|
// sound projection available
|
||||||
|
return soundNoise.get()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSoundVolume() =
|
||||||
|
super.getSoundVolume() * (random.nextFloat() * 1.1f + 0.4f) *
|
||||||
|
findNearbySoundProjection().fold(1.0f) { v, p -> v * p.volume }
|
||||||
|
|
||||||
|
override fun getVoicePitch() = super.getVoicePitch() * (random.nextFloat() * 0.55f + 0.7f)
|
||||||
|
|
||||||
|
override fun getAmbientSoundInterval() =
|
||||||
|
80 - random.nextInt((findNearbySoundProjection().firstOrNull()?.rate ?: 1) * 5)
|
||||||
|
|
||||||
|
override fun isEffectiveAi() = super.isEffectiveAi() && checkProjectionEffect()
|
||||||
|
|
||||||
}
|
}
|
@@ -10,6 +10,7 @@ import net.minecraft.client.renderer.entity.EntityRendererProvider
|
|||||||
import net.minecraft.client.renderer.entity.MobRenderer
|
import net.minecraft.client.renderer.entity.MobRenderer
|
||||||
import net.minecraft.client.renderer.entity.layers.CustomHeadLayer
|
import net.minecraft.client.renderer.entity.layers.CustomHeadLayer
|
||||||
import net.minecraft.client.renderer.entity.layers.ItemInHandLayer
|
import net.minecraft.client.renderer.entity.layers.ItemInHandLayer
|
||||||
|
import quaedam.Quaedam
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
class ProjectedPersonRenderer(context: EntityRendererProvider.Context) :
|
class ProjectedPersonRenderer(context: EntityRendererProvider.Context) :
|
||||||
@@ -21,7 +22,12 @@ class ProjectedPersonRenderer(context: EntityRendererProvider.Context) :
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
init {
|
init {
|
||||||
EntityRendererRegistry.register(ProjectedPersonEntity.entity, ::ProjectedPersonRenderer)
|
Quaedam.lateinit += {
|
||||||
|
EntityRendererRegistry.register(
|
||||||
|
ProjectedPersonEntity.entity,
|
||||||
|
::ProjectedPersonRenderer
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -72,7 +72,7 @@ data class ProjectedPersonShape(
|
|||||||
|
|
||||||
object Names {
|
object Names {
|
||||||
|
|
||||||
val id = ResourceLocation("quaedam", "projected-person-names")
|
val id = Quaedam.resource("projected-person-names")
|
||||||
|
|
||||||
var names = emptySet<String>()
|
var names = emptySet<String>()
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ data class ProjectedPersonShape(
|
|||||||
|
|
||||||
object Skins {
|
object Skins {
|
||||||
|
|
||||||
val id = ResourceLocation("quaedam", "skins")
|
val id = Quaedam.resource("skins")
|
||||||
|
|
||||||
var skins = emptyList<ResourceLocation>()
|
var skins = emptyList<ResourceLocation>()
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ import net.minecraft.world.item.BlockItem
|
|||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import quaedam.Quaedam
|
import quaedam.Quaedam
|
||||||
import quaedam.projection.ProjectionEffectType
|
import quaedam.projection.ProjectionEffectType
|
||||||
|
import quaedam.projection.SimpleProjectionEntity
|
||||||
|
|
||||||
object SwarmProjection {
|
object SwarmProjection {
|
||||||
|
|
||||||
@@ -23,6 +24,10 @@ object SwarmProjection {
|
|||||||
ProjectionEffectType { SwarmProjectionEffect() }
|
ProjectionEffectType { SwarmProjectionEffect() }
|
||||||
}!!
|
}!!
|
||||||
|
|
||||||
|
val blockEntity = Quaedam.blockEntities.register(ID) {
|
||||||
|
SimpleProjectionEntity.createBlockEntityType(block, ::SwarmProjectionEffect)
|
||||||
|
}!!
|
||||||
|
|
||||||
init {
|
init {
|
||||||
ProjectedPersonEntity
|
ProjectedPersonEntity
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,9 @@
|
|||||||
package quaedam.projection.swarm
|
package quaedam.projection.swarm
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
import quaedam.projection.EntityProjectionBlock
|
||||||
import net.minecraft.server.level.ServerLevel
|
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
|
||||||
import quaedam.projection.ProjectionBlock
|
|
||||||
|
|
||||||
object SwarmProjectionBlock : ProjectionBlock<SwarmProjectionEffect>() {
|
object SwarmProjectionBlock : EntityProjectionBlock<SwarmProjectionEffect>() {
|
||||||
|
|
||||||
override fun createProjectionEffect(
|
override val blockEntity = SwarmProjection.blockEntity
|
||||||
level: ServerLevel,
|
|
||||||
state: BlockState,
|
|
||||||
pos: BlockPos
|
|
||||||
) = SwarmProjectionEffect()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -3,14 +3,24 @@ package quaedam.projection.swarm
|
|||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.world.entity.MobSpawnType
|
||||||
|
import net.minecraft.world.level.levelgen.Heightmap
|
||||||
|
import quaedam.config.QuaedamConfig
|
||||||
import quaedam.projection.ProjectionEffect
|
import quaedam.projection.ProjectionEffect
|
||||||
|
import quaedam.projector.Projector
|
||||||
|
import quaedam.projector.ProjectorBlockEntity
|
||||||
|
import quaedam.shell.ProjectionEffectShell
|
||||||
|
import quaedam.shell.buildProjectionEffectShell
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
data class SwarmProjectionEffect(
|
data class SwarmProjectionEffect(
|
||||||
var maxCount: Int = 10,
|
var maxCount: Int = 180,
|
||||||
) : ProjectionEffect() {
|
) : ProjectionEffect(), ProjectionEffectShell.Provider {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG_MAX_COUNT = "MaxCount"
|
const val TAG_MAX_COUNT = "MaxCount"
|
||||||
|
|
||||||
|
val maxMaxCount get() = QuaedamConfig.current.valuesInt["projection.swarm.max_max_count"] ?: 250
|
||||||
}
|
}
|
||||||
|
|
||||||
override val type
|
override val type
|
||||||
@@ -20,11 +30,40 @@ data class SwarmProjectionEffect(
|
|||||||
tag.putInt(TAG_MAX_COUNT, maxCount)
|
tag.putInt(TAG_MAX_COUNT, maxCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fromNbt(tag: CompoundTag) {
|
override fun fromNbt(tag: CompoundTag, trusted: Boolean) {
|
||||||
maxCount = tag.getInt(TAG_MAX_COUNT)
|
maxCount = tag.getInt(TAG_MAX_COUNT)
|
||||||
|
if (!trusted) {
|
||||||
|
maxCount = min(maxCount, maxMaxCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun randomTick(level: ServerLevel, pos: BlockPos) {
|
override fun randomTick(level: ServerLevel, pos: BlockPos) {
|
||||||
|
val projector = level.getBlockEntity(pos) as ProjectorBlockEntity
|
||||||
|
val entities = level.getEntitiesOfClass(ProjectedPersonEntity::class.java, projector.effectAreaAABB).size
|
||||||
|
if (entities < maxCount) {
|
||||||
|
val area = projector.effectArea
|
||||||
|
for (i in 0..(min(level.random.nextInt(maxCount - entities), 6))) {
|
||||||
|
var spawnPos = BlockPos(
|
||||||
|
level.random.nextInt(area.minX(), area.maxX()),
|
||||||
|
area.minY(),
|
||||||
|
level.random.nextInt(area.minZ(), area.maxZ()),
|
||||||
|
)
|
||||||
|
spawnPos = spawnPos.atY(level.getHeight(Heightmap.Types.WORLD_SURFACE, spawnPos.x, spawnPos.z))
|
||||||
|
if (Projector.findNearbyProjections(level, spawnPos, SwarmProjection.effect.get()).isEmpty())
|
||||||
|
continue
|
||||||
|
val belowState = level.getBlockState(spawnPos.below())
|
||||||
|
val state = level.getBlockState(spawnPos)
|
||||||
|
if (belowState.isAir || !belowState.fluidState.isEmpty || !belowState.canOcclude())
|
||||||
|
continue
|
||||||
|
if (!state.fluidState.isEmpty)
|
||||||
|
continue
|
||||||
|
ProjectedPersonEntity.entity.get().spawn(level, spawnPos, MobSpawnType.TRIGGERED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createShell() = buildProjectionEffectShell(this) {
|
||||||
|
intSlider("quaedam.shell.swarm.max_count", ::maxCount, 0..maxMaxCount step 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,58 @@
|
|||||||
|
package quaedam.projection.swarm.ai
|
||||||
|
|
||||||
|
import net.minecraft.core.GlobalPos
|
||||||
|
import net.minecraft.world.entity.ai.behavior.AcquirePoi
|
||||||
|
import net.minecraft.world.entity.ai.behavior.StrollAroundPoi
|
||||||
|
import net.minecraft.world.entity.ai.behavior.StrollToPoi
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||||
|
import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||||
|
import net.minecraft.world.entity.ai.village.poi.PoiTypes
|
||||||
|
import net.minecraft.world.level.block.Blocks
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.projection.music.SmartInstrumentBlock
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object AmusementAI {
|
||||||
|
|
||||||
|
const val ID = "amusement"
|
||||||
|
|
||||||
|
val poiType = Quaedam.poiTypes.register(ID) {
|
||||||
|
PoiType(
|
||||||
|
setOf(
|
||||||
|
Blocks.NOTE_BLOCK,
|
||||||
|
SmartInstrumentBlock,
|
||||||
|
Blocks.HONEY_BLOCK,
|
||||||
|
Blocks.TARGET,
|
||||||
|
).flatMap { it.stateDefinition.possibleStates }.toSet(),
|
||||||
|
16, 10
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val poiTypes by lazy {
|
||||||
|
setOf(
|
||||||
|
poiType.key,
|
||||||
|
PoiTypes.LIBRARIAN,
|
||||||
|
PoiTypes.MEETING,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val memory = Quaedam.memoryTypes.register(ID) {
|
||||||
|
MemoryModuleType(Optional.of(GlobalPos.CODEC))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
fun createAcquirePoi() =
|
||||||
|
AcquirePoi.create({ it.`is` { key -> key in poiTypes } }, memory.get(), false, Optional.empty())
|
||||||
|
|
||||||
|
fun createStrollToPoi() =
|
||||||
|
StrollToPoi.create(memory.get(), 0.4f, 7, 15)
|
||||||
|
|
||||||
|
fun createStrollToPoiBaby() =
|
||||||
|
StrollToPoi.create(memory.get(), 0.7f, 5, 10)
|
||||||
|
|
||||||
|
fun createStrollAroundPoi() =
|
||||||
|
StrollAroundPoi.create(memory.get(), 0.4f, 10)
|
||||||
|
|
||||||
|
fun createStrollAroundPoiBaby() =
|
||||||
|
StrollAroundPoi.create(memory.get(), 0.55f, 8)
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
package quaedam.projection.swarm.ai
|
||||||
|
|
||||||
|
import net.minecraft.core.GlobalPos
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.world.entity.LivingEntity
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||||
|
import net.minecraft.world.entity.ai.sensing.Sensor
|
||||||
|
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||||
|
import net.minecraft.world.level.block.BedBlock
|
||||||
|
import net.minecraft.world.level.block.entity.BedBlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.properties.BedPart
|
||||||
|
import quaedam.Quaedam
|
||||||
|
|
||||||
|
class BedInChunkSensor : Sensor<LivingEntity>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val ID = "bed_in_chunk"
|
||||||
|
|
||||||
|
val sensor = Quaedam.sensors.register(ID) {
|
||||||
|
SensorType(::BedInChunkSensor)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun requires() = setOf(MemoryModuleType.NEAREST_BED)
|
||||||
|
|
||||||
|
override fun doTick(level: ServerLevel, entity: LivingEntity) {
|
||||||
|
if (entity.tickCount and 0b11111 == 0 && !entity.isSleeping) { // 32gt
|
||||||
|
level.getChunkAt(entity.blockPosition()).blockEntities
|
||||||
|
.filterValues { it is BedBlockEntity }
|
||||||
|
.keys
|
||||||
|
.filter { level.getBlockState(it).getValue(BedBlock.PART) == BedPart.HEAD }
|
||||||
|
.filter { !level.getBlockState(it).getValue(BedBlock.OCCUPIED) }
|
||||||
|
.minByOrNull { it.distManhattan(entity.blockPosition()) }
|
||||||
|
?.also { entity.brain.setMemory(MemoryModuleType.NEAREST_BED, it) }
|
||||||
|
?.also {
|
||||||
|
if (entity.brain.checkMemory(MemoryModuleType.HOME, MemoryStatus.REGISTERED)) {
|
||||||
|
entity.brain.setMemory(MemoryModuleType.HOME, GlobalPos.of(level.dimension(), it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,132 @@
|
|||||||
|
package quaedam.projection.swarm.ai
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.sounds.SoundEvents
|
||||||
|
import net.minecraft.world.Container
|
||||||
|
import net.minecraft.world.entity.Mob
|
||||||
|
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||||
|
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||||
|
import net.minecraft.world.entity.npc.InventoryCarrier
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
|
||||||
|
import net.minecraft.world.level.block.entity.ChestBlockEntity
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class ExchangeItem<E> : Behavior<E>(
|
||||||
|
mapOf(
|
||||||
|
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
|
||||||
|
NearestVisibleContainer.memory.get() to MemoryStatus.VALUE_PRESENT,
|
||||||
|
), 5 * 20, 12 * 20
|
||||||
|
) where E : Mob, E : InventoryCarrier {
|
||||||
|
|
||||||
|
private var target: BlockPos? = null
|
||||||
|
private var closeAt: Long? = null
|
||||||
|
|
||||||
|
override fun start(level: ServerLevel, entity: E, l: Long) {
|
||||||
|
target = entity.brain.getMemory(NearestVisibleContainer.memory.get()).get()
|
||||||
|
closeAt = null
|
||||||
|
entity.brain.setMemory(MemoryModuleType.WALK_TARGET, WalkTarget(target!!, 1.0f, 2))
|
||||||
|
entity.brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canStillUse(level: ServerLevel, owner: E, gameTime: Long) =
|
||||||
|
owner.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent
|
||||||
|
|| owner.brain.getMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE).isEmpty
|
||||||
|
|| (closeAt != null && closeAt!! < gameTime)
|
||||||
|
|
||||||
|
override fun tick(level: ServerLevel, owner: E, gameTime: Long) {
|
||||||
|
if (closeAt == null) {
|
||||||
|
if (owner.brain.getMemory(MemoryModuleType.WALK_TARGET).isEmpty) {
|
||||||
|
// reached
|
||||||
|
val chest = level.getBlockEntity(target!!) ?: return
|
||||||
|
if (chest !is BaseContainerBlockEntity)
|
||||||
|
return
|
||||||
|
if (chest is ChestBlockEntity) {
|
||||||
|
ChestBlockEntity.playSound(level, target!!, level.getBlockState(target!!), SoundEvents.CHEST_OPEN)
|
||||||
|
}
|
||||||
|
if (chest.isEmpty && level.random.nextBoolean()) {
|
||||||
|
closeAt = gameTime + 7
|
||||||
|
} else {
|
||||||
|
closeAt = gameTime + 10 + level.random.nextInt(100)
|
||||||
|
exchangeItems(level, owner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stop(level: ServerLevel, owner: E, gameTime: Long) {
|
||||||
|
owner.brain.eraseMemory(MemoryModuleType.WALK_TARGET)
|
||||||
|
owner.brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)
|
||||||
|
if (closeAt != null) {
|
||||||
|
// opened
|
||||||
|
val chest = level.getBlockEntity(target!!) ?: return
|
||||||
|
if (chest is ChestBlockEntity) {
|
||||||
|
ChestBlockEntity.playSound(level, target!!, level.getBlockState(target!!), SoundEvents.CHEST_CLOSE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exchangeItems(level: ServerLevel, entity: E) {
|
||||||
|
val container = level.getBlockEntity(target!!) ?: return
|
||||||
|
if (container !is Container)
|
||||||
|
return
|
||||||
|
val inventory = entity.inventory
|
||||||
|
for (i in 1..10) {
|
||||||
|
val maxCount = 1 + level.random.nextInt(16)
|
||||||
|
if (level.random.nextBoolean()) {
|
||||||
|
// take
|
||||||
|
val slot = level.random.nextInt(container.containerSize)
|
||||||
|
val item = container.getItem(slot)
|
||||||
|
if (!item.isEmpty) {
|
||||||
|
val takeCount = min(item.count, maxCount)
|
||||||
|
val takeItem = item.copyWithCount(takeCount)
|
||||||
|
if (entity.canHoldItem(takeItem)) {
|
||||||
|
val remaining = inventory.addItem(/*entity.equipItemIfPossible(takeItem)*/ takeItem)
|
||||||
|
val actualCount = takeCount - remaining.count
|
||||||
|
item.shrink(actualCount)
|
||||||
|
container.setItem(slot, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// put
|
||||||
|
val slot = level.random.nextInt(inventory.containerSize)
|
||||||
|
val item = inventory.getItem(slot)
|
||||||
|
if (!item.isEmpty) {
|
||||||
|
val takeCount = min(item.count, maxCount)
|
||||||
|
val takeItem = item.copyWithCount(takeCount)
|
||||||
|
for (target in 0 until container.containerSize) {
|
||||||
|
val targetItem = container.getItem(target)
|
||||||
|
if (ItemStack.isSameItemSameTags(targetItem, takeItem)) {
|
||||||
|
val resultCount = min(targetItem.count + takeItem.count, item.maxStackSize)
|
||||||
|
val putCount = resultCount - targetItem.count
|
||||||
|
if (putCount != 0) {
|
||||||
|
targetItem.grow(putCount)
|
||||||
|
container.setItem(target, targetItem)
|
||||||
|
takeItem.shrink(putCount)
|
||||||
|
if (takeItem.isEmpty) break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!takeItem.isEmpty) {
|
||||||
|
for (target in 0 until container.containerSize) {
|
||||||
|
val targetItem = container.getItem(target)
|
||||||
|
if (targetItem.isEmpty) {
|
||||||
|
container.setItem(target, takeItem.copyAndClear())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val putCount = takeCount - takeItem.count
|
||||||
|
item.shrink(putCount)
|
||||||
|
inventory.setItem(slot, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.setChanged()
|
||||||
|
inventory.setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
package quaedam.projection.swarm.ai
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.LivingEntity
|
||||||
|
import net.minecraft.world.entity.ai.behavior.OneShot
|
||||||
|
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||||
|
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity
|
||||||
|
import net.minecraft.world.entity.npc.InventoryCarrier
|
||||||
|
import net.minecraft.world.entity.schedule.Activity
|
||||||
|
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
fun <E> LostItem(chance: Int): OneShot<E>
|
||||||
|
where E : LivingEntity, E : InventoryCarrier = BehaviorBuilder.create { instance ->
|
||||||
|
instance.point(Trigger { level, entity: E, l: Long ->
|
||||||
|
if (entity.brain.isActive(Activity.REST)) return@Trigger false
|
||||||
|
if (level.random.nextInt(chance) != 0) return@Trigger false
|
||||||
|
val inventory = entity.inventory
|
||||||
|
val item = inventory.getItem(level.random.nextInt(inventory.containerSize))
|
||||||
|
if (!item.isEmpty) {
|
||||||
|
val count = level.random.nextInt(item.count)
|
||||||
|
item.shrink(count)
|
||||||
|
inventory.setChanged()
|
||||||
|
val itemEntity = ItemEntity(level, entity.x, entity.y + 0.25, entity.z, item.copyWithCount(count))
|
||||||
|
itemEntity.setDefaultPickUpDelay()
|
||||||
|
level.addFreshEntity(itemEntity)
|
||||||
|
return@Trigger true
|
||||||
|
} else {
|
||||||
|
return@Trigger false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
package quaedam.projection.swarm.ai
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.world.entity.LivingEntity
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||||
|
import net.minecraft.world.entity.ai.sensing.Sensor
|
||||||
|
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||||
|
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.utils.getChunksNearby
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class NearestVisibleContainer : Sensor<LivingEntity>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val ID = "nearest_visible_container"
|
||||||
|
|
||||||
|
val sensor = Quaedam.sensors.register(ID) {
|
||||||
|
SensorType(::NearestVisibleContainer)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val memory = Quaedam.memoryTypes.register(ID) {
|
||||||
|
MemoryModuleType(Optional.of(BlockPos.CODEC))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun requires() = setOf(memory.get())
|
||||||
|
|
||||||
|
override fun doTick(level: ServerLevel, entity: LivingEntity) {
|
||||||
|
if (entity.tickCount and 0b11111 == 0) { // 32gt
|
||||||
|
val pos = level.getChunksNearby(entity.blockPosition(), 1)
|
||||||
|
.flatMap { it.blockEntities.filterValues { be -> be is BaseContainerBlockEntity }.keys }
|
||||||
|
.sortedBy { it.distManhattan(entity.blockPosition()) / 5 }
|
||||||
|
.shuffled(Random(System.currentTimeMillis() / 10000))
|
||||||
|
.firstOrNull()
|
||||||
|
entity.brain.setMemory(memory.get(), pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,208 @@
|
|||||||
|
package quaedam.projection.swarm.ai
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import net.minecraft.core.registries.Registries
|
||||||
|
import net.minecraft.tags.TagKey
|
||||||
|
import net.minecraft.world.entity.LivingEntity
|
||||||
|
import net.minecraft.world.entity.ai.Brain
|
||||||
|
import net.minecraft.world.entity.ai.behavior.*
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||||
|
import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities
|
||||||
|
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||||
|
import net.minecraft.world.entity.monster.Monster
|
||||||
|
import net.minecraft.world.entity.schedule.Activity
|
||||||
|
import net.minecraft.world.entity.schedule.Schedule
|
||||||
|
import net.minecraft.world.entity.schedule.ScheduleBuilder
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.projection.swarm.ProjectedPersonEntity
|
||||||
|
import quaedam.utils.weight
|
||||||
|
import quaedam.utils.weightR
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
|
||||||
|
object ProjectedPersonAI {
|
||||||
|
|
||||||
|
val tagEnemy = TagKey.create(Registries.ENTITY_TYPE, Quaedam.resource("projected_person/enemy"))
|
||||||
|
val tagNoAttack = TagKey.create(Registries.ENTITY_TYPE, Quaedam.resource("projected_person/no_attack"))
|
||||||
|
|
||||||
|
val defaultSchedule = Quaedam.schedules.register("projected_person_default") {
|
||||||
|
ScheduleBuilder(Schedule()).changeActivityAt(10, Activity.IDLE)
|
||||||
|
.changeActivityAt(10, Activity.IDLE)
|
||||||
|
.changeActivityAt(900, Activity.WORK)
|
||||||
|
.changeActivityAt(6300, Activity.IDLE)
|
||||||
|
.changeActivityAt(9000, Activity.WORK)
|
||||||
|
.changeActivityAt(10700, Activity.IDLE)
|
||||||
|
.changeActivityAt(11000, Activity.PLAY)
|
||||||
|
.changeActivityAt(11500, Activity.IDLE)
|
||||||
|
.changeActivityAt(12000, Activity.REST)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
val babySchedule = Quaedam.schedules.register("projected_person_baby") {
|
||||||
|
ScheduleBuilder(Schedule()).changeActivityAt(10, Activity.IDLE)
|
||||||
|
.changeActivityAt(10, Activity.IDLE)
|
||||||
|
.changeActivityAt(3200, Activity.PLAY)
|
||||||
|
.changeActivityAt(7000, Activity.IDLE)
|
||||||
|
.changeActivityAt(9000, Activity.PLAY)
|
||||||
|
.changeActivityAt(11000, Activity.REST)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
BedInChunkSensor
|
||||||
|
AmusementAI
|
||||||
|
WorkPoiAI
|
||||||
|
NearestVisibleContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
private val memoryTypes by lazy {
|
||||||
|
listOf(
|
||||||
|
MemoryModuleType.PATH,
|
||||||
|
MemoryModuleType.LOOK_TARGET,
|
||||||
|
MemoryModuleType.WALK_TARGET,
|
||||||
|
MemoryModuleType.ATTACK_TARGET,
|
||||||
|
MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
|
||||||
|
MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM,
|
||||||
|
MemoryModuleType.HURT_BY,
|
||||||
|
MemoryModuleType.ATTACK_COOLING_DOWN,
|
||||||
|
MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE,
|
||||||
|
MemoryModuleType.HOME,
|
||||||
|
MemoryModuleType.LAST_WOKEN,
|
||||||
|
MemoryModuleType.NEAREST_BED,
|
||||||
|
NearestVisibleContainer.memory.get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val sensorTypes by lazy {
|
||||||
|
listOf(
|
||||||
|
SensorType.NEAREST_LIVING_ENTITIES,
|
||||||
|
SensorType.NEAREST_PLAYERS,
|
||||||
|
SensorType.NEAREST_ITEMS,
|
||||||
|
SensorType.HURT_BY,
|
||||||
|
BedInChunkSensor.sensor.get(),
|
||||||
|
NearestVisibleContainer.sensor.get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun provider(): Brain.Provider<out ProjectedPersonEntity> = Brain.provider(memoryTypes, sensorTypes)
|
||||||
|
|
||||||
|
fun initBrain(entity: ProjectedPersonEntity, brain: Brain<ProjectedPersonEntity>) {
|
||||||
|
initCoreActivity(brain)
|
||||||
|
initIdleActivity(brain)
|
||||||
|
initPlayActivity(brain)
|
||||||
|
initWorkActivity(brain)
|
||||||
|
initRestActivity(brain)
|
||||||
|
brain.setCoreActivities(setOf(Activity.CORE))
|
||||||
|
brain.setDefaultActivity(Activity.IDLE)
|
||||||
|
updateSchedule(entity, brain, baby = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSchedule(entity: ProjectedPersonEntity, brain: Brain<ProjectedPersonEntity>, baby: Boolean) {
|
||||||
|
if (baby) {
|
||||||
|
brain.schedule = babySchedule.get()
|
||||||
|
} else {
|
||||||
|
brain.schedule = defaultSchedule.get()
|
||||||
|
}
|
||||||
|
brain.updateActivityFromSchedule(entity.level().dayTime, entity.level().gameTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSchedule(entity: ProjectedPersonEntity) = updateSchedule(entity, entity.brain, entity.shape.baby)
|
||||||
|
|
||||||
|
private fun initCoreActivity(brain: Brain<ProjectedPersonEntity>) {
|
||||||
|
brain.addActivity(
|
||||||
|
Activity.CORE, ImmutableList.of(
|
||||||
|
0 weight UpdateActivityFromSchedule.create(),
|
||||||
|
0 weight Swim(0.8f),
|
||||||
|
0 weight WakeUp.create(),
|
||||||
|
0 weight StopAttackingIfTargetInvalid.create(),
|
||||||
|
3 weight LookAtTargetSink(40, 70),
|
||||||
|
3 weight MoveToTargetSink(),
|
||||||
|
3 weight InteractWithDoor.create(),
|
||||||
|
3 weight SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, 1.2f, 6, false),
|
||||||
|
4 weight MeleeAttack.create(15),
|
||||||
|
5 weight LostItem(400),
|
||||||
|
5 weight StartAttacking.create(::findAttackTarget),
|
||||||
|
5 weight SetWalkTargetFromAttackTargetIfTargetOutOfReach.create(1.1f),
|
||||||
|
10 weight GoToWantedItem.create(1.2f, false, 7),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initIdleActivity(brain: Brain<ProjectedPersonEntity>) {
|
||||||
|
brain.addActivity(
|
||||||
|
Activity.IDLE, ImmutableList.of(
|
||||||
|
5 weight AmusementAI.createStrollAroundPoi(),
|
||||||
|
7 weight AmusementAI.createStrollToPoi(),
|
||||||
|
9 weight AmusementAI.createAcquirePoi(),
|
||||||
|
10 weight createStrollBehavior(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initPlayActivity(brain: Brain<ProjectedPersonEntity>) {
|
||||||
|
brain.addActivity(
|
||||||
|
Activity.PLAY, ImmutableList.of(
|
||||||
|
3 weight GoToWantedItem.create(1.75f, true, 32),
|
||||||
|
5 weight AmusementAI.createStrollAroundPoiBaby(),
|
||||||
|
7 weight AmusementAI.createStrollToPoiBaby(),
|
||||||
|
9 weight AmusementAI.createAcquirePoi(),
|
||||||
|
10 weight JumpOnBed(1.0f),
|
||||||
|
10 weight createStrollBehavior(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initWorkActivity(brain: Brain<ProjectedPersonEntity>) {
|
||||||
|
brain.addActivity(
|
||||||
|
Activity.WORK, ImmutableList.of(
|
||||||
|
5 weight ExchangeItem(),
|
||||||
|
7 weight WorkPoiAI.createStrollAroundPoi(),
|
||||||
|
7 weight WorkPoiAI.createStrollToPoi(),
|
||||||
|
10 weight RunOne(
|
||||||
|
mapOf(),
|
||||||
|
listOf(
|
||||||
|
1 weightR createStrollBehavior(),
|
||||||
|
1 weightR WorkPoiAI.createAcquirePoi(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initRestActivity(brain: Brain<ProjectedPersonEntity>) {
|
||||||
|
brain.addActivity(
|
||||||
|
Activity.REST, ImmutableList.of(
|
||||||
|
0 weight SleepInBed(),
|
||||||
|
3 weight GoToTargetLocation.create(MemoryModuleType.NEAREST_BED, 1, 1.05f),
|
||||||
|
3 weight RunOne(
|
||||||
|
mapOf(
|
||||||
|
MemoryModuleType.HOME to MemoryStatus.VALUE_ABSENT
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
1 weightR createStrollBehavior()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStrollBehavior() = RunOne(
|
||||||
|
listOf(
|
||||||
|
2 weightR RandomStroll.stroll(1.0f, 42, 12),
|
||||||
|
2 weightR SetWalkTargetFromLookTarget.create(1.0f, 5),
|
||||||
|
1 weightR DoNothing(30, 60)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun findAttackTarget(entity: ProjectedPersonEntity): Optional<out LivingEntity> {
|
||||||
|
val entities = entity.brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).getOrNull()
|
||||||
|
?: NearestVisibleLivingEntities.empty()
|
||||||
|
return entities.findClosest { target: LivingEntity ->
|
||||||
|
entity.canAttack(target)
|
||||||
|
&& !target.type.`is`(tagNoAttack)
|
||||||
|
&& (target.type.`is`(tagEnemy) || target is Monster)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,15 +1,21 @@
|
|||||||
package quaedam.projection.swarm
|
package quaedam.projection.swarm.ai
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation
|
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.level.pathfinder.Path
|
import net.minecraft.world.level.pathfinder.Path
|
||||||
|
import quaedam.misc.causality.CausalityAnchor
|
||||||
|
import quaedam.projection.swarm.ProjectedPersonEntity
|
||||||
|
import quaedam.projection.swarm.SwarmProjection
|
||||||
import quaedam.projector.Projector
|
import quaedam.projector.Projector
|
||||||
|
|
||||||
class ProjectedPersonNavigation(val entity: ProjectedPersonEntity, level: Level) : GroundPathNavigation(entity, level) {
|
class ProjectedPersonNavigation(val entity: ProjectedPersonEntity, level: Level) : GroundPathNavigation(entity, level) {
|
||||||
|
|
||||||
override fun createPath(set: MutableSet<BlockPos>, i: Int, bl: Boolean, j: Int, f: Float): Path? {
|
override fun createPath(set: MutableSet<BlockPos>, i: Int, bl: Boolean, j: Int, f: Float): Path? {
|
||||||
if (set.any { Projector.findNearbyProjections(level, it, SwarmProjection.effect.get()).isEmpty() }) {
|
if (set.any {
|
||||||
|
Projector.findNearbyProjections(level, it, SwarmProjection.effect.get())
|
||||||
|
.isEmpty() && !CausalityAnchor.checkEffect(level, it)
|
||||||
|
}) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return super.createPath(set, i, bl, j, f)
|
return super.createPath(set, i, bl, j, f)
|
@@ -0,0 +1,49 @@
|
|||||||
|
package quaedam.projection.swarm.ai
|
||||||
|
|
||||||
|
import net.minecraft.core.GlobalPos
|
||||||
|
import net.minecraft.world.entity.ai.behavior.AcquirePoi
|
||||||
|
import net.minecraft.world.entity.ai.behavior.StrollAroundPoi
|
||||||
|
import net.minecraft.world.entity.ai.behavior.StrollToPoi
|
||||||
|
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||||
|
import net.minecraft.world.entity.ai.village.poi.PoiTypes
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object WorkPoiAI {
|
||||||
|
|
||||||
|
const val ID = "work"
|
||||||
|
|
||||||
|
val poiTypes by lazy {
|
||||||
|
setOf(
|
||||||
|
PoiTypes.ARMORER,
|
||||||
|
PoiTypes.BUTCHER,
|
||||||
|
PoiTypes.CARTOGRAPHER,
|
||||||
|
PoiTypes.CLERIC,
|
||||||
|
PoiTypes.FARMER,
|
||||||
|
PoiTypes.FISHERMAN,
|
||||||
|
PoiTypes.FLETCHER,
|
||||||
|
PoiTypes.LEATHERWORKER,
|
||||||
|
PoiTypes.LIBRARIAN,
|
||||||
|
PoiTypes.MASON,
|
||||||
|
PoiTypes.SHEPHERD,
|
||||||
|
PoiTypes.TOOLSMITH,
|
||||||
|
PoiTypes.WEAPONSMITH,
|
||||||
|
PoiTypes.LODESTONE,
|
||||||
|
PoiTypes.LIGHTNING_ROD,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val memory = Quaedam.memoryTypes.register(ID) {
|
||||||
|
MemoryModuleType(Optional.of(GlobalPos.CODEC))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
fun createAcquirePoi() =
|
||||||
|
AcquirePoi.create({ it.`is` { key -> key in poiTypes } }, memory.get(), false, Optional.empty())
|
||||||
|
|
||||||
|
fun createStrollToPoi() =
|
||||||
|
StrollToPoi.create(memory.get(), 0.4f, 7, 4)
|
||||||
|
|
||||||
|
fun createStrollAroundPoi() =
|
||||||
|
StrollAroundPoi.create(memory.get(), 0.4f, 5)
|
||||||
|
|
||||||
|
}
|
@@ -6,6 +6,8 @@ import net.minecraft.world.item.Item
|
|||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
import quaedam.Quaedam
|
import quaedam.Quaedam
|
||||||
|
import quaedam.config.QuaedamConfig
|
||||||
|
import quaedam.misc.reality.RealityStabler
|
||||||
import quaedam.projection.ProjectionEffect
|
import quaedam.projection.ProjectionEffect
|
||||||
import quaedam.projection.ProjectionEffectType
|
import quaedam.projection.ProjectionEffectType
|
||||||
import quaedam.utils.getChunksNearby
|
import quaedam.utils.getChunksNearby
|
||||||
@@ -13,7 +15,6 @@ import quaedam.utils.getChunksNearby
|
|||||||
object Projector {
|
object Projector {
|
||||||
|
|
||||||
const val ID = "projector"
|
const val ID = "projector"
|
||||||
const val EFFECT_RADIUS = 4
|
|
||||||
|
|
||||||
val block = Quaedam.blocks.register(ID) { ProjectorBlock }!!
|
val block = Quaedam.blocks.register(ID) { ProjectorBlock }!!
|
||||||
|
|
||||||
@@ -29,18 +30,28 @@ object Projector {
|
|||||||
BlockEntityType.Builder.of(::ProjectorBlockEntity, block.get()).build(null)
|
BlockEntityType.Builder.of(::ProjectorBlockEntity, block.get()).build(null)
|
||||||
}!!
|
}!!
|
||||||
|
|
||||||
fun findNearbyProjectors(level: Level, pos: BlockPos) = level.getChunksNearby(pos, EFFECT_RADIUS)
|
val currentEffectRadius get() = QuaedamConfig.current.valuesInt["projector.effect_radius"] ?: 4
|
||||||
|
|
||||||
|
fun findNearbyProjectors(level: Level, pos: BlockPos) = level.getChunksNearby(pos, currentEffectRadius)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
it.blockEntities.filter { (_, v) -> v is ProjectorBlockEntity }
|
it.blockEntities.filter { (_, v) -> v is ProjectorBlockEntity && pos in v }
|
||||||
.keys
|
.keys
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
}
|
}
|
||||||
.toSet()
|
.toSet()
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T : ProjectionEffect> findNearbyProjections(level: Level, pos: BlockPos, type: ProjectionEffectType<T>) =
|
fun <T : ProjectionEffect> findNearbyProjections(
|
||||||
findNearbyProjectors(level, pos)
|
level: Level,
|
||||||
|
pos: BlockPos,
|
||||||
|
type: ProjectionEffectType<T>
|
||||||
|
): List<T> {
|
||||||
|
if (RealityStabler.checkEffect(level, pos)) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
return findNearbyProjectors(level, pos)
|
||||||
.map { level.getBlockEntity(it) as ProjectorBlockEntity }
|
.map { level.getBlockEntity(it) as ProjectorBlockEntity }
|
||||||
.mapNotNull { it.effects[type] as T? }
|
.mapNotNull { it.effects[type] as T? }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package quaedam.projector
|
package quaedam.projector
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
import net.minecraft.util.RandomSource
|
import net.minecraft.util.RandomSource
|
||||||
import net.minecraft.world.InteractionHand
|
import net.minecraft.world.InteractionHand
|
||||||
@@ -13,7 +14,10 @@ import net.minecraft.world.level.block.Block
|
|||||||
import net.minecraft.world.level.block.EntityBlock
|
import net.minecraft.world.level.block.EntityBlock
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import net.minecraft.world.level.material.MapColor
|
import net.minecraft.world.level.material.MapColor
|
||||||
|
import net.minecraft.world.level.material.PushReaction
|
||||||
import net.minecraft.world.phys.BlockHitResult
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
|
import quaedam.shell.ProjectionShellItem
|
||||||
|
import quaedam.utils.sendBlockUpdated
|
||||||
|
|
||||||
object ProjectorBlock : Block(Properties.of()
|
object ProjectorBlock : Block(Properties.of()
|
||||||
.jumpFactor(0.8f)
|
.jumpFactor(0.8f)
|
||||||
@@ -21,7 +25,9 @@ object ProjectorBlock : Block(Properties.of()
|
|||||||
.mapColor(MapColor.COLOR_BLACK)
|
.mapColor(MapColor.COLOR_BLACK)
|
||||||
.randomTicks()
|
.randomTicks()
|
||||||
.strength(4.0f)
|
.strength(4.0f)
|
||||||
.requiresCorrectToolForDrops()), EntityBlock {
|
.requiresCorrectToolForDrops()
|
||||||
|
.pushReaction(PushReaction.IGNORE)
|
||||||
|
), EntityBlock {
|
||||||
|
|
||||||
fun checkUpdate(level: Level, pos: BlockPos) {
|
fun checkUpdate(level: Level, pos: BlockPos) {
|
||||||
if (!level.isClientSide) {
|
if (!level.isClientSide) {
|
||||||
@@ -38,8 +44,23 @@ object ProjectorBlock : Block(Properties.of()
|
|||||||
interactionHand: InteractionHand,
|
interactionHand: InteractionHand,
|
||||||
blockHitResult: BlockHitResult
|
blockHitResult: BlockHitResult
|
||||||
): InteractionResult {
|
): InteractionResult {
|
||||||
|
if (player.getItemInHand(interactionHand).item == ProjectionShellItem) {
|
||||||
|
if (!level.isClientSide) {
|
||||||
|
val entity = level.getBlockEntity(blockPos) as ProjectorBlockEntity
|
||||||
|
var newRadius = entity.effectRadius + 1
|
||||||
|
if (newRadius > Projector.currentEffectRadius) {
|
||||||
|
newRadius = 0
|
||||||
|
}
|
||||||
|
entity.updateEffectArea(newRadius)
|
||||||
|
entity.setChanged()
|
||||||
|
entity.sendBlockUpdated()
|
||||||
|
checkUpdate(level, blockPos)
|
||||||
|
player.sendSystemMessage(Component.translatable("quaedam.projector.radius_updated", newRadius))
|
||||||
|
}
|
||||||
|
return InteractionResult.sidedSuccess(level.isClientSide)
|
||||||
|
}
|
||||||
checkUpdate(level, blockPos)
|
checkUpdate(level, blockPos)
|
||||||
return InteractionResult.SUCCESS
|
return InteractionResult.PASS
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun newBlockEntity(pos: BlockPos, state: BlockState) = ProjectorBlockEntity(pos, state)
|
override fun newBlockEntity(pos: BlockPos, state: BlockState) = ProjectorBlockEntity(pos, state)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package quaedam.projector
|
package quaedam.projector
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.SectionPos
|
||||||
import net.minecraft.core.Vec3i
|
import net.minecraft.core.Vec3i
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.network.protocol.Packet
|
import net.minecraft.network.protocol.Packet
|
||||||
@@ -12,26 +13,28 @@ import net.minecraft.world.level.ChunkPos
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import net.minecraft.world.level.levelgen.structure.BoundingBox
|
import net.minecraft.world.level.levelgen.structure.BoundingBox
|
||||||
|
import net.minecraft.world.phys.AABB
|
||||||
import quaedam.projection.ProjectionEffect
|
import quaedam.projection.ProjectionEffect
|
||||||
import quaedam.projection.ProjectionEffectType
|
import quaedam.projection.ProjectionEffectType
|
||||||
import quaedam.projection.ProjectionProvider
|
import quaedam.projection.ProjectionProvider
|
||||||
import quaedam.utils.sendBlockUpdated
|
import quaedam.utils.sendBlockUpdated
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
||||||
BlockEntity(Projector.blockEntity.get(), pos, state) {
|
BlockEntity(Projector.blockEntity.get(), pos, state) {
|
||||||
|
|
||||||
val effectAreaChunk by lazy {
|
companion object {
|
||||||
val chunk = level!!.getChunk(pos).pos
|
const val TAG_EFFECT_RADIUS = "EffectRadius"
|
||||||
ChunkPos(chunk.x - Projector.EFFECT_RADIUS, chunk.z - Projector.EFFECT_RADIUS) to
|
const val TAG_PROJECTION_EFFECTS = "ProjectionEffects"
|
||||||
ChunkPos(chunk.x + Projector.EFFECT_RADIUS, chunk.z + Projector.EFFECT_RADIUS)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val effectArea: BoundingBox by lazy {
|
var effectRadius: Int = 0
|
||||||
val chunk = level!!.getChunk(pos).pos
|
lateinit var effectArea: BoundingBox
|
||||||
val (minChunk, maxChunk) = effectAreaChunk
|
lateinit var effectAreaAABB: AABB
|
||||||
val minBlock = BlockPos(minChunk.minBlockX, level!!.minBuildHeight, minChunk.minBlockZ)
|
|
||||||
val maxBlock = BlockPos(maxChunk.maxBlockX, level!!.maxBuildHeight, maxChunk.maxBlockZ)
|
init {
|
||||||
BoundingBox.fromCorners(minBlock, maxBlock)
|
updateEffectArea(Projector.currentEffectRadius)
|
||||||
}
|
}
|
||||||
|
|
||||||
val checkArea: BoundingBox by lazy {
|
val checkArea: BoundingBox by lazy {
|
||||||
@@ -46,18 +49,20 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
|||||||
effects.map { (type, effect) ->
|
effects.map { (type, effect) ->
|
||||||
effectsTag.put(type.id.toString(), effect.toNbt())
|
effectsTag.put(type.id.toString(), effect.toNbt())
|
||||||
}
|
}
|
||||||
tag.put("ProjectionEffects", effectsTag)
|
tag.putInt(TAG_EFFECT_RADIUS, effectRadius)
|
||||||
|
tag.put(TAG_PROJECTION_EFFECTS, effectsTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun load(tag: CompoundTag) {
|
override fun load(tag: CompoundTag) {
|
||||||
super.load(tag)
|
super.load(tag)
|
||||||
val effectsTag = tag["ProjectionEffects"]
|
updateEffectArea(max(min(tag.getInt(TAG_EFFECT_RADIUS), Projector.currentEffectRadius), 0))
|
||||||
|
val effectsTag = tag[TAG_PROJECTION_EFFECTS]
|
||||||
val effects = mutableMapOf<ProjectionEffectType<*>, ProjectionEffect>()
|
val effects = mutableMapOf<ProjectionEffectType<*>, ProjectionEffect>()
|
||||||
if (effectsTag != null && effectsTag is CompoundTag) {
|
if (effectsTag != null && effectsTag is CompoundTag) {
|
||||||
effectsTag.allKeys.forEach { id ->
|
effectsTag.allKeys.forEach { id ->
|
||||||
val type = ProjectionEffectType.registry[ResourceLocation(id)]
|
val type = ProjectionEffectType.registry[ResourceLocation(id)]
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
val effect = type.constructor().apply { fromNbt(effectsTag[id] as CompoundTag) }
|
val effect = type.constructor().apply { fromNbt(effectsTag[id] as CompoundTag, true) }
|
||||||
effects[type] = effect
|
effects[type] = effect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +70,20 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
|||||||
updateEffects(effects, notify = false)
|
updateEffects(effects, notify = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateEffectArea(radius: Int) {
|
||||||
|
effectRadius = radius
|
||||||
|
val chunk = ChunkPos(SectionPos.blockToSectionCoord(blockPos.x), SectionPos.blockToSectionCoord(blockPos.z))
|
||||||
|
val minChunk = ChunkPos(chunk.x - radius, chunk.z - radius)
|
||||||
|
val maxChunk = ChunkPos(chunk.x + radius, chunk.z + radius)
|
||||||
|
// Y is not the limit value of Int because at
|
||||||
|
// Lnet/minecraft/world/level/entity/EntitySectionStorage;forEachAccessibleNonEmptySection(Lnet/minecraft/world/phys/AABB;Lnet/minecraft/util/AbortableIterationConsumer;)V
|
||||||
|
// it may get overflow
|
||||||
|
val minBlock = BlockPos(minChunk.minBlockX, Short.MIN_VALUE.toInt(), minChunk.minBlockZ)
|
||||||
|
val maxBlock = BlockPos(maxChunk.maxBlockX, Short.MAX_VALUE.toInt(), maxChunk.maxBlockZ)
|
||||||
|
effectArea = BoundingBox.fromCorners(minBlock, maxBlock)
|
||||||
|
effectAreaAABB = AABB(minBlock, maxBlock)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
||||||
|
|
||||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
|
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
|
||||||
@@ -95,6 +114,7 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
|||||||
if (!level.isClientSide && notify) {
|
if (!level.isClientSide && notify) {
|
||||||
sendBlockUpdated()
|
sendBlockUpdated()
|
||||||
}
|
}
|
||||||
|
setChanged()
|
||||||
val addedEffects = effects.filterKeys { it !in oldEffects }
|
val addedEffects = effects.filterKeys { it !in oldEffects }
|
||||||
val removedEffects = oldEffects.filterKeys { it !in effects }
|
val removedEffects = oldEffects.filterKeys { it !in effects }
|
||||||
val updatedEffects = effects.filter { (k, v) -> oldEffects[k] != null && oldEffects[k] != v }
|
val updatedEffects = effects.filter { (k, v) -> oldEffects[k] != null && oldEffects[k] != v }
|
||||||
@@ -110,9 +130,6 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
|||||||
|
|
||||||
fun collectEffects(): Map<ProjectionEffectType<*>, ProjectionEffect> {
|
fun collectEffects(): Map<ProjectionEffectType<*>, ProjectionEffect> {
|
||||||
val level = level!! as ServerLevel
|
val level = level!! as ServerLevel
|
||||||
if (!level.getBlockState(blockPos.below()).isAir) {
|
|
||||||
return emptyMap()
|
|
||||||
}
|
|
||||||
val effects = mutableMapOf<ProjectionEffectType<*>, ProjectionEffect>()
|
val effects = mutableMapOf<ProjectionEffectType<*>, ProjectionEffect>()
|
||||||
for (x in checkArea.minX()..checkArea.maxX()) {
|
for (x in checkArea.minX()..checkArea.maxX()) {
|
||||||
for (y in checkArea.minY()..checkArea.maxY()) {
|
for (y in checkArea.minY()..checkArea.maxY()) {
|
||||||
@@ -121,7 +138,7 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
|||||||
val blockState = level.getBlockState(pos)
|
val blockState = level.getBlockState(pos)
|
||||||
val block = blockState.block
|
val block = blockState.block
|
||||||
if (block is ProjectionProvider<*>) {
|
if (block is ProjectionProvider<*>) {
|
||||||
val projection = block.createProjectionEffect(level, blockState, pos)
|
val projection = block.applyProjectionEffect(level, blockState, pos)
|
||||||
if (projection != null) {
|
if (projection != null) {
|
||||||
effects[projection.type] = projection
|
effects[projection.type] = projection
|
||||||
}
|
}
|
||||||
|
143
common/src/main/kotlin/quaedam/shell/ProjectionEffectShell.kt
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
package quaedam.shell
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.components.AbstractSliderButton
|
||||||
|
import net.minecraft.client.gui.components.CycleButton
|
||||||
|
import net.minecraft.client.gui.components.StringWidget
|
||||||
|
import net.minecraft.client.gui.layouts.LayoutElement
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import quaedam.projection.ProjectionEffect
|
||||||
|
import kotlin.reflect.KMutableProperty0
|
||||||
|
|
||||||
|
class ProjectionEffectShell(val effect: ProjectionEffect) {
|
||||||
|
|
||||||
|
interface Provider {
|
||||||
|
|
||||||
|
fun createShell(): ProjectionEffectShell
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val rows = mutableListOf<Row>()
|
||||||
|
val width = 150
|
||||||
|
val height = 20
|
||||||
|
|
||||||
|
data class Row(val text: Component, val renderer: ShellRenderer)
|
||||||
|
|
||||||
|
fun row(key: String, renderer: ShellRenderer) {
|
||||||
|
rows += Row(Component.translatable(key), renderer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun text(key: String, value: Component) = row(key) { StringWidget(value, it.font) }
|
||||||
|
|
||||||
|
fun doubleSlider(key: String, property: KMutableProperty0<Double>, range: ClosedRange<Double>, rawStep: Double) {
|
||||||
|
val len = range.endInclusive - range.start
|
||||||
|
val step = rawStep / len
|
||||||
|
row(key) {
|
||||||
|
object : AbstractSliderButton(
|
||||||
|
0, 0, width, height,
|
||||||
|
Component.literal(String.format("%.2f", property.get())), (property.get() - range.start) / len
|
||||||
|
) {
|
||||||
|
override fun updateMessage() {
|
||||||
|
message = Component.literal(String.format("%.2f", property.get()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyValue() {
|
||||||
|
val diff = value % step
|
||||||
|
if (diff < step * 0.5) {
|
||||||
|
value -= diff
|
||||||
|
} else {
|
||||||
|
value += (step - diff)
|
||||||
|
}
|
||||||
|
property.set(range.start + (value * len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun floatSlider(key: String, property: KMutableProperty0<Float>, range: ClosedRange<Float>, rawStep: Float) {
|
||||||
|
val len = range.endInclusive - range.start
|
||||||
|
val step = rawStep / len
|
||||||
|
row(key) {
|
||||||
|
object : AbstractSliderButton(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
Component.literal(String.format("%.2f", property.get())),
|
||||||
|
(property.get() - range.start) / len.toDouble()
|
||||||
|
) {
|
||||||
|
override fun updateMessage() {
|
||||||
|
message = Component.literal(String.format("%.2f", property.get()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyValue() {
|
||||||
|
val diff = value % step
|
||||||
|
if (diff < step * 0.5) {
|
||||||
|
value -= diff
|
||||||
|
} else {
|
||||||
|
value += (step - diff)
|
||||||
|
}
|
||||||
|
property.set(range.start + (value.toFloat() * len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intSlider(key: String, property: KMutableProperty0<Int>, range: IntProgression) {
|
||||||
|
val len = range.last - range.first
|
||||||
|
val step = range.step.toDouble() / len
|
||||||
|
row(key) {
|
||||||
|
object : AbstractSliderButton(
|
||||||
|
0, 0, width, height,
|
||||||
|
Component.literal(property.get().toString()), (property.get() - range.first).toDouble() / len
|
||||||
|
) {
|
||||||
|
override fun updateMessage() {
|
||||||
|
message = Component.literal(property.get().toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyValue() {
|
||||||
|
val diff = value % step
|
||||||
|
if (diff < step * 0.5) {
|
||||||
|
value -= diff
|
||||||
|
} else {
|
||||||
|
value += (step - diff)
|
||||||
|
}
|
||||||
|
property.set(range.first + (value * len).toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intCycle(key: String, property: KMutableProperty0<Int>, range: IntProgression) =
|
||||||
|
row(key) {
|
||||||
|
CycleButton.builder<Int> {
|
||||||
|
property.set(it)
|
||||||
|
Component.literal(it.toString())
|
||||||
|
}
|
||||||
|
.displayOnlyValue()
|
||||||
|
.withValues(range.toList())
|
||||||
|
.withInitialValue(property.get())
|
||||||
|
.create(0, 0, width, height, Component.translatable(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun boolean(key: String, property: KMutableProperty0<Boolean>) =
|
||||||
|
row(key) {
|
||||||
|
CycleButton.builder<Boolean> {
|
||||||
|
property.set(it)
|
||||||
|
Component.translatable("$key.$it")
|
||||||
|
}
|
||||||
|
.displayOnlyValue()
|
||||||
|
.withValues(listOf(true, false))
|
||||||
|
.withInitialValue(property.get())
|
||||||
|
.create(0, 0, width, height, Component.translatable(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun buildProjectionEffectShell(effect: ProjectionEffect, builder: ProjectionEffectShell.() -> Unit) =
|
||||||
|
ProjectionEffectShell(effect).apply(builder)
|
||||||
|
|
||||||
|
data class ShellRenderContext(val screen: ProjectionShellScreen) {
|
||||||
|
val font get() = screen.getFont()
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias ShellRenderer = (ctx: ShellRenderContext) -> LayoutElement
|
25
common/src/main/kotlin/quaedam/shell/ProjectionShell.kt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package quaedam.shell
|
||||||
|
|
||||||
|
import dev.architectury.networking.NetworkChannel
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.shell.network.ClientboundPSHLockResultPacket
|
||||||
|
import quaedam.shell.network.ClientboundPSHLockRevokePacket
|
||||||
|
import quaedam.shell.network.ServerboundPSHLockAcquirePacket
|
||||||
|
import quaedam.shell.network.ServerboundPSHLockReleasePacket
|
||||||
|
|
||||||
|
object ProjectionShell {
|
||||||
|
|
||||||
|
const val ID = "projection_shell"
|
||||||
|
|
||||||
|
val item = Quaedam.items.register(ID) { ProjectionShellItem }!!
|
||||||
|
|
||||||
|
val channel: NetworkChannel = NetworkChannel.create(Quaedam.resource(ID))
|
||||||
|
|
||||||
|
init {
|
||||||
|
ServerboundPSHLockAcquirePacket
|
||||||
|
ServerboundPSHLockReleasePacket
|
||||||
|
ClientboundPSHLockRevokePacket
|
||||||
|
ClientboundPSHLockResultPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
common/src/main/kotlin/quaedam/shell/ProjectionShellBlock.kt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package quaedam.shell
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
|
||||||
|
interface ProjectionShellBlock {
|
||||||
|
|
||||||
|
fun getProjectionEffectForShell(level: Level, pos: BlockPos): ProjectionEffectShell
|
||||||
|
|
||||||
|
fun applyFromShell(level: Level, pos: BlockPos, shell: ProjectionEffectShell)
|
||||||
|
|
||||||
|
}
|
24
common/src/main/kotlin/quaedam/shell/ProjectionShellItem.kt
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package quaedam.shell
|
||||||
|
|
||||||
|
import net.minecraft.world.InteractionResult
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.item.context.UseOnContext
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.shell.network.ServerboundPSHLockAcquirePacket
|
||||||
|
|
||||||
|
object ProjectionShellItem : Item(
|
||||||
|
Properties()
|
||||||
|
.stacksTo(1)
|
||||||
|
.`arch$tab`(Quaedam.creativeModeTab)
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun useOn(context: UseOnContext): InteractionResult {
|
||||||
|
val block = context.level.getBlockState(context.clickedPos).block
|
||||||
|
if (block is ProjectionShellBlock && context.level.isClientSide) {
|
||||||
|
ProjectionShell.channel.sendToServer(ServerboundPSHLockAcquirePacket(context.clickedPos))
|
||||||
|
return InteractionResult.SUCCESS
|
||||||
|
}
|
||||||
|
return InteractionResult.PASS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
common/src/main/kotlin/quaedam/shell/ProjectionShellMutex.kt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package quaedam.shell
|
||||||
|
|
||||||
|
import dev.architectury.event.events.common.TickEvent
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.GlobalPos
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import quaedam.mixininterface.ProjectionShellMutexAccessor
|
||||||
|
import quaedam.shell.network.ClientboundPSHLockRevokePacket
|
||||||
|
|
||||||
|
object ProjectionShellMutex {
|
||||||
|
|
||||||
|
init {
|
||||||
|
TickEvent.SERVER_POST.register { server ->
|
||||||
|
if (server.tickCount and 8 == 0) {
|
||||||
|
val mutex = (server as ProjectionShellMutexAccessor).`quaedam$getProjectionShellMutex`()
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
mutex.forEach { (pos, lock) ->
|
||||||
|
if (lock.player !in server.playerList.players) {
|
||||||
|
mutex.remove(pos)
|
||||||
|
} else if (currentTime - lock.time > 60 * 1000) {
|
||||||
|
mutex.remove(pos)
|
||||||
|
ProjectionShell.channel.sendToPlayer(lock.player, ClientboundPSHLockRevokePacket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tryLock(level: ServerLevel, pos: BlockPos, player: ServerPlayer): Boolean {
|
||||||
|
val mutex = (level.server as ProjectionShellMutexAccessor).`quaedam$getProjectionShellMutex`()
|
||||||
|
val gPos = GlobalPos.of(level.dimension(), pos)
|
||||||
|
if (mutex.values.any { it.player == player }) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (gPos !in mutex) {
|
||||||
|
mutex[gPos] = Lock(player, System.currentTimeMillis())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun release(level: ServerLevel, pos: BlockPos, player: ServerPlayer) {
|
||||||
|
val mutex = (level.server as ProjectionShellMutexAccessor).`quaedam$getProjectionShellMutex`()
|
||||||
|
val gPos = GlobalPos.of(level.dimension(), pos)
|
||||||
|
if (mutex[gPos]?.player == player) {
|
||||||
|
mutex.remove(gPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Lock(val player: ServerPlayer, val time: Long)
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,86 @@
|
|||||||
|
package quaedam.shell
|
||||||
|
|
||||||
|
import dev.architectury.utils.GameInstance
|
||||||
|
import net.minecraft.client.gui.GuiGraphics
|
||||||
|
import net.minecraft.client.gui.components.Button
|
||||||
|
import net.minecraft.client.gui.components.StringWidget
|
||||||
|
import net.minecraft.client.gui.layouts.GridLayout
|
||||||
|
import net.minecraft.client.gui.screens.Screen
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import quaedam.shell.network.ServerboundPSHLockReleasePacket
|
||||||
|
|
||||||
|
class ProjectionShellScreen(val level: Level, val pos: BlockPos, val shell: ProjectionEffectShell) :
|
||||||
|
Screen(Component.translatable("quaedam.screen.projection_shell")) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val BORDER = 15
|
||||||
|
}
|
||||||
|
|
||||||
|
var layout = GridLayout()
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
|
super.init()
|
||||||
|
layout = GridLayout()
|
||||||
|
layout.spacing(4)
|
||||||
|
val renderContext = ShellRenderContext(this)
|
||||||
|
if (shell.rows.isNotEmpty()) {
|
||||||
|
val rows = layout.createRowHelper(2)
|
||||||
|
shell.rows.forEach {
|
||||||
|
rows.addChild(StringWidget(150, 20, it.text, font))
|
||||||
|
rows.addChild(it.renderer(renderContext))
|
||||||
|
}
|
||||||
|
run { // Buttons
|
||||||
|
rows.addChild(StringWidget(Component.empty(), font))
|
||||||
|
rows.addChild(Button.builder(Component.translatable("quaedam.screen.projection_shell.save")) {
|
||||||
|
GameInstance.getClient().setScreen(null)
|
||||||
|
}.build().apply(::setInitialFocus))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val rows = layout.createRowHelper(1)
|
||||||
|
rows.addChild(StringWidget(Component.translatable("quaedam.screen.projection_shell.empty"), font))
|
||||||
|
rows.addChild(Button.builder(Component.translatable("quaedam.screen.projection_shell.close")) {
|
||||||
|
GameInstance.getClient().setScreen(null)
|
||||||
|
}.build().apply(::setInitialFocus))
|
||||||
|
}
|
||||||
|
layout.arrangeElements()
|
||||||
|
layout.x = (width - layout.width) / 2
|
||||||
|
layout.y = (height - layout.height) / 2
|
||||||
|
layout.visitWidgets(::addRenderableWidget)
|
||||||
|
addRenderableOnly(StringWidget(level.getBlockState(pos).block.name, font).apply {
|
||||||
|
x = (this@ProjectionShellScreen.width - width) / 2
|
||||||
|
y = layout.y - 2 * BORDER - font.lineHeight
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFont() = font
|
||||||
|
|
||||||
|
override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||||
|
renderBackground(guiGraphics)
|
||||||
|
super.render(guiGraphics, mouseX, mouseY, partialTick)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun renderBackground(guiGraphics: GuiGraphics) {
|
||||||
|
super.renderBackground(guiGraphics)
|
||||||
|
guiGraphics.fill(
|
||||||
|
layout.x - BORDER,
|
||||||
|
layout.y - BORDER,
|
||||||
|
layout.x + layout.width + BORDER,
|
||||||
|
layout.y + layout.height + BORDER,
|
||||||
|
0x11c6c6c6
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removed() {
|
||||||
|
super.removed()
|
||||||
|
val block = level.getBlockState(pos).block
|
||||||
|
if (block is ProjectionShellBlock) {
|
||||||
|
block.applyFromShell(level, pos, shell)
|
||||||
|
}
|
||||||
|
ProjectionShell.channel.sendToServer(ServerboundPSHLockReleasePacket(pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isPauseScreen() = false
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
package quaedam.shell.network
|
||||||
|
|
||||||
|
import dev.architectury.networking.NetworkManager.PacketContext
|
||||||
|
import dev.architectury.utils.GameInstance
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import quaedam.Quaedam
|
||||||
|
import quaedam.shell.ProjectionShell
|
||||||
|
import quaedam.shell.ProjectionShellBlock
|
||||||
|
import quaedam.shell.ProjectionShellScreen
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
data class ClientboundPSHLockResultPacket(val pos: BlockPos, val result: Boolean) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
ProjectionShell.channel.register(
|
||||||
|
ClientboundPSHLockResultPacket::class.java,
|
||||||
|
ClientboundPSHLockResultPacket::encode,
|
||||||
|
::ClientboundPSHLockResultPacket,
|
||||||
|
ClientboundPSHLockResultPacket::apply
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(buf: FriendlyByteBuf) : this(buf.readBlockPos(), buf.readBoolean())
|
||||||
|
|
||||||
|
fun encode(buf: FriendlyByteBuf) {
|
||||||
|
buf.writeBlockPos(pos)
|
||||||
|
buf.writeBoolean(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apply(context: Supplier<PacketContext>) {
|
||||||
|
val ctx = context.get()
|
||||||
|
if (ctx.player.level().isClientSide) {
|
||||||
|
val client = GameInstance.getClient()
|
||||||
|
if (result) {
|
||||||
|
val level = ctx.player.level()
|
||||||
|
val block = level.getBlockState(pos).block
|
||||||
|
if (block is ProjectionShellBlock) {
|
||||||
|
ctx.queue {
|
||||||
|
try {
|
||||||
|
client.setScreen(
|
||||||
|
ProjectionShellScreen(
|
||||||
|
level,
|
||||||
|
pos,
|
||||||
|
block.getProjectionEffectForShell(level, pos)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Quaedam.logger.error("Failed to open projection shell screen", e)
|
||||||
|
ProjectionShell.channel.sendToServer(ServerboundPSHLockReleasePacket(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Quaedam.logger.warn("ClientboundPSHLockResultPacket with non-shell-provider block received")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.queue {
|
||||||
|
client.setScreen(null)
|
||||||
|
client.gui.setOverlayMessage(
|
||||||
|
Component.translatable("quaedam.screen.projection_shell.lock_failed"),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,37 @@
|
|||||||
|
package quaedam.shell.network
|
||||||
|
|
||||||
|
import dev.architectury.networking.NetworkManager.PacketContext
|
||||||
|
import dev.architectury.utils.GameInstance
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import quaedam.shell.ProjectionShell
|
||||||
|
import quaedam.shell.ProjectionShellScreen
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
object ClientboundPSHLockRevokePacket {
|
||||||
|
|
||||||
|
init {
|
||||||
|
ProjectionShell.channel.register(
|
||||||
|
ClientboundPSHLockRevokePacket::class.java,
|
||||||
|
{ _, _ -> },
|
||||||
|
{ ClientboundPSHLockRevokePacket },
|
||||||
|
{ _, ctx -> apply(ctx) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun apply(context: Supplier<PacketContext>) {
|
||||||
|
val ctx = context.get()
|
||||||
|
if (ctx.player.level().isClientSide) {
|
||||||
|
ctx.queue {
|
||||||
|
val client = GameInstance.getClient()
|
||||||
|
if (client.screen is ProjectionShellScreen) {
|
||||||
|
client.setScreen(null)
|
||||||
|
client.gui.setOverlayMessage(
|
||||||
|
Component.translatable("quaedam.screen.projection_shell.lock_revoked"),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,48 @@
|
|||||||
|
package quaedam.shell.network
|
||||||
|
|
||||||
|
import dev.architectury.networking.NetworkManager.PacketContext
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import quaedam.shell.ProjectionShell
|
||||||
|
import quaedam.shell.ProjectionShellBlock
|
||||||
|
import quaedam.shell.ProjectionShellMutex
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
data class ServerboundPSHLockAcquirePacket(val pos: BlockPos) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
ProjectionShell.channel.register(
|
||||||
|
ServerboundPSHLockAcquirePacket::class.java,
|
||||||
|
ServerboundPSHLockAcquirePacket::encode,
|
||||||
|
::ServerboundPSHLockAcquirePacket,
|
||||||
|
ServerboundPSHLockAcquirePacket::apply
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(buf: FriendlyByteBuf) : this(buf.readBlockPos())
|
||||||
|
|
||||||
|
fun encode(buf: FriendlyByteBuf) {
|
||||||
|
buf.writeBlockPos(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apply(context: Supplier<PacketContext>) {
|
||||||
|
val ctx = context.get()
|
||||||
|
if (!ctx.player.level().isClientSide) {
|
||||||
|
ctx.queue {
|
||||||
|
val player = ctx.player as ServerPlayer
|
||||||
|
val level = ctx.player.level() as ServerLevel
|
||||||
|
if (level.getBlockState(pos).block !is ProjectionShellBlock) {
|
||||||
|
ProjectionShell.channel.sendToPlayer(player, ClientboundPSHLockResultPacket(pos, false))
|
||||||
|
return@queue
|
||||||
|
}
|
||||||
|
val result = ProjectionShellMutex.tryLock(level, pos, player)
|
||||||
|
ProjectionShell.channel.sendToPlayer(player, ClientboundPSHLockResultPacket(pos, result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
package quaedam.shell.network
|
||||||
|
|
||||||
|
import dev.architectury.networking.NetworkManager.PacketContext
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import quaedam.shell.ProjectionShell
|
||||||
|
import quaedam.shell.ProjectionShellMutex
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
data class ServerboundPSHLockReleasePacket(val pos: BlockPos) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
ProjectionShell.channel.register(
|
||||||
|
ServerboundPSHLockReleasePacket::class.java,
|
||||||
|
ServerboundPSHLockReleasePacket::encode,
|
||||||
|
::ServerboundPSHLockReleasePacket,
|
||||||
|
ServerboundPSHLockReleasePacket::apply
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(buf: FriendlyByteBuf) : this(buf.readBlockPos())
|
||||||
|
|
||||||
|
fun encode(buf: FriendlyByteBuf) {
|
||||||
|
buf.writeBlockPos(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apply(context: Supplier<PacketContext>) {
|
||||||
|
val ctx = context.get()
|
||||||
|
if (!ctx.player.level().isClientSide) {
|
||||||
|
ctx.queue {
|
||||||
|
val player = ctx.player as ServerPlayer
|
||||||
|
ProjectionShellMutex.release(ctx.player.level() as ServerLevel, pos, player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"facing=east": {
|
||||||
|
"model": "quaedam:block/causality_anchor",
|
||||||
|
"y": 270
|
||||||
|
},
|
||||||
|
"facing=south": {
|
||||||
|
"model": "quaedam:block/causality_anchor",
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"facing=west": {
|
||||||
|
"model": "quaedam:block/causality_anchor",
|
||||||
|
"y": 90
|
||||||
|
},
|
||||||
|
"facing=north": {
|
||||||
|
"model": "quaedam:block/causality_anchor",
|
||||||
|
"y": 180
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": {
|
||||||
|
"model": "quaedam:block/music_projection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": {
|
||||||
|
"model": "quaedam:block/noise_projection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"facing=east": {
|
||||||
|
"model": "quaedam:block/reality_stabler",
|
||||||
|
"y": 270
|
||||||
|
},
|
||||||
|
"facing=south": {
|
||||||
|
"model": "quaedam:block/reality_stabler",
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"facing=west": {
|
||||||
|
"model": "quaedam:block/reality_stabler",
|
||||||
|
"y": 90
|
||||||
|
},
|
||||||
|
"facing=north": {
|
||||||
|
"model": "quaedam:block/reality_stabler",
|
||||||
|
"y": 180
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": {
|
||||||
|
"model": "quaedam:block/smart_instrument"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": {
|
||||||
|
"model": "quaedam:block/sound_projection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
@@ -3,5 +3,46 @@
|
|||||||
"block.quaedam.projector": "Projector",
|
"block.quaedam.projector": "Projector",
|
||||||
"block.quaedam.skylight_projection": "Skylight Projection",
|
"block.quaedam.skylight_projection": "Skylight Projection",
|
||||||
"block.quaedam.swarm_projection": "Swarm Projection",
|
"block.quaedam.swarm_projection": "Swarm Projection",
|
||||||
"entity.quaedam.projected_person": "Virtual Person"
|
"block.quaedam.sound_projection": "Sound Projection",
|
||||||
|
"block.quaedam.noise_projection": "Noise Projection",
|
||||||
|
"block.quaedam.music_projection": "Music Projection",
|
||||||
|
"block.quaedam.smart_instrument": "Smart Instrument",
|
||||||
|
"block.quaedam.causality_anchor": "Causality Anchor",
|
||||||
|
"block.quaedam.reality_stabler": "Reality Stabler",
|
||||||
|
"entity.quaedam.projected_person": "Virtual Person",
|
||||||
|
"item.quaedam.projection_shell": "Projection Shell",
|
||||||
|
"item.quaedam.iron_copper_metal": "Copper-iron Alloy",
|
||||||
|
"item.quaedam.projection_metal": "Projection Metal",
|
||||||
|
"quaedam.projector.radius_updated": "Current effect radius: %s",
|
||||||
|
"quaedam.screen.projection_shell": "Projection Shell",
|
||||||
|
"quaedam.screen.projection_shell.lock_revoked": "Timeout! Connection Lost",
|
||||||
|
"quaedam.screen.projection_shell.lock_failed": "Permission denied!",
|
||||||
|
"quaedam.screen.projection_shell.save": "Save",
|
||||||
|
"quaedam.screen.projection_shell.empty": "No Options Available!",
|
||||||
|
"quaedam.screen.projection_shell.close": "Close",
|
||||||
|
"quaedam.shell.skylight.factor": "Factor",
|
||||||
|
"quaedam.shell.noise.rate": "Rate",
|
||||||
|
"quaedam.shell.noise.amount": "Amount",
|
||||||
|
"quaedam.shell.noise.volume": "Volume",
|
||||||
|
"quaedam.shell.swarm.max_count": "Max Count",
|
||||||
|
"quaedam.shell.sound.rate": "Rate",
|
||||||
|
"quaedam.shell.sound.volume": "Volume",
|
||||||
|
"quaedam.shell.music.volume_factor": "Volume Factor",
|
||||||
|
"quaedam.shell.music.particle": "Particle",
|
||||||
|
"quaedam.shell.music.particle.true": "Display",
|
||||||
|
"quaedam.shell.music.particle.false": "Hidden",
|
||||||
|
"advancements.quaedam.causality_anchor.title": "Causality",
|
||||||
|
"advancements.quaedam.causality_anchor.description": "Is there any reason for this?",
|
||||||
|
"advancements.quaedam.projector.title": "Quaedam",
|
||||||
|
"advancements.quaedam.projector.description": "Use projectors to project",
|
||||||
|
"advancements.quaedam.reality_stabler.title": "More Stable",
|
||||||
|
"advancements.quaedam.reality_stabler.description": "Use reality stabler to stable the reality",
|
||||||
|
"advancements.quaedam.smart_instrument.title": "Better than Note Block",
|
||||||
|
"advancements.quaedam.smart_instrument.description": "Use smart instrument",
|
||||||
|
"advancements.quaedam.sound_projection.title": "Get Noisier",
|
||||||
|
"advancements.quaedam.sound_projection.description": "Make a sound projection",
|
||||||
|
"advancements.quaedam.swarm_projection.title": "Too many people",
|
||||||
|
"advancements.quaedam.swarm_projection.description": "Make a swarm projection",
|
||||||
|
"advancements.quaedam.kill_projected_person.title": "Go away",
|
||||||
|
"advancements.quaedam.kill_projected_person.description": "Kill a projection person\n\nWhy are you doing this?\nThis is bad."
|
||||||
}
|
}
|
@@ -1,7 +1,48 @@
|
|||||||
{
|
{
|
||||||
"category.quaedam": "Quaedam",
|
"category.quaedam": "Quaedam",
|
||||||
"block.quaedam.projector": "投影仪",
|
"block.quaedam.projector": "投影仪",
|
||||||
"block.quaedam.skylight_projection": "天光投影",
|
"block.quaedam.skylight_projection": "天光投影",
|
||||||
"block.quaedam.swarm_projection": "人群投影",
|
"block.quaedam.swarm_projection": "人群投影",
|
||||||
"entity.quaedam.projected_person": "虚拟个体"
|
"block.quaedam.sound_projection": "声音投影",
|
||||||
|
"block.quaedam.noise_projection": "噪声投影",
|
||||||
|
"block.quaedam.music_projection": "音乐投影",
|
||||||
|
"block.quaedam.causality_anchor": "因果锚",
|
||||||
|
"block.quaedam.reality_stabler": "现实稳定器",
|
||||||
|
"block.quaedam.smart_instrument": "智能乐器",
|
||||||
|
"entity.quaedam.projected_person": "虚拟个体",
|
||||||
|
"item.quaedam.projection_shell": "投影操作面板",
|
||||||
|
"item.quaedam.iron_copper_metal": "铜铁合金",
|
||||||
|
"item.quaedam.projection_metal": "投影金属",
|
||||||
|
"quaedam.projector.radius_updated": "当前效果半径:%s",
|
||||||
|
"quaedam.screen.projection_shell": "投影操作",
|
||||||
|
"quaedam.screen.projection_shell.lock_revoked": "超时!连接丢失",
|
||||||
|
"quaedam.screen.projection_shell.lock_failed": "正被使用",
|
||||||
|
"quaedam.screen.projection_shell.save": "保存",
|
||||||
|
"quaedam.screen.projection_shell.empty": "无可用选项!",
|
||||||
|
"quaedam.screen.projection_shell.close": "关闭",
|
||||||
|
"quaedam.shell.skylight.factor": "因子",
|
||||||
|
"quaedam.shell.noise.rate": "速率",
|
||||||
|
"quaedam.shell.noise.amount": "数量",
|
||||||
|
"quaedam.shell.noise.volume": "响度因子",
|
||||||
|
"quaedam.shell.swarm.max_count": "最大数量",
|
||||||
|
"quaedam.shell.sound.rate": "速率",
|
||||||
|
"quaedam.shell.sound.volume": "响度因子",
|
||||||
|
"quaedam.shell.music.volume_factor": "响度因子",
|
||||||
|
"quaedam.shell.music.particle": "粒子效果",
|
||||||
|
"quaedam.shell.music.particle.true": "显示",
|
||||||
|
"quaedam.shell.music.particle.false": "隐藏",
|
||||||
|
"advancements.quaedam.causality_anchor.title": "因果律",
|
||||||
|
"advancements.quaedam.causality_anchor.description": "这不合理",
|
||||||
|
"advancements.quaedam.projector.title": "Quaedam",
|
||||||
|
"advancements.quaedam.projector.description": "使用投影仪进行投影",
|
||||||
|
"advancements.quaedam.reality_stabler.title": "更加稳定",
|
||||||
|
"advancements.quaedam.reality_stabler.description": "使用现实稳定器稳定现实",
|
||||||
|
"advancements.quaedam.smart_instrument.title": "比音符盒更好",
|
||||||
|
"advancements.quaedam.smart_instrument.description": "使用智能乐器",
|
||||||
|
"advancements.quaedam.sound_projection.title": "更加吵闹",
|
||||||
|
"advancements.quaedam.sound_projection.description": "制作声音投影",
|
||||||
|
"advancements.quaedam.swarm_projection.title": "太多人了",
|
||||||
|
"advancements.quaedam.swarm_projection.description": "制作人群投影",
|
||||||
|
"advancements.quaedam.kill_projected_person.title": "走开",
|
||||||
|
"advancements.quaedam.kill_projected_person.description": "杀死一个投影人\n\n你为什么要这样做呢?\n这是不好的。"
|
||||||
}
|
}
|
||||||
|
48
common/src/main/resources/assets/quaedam/lang/zh_meme.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"category.quaedam": "有些事",
|
||||||
|
"block.quaedam.projector": "不会发光的投影仪",
|
||||||
|
"block.quaedam.skylight_projection": "天窗发射",
|
||||||
|
"block.quaedam.swarm_projection": "群体发射",
|
||||||
|
"block.quaedam.sound_projection": "生活上的噪声发射",
|
||||||
|
"block.quaedam.noise_projection": "物理上的噪声发射",
|
||||||
|
"block.quaedam.music_projection": "音乐(lè)发射",
|
||||||
|
"block.quaedam.causality_anchor": "宇宙万法的那个源头————如",
|
||||||
|
"block.quaedam.reality_stabler": "投影?退,退,退",
|
||||||
|
"block.quaedam.smart_instrument": "SMART(消歧义)笔记方块",
|
||||||
|
"entity.quaedam.projected_person": "没用的废物",
|
||||||
|
"item.quaedam.projection_shell": "投射滥权终端",
|
||||||
|
"item.quaedam.iron_copper_metal": "有点生锈的铁锭",
|
||||||
|
"item.quaedam.projection_metal": "投影Metal©",
|
||||||
|
"quaedam.projector.radius_updated": "现在乱七八糟的效果能碰到的范围:%s",
|
||||||
|
"quaedam.screen.projection_shell": "控制面板",
|
||||||
|
"quaedam.screen.projection_shell.lock_revoked": "土豆熟了",
|
||||||
|
"quaedam.screen.projection_shell.lock_failed": "宁配吗?",
|
||||||
|
"quaedam.screen.projection_shell.save": "我好了",
|
||||||
|
"quaedam.screen.projection_shell.empty": "空",
|
||||||
|
"quaedam.screen.projection_shell.close": "怒退",
|
||||||
|
"quaedam.shell.skylight.factor": "防晒系数",
|
||||||
|
"quaedam.shell.noise.rate": "急急急等级",
|
||||||
|
"quaedam.shell.noise.amount": "声卡压榨等级",
|
||||||
|
"quaedam.shell.noise.volume": "振幅大小",
|
||||||
|
"quaedam.shell.swarm.max_count": "显卡和处理器迫害等级",
|
||||||
|
"quaedam.shell.sound.rate": "急急急等级",
|
||||||
|
"quaedam.shell.sound.volume": "振幅大小",
|
||||||
|
"quaedam.shell.music.volume_factor": "振幅大小",
|
||||||
|
"quaedam.shell.music.particle": "会变色的颗粒buff",
|
||||||
|
"quaedam.shell.music.particle.true": "打开",
|
||||||
|
"quaedam.shell.music.particle.false": "关掉,关掉,一定要关掉",
|
||||||
|
"advancements.quaedam.causality_anchor.title": "因果律",
|
||||||
|
"advancements.quaedam.causality_anchor.description": "这不合理",
|
||||||
|
"advancements.quaedam.projector.title": "有些事",
|
||||||
|
"advancements.quaedam.projector.description": "你是黑魔法(指着 JVMTI)师吗?",
|
||||||
|
"advancements.quaedam.reality_stabler.title": "水滴",
|
||||||
|
"advancements.quaedam.reality_stabler.description": "tql,这是强互作用力做的吗",
|
||||||
|
"advancements.quaedam.smart_instrument.title": "SMART(消歧义)",
|
||||||
|
"advancements.quaedam.smart_instrument.description": "音乐盘?不需要的",
|
||||||
|
"advancements.quaedam.sound_projection.title": "不要偷偷摸摸",
|
||||||
|
"advancements.quaedam.sound_projection.description": "又没有幽(yóu)匿分贝仪、幽(yóu)匿尖叫体或大聪明,怕什么",
|
||||||
|
"advancements.quaedam.swarm_projection.title": "多来点,爱看",
|
||||||
|
"advancements.quaedam.swarm_projection.description": "",
|
||||||
|
"advancements.quaedam.kill_projected_person.title": "失败",
|
||||||
|
"advancements.quaedam.kill_projected_person.description": "投影人这么可爱,为什么要失败投影人\n\n你干嘛~~\n坏,太坏了,你不能这样。"
|
||||||
|
}
|
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:block/block",
|
||||||
|
"texture_size": [64, 64],
|
||||||
|
"textures": {
|
||||||
|
"0": "quaedam:block/causality_anchor",
|
||||||
|
"particle": "quaedam:block/causality_anchor"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"name": "s2",
|
||||||
|
"from": [13, -6, 14],
|
||||||
|
"to": [15, 6, 16],
|
||||||
|
"rotation": {"angle": -22.5, "axis": "x", "origin": [-1, 0, 0]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [4, 4, 4.5, 7], "texture": "#0"},
|
||||||
|
"east": {"uv": [4.5, 4, 5, 7], "texture": "#0"},
|
||||||
|
"south": {"uv": [0, 5, 0.5, 8], "texture": "#0"},
|
||||||
|
"west": {"uv": [0.5, 5, 1, 8], "texture": "#0"},
|
||||||
|
"up": {"uv": [5.75, 7.5, 5.25, 7], "texture": "#0"},
|
||||||
|
"down": {"uv": [7.5, 5.5, 7, 6], "texture": "#0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s3",
|
||||||
|
"from": [1, -6, 14],
|
||||||
|
"to": [3, 6, 16],
|
||||||
|
"rotation": {"angle": -22.5, "axis": "x", "origin": [1, 0, 0]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [1, 5, 1.5, 8], "texture": "#0"},
|
||||||
|
"east": {"uv": [1.5, 5, 2, 8], "texture": "#0"},
|
||||||
|
"south": {"uv": [2, 5, 2.5, 8], "texture": "#0"},
|
||||||
|
"west": {"uv": [2.5, 5, 3, 8], "texture": "#0"},
|
||||||
|
"up": {"uv": [6.25, 7.5, 5.75, 7], "texture": "#0"},
|
||||||
|
"down": {"uv": [7.5, 6, 7, 6.5], "texture": "#0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s4",
|
||||||
|
"from": [1, 0, -1],
|
||||||
|
"to": [3, 12, 1],
|
||||||
|
"rotation": {"angle": 22.5, "axis": "x", "origin": [1, 0, 0]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [3, 5, 3.5, 8], "texture": "#0"},
|
||||||
|
"east": {"uv": [3.5, 5, 4, 8], "texture": "#0"},
|
||||||
|
"south": {"uv": [5, 4, 5.5, 7], "texture": "#0"},
|
||||||
|
"west": {"uv": [5.5, 4, 6, 7], "texture": "#0"},
|
||||||
|
"up": {"uv": [6.75, 7.5, 6.25, 7], "texture": "#0"},
|
||||||
|
"down": {"uv": [7.5, 6.5, 7, 7], "texture": "#0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s1",
|
||||||
|
"from": [13, 0, -1],
|
||||||
|
"to": [15, 12, 1],
|
||||||
|
"rotation": {"angle": 22.5, "axis": "x", "origin": [-1, 0, 0]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [6, 0, 6.5, 3], "texture": "#0"},
|
||||||
|
"east": {"uv": [6, 4, 6.5, 7], "texture": "#0"},
|
||||||
|
"south": {"uv": [6.5, 0, 7, 3], "texture": "#0"},
|
||||||
|
"west": {"uv": [6.5, 4, 7, 7], "texture": "#0"},
|
||||||
|
"up": {"uv": [7.25, 7.5, 6.75, 7], "texture": "#0"},
|
||||||
|
"down": {"uv": [7.75, 4, 7.25, 4.5], "texture": "#0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "platform",
|
||||||
|
"from": [0, 10, 3],
|
||||||
|
"to": [16, 12, 13],
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [4, 3, 8, 3.5], "texture": "#0"},
|
||||||
|
"east": {"uv": [7, 0, 9.5, 0.5], "texture": "#0"},
|
||||||
|
"south": {"uv": [4, 3.5, 8, 4], "texture": "#0"},
|
||||||
|
"west": {"uv": [7, 0.5, 9.5, 1], "texture": "#0"},
|
||||||
|
"up": {"uv": [4, 2.5, 0, 0], "texture": "#0"},
|
||||||
|
"down": {"uv": [4, 2.5, 0, 5], "texture": "#0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "core",
|
||||||
|
"from": [6, 14, 7],
|
||||||
|
"to": [10, 16, 9],
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [7, 1.5, 8, 2], "texture": "#0"},
|
||||||
|
"east": {"uv": [7.25, 4.5, 7.75, 5], "texture": "#0"},
|
||||||
|
"south": {"uv": [7, 2, 8, 2.5], "texture": "#0"},
|
||||||
|
"west": {"uv": [7.25, 5, 7.75, 5.5], "texture": "#0"},
|
||||||
|
"up": {"uv": [8, 3, 7, 2.5], "texture": "#0"},
|
||||||
|
"down": {"uv": [5, 7, 4, 7.5], "texture": "#0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "screen",
|
||||||
|
"from": [4, 6, -2],
|
||||||
|
"to": [12, 12, -1],
|
||||||
|
"rotation": {"angle": 22.5, "axis": "x", "origin": [0, 0, 0]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [4, 0, 6, 1.5], "texture": "#0"},
|
||||||
|
"east": {"uv": [7, 4, 7.25, 5.5], "texture": "#0"},
|
||||||
|
"south": {"uv": [4, 1.5, 6, 3], "texture": "#0"},
|
||||||
|
"west": {"uv": [5, 7, 5.25, 8.5], "texture": "#0"},
|
||||||
|
"up": {"uv": [9, 1.25, 7, 1], "texture": "#0"},
|
||||||
|
"down": {"uv": [9, 1.25, 7, 1.5], "texture": "#0"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/projection",
|
||||||
|
"textures": {
|
||||||
|
"ext": "quaedam:block/music_projection"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/projection",
|
||||||
|
"textures": {
|
||||||
|
"ext": "quaedam:block/noise_projection"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:block/block",
|
||||||
|
"texture_size": [64, 64],
|
||||||
|
"textures": {
|
||||||
|
"0": "quaedam:block/reality_stabler",
|
||||||
|
"particle": "quaedam:block/reality_stabler"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"name": "bottom",
|
||||||
|
"from": [1, 0, 1],
|
||||||
|
"to": [15, 1, 15],
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [7.5, 10.5, 11, 10.75], "texture": "#0"},
|
||||||
|
"east": {"uv": [7.5, 10.75, 11, 11], "texture": "#0"},
|
||||||
|
"south": {"uv": [7.5, 11, 11, 11.25], "texture": "#0"},
|
||||||
|
"west": {"uv": [11, 10.5, 14.5, 10.75], "texture": "#0"},
|
||||||
|
"up": {"uv": [11.5, 3.5, 8, 0], "texture": "#0"},
|
||||||
|
"down": {"uv": [11.5, 3.5, 8, 7], "texture": "#0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "center",
|
||||||
|
"from": [0, 1, 0],
|
||||||
|
"to": [16, 14, 16],
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [4, 0, 8, 3.25], "texture": "#0"},
|
||||||
|
"east": {"uv": [4, 3.25, 8, 6.5], "texture": "#0"},
|
||||||
|
"south": {"uv": [4, 6.5, 8, 9.75], "texture": "#0"},
|
||||||
|
"west": {"uv": [0, 8, 4, 11.25], "texture": "#0"},
|
||||||
|
"up": {"uv": [4, 4, 0, 0], "texture": "#0"},
|
||||||
|
"down": {"uv": [4, 4, 0, 8], "texture": "#0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "top",
|
||||||
|
"from": [1, 14, 1],
|
||||||
|
"to": [15, 15, 15],
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [11, 10.75, 14.5, 11], "texture": "#0"},
|
||||||
|
"east": {"uv": [11, 11, 14.5, 11.25], "texture": "#0"},
|
||||||
|
"south": {"uv": [0, 11.25, 3.5, 11.5], "texture": "#0"},
|
||||||
|
"west": {"uv": [7.5, 11.25, 11, 11.5], "texture": "#0"},
|
||||||
|
"up": {"uv": [11.5, 10.5, 8, 7], "texture": "#0"},
|
||||||
|
"down": {"uv": [7.5, 9.75, 4, 13.25], "texture": "#0"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:block/note_block"
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/projection",
|
||||||
|
"textures": {
|
||||||
|
"ext": "quaedam:block/sound_projection"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/causality_anchor"
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "quaedam:item/iron_copper_metal"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/music_projection"
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/noise_projection"
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "quaedam:item/projection_metal"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "quaedam:item/projection_shell"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/reality_stabler"
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/smart_instrument"
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "quaedam:block/sound_projection"
|
||||||
|
}
|
178
common/src/main/resources/assets/quaedam/sounds.json
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
{
|
||||||
|
"entity.projected_person.noise": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "entity.villager.ambient",
|
||||||
|
"weight": 4,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.yes",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.no",
|
||||||
|
"weight": 1,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.trade",
|
||||||
|
"weight": 3,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_librarian",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_cleric",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_mason",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_shepherd",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.wandering_trader.drink_milk",
|
||||||
|
"weight": 1,
|
||||||
|
"volume": 0.5,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.wandering_trader.no",
|
||||||
|
"weight": 1,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.wandering_trader.yes",
|
||||||
|
"weight": 1,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.sand.step",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.scaffolding.step",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.stone.step",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.grass.step",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.wet_grass.step",
|
||||||
|
"weight": 1,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.bamboo_wood.step",
|
||||||
|
"weight": 1,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.cherry_wood.step",
|
||||||
|
"weight": 1,
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.wood.step",
|
||||||
|
"weight": 2,
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"quaedam.projection.noise": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_armorer",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_butcher",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_cartographer",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_cleric",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_farmer",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_fisherman",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_fletcher",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_leatherworker",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_librarian",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_mason",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_shepherd",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_toolsmith",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.villager.work_weaponsmith",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.anvil.use",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.furnace.fire_crackle",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.blastfurnace.fire_crackle",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.metal.break",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "block.wood.break",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 396 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 254 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 907 B |
After Width: | Height: | Size: 845 B |
After Width: | Height: | Size: 834 B |
After Width: | Height: | Size: 888 B |
After Width: | Height: | Size: 707 B |
After Width: | Height: | Size: 930 B |
After Width: | Height: | Size: 872 B |