Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TemporalInteraction(
internal var state = STARTING
private set
private var playTime = Duration.ofMillis(-1)
private val frame: Int get() = (playTime.toMillis() / 50).toInt()
val frame: Int get() = (playTime.toMillis() / 50).toInt()

override val priority by lazy { Query.findPageById(pageId)?.priority ?: 0 }

Expand Down Expand Up @@ -138,6 +138,8 @@ private val Player.temporalInteraction: TemporalInteraction?
session?.interaction as? TemporalInteraction
}

fun Player.currentTemporalFrame(): Int? = temporalInteraction?.frame

fun Player.isPlayingTemporal(pageId: String): Boolean = temporalInteraction?.pageId == pageId

fun Player.isPlayingTemporal(): Boolean = temporalInteraction != null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.typewritermc.basic.entries.cinematic

import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWorldBorderSize
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayWorldBorderLerpSize
import com.typewritermc.core.books.pages.Colors
import com.typewritermc.core.extension.annotations.Entry
import com.typewritermc.core.extension.annotations.Segments
import com.typewritermc.engine.paper.entry.Criteria
import com.typewritermc.engine.paper.entry.entries.CinematicAction
import com.typewritermc.engine.paper.entry.entries.PrimaryCinematicEntry
import com.typewritermc.engine.paper.entry.entries.Segment
import com.typewritermc.engine.paper.entry.temporal.SimpleCinematicAction
import com.typewritermc.engine.paper.extensions.packetevents.sendPacket
import com.typewritermc.engine.paper.extensions.packetevents.sendPacketTo
import org.bukkit.entity.Player
import java.time.Duration
import java.util.*

@Entry("worldborder_cinematic", "A cinematic that resizes the worldborder over time", Colors.CYAN, "mdi:resize")
/**
* The `Worldborder cinematic` entry can animate the worldborder client-side.
* The border animation speed is not linked to the server's tick rate, since the animation fully runs on the client.
*
* ## How could this be used?
*
* This entry could be used to show the worldborder changing size during a cinematic, for example to emphasize a narrator talking about
* how the border could change on a server based on certain conditions.
*/
class WorldborderCinematicEntry(
override val id: String = "",
override val name: String = "",
override val criteria: List<Criteria> = emptyList(),
@Segments(icon = "mdi:resize")
val segments: List<WorldborderSegment> = emptyList(),
) : PrimaryCinematicEntry {
override fun create(player: Player): CinematicAction {
return WorldborderCinematicAction(
player,
this,
)
}
}

data class WorldborderSegment(
override val startFrame: Int = 0,
override val endFrame: Int = 0,
val newSize: Double = 0.0,
val oldSize: Optional<Double> = Optional.empty(),
val transitionTime: Duration = Duration.ofMillis(1000),
val fadeBack: Boolean = false
) : Segment

class WorldborderCinematicAction(
private val player: Player,
entry: WorldborderCinematicEntry,
) : SimpleCinematicAction<WorldborderSegment>() {

override val segments: List<WorldborderSegment> = entry.segments

override suspend fun startSegment(segment: WorldborderSegment) {
super.startSegment(segment)

val oldSize = segment.oldSize.orElseGet(this::borderSize)
val packet = WrapperPlayWorldBorderLerpSize(oldSize, segment.newSize, segment.transitionTime.toMillis())
packet.sendPacketTo(player)
}

private fun borderSize(): Double {
return player.world.worldBorder.size
}

override suspend fun stopSegment(segment: WorldborderSegment) {
super.stopSegment(segment)

val packet = if (!segment.fadeBack) WrapperPlayServerWorldBorderSize(borderSize())
else WrapperPlayWorldBorderLerpSize(segment.newSize, borderSize(), segment.transitionTime.toMillis())
player.sendPacket(packet)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class PlayerRespawnEventEntry(
enum class RespawnLocationType {
BED,
ANCHOR,
NONE
}

enum class RespawnContextKeys(override val klass: KClass<*>) : EntryContextKey {
Expand All @@ -60,6 +61,7 @@ fun onRespawn(event: PlayerRespawnEvent, query: Query<PlayerRespawnEventEntry>)
when (it) {
RespawnLocationType.BED -> event.isBedSpawn
RespawnLocationType.ANCHOR -> event.isAnchorSpawn
RespawnLocationType.NONE -> !event.isBedSpawn && !event.isAnchorSpawn
}
}
}.toList()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.typewritermc.basic.entries.fact

import com.google.gson.annotations.SerializedName
import com.typewritermc.core.books.pages.Colors
import com.typewritermc.core.books.pages.PageType
import com.typewritermc.core.entries.Ref
import com.typewritermc.core.entries.emptyRef
import com.typewritermc.core.extension.annotations.Entry
import com.typewritermc.core.extension.annotations.Help
import com.typewritermc.core.extension.annotations.Page
import com.typewritermc.engine.paper.entry.entries.GroupEntry
import com.typewritermc.engine.paper.entry.entries.ReadableFactEntry
import com.typewritermc.engine.paper.entry.temporal.currentTemporalFrame
import com.typewritermc.engine.paper.entry.temporal.isPlayingTemporal
import com.typewritermc.engine.paper.facts.FactData
import org.bukkit.entity.Player
import java.util.*

@Entry(
"cinematic_section_fact",
"Whether the player is within a certain section of a cinematic",
Colors.PURPLE,
"mdi:camera-burst"
)
/**
* The 'Cinematic Section Fact' is a fact that can return different values depending
* on what part of a cinematic the player is in.
*
* The specified `sections` can be used to map a certain range of cinematic frames to a value
* that will then be returned by the fact if the player is in that range. If none of the ranges
* match or the player is not currently in a cinematic, the value from `default` will be returned instead.
*
* Optionally, a cinematic page can be specified to only check for the given sections
* in that page, instead of any active cinematic.
*
*
* <fields.ReadonlyFactInfo />
*
* ## How could this be used?
* With this fact, it is possible to make an entry act differently depending on
* what section of a cinematic the player is in.
*/
class CinematicSectionFactEntry(
override val id: String = "",
override val name: String = "",
override val comment: String = "",
override val group: Ref<GroupEntry> = emptyRef(),
@Page(PageType.CINEMATIC)
@SerializedName("cinematic")
val pageId: String = "",
val sections: List<CinematicSection> = emptyList(),
@Help("Will be returned when the player is not in a cinematic, or not in any of the given sections")
val default: Optional<Int> = Optional.empty()
) : ReadableFactEntry {
override fun readSinglePlayer(player: Player): FactData {
val inCinematic = if (pageId.isNotBlank()) player.isPlayingTemporal(pageId) else player.isPlayingTemporal()
val default = default.orElse(0)
if (!inCinematic) return FactData(default)

val output = sections.firstOrNull { it.frameRange.contains(player.currentTemporalFrame() ?: -1) }?.value
return FactData(output ?: default)
}

data class CinematicSection(
@Help("The range of cinematic frames the player has to be in")
val frameRange: ClosedRange<Int> = IntRange.EMPTY,
val value: Int = 0
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class InCinematicFactEntry(
override val name: String = "",
override val comment: String = "",
override val group: Ref<GroupEntry> = emptyRef(),
@Help("When not set, it will filter based when any cinematic is active.")
@Help("When not set, it will filter based on if any cinematic is active.")
@Page(PageType.CINEMATIC)
@SerializedName("cinematic")
val pageId: String = "",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.typewritermc.entity.entries.data.minecraft.living.armorstand

import com.typewritermc.core.books.pages.Colors
import com.typewritermc.core.extension.annotations.Entry
import com.typewritermc.core.extension.annotations.Tags
import com.typewritermc.engine.paper.entry.entity.SinglePropertyCollectorSupplier
import com.typewritermc.engine.paper.entry.entries.EntityData
import com.typewritermc.engine.paper.entry.entries.EntityProperty
import com.typewritermc.engine.paper.extensions.packetevents.metas
import me.tofaa.entitylib.meta.other.ArmorStandMeta
import me.tofaa.entitylib.wrapper.WrapperEntity
import org.bukkit.entity.Player
import java.util.*
import kotlin.reflect.KClass

@Entry("invisible_data", "Whether the armor stand itself is invisible", Colors.RED, "mdi:mirror-variant")
@Tags("invisible_data", "armor_stand_data")
class InvisibleData(
override val id: String = "",
override val name: String = "",
val invisible: Boolean = false,
override val priorityOverride: Optional<Int> = Optional.empty(),
) : EntityData<InvisibleProperty> {
override fun type(): KClass<InvisibleProperty> = InvisibleProperty::class

override fun build(player: Player): InvisibleProperty = InvisibleProperty(invisible)
}

data class InvisibleProperty(val invisible: Boolean) : EntityProperty {
companion object : SinglePropertyCollectorSupplier<InvisibleProperty>(InvisibleProperty::class, InvisibleProperty(false))
}

fun applyInvisibleData(entity: WrapperEntity, property: InvisibleProperty) {
entity.metas {
meta<ArmorStandMeta> { isInvisible = property.invisible }
error("Could not apply InvisibleData to ${entity.entityType} entity.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private class ArmorStandEntity(player: Player) : WrapperFakeEntity(
is MarkerProperty -> applyMarkerData(entity, property)
is RotationProperty -> applyRotationData(entity, property)
is SmallProperty -> applySmallData(entity, property)
is InvisibleProperty -> applyInvisibleData(entity, property)
else -> {}
}
if (applyGenericEntityData(entity, property)) return
Expand Down