From f9dabab80a5994944935499280a83ef6f975f863 Mon Sep 17 00:00:00 2001 From: Seppe Volkaerts Date: Tue, 18 Sep 2018 16:02:35 +0200 Subject: [PATCH] Thoughts on kotlin scripting. --- build.gradle | 6 +- .../api/event/LanternEventFactory.java | 7 ++ .../server/world/LanternWeatherUniverse.java | 2 +- .../server/world/LanternWorld.java | 4 +- .../kotlin/org/lanternpowered/api/Lantern.kt | 2 + .../api/catalog/CatalogTypeProperty.kt | 41 ++++++++ .../catalog/MissingCatalogTypeException.kt | 63 ++++++++++++ .../org/lanternpowered/api/entity/Entity.kt | 7 +- .../api/entity/spawn/EntitySpawner.kt | 1 + .../api/entity/weather/Weather.kt | 96 +++++++++++++++++++ .../lanternpowered/api/ext/CollectionExt.kt | 16 ++++ .../org/lanternpowered/api/ext/TextExt.kt | 4 + .../org/lanternpowered/api/ext/WeatherExt.kt | 2 +- .../org/lanternpowered/api/ext/WorldExt.kt | 5 + .../lanternpowered/api/script/RecipeExt.kt | 51 ++++++++++ .../lanternpowered/api/script/ScriptExt.kt | 51 ++++++++++ .../text/translation/TranslationRegistry.kt | 31 ++++++ .../api/world/weather/WeatherBuilder.kt | 8 ++ .../api/world/weather/WeatherOptions.kt | 7 ++ .../org/lanternpowered/api/x/XGameRegistry.kt | 3 + .../api/x/world/chunk/XChunk.kt | 34 +++++++ .../api/x/world/weather/XWeatherUniverse.kt | 2 +- .../minecraft/enchantment/_loot_bonus.kts | 17 ++++ .../minecraft/enchantment/fire_aspect.kts | 12 +++ .../data/minecraft/enchantment/fortune.kts | 6 ++ .../data/minecraft/enchantment/knockback.kts | 12 +++ .../data/minecraft/enchantment/looting.kts | 6 ++ .../minecraft/enchantment/luck_of_the_sea.kts | 6 ++ .../data/minecraft/recipe/acacia_stairs.kts | 17 ++++ .../data/minecraft/weather/clear.kts | 12 +++ .../resources/data/minecraft/weather/rain.kts | 13 +++ .../data/minecraft/weather/thunder_storm.kts | 23 +++++ 32 files changed, 557 insertions(+), 10 deletions(-) create mode 100644 src/main/kotlin/org/lanternpowered/api/catalog/CatalogTypeProperty.kt create mode 100644 src/main/kotlin/org/lanternpowered/api/catalog/MissingCatalogTypeException.kt create mode 100644 src/main/kotlin/org/lanternpowered/api/entity/weather/Weather.kt create mode 100644 src/main/kotlin/org/lanternpowered/api/script/RecipeExt.kt create mode 100644 src/main/kotlin/org/lanternpowered/api/script/ScriptExt.kt create mode 100644 src/main/kotlin/org/lanternpowered/api/text/translation/TranslationRegistry.kt create mode 100644 src/main/kotlin/org/lanternpowered/api/x/world/chunk/XChunk.kt create mode 100644 src/main/resources/data/minecraft/enchantment/_loot_bonus.kts create mode 100644 src/main/resources/data/minecraft/enchantment/fire_aspect.kts create mode 100644 src/main/resources/data/minecraft/enchantment/fortune.kts create mode 100644 src/main/resources/data/minecraft/enchantment/knockback.kts create mode 100644 src/main/resources/data/minecraft/enchantment/looting.kts create mode 100644 src/main/resources/data/minecraft/enchantment/luck_of_the_sea.kts create mode 100644 src/main/resources/data/minecraft/recipe/acacia_stairs.kts create mode 100644 src/main/resources/data/minecraft/weather/clear.kts create mode 100644 src/main/resources/data/minecraft/weather/rain.kts create mode 100644 src/main/resources/data/minecraft/weather/thunder_storm.kts diff --git a/build.gradle b/build.gradle index 753bb0c98..5f1822b84 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ plugins { id 'net.minecrell.licenser' version '0.4' id 'com.github.johnrengelman.shadow' version '2.0.1' id 'org.spongepowered.plugin' version '0.8.1' - id 'org.jetbrains.kotlin.jvm' version '1.2.61' + id 'org.jetbrains.kotlin.jvm' version '1.2.70' } apply from: rootProject.file('gradle/lantern.gradle') @@ -76,6 +76,9 @@ dependencies { // Kotlin compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' compile 'org.jetbrains.kotlin:kotlin-reflect' + compile 'org.jetbrains.kotlin:kotlin-script-util' + compile 'org.jetbrains.kotlin:kotlin-script-runtime' + compile 'org.jetbrains.kotlin:kotlin-compiler-embeddable' // Launch Options compile 'net.sf.jopt-simple:jopt-simple:5.0.4' @@ -222,6 +225,7 @@ task theJar(type: ShadowJar) { def dependencyEntries = new ArrayList<>() dependencyArtifacts.each { ResolvedArtifact a -> dependencyEntries.add(a.moduleVersion.id.toString()) } + dependencyEntries.sort() def data = new HashMap<>() data['repositories'] = [] diff --git a/src/main/java/org/lanternpowered/api/event/LanternEventFactory.java b/src/main/java/org/lanternpowered/api/event/LanternEventFactory.java index 751071ecb..ad7734aac 100644 --- a/src/main/java/org/lanternpowered/api/event/LanternEventFactory.java +++ b/src/main/java/org/lanternpowered/api/event/LanternEventFactory.java @@ -30,6 +30,7 @@ import org.spongepowered.api.entity.EntityType; import org.spongepowered.api.entity.Transform; import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.action.LightningEvent; import org.spongepowered.api.event.cause.Cause; import org.spongepowered.api.event.entity.ConstructEntityEvent; import org.spongepowered.api.event.entity.SpawnEntityEvent; @@ -76,4 +77,10 @@ public static ConstructEntityEvent.Pre createConstructEntityEventPre( @NotNull Transform transform) { return SpongeEventFactory.createConstructEntityEventPre(cause, targetType, transform); } + + @NotNull + public static LightningEvent.Pre createLightningEventPre( + @NotNull Cause cause) { + return SpongeEventFactory.createLightningEventPre(cause); + } } diff --git a/src/main/java/org/lanternpowered/server/world/LanternWeatherUniverse.java b/src/main/java/org/lanternpowered/server/world/LanternWeatherUniverse.java index ebeacbc56..fbc5b99be 100644 --- a/src/main/java/org/lanternpowered/server/world/LanternWeatherUniverse.java +++ b/src/main/java/org/lanternpowered/server/world/LanternWeatherUniverse.java @@ -209,7 +209,7 @@ private boolean setWeather(CauseStack causeStack, Weather weather, long duration } @Override - public double getDarkness() { + public double getSkyDarkness() { return this.darkness; } diff --git a/src/main/java/org/lanternpowered/server/world/LanternWorld.java b/src/main/java/org/lanternpowered/server/world/LanternWorld.java index a4653e210..fc6e343f5 100644 --- a/src/main/java/org/lanternpowered/server/world/LanternWorld.java +++ b/src/main/java/org/lanternpowered/server/world/LanternWorld.java @@ -1014,8 +1014,8 @@ public void setWeather(Weather weather, long duration) { } @Override - public double getDarkness() { - return this.weatherUniverse == null ? 0 : this.weatherUniverse.getDarkness(); + public double getSkyDarkness() { + return this.weatherUniverse == null ? 0 : this.weatherUniverse.getSkyDarkness(); } @Override diff --git a/src/main/kotlin/org/lanternpowered/api/Lantern.kt b/src/main/kotlin/org/lanternpowered/api/Lantern.kt index 4a58c0f9b..937146ce2 100644 --- a/src/main/kotlin/org/lanternpowered/api/Lantern.kt +++ b/src/main/kotlin/org/lanternpowered/api/Lantern.kt @@ -28,6 +28,7 @@ package org.lanternpowered.api import org.lanternpowered.api.entity.spawn.EntitySpawner import org.lanternpowered.api.event.EventManager import org.lanternpowered.api.plugin.PluginManager +import org.lanternpowered.api.text.translation.TranslationRegistry import org.lanternpowered.api.x.XGameRegistry import org.lanternpowered.api.x.XServer import org.lanternpowered.api.x.cause.XCauseStackManager @@ -45,4 +46,5 @@ object Lantern { @JvmStatic inline val scheduler: Scheduler get() = Sponge.getScheduler() @JvmStatic inline val serviceManager: ServiceManager get() = Sponge.getServiceManager() @JvmStatic inline val entitySpawner: EntitySpawner get() = this.server.entitySpawner + @JvmStatic inline val translationRegistry: TranslationRegistry get() = this.registry.translations } diff --git a/src/main/kotlin/org/lanternpowered/api/catalog/CatalogTypeProperty.kt b/src/main/kotlin/org/lanternpowered/api/catalog/CatalogTypeProperty.kt new file mode 100644 index 000000000..358c9d83c --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/catalog/CatalogTypeProperty.kt @@ -0,0 +1,41 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.lanternpowered.api.catalog + +import org.lanternpowered.api.Lantern +import org.lanternpowered.api.ext.* +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KClass +import kotlin.reflect.KProperty + +class CatalogTypeProperty( + private val key: CatalogKey, + private val type: KClass +) : ReadOnlyProperty { + + override fun getValue(thisRef: Nothing?, property: KProperty<*>): T = Lantern.registry.getType(this.type, this.key) + ?: throw MissingCatalogTypeException("Missing catalog \"$key\" of type ${type.qualifiedName ?: type.java.name}") +} diff --git a/src/main/kotlin/org/lanternpowered/api/catalog/MissingCatalogTypeException.kt b/src/main/kotlin/org/lanternpowered/api/catalog/MissingCatalogTypeException.kt new file mode 100644 index 000000000..b54e133ae --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/catalog/MissingCatalogTypeException.kt @@ -0,0 +1,63 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.lanternpowered.api.catalog + +/** + * An exception that is thrown when something a catalog type is missing. + */ +class MissingCatalogTypeException : RuntimeException { + + /** + * Constructs a new [MissingCatalogTypeException]. + */ + constructor() : super() + + /** + * Constructs a new [MissingCatalogTypeException] with the + * given message. + * + * @param message The message + */ + constructor(message: String) : super(message) + + /** + * Constructs a new [MissingCatalogTypeException] with the + * given message and underlying cause. + * + * @param message The message + * @param cause The underlying cause + */ + constructor(message: String, cause: Throwable) : super(message, cause) + + /** + * Constructs a new [MissingCatalogTypeException] with the + * underlying cause. + * + * @param cause The underlying cause + */ + constructor(cause: Throwable) : super(cause) + +} diff --git a/src/main/kotlin/org/lanternpowered/api/entity/Entity.kt b/src/main/kotlin/org/lanternpowered/api/entity/Entity.kt index ec18ede33..288d227b7 100644 --- a/src/main/kotlin/org/lanternpowered/api/entity/Entity.kt +++ b/src/main/kotlin/org/lanternpowered/api/entity/Entity.kt @@ -25,8 +25,7 @@ */ package org.lanternpowered.api.entity -import org.spongepowered.api.entity.Entity -import org.spongepowered.api.entity.Transform -typealias Transform = Transform -typealias Entity = Entity +typealias Transform = org.spongepowered.api.entity.Transform +typealias Entity = org.spongepowered.api.entity.Entity +typealias EntityTypes = org.spongepowered.api.entity.EntityTypes diff --git a/src/main/kotlin/org/lanternpowered/api/entity/spawn/EntitySpawner.kt b/src/main/kotlin/org/lanternpowered/api/entity/spawn/EntitySpawner.kt index c7720f369..fcac66fe2 100644 --- a/src/main/kotlin/org/lanternpowered/api/entity/spawn/EntitySpawner.kt +++ b/src/main/kotlin/org/lanternpowered/api/entity/spawn/EntitySpawner.kt @@ -35,6 +35,7 @@ import org.lanternpowered.api.ext.* import org.lanternpowered.api.world.World import org.spongepowered.api.entity.Entity import org.spongepowered.api.entity.EntityType +import org.spongepowered.api.entity.weather.Lightning import org.spongepowered.api.event.entity.ConstructEntityEvent import org.spongepowered.api.event.entity.SpawnEntityEvent import java.util.Collections diff --git a/src/main/kotlin/org/lanternpowered/api/entity/weather/Weather.kt b/src/main/kotlin/org/lanternpowered/api/entity/weather/Weather.kt new file mode 100644 index 000000000..3378ef4de --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/entity/weather/Weather.kt @@ -0,0 +1,96 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.lanternpowered.api.entity.weather + +import com.flowpowered.math.vector.Vector3d +import org.lanternpowered.api.Lantern +import org.lanternpowered.api.cause.CauseStack +import org.lanternpowered.api.entity.EntityTypes +import org.lanternpowered.api.entity.Transform +import org.lanternpowered.api.event.LanternEventFactory +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.util.AABB +import org.lanternpowered.api.world.chunk.Chunk +import org.lanternpowered.api.world.World + +typealias Lightning = org.spongepowered.api.entity.weather.Lightning + +object LightningSpawner { + + /** + * When there are entities in this region around the lightning + * bolt, the bolt will be redirected to the entity. + */ + private val moveToEntityRegion = AABB(-1.5, -3.0, -1.5, 1.5, 256.0, 1.5) + + /** + * Spawns [Lightning] randomly across all the loaded [Chunk]s in the [World]. + * + * @param world The world to spawn lightning bolts in + * @param chance The chance that a lightning bolt will spawn per attempt in a chunk + * @param attemptsPerChunk The attempts to spawn a bolt per chunk + * @param moveToEntityRegion The region to check for entities around the bolt, if found, move bolt to their location + * @param locationFilter Advanced filtering whether the [Transform] is valid + */ + fun spawnLightning( + world: World, + chance: Double = 0.0000002, + attemptsPerChunk: Int = 2, + moveToEntityRegion: AABB = this.moveToEntityRegion, + locationFilter: (Transform) -> Boolean = { true } + ) { + val chunks = world.loadedChunks + val chanceInt = (1f / Math.max(chance, 0.000000000001)).toInt() + + for (chunk in chunks) { + for (i in 0 until attemptsPerChunk) { + if (random.nextInt(chanceInt) != 0) { + continue + } + + val value = random.nextInt(0x10000) + val x = chunk.x shl 4 or (value and 0xf) + val z = chunk.z shl 4 or (value shr 4 and 0xf) + + var pos = Vector3d(x.toDouble(), world.getHighestYAt(x, z).toDouble(), z.toDouble()) + + // Look for nearby entities to see if the lightning bolt should be moved + world.getIntersectingEntities(moveToEntityRegion.offset(pos)).pickRandom()?.let { + pos = it.location.position + } + + val transform = Transform(world, pos) + if (!locationFilter(transform)) continue + + val lightningPreEvent = LanternEventFactory.createLightningEventPre(CauseStack.current().currentCause) + Lantern.eventManager.post(lightningPreEvent) + if (!lightningPreEvent.isCancelled) { + Lantern.entitySpawner.spawn(EntityTypes.LIGHTNING, Transform(world, pos)) + } + } + } + } +} diff --git a/src/main/kotlin/org/lanternpowered/api/ext/CollectionExt.kt b/src/main/kotlin/org/lanternpowered/api/ext/CollectionExt.kt index ae1fdd762..6e6904d7d 100644 --- a/src/main/kotlin/org/lanternpowered/api/ext/CollectionExt.kt +++ b/src/main/kotlin/org/lanternpowered/api/ext/CollectionExt.kt @@ -93,3 +93,19 @@ inline fun Iterable.asUnmodifiable(): Iterable = UnmodifiableIterable( * Creates a unmodifiable [Iterator] view for this iterator. */ inline fun Iterator.asUnmodifiable(): Iterator = UnmodifiableIterator(this) + +// Random + +fun Collection.pickRandom(): E? { + val size = this.size + if (size == 0) { + return null + } + val index = random.nextInt(size) + forEachIndexed { i, element -> + if (i == index) { + return element + } + } + throw IllegalStateException("Should never be reached") +} diff --git a/src/main/kotlin/org/lanternpowered/api/ext/TextExt.kt b/src/main/kotlin/org/lanternpowered/api/ext/TextExt.kt index 71efe2d2f..663249906 100644 --- a/src/main/kotlin/org/lanternpowered/api/ext/TextExt.kt +++ b/src/main/kotlin/org/lanternpowered/api/ext/TextExt.kt @@ -27,12 +27,14 @@ package org.lanternpowered.api.ext +import org.lanternpowered.api.Lantern import org.lanternpowered.api.text.Text import org.lanternpowered.api.text.TextBuilder import org.lanternpowered.api.text.format.TextColor import org.lanternpowered.api.text.format.TextFormat import org.lanternpowered.api.text.format.TextStyle import org.lanternpowered.api.text.serializer.TextSerializers +import org.lanternpowered.api.text.translation.Translation import org.spongepowered.api.command.CommandSource import org.spongepowered.api.text.action.ClickAction import org.spongepowered.api.text.action.TextActions @@ -69,3 +71,5 @@ inline operator fun TextFormat.plus(that: TextColor): TextFormat = color(that) inline operator fun TextStyle.plus(that: TextStyle): TextStyle = and(that) inline operator fun TextStyle.minus(that: TextStyle): TextStyle = andNot(that) inline operator fun TextStyle.unaryMinus(): TextStyle = negate() + +fun translationOf(id: String): Translation = Lantern.translationRegistry[id] diff --git a/src/main/kotlin/org/lanternpowered/api/ext/WeatherExt.kt b/src/main/kotlin/org/lanternpowered/api/ext/WeatherExt.kt index 8edf7a1eb..e5d5581d9 100644 --- a/src/main/kotlin/org/lanternpowered/api/ext/WeatherExt.kt +++ b/src/main/kotlin/org/lanternpowered/api/ext/WeatherExt.kt @@ -51,7 +51,7 @@ inline val WeatherUniverse.rainStrength: Double get() = (this as XWeatherUnivers /** * The current darkness of the sky. */ -inline val WeatherUniverse.darkness: Double get() = (this as XWeatherUniverse).darkness +inline val WeatherUniverse.skyDarkness: Double get() = (this as XWeatherUniverse).skyDarkness // We cannot call typeTokenOf here, chaining reified calls and using // anonymous classes can end up in weird errors. But only in specific diff --git a/src/main/kotlin/org/lanternpowered/api/ext/WorldExt.kt b/src/main/kotlin/org/lanternpowered/api/ext/WorldExt.kt index a3e749f8f..3378f65d1 100644 --- a/src/main/kotlin/org/lanternpowered/api/ext/WorldExt.kt +++ b/src/main/kotlin/org/lanternpowered/api/ext/WorldExt.kt @@ -32,7 +32,9 @@ import org.lanternpowered.api.block.entity.BlockEntity import org.lanternpowered.api.entity.Transform import org.lanternpowered.api.world.Location import org.lanternpowered.api.world.World +import org.lanternpowered.api.world.chunk.Chunk import org.lanternpowered.api.x.world.XWorld +import org.lanternpowered.api.x.world.chunk.XChunk import org.lanternpowered.api.x.world.extent.XEntityUniverse import org.lanternpowered.api.x.world.extent.XExtent import org.lanternpowered.api.x.world.weather.XWeatherUniverse @@ -87,3 +89,6 @@ fun Extent.hasIntersectingEntities(box: AABB) */ fun Extent.hasIntersectingEntities(box: AABB, filter: (Entity) -> Boolean) = (this as XExtent).hasIntersectingEntities(box, filter) + +val Chunk.x: Int get() = (this as XChunk).x +val Chunk.z: Int get() = (this as XChunk).z diff --git a/src/main/kotlin/org/lanternpowered/api/script/RecipeExt.kt b/src/main/kotlin/org/lanternpowered/api/script/RecipeExt.kt new file mode 100644 index 000000000..5ee2dedb9 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/script/RecipeExt.kt @@ -0,0 +1,51 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.lanternpowered.api.script + +import org.lanternpowered.api.item.inventory.ItemStack +import org.lanternpowered.api.item.inventory.ItemStackSnapshot +import org.spongepowered.api.item.recipe.crafting.Ingredient +import org.spongepowered.api.item.recipe.crafting.ShapedCraftingRecipe + +fun shapedRecipe(fn: ShapedCraftingRecipe.Builder.() -> Unit): ShapedCraftingRecipe.Builder = ShapedCraftingRecipe.builder().apply(fn) + +fun ShapedCraftingRecipe.Builder.result(result: ItemStackSnapshot): ShapedCraftingRecipe.Builder + = (this as ShapedCraftingRecipe.Builder.ResultStep).result(result) + +fun ShapedCraftingRecipe.Builder.result(result: ItemStack): ShapedCraftingRecipe.Builder + = (this as ShapedCraftingRecipe.Builder.ResultStep).result(result) + +fun ShapedCraftingRecipe.Builder.aisle(vararg aisle: String): ShapedCraftingRecipe.Builder + = (this as ShapedCraftingRecipe.Builder.AisleStep).aisle(*aisle) + +fun ShapedCraftingRecipe.Builder.ingredients(vararg pairs: Pair): ShapedCraftingRecipe.Builder + = (this as ShapedCraftingRecipe.Builder.AisleStep).where(mapOf(*pairs)) + +fun ShapedCraftingRecipe.Builder.ingredient(key: Char, ingredient: Ingredient): ShapedCraftingRecipe.Builder + = (this as ShapedCraftingRecipe.Builder.AisleStep).where(key, ingredient) + +fun ShapedCraftingRecipe.Builder.group(group: String?): ShapedCraftingRecipe.Builder + = (this as ShapedCraftingRecipe.Builder.EndStep).group(group) diff --git a/src/main/kotlin/org/lanternpowered/api/script/ScriptExt.kt b/src/main/kotlin/org/lanternpowered/api/script/ScriptExt.kt new file mode 100644 index 000000000..dc9d21a03 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/script/ScriptExt.kt @@ -0,0 +1,51 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.lanternpowered.api.script + +import org.lanternpowered.api.catalog.CatalogKey +import org.lanternpowered.api.catalog.CatalogType +import org.lanternpowered.api.catalog.CatalogTypeProperty +import org.lanternpowered.api.item.enchantment.EnchantmentTypeBuilder +import org.lanternpowered.api.world.weather.WeatherBuilder + +// These methods are only allowed to be called in scripts + +// TODO: Error when called anywhere else + +inline fun catalogRef(key: CatalogKey): CatalogTypeProperty = CatalogTypeProperty(key, T::class) + +/** + * Gets a read only property which provides access to a [CatalogType] + * of a specific type. This can be used when a specific catalog type + * isn't available yet. + */ +inline fun catalogRef(key: String): CatalogTypeProperty = CatalogTypeProperty(CatalogKey.resolve(key), T::class) + +fun weather(fn: WeatherBuilder.() -> Unit) = WeatherBuilder().apply(fn) + +fun enchantment(vararg parentScripts: String, fn: EnchantmentTypeBuilder.() -> Unit) = EnchantmentTypeBuilder().apply(fn) +fun enchantment(vararg parentScripts: CatalogKey, fn: EnchantmentTypeBuilder.() -> Unit) = EnchantmentTypeBuilder().apply(fn) +fun enchantment(fn: EnchantmentTypeBuilder.() -> Unit) = EnchantmentTypeBuilder().apply(fn) diff --git a/src/main/kotlin/org/lanternpowered/api/text/translation/TranslationRegistry.kt b/src/main/kotlin/org/lanternpowered/api/text/translation/TranslationRegistry.kt new file mode 100644 index 000000000..39e77c4c2 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/text/translation/TranslationRegistry.kt @@ -0,0 +1,31 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.lanternpowered.api.text.translation + +interface TranslationRegistry { + + operator fun get(id: String): Translation +} diff --git a/src/main/kotlin/org/lanternpowered/api/world/weather/WeatherBuilder.kt b/src/main/kotlin/org/lanternpowered/api/world/weather/WeatherBuilder.kt index 99603901b..9c7028d07 100644 --- a/src/main/kotlin/org/lanternpowered/api/world/weather/WeatherBuilder.kt +++ b/src/main/kotlin/org/lanternpowered/api/world/weather/WeatherBuilder.kt @@ -26,6 +26,7 @@ package org.lanternpowered.api.world.weather import org.lanternpowered.api.util.builder.TranslatableCatalogBuilder +import org.lanternpowered.api.world.World import org.lanternpowered.api.x.world.weather.XWeather /** @@ -41,4 +42,11 @@ interface WeatherBuilder : TranslatableCatalogBuilder * @return This builder, for chaining */ fun option(option: WeatherOption, value: V): WeatherBuilder + + /** + * Adds a action that will be executed every tick that the weather is active + * + * @param fn The action function + */ + fun action(fn: (World) -> Unit) } diff --git a/src/main/kotlin/org/lanternpowered/api/world/weather/WeatherOptions.kt b/src/main/kotlin/org/lanternpowered/api/world/weather/WeatherOptions.kt index f380d3698..993fc94d4 100644 --- a/src/main/kotlin/org/lanternpowered/api/world/weather/WeatherOptions.kt +++ b/src/main/kotlin/org/lanternpowered/api/world/weather/WeatherOptions.kt @@ -27,6 +27,7 @@ package org.lanternpowered.api.world.weather import org.lanternpowered.api.catalog.CatalogKey import org.lanternpowered.api.ext.* +import org.spongepowered.api.util.weighted.WeightedTable /** * An iteration with all the default [WeatherOption]s. @@ -47,4 +48,10 @@ object WeatherOptions { * A provider which provides the duration (in seconds) when the weather is naturally switched. */ @JvmStatic val DURATION = weatherOptionOf(CatalogKey.minecraft("duration")) { random.nextDouble(300.0 .. 900.0) } + + /** + * All weathers have a weight, when they are put in a [WeightedTable] + * it will be used to determine the next [Weather]. + */ + @JvmStatic val WEIGHT = weatherOptionOf(CatalogKey.minecraft("weight"), 500) } diff --git a/src/main/kotlin/org/lanternpowered/api/x/XGameRegistry.kt b/src/main/kotlin/org/lanternpowered/api/x/XGameRegistry.kt index 953f11cc6..7da4aaa57 100644 --- a/src/main/kotlin/org/lanternpowered/api/x/XGameRegistry.kt +++ b/src/main/kotlin/org/lanternpowered/api/x/XGameRegistry.kt @@ -26,9 +26,12 @@ package org.lanternpowered.api.x import org.lanternpowered.api.GameRegistry +import org.lanternpowered.api.text.translation.TranslationRegistry import org.lanternpowered.api.x.text.XTextFactory interface XGameRegistry : GameRegistry { override fun getTextFactory(): XTextFactory + + val translations: TranslationRegistry } diff --git a/src/main/kotlin/org/lanternpowered/api/x/world/chunk/XChunk.kt b/src/main/kotlin/org/lanternpowered/api/x/world/chunk/XChunk.kt new file mode 100644 index 000000000..d0bb3f265 --- /dev/null +++ b/src/main/kotlin/org/lanternpowered/api/x/world/chunk/XChunk.kt @@ -0,0 +1,34 @@ +/* + * This file is part of LanternServer, licensed under the MIT License (MIT). + * + * Copyright (c) LanternPowered + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.lanternpowered.api.x.world.chunk + +import org.lanternpowered.api.world.chunk.Chunk + +interface XChunk : Chunk { + + val x: Int + val z: Int +} diff --git a/src/main/kotlin/org/lanternpowered/api/x/world/weather/XWeatherUniverse.kt b/src/main/kotlin/org/lanternpowered/api/x/world/weather/XWeatherUniverse.kt index b130c35c7..d33abd5c4 100644 --- a/src/main/kotlin/org/lanternpowered/api/x/world/weather/XWeatherUniverse.kt +++ b/src/main/kotlin/org/lanternpowered/api/x/world/weather/XWeatherUniverse.kt @@ -35,7 +35,7 @@ interface XWeatherUniverse : WeatherUniverse { /** * The current darkness of the sky. */ - val darkness: Double + val skyDarkness: Double /** * The current rain strength. diff --git a/src/main/resources/data/minecraft/enchantment/_loot_bonus.kts b/src/main/resources/data/minecraft/enchantment/_loot_bonus.kts new file mode 100644 index 000000000..b3519658d --- /dev/null +++ b/src/main/resources/data/minecraft/enchantment/_loot_bonus.kts @@ -0,0 +1,17 @@ +import org.lanternpowered.api.item.enchantment.EnchantmentTypes +import org.lanternpowered.api.script.enchantment + +// Script files prefixed with _ can be extended + +// This a basic (parent) script for all the +// loot bonus enchantments. + +enchantment { + enchantabilityRange { + val min = 15 + (it - 1) * 9 + val max = min + 50 + min..max + } + maxLevel(3) + compatibilityTester { it != EnchantmentTypes.SILK_TOUCH } +} diff --git a/src/main/resources/data/minecraft/enchantment/fire_aspect.kts b/src/main/resources/data/minecraft/enchantment/fire_aspect.kts new file mode 100644 index 000000000..eca488d2a --- /dev/null +++ b/src/main/resources/data/minecraft/enchantment/fire_aspect.kts @@ -0,0 +1,12 @@ +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.script.enchantment + +enchantment { + name(translationOf("enchantment.fire")) + maxLevel(2) + enchantabilityRange { + val min = 10 + (it - 1) * 20 + val max = min + 50 + min..max + } +} diff --git a/src/main/resources/data/minecraft/enchantment/fortune.kts b/src/main/resources/data/minecraft/enchantment/fortune.kts new file mode 100644 index 000000000..3b522b58a --- /dev/null +++ b/src/main/resources/data/minecraft/enchantment/fortune.kts @@ -0,0 +1,6 @@ +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.script.enchantment + +enchantment("minecraft:enchantment/_loot_bonus.kts") { + name(translationOf("enchantment.lootBonusDigger")) +} diff --git a/src/main/resources/data/minecraft/enchantment/knockback.kts b/src/main/resources/data/minecraft/enchantment/knockback.kts new file mode 100644 index 000000000..d23ea81c0 --- /dev/null +++ b/src/main/resources/data/minecraft/enchantment/knockback.kts @@ -0,0 +1,12 @@ +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.script.enchantment + +enchantment { + name(translationOf("enchantment.knockback")) + maxLevel(2) + enchantabilityRange { + val min = 5 + (it - 1) * 20 + val max = min + 50 + min..max + } +} diff --git a/src/main/resources/data/minecraft/enchantment/looting.kts b/src/main/resources/data/minecraft/enchantment/looting.kts new file mode 100644 index 000000000..76aac081f --- /dev/null +++ b/src/main/resources/data/minecraft/enchantment/looting.kts @@ -0,0 +1,6 @@ +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.script.enchantment + +enchantment("minecraft:enchantment/_loot_bonus.kts") { + name(translationOf("enchantment.lootBonus")) +} diff --git a/src/main/resources/data/minecraft/enchantment/luck_of_the_sea.kts b/src/main/resources/data/minecraft/enchantment/luck_of_the_sea.kts new file mode 100644 index 000000000..04fe5bb58 --- /dev/null +++ b/src/main/resources/data/minecraft/enchantment/luck_of_the_sea.kts @@ -0,0 +1,6 @@ +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.script.enchantment + +enchantment("minecraft:enchantment/_loot_bonus.kts") { + name(translationOf("enchantment.lootBonusFishing")) +} diff --git a/src/main/resources/data/minecraft/recipe/acacia_stairs.kts b/src/main/resources/data/minecraft/recipe/acacia_stairs.kts new file mode 100644 index 000000000..e11dc5027 --- /dev/null +++ b/src/main/resources/data/minecraft/recipe/acacia_stairs.kts @@ -0,0 +1,17 @@ +package data.minecraft.recipe + +import org.lanternpowered.api.item.inventory.ItemStack +import org.lanternpowered.api.script.* +import org.spongepowered.api.item.ItemTypes +import org.spongepowered.api.item.recipe.crafting.Ingredient + +shapedRecipe { + result(ItemStack(ItemTypes.OAK_STAIRS, 8)) + group("wooden_stairs") + aisle( + "# ", + "## ", + "###" + ) + ingredient('#', Ingredient.of(ItemTypes.LOG)) +} diff --git a/src/main/resources/data/minecraft/weather/clear.kts b/src/main/resources/data/minecraft/weather/clear.kts new file mode 100644 index 000000000..29a138f45 --- /dev/null +++ b/src/main/resources/data/minecraft/weather/clear.kts @@ -0,0 +1,12 @@ +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.script.weather +import org.lanternpowered.api.world.weather.WeatherOptions + +weather { + name("Clear") + + option(WeatherOptions.DURATION) { + random.nextDouble(300.0 .. 900.0) + } + option(WeatherOptions.WEIGHT, 500) +} diff --git a/src/main/resources/data/minecraft/weather/rain.kts b/src/main/resources/data/minecraft/weather/rain.kts new file mode 100644 index 000000000..36f1bbb00 --- /dev/null +++ b/src/main/resources/data/minecraft/weather/rain.kts @@ -0,0 +1,13 @@ +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.script.weather +import org.lanternpowered.api.world.weather.WeatherOptions + +weather { + name("Rain") + + option(WeatherOptions.RAIN_STRENGTH, 1.0) + option(WeatherOptions.DURATION) { + random.nextDouble(250.0 .. 500.0) + } + option(WeatherOptions.WEIGHT, 300) +} diff --git a/src/main/resources/data/minecraft/weather/thunder_storm.kts b/src/main/resources/data/minecraft/weather/thunder_storm.kts new file mode 100644 index 000000000..cd15e7c1b --- /dev/null +++ b/src/main/resources/data/minecraft/weather/thunder_storm.kts @@ -0,0 +1,23 @@ +import org.lanternpowered.api.entity.weather.LightningSpawner +import org.lanternpowered.api.ext.* +import org.lanternpowered.api.script.weather +import org.lanternpowered.api.world.weather.WeatherOptions + +weather { + name("Thunder Storm") + + option(WeatherOptions.RAIN_STRENGTH, 1.0) + option(WeatherOptions.SKY_DARKNESS, 1.0) + option(WeatherOptions.DURATION) { + random.nextDouble(200.0 .. 300.0) + } + option(WeatherOptions.WEIGHT, 150) + + action { world -> + // Only strike lighting if the sky darkness has transitioned above 0.8 + if (world.skyDarkness < 0.8) return@action + + // Spawn lightning in the world + LightningSpawner.spawnLightning(world, chance = 0.000004, attemptsPerChunk = 2) + } +}