diff --git a/engine/engine-core/src/main/kotlin/com/typewritermc/core/interaction/Interaction.kt b/engine/engine-core/src/main/kotlin/com/typewritermc/core/interaction/Interaction.kt index 38a242506f..cc62cd99e7 100644 --- a/engine/engine-core/src/main/kotlin/com/typewritermc/core/interaction/Interaction.kt +++ b/engine/engine-core/src/main/kotlin/com/typewritermc/core/interaction/Interaction.kt @@ -78,6 +78,12 @@ class InteractionContextBuilder { put(this, value) } + operator fun Ref.set(key: EntryContextKey, value: T) { + put(EntryInteractionContextKey(this, key), value) + } + + operator fun Entry.set(key: EntryContextKey, value: T) = ref().set(key, value) + fun build(): InteractionContext { return InteractionContext(data) } diff --git a/extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/event/StandOnBlockEventEntry.kt b/extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/event/StandOnBlockEventEntry.kt new file mode 100644 index 0000000000..4f6b67fb22 --- /dev/null +++ b/extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/event/StandOnBlockEventEntry.kt @@ -0,0 +1,119 @@ +package com.typewritermc.basic.entries.event + +import com.typewritermc.core.books.pages.Colors +import com.typewritermc.core.entries.Query +import com.typewritermc.core.entries.Ref +import com.typewritermc.core.extension.annotations.* +import com.typewritermc.core.interaction.EntryContextKey +import com.typewritermc.core.interaction.context +import com.typewritermc.core.utils.point.Position +import com.typewritermc.core.utils.point.toBlockPosition +import com.typewritermc.engine.paper.entry.TriggerableEntry +import com.typewritermc.engine.paper.entry.entries.ConstVar +import com.typewritermc.engine.paper.entry.entries.EventEntry +import com.typewritermc.engine.paper.entry.entries.Var +import com.typewritermc.engine.paper.entry.triggerAllFor +import com.typewritermc.engine.paper.utils.toPosition +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.event.player.PlayerMoveEvent +import java.util.* +import kotlin.jvm.optionals.getOrNull +import kotlin.reflect.KClass + +@Entry( + "stand_on_block_event", + "Triggered when a player stands on a block", + Colors.YELLOW, + "material-symbols:lightning-stand" +) +@ContextKeys(StandOnBlockContextKeys::class) +/** + * The `Stand On Block Event` is triggered when a player stands on a block. + * + * ## How could this be used? + * This could be used to send a player flying when the stand on a netherite block. + */ +class StandOnBlockEventEntry( + override val id: String = "", + override val name: String = "", + override val triggers: List> = emptyList(), + val block: Optional> = Optional.empty(), + val position: Optional> = Optional.empty(), + @Help( + """ + Cancel the event when triggered. + It will only cancel the event if all the criteria are met. + If set to false, it will not modify the event. + """ + ) + val cancel: Var = ConstVar(false), +) : EventEntry + +enum class StandOnBlockContextKeys(override val klass: KClass<*>) : EntryContextKey { + @KeyType(Position::class) + PLAYER_POSITION(Position::class), + + @KeyType(Position::class) + BLOCK_POSITION(Position::class), + + @KeyType(Material::class) + BLOCK_MATERIAL(Material::class), +} + +fun hasBlock(location: Location, block: Material): Boolean { + return blockLocation(location, block) != null +} + +fun blockLocation(location: Location, block: Material): Location? { + val loc = location.clone() + if (loc.block.type == block) return location + loc.add(0.0, 1.0, 0.0) + if (loc.block.type == block) return loc + loc.add(0.0, -1.0, 0.0) + if (loc.block.type == block) return loc + return null +} + +@EntryListener(StandOnBlockEventEntry::class) +fun onStandOnBlock(event: PlayerMoveEvent, query: Query) { + if (!event.hasChangedBlock()) return + val player = event.player + val from = event.from + val to = event.to + val entries = query.findWhere { + if (it.block.isPresent) { + val block = it.block.get().get(player, context()) + if (!(!hasBlock(from, block) && hasBlock(to, block))) return@findWhere false + } + if (it.position.isPresent) { + val position = it.position.get().get(player, context()) + if (!(!position.sameBlock(from.toPosition()) && position.sameBlock(to.toPosition()))) return@findWhere false + } + true + }.toList() + + entries.triggerAllFor(event.player, context { + entries.forEach { entry -> + entry[StandOnBlockContextKeys.PLAYER_POSITION] = to.toPosition() + + val blockType = entry.block.getOrNull()?.get(player, context()) + if (blockType != null) { + entry[StandOnBlockContextKeys.BLOCK_MATERIAL] = blockType + entry[StandOnBlockContextKeys.BLOCK_POSITION] = blockLocation( + to, + blockType + )?.toPosition()?.toBlockPosition() ?: to.toPosition().toBlockPosition() + } else if (to.block.type != Material.AIR) { + entry[StandOnBlockContextKeys.BLOCK_MATERIAL] = to.block.type + entry[StandOnBlockContextKeys.BLOCK_POSITION] = to.toPosition().toBlockPosition() + } else { + val blockBelow = to.clone().add(0.0, -1.0, 0.0) + entry[StandOnBlockContextKeys.BLOCK_MATERIAL] = blockBelow.block.type + entry[StandOnBlockContextKeys.BLOCK_POSITION] = blockBelow.toPosition().toBlockPosition() + } + } + }) + + if (entries.any { it.cancel.get(player) }) event.isCancelled = true +} \ No newline at end of file diff --git a/extensions/_DocsExtension/src/main/kotlin/com/typewritermc/example/entries/trigger/ExampleEventEntry.kt b/extensions/_DocsExtension/src/main/kotlin/com/typewritermc/example/entries/trigger/ExampleEventEntry.kt index 5edeab5376..e1226d22ce 100644 --- a/extensions/_DocsExtension/src/main/kotlin/com/typewritermc/example/entries/trigger/ExampleEventEntry.kt +++ b/extensions/_DocsExtension/src/main/kotlin/com/typewritermc/example/entries/trigger/ExampleEventEntry.kt @@ -40,6 +40,7 @@ fun onEvent(event: SomeBukkitEvent, query: Query) { // @Entry("example_event_with_context_keys", "An example event entry with context keys.", Colors.YELLOW, "material-symbols:bigtop-updates") // This tells Typewriter that this entry exposes some context +// highlight-next-line @ContextKeys(ExampleContextKeys::class) class ExampleEventEntryWithContextKeys( override val id: String = "", @@ -47,6 +48,7 @@ class ExampleEventEntryWithContextKeys( override val triggers: List> = emptyList(), ) : EventEntry +// highlight-start enum class ExampleContextKeys(override val klass: KClass<*>) : EntryContextKey { // The two `String::class` have to be the same. // The @KeyType is for the panel to know @@ -61,10 +63,12 @@ enum class ExampleContextKeys(override val klass: KClass<*>) : EntryContextKey { @KeyType(Position::class) POSITION(Position::class) } +// highlight-end @EntryListener(ExampleEventEntryWithContextKeys::class) fun onEventAddContext(event: SomeBukkitEvent, query: Query) { val entries = query.find() + // highlight-start entries.triggerAllFor(event.player) { // Make sure these values are drawn from the event. // You MUST supply all the context keys. @@ -72,6 +76,7 @@ fun onEventAddContext(event: SomeBukkitEvent, query: Query