1
0
mirror of https://github.com/chylex/Hardcore-Ender-Expansion-2.git synced 2025-11-19 08:22:35 +01:00

7 Commits

50 changed files with 826 additions and 232 deletions

View File

@@ -22,7 +22,6 @@ import net.minecraft.block.BlockState
import net.minecraft.enchantment.EnchantmentHelper
import net.minecraft.enchantment.Enchantments
import net.minecraft.entity.Entity
import net.minecraft.entity.EntitySpawnPlacementRegistry.PlacementType
import net.minecraft.entity.EntityType
import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockReader
@@ -48,6 +47,14 @@ class BlockDustyStoneUnstable(builder: BlockBuilder) : BlockDustyStone(builder),
return null
}
private fun isNonCreative(entity: Entity): Boolean {
return entity !is EntityPlayer || !entity.isCreative
}
private fun isLightMob(entity: Entity): Boolean {
return entity.height <= 0.5F || (entity.height <= 1F && entity.width <= 0.5F)
}
}
override fun canHarvestBlock(state: BlockState, world: IBlockReader, pos: BlockPos, player: EntityPlayer): Boolean {
@@ -87,7 +94,7 @@ class BlockDustyStoneUnstable(builder: BlockBuilder) : BlockDustyStone(builder),
}
override fun onEntityCollisionAbove(world: World, pos: BlockPos, entity: Entity) {
if (!world.isRemote && world.totalTime % 4L == 0L && !(entity.height <= 0.5F || (entity.height <= 1F && entity.width <= 0.5F)) && isNonCreative(entity)) {
if (!world.isRemote && world.totalTime % 4L == 0L && !isLightMob(entity) && isNonCreative(entity)) {
if (!doCrumbleTest(world, pos)) {
return
}
@@ -103,7 +110,7 @@ class BlockDustyStoneUnstable(builder: BlockBuilder) : BlockDustyStone(builder),
}
override fun onFallenUpon(world: World, pos: BlockPos, entity: Entity, fallDistance: Float) {
if (!world.isRemote && entity is EntityLivingBase && fallDistance > 1.3F && isNonCreative(entity)) {
if (!world.isRemote && entity is EntityLivingBase && fallDistance > (if (isLightMob(entity)) 3.3F else 1.3F) && isNonCreative(entity)) {
val rand = world.rand
val aabb = entity.boundingBox
val y = pos.y
@@ -135,12 +142,8 @@ class BlockDustyStoneUnstable(builder: BlockBuilder) : BlockDustyStone(builder),
super.onFallenUpon(world, pos, entity, fallDistance)
}
override fun canCreatureSpawn(state: BlockState, world: IBlockReader, pos: BlockPos, placementType: PlacementType, entityType: EntityType<*>?): Boolean {
return super.canCreatureSpawn(state, world, pos, placementType, entityType) && getCrumbleStartPos(world, pos) == null
}
private fun isNonCreative(entity: Entity): Boolean {
return entity !is EntityPlayer || !entity.isCreative
override fun canEntitySpawn(state: BlockState, world: IBlockReader, pos: BlockPos, entityType: EntityType<*>): Boolean {
return super.canEntitySpawn(state, world, pos, entityType) && getCrumbleStartPos(world, pos) == null
}
private fun doCrumbleTest(world: World, pos: BlockPos): Boolean {

View File

@@ -9,14 +9,13 @@ import chylex.hee.game.entity.technical.EntityTechnicalTrigger.Types.ENERGY_SHRI
import chylex.hee.game.world.center
import chylex.hee.game.world.feature.energyshrine.EnergyShrinePieces
import net.minecraft.block.BlockState
import net.minecraft.entity.EntitySpawnPlacementRegistry.PlacementType
import net.minecraft.entity.EntityType
import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockReader
import net.minecraft.world.IEntityReader
class BlockGloomrock(builder: BlockBuilder) : BlockSimple(builder) {
override fun canCreatureSpawn(state: BlockState, world: IBlockReader, pos: BlockPos, type: PlacementType, entityType: EntityType<*>?): Boolean {
override fun canEntitySpawn(state: BlockState, world: IBlockReader, pos: BlockPos, entityType: EntityType<*>?): Boolean {
if (world !is IEntityReader) {
HEE.log.warn("[BlockGloomrock] attempted to check spawn on a world != IEntityReader (${world.javaClass})")
return false

View File

@@ -25,6 +25,7 @@ import chylex.hee.system.random.nextFloat
import chylex.hee.system.random.nextInt
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.entity.EntityType
import net.minecraft.item.BlockItemUseContext
import net.minecraft.item.ItemStack
import net.minecraft.state.StateContainer.Builder
@@ -88,6 +89,12 @@ open class BlockGraveDirt(builder: BlockBuilder) : BlockSimpleShaped(builder, Ax
super.getShape(state, source, pos, context)
}
// Mobs
override fun canEntitySpawn(state: BlockState, worldIn: IBlockReader, pos: BlockPos, type: EntityType<*>): Boolean {
return true
}
// Explosions
override fun canDropFromExplosion(explosion: Explosion): Boolean {

View File

@@ -76,8 +76,8 @@ class EntityMobAngryEnderman(type: EntityType<EntityMobAngryEnderman>, world: Wo
}
override fun updateAITasks() {
teleportHandler.update()
waterHandler.update()
teleportHandler.tickServer()
waterHandler.tickServer()
val currentTarget = attackTarget

View File

@@ -26,6 +26,10 @@ import chylex.hee.game.mechanics.causatum.events.CausatumEventEndermanKill
import chylex.hee.game.world.playServer
import chylex.hee.init.ModEntities
import chylex.hee.init.ModSounds
import chylex.hee.system.component.EntityComponents
import chylex.hee.system.component.general.deserializeFrom
import chylex.hee.system.component.general.serializeTo
import chylex.hee.system.component.general.tick
import chylex.hee.system.facades.Resource
import chylex.hee.system.forge.SubscribeAllEvents
import chylex.hee.system.forge.SubscribeEvent
@@ -76,8 +80,6 @@ class EntityMobEnderman(type: EntityType<EntityMobEnderman>, world: World) : Ent
@SubscribeAllEvents(modid = HEE.ID)
companion object {
private const val TELEPORT_HANDLER_TAG = "Teleport"
private const val WATER_HANDLER_TAG = "Water"
private const val CAN_PICK_UP_BLOCKS_TAG = "CanPickUpBlocks"
private const val HELD_BLOCK_TIMER_TAG = "HeldBlockTimer"
private const val HELD_BLOCK_DESPAWNS_TAG = "HeldBlockDespawns"
@@ -202,8 +204,9 @@ class EntityMobEnderman(type: EntityType<EntityMobEnderman>, world: World) : Ent
// Instance
private lateinit var components: EntityComponents
private lateinit var teleportHandler: EndermanTeleportHandler
private lateinit var waterHandler: EndermanWaterHandler
private lateinit var blockHandler: EndermanBlockHandler
private lateinit var aiAttackTarget: AIToggle
@@ -227,6 +230,13 @@ class EntityMobEnderman(type: EntityType<EntityMobEnderman>, world: World) : Ent
// Initialization
override fun registerData() {
super.registerData()
components = EntityComponents()
components.attach(EndermanWaterHandler(this, takeDamageAfterWetTicks = 80))
}
override fun registerAttributes() {
super.registerAttributes()
@@ -238,8 +248,7 @@ class EntityMobEnderman(type: EntityType<EntityMobEnderman>, world: World) : Ent
}
override fun registerGoals() {
teleportHandler = EndermanTeleportHandler(this)
waterHandler = EndermanWaterHandler(this, takeDamageAfterWetTicks = 80)
teleportHandler = EndermanTeleportHandler(this).also(components::attach)
blockHandler = EndermanBlockHandler(this)
aiWatchTargetInShock = AIWatchTargetInShock(this, maxDistance = 72.0)
@@ -265,10 +274,9 @@ class EntityMobEnderman(type: EntityType<EntityMobEnderman>, world: World) : Ent
override fun livingTick() {
super.livingTick()
components.tick(this)
if (!world.isRemote) {
teleportHandler.update()
waterHandler.update()
if (heldBlockTimer > 0 && --heldBlockTimer == 0.toShort()) {
if (heldBlockDespawns || !blockHandler.tryPlaceBlock(allowPlayerProximity = false)) {
teleportHandler.teleportOutOfWorld(force = rand.nextBoolean())
@@ -536,8 +544,7 @@ class EntityMobEnderman(type: EntityType<EntityMobEnderman>, world: World) : Ent
override fun writeAdditional(nbt: TagCompound) = nbt.heeTag.use {
super.writeAdditional(nbt)
put(TELEPORT_HANDLER_TAG, teleportHandler.serializeNBT())
put(WATER_HANDLER_TAG, waterHandler.serializeNBT())
components.serializeTo(this)
putBoolean(CAN_PICK_UP_BLOCKS_TAG, aiPickUpBlocks.enabled)
putShort(HELD_BLOCK_TIMER_TAG, heldBlockTimer)
@@ -547,8 +554,7 @@ class EntityMobEnderman(type: EntityType<EntityMobEnderman>, world: World) : Ent
override fun readAdditional(nbt: TagCompound) = nbt.heeTag.use {
super.readAdditional(nbt)
teleportHandler.deserializeNBT(getCompound(TELEPORT_HANDLER_TAG))
waterHandler.deserializeNBT(getCompound(WATER_HANDLER_TAG))
components.deserializeFrom(this)
aiPickUpBlocks.enabled = getBoolean(CAN_PICK_UP_BLOCKS_TAG)
heldBlockTimer = getShort(HELD_BLOCK_TIMER_TAG)

View File

@@ -56,11 +56,15 @@ import net.minecraft.block.BlockState
import net.minecraft.entity.CreatureAttribute
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityType
import net.minecraft.entity.ILivingEntityData
import net.minecraft.entity.SharedMonsterAttributes.ATTACK_DAMAGE
import net.minecraft.entity.SharedMonsterAttributes.FOLLOW_RANGE
import net.minecraft.entity.SharedMonsterAttributes.MAX_HEALTH
import net.minecraft.entity.SharedMonsterAttributes.MOVEMENT_SPEED
import net.minecraft.entity.SpawnReason
import net.minecraft.entity.SpawnReason.SPAWNER
import net.minecraft.entity.ai.attributes.AttributeModifier
import net.minecraft.nbt.CompoundNBT
import net.minecraft.network.IPacket
import net.minecraft.network.datasync.DataSerializers
import net.minecraft.pathfinding.PathNavigator
@@ -76,6 +80,8 @@ import net.minecraft.util.math.RayTraceResult.Type
import net.minecraft.util.math.Vec3d
import net.minecraft.world.Difficulty.HARD
import net.minecraft.world.Difficulty.NORMAL
import net.minecraft.world.DifficultyInstance
import net.minecraft.world.IWorld
import net.minecraft.world.IWorldReader
import net.minecraft.world.LightType.BLOCK
import net.minecraft.world.LightType.SKY
@@ -213,7 +219,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
}
else if (wakeUpTimer > 0) {
if (--wakeUpTimer == 0) {
wakeUp(instant = false, preventSleep = false)
wakeUp(preventSleep = false)
}
}
else if (ticksExisted % 4 == 0) {
@@ -291,10 +297,18 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
}
}
private fun wakeUp(instant: Boolean, preventSleep: Boolean) {
fun wakeUpInstantly(preventSleep: Boolean) {
wakeUp(preventSleep, aiDelayTicks = 1)
}
fun wakeUp(preventSleep: Boolean) {
wakeUp(preventSleep, aiDelayTicks = rand.nextInt(25, 40))
}
fun wakeUp(preventSleep: Boolean, aiDelayTicks: Int) {
if (isSleeping) {
isSleepingProp = false
wakeUpDelayAI = if (instant) 1 else rand.nextInt(25, 40)
wakeUpDelayAI = aiDelayTicks
if (preventSleep) {
canSleepAgain = false
@@ -303,7 +317,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
}
override fun setFire(seconds: Int) {
wakeUp(instant = true, preventSleep = true)
wakeUpInstantly(preventSleep = true)
super.setFire(seconds)
}
@@ -314,7 +328,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
// Behavior (Light)
override fun onLightStartled(): Boolean {
wakeUp(instant = false, preventSleep = true)
wakeUp(preventSleep = true)
if (world.totalTime < lightStartleResetTime) {
return false
@@ -401,7 +415,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
return false
}
wakeUp(instant = true, preventSleep = true)
wakeUpInstantly(preventSleep = true)
if (!super.attackEntityFrom(source, if (source.isFireDamage) amount * 1.25F else amount)) {
return false
@@ -445,6 +459,16 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
return true
}
// Spawning
override fun onInitialSpawn(world: IWorld, difficulty: DifficultyInstance, reason: SpawnReason, data: ILivingEntityData?, nbt: CompoundNBT?): ILivingEntityData? {
if (reason == SPAWNER) {
wakeUpInstantly(preventSleep = true)
}
return super.onInitialSpawn(world, difficulty, reason, data, nbt)
}
// Despawning
override fun isDespawnPeaceful(): Boolean {
@@ -515,7 +539,7 @@ class EntityMobSpiderling(type: EntityType<EntityMobSpiderling>, world: World) :
val sleepState = getInt(SLEEP_STATE_TAG)
if (sleepState != 2) {
wakeUp(instant = true, preventSleep = sleepState != 1)
wakeUpInstantly(preventSleep = sleepState != 1)
}
lightStartleResetTime = getLong(LIGHT_STARTLE_RESET_TIME_TAG)

View File

@@ -137,19 +137,19 @@ class EntityMobUndread(type: EntityType<EntityMobUndread>, world: World) : Entit
super.onDeathUpdate()
}
override fun playStepSound(pos: BlockPos, state: BlockState) {
public override fun playStepSound(pos: BlockPos, state: BlockState) {
playSound(Sounds.ENTITY_ZOMBIE_STEP, rand.nextFloat(0.4F, 0.5F), rand.nextFloat(0.9F, 1F))
}
override fun getHurtSound(source: DamageSource): SoundEvent {
public override fun getHurtSound(source: DamageSource): SoundEvent {
return ModSounds.MOB_UNDREAD_HURT
}
override fun getDeathSound(): SoundEvent {
public override fun getDeathSound(): SoundEvent {
return ModSounds.MOB_UNDREAD_DEATH
}
override fun getSoundPitch(): Float {
public override fun getSoundPitch(): Float {
return rand.nextFloat(0.8F, 1F)
}
}

View File

@@ -7,8 +7,8 @@ import chylex.hee.game.particle.spawner.ParticleSpawnerCustom
import chylex.hee.game.particle.spawner.properties.IShape.Point
import chylex.hee.game.world.bottomCenter
import chylex.hee.network.client.PacketClientFX
import chylex.hee.network.fx.IFxData
import chylex.hee.network.fx.IFxHandler
import chylex.hee.network.fx.FxVecData
import chylex.hee.network.fx.FxVecHandler
import chylex.hee.system.color.IntColor.Companion.RGB
import chylex.hee.system.math.Vec
import chylex.hee.system.math.Vec3
@@ -21,10 +21,7 @@ import chylex.hee.system.serialization.NBTList.Companion.putList
import chylex.hee.system.serialization.NBTObjectList
import chylex.hee.system.serialization.TagCompound
import chylex.hee.system.serialization.getListOfCompounds
import chylex.hee.system.serialization.readVec
import chylex.hee.system.serialization.use
import chylex.hee.system.serialization.writeVec
import net.minecraft.network.PacketBuffer
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
@@ -50,15 +47,9 @@ class EnderEyeSpawnerParticles(private val entity: EntityBossEnderEye) : INBTSer
hideOnMinimalSetting = false
)
class ParticleData(private val point: Vec3d) : IFxData {
override fun write(buffer: PacketBuffer) = buffer.use {
writeVec(point)
}
}
val FX_PARTICLE = object : IFxHandler<ParticleData> {
override fun handle(buffer: PacketBuffer, world: World, rand: Random) = buffer.use {
PARTICLE_TICK.spawn(Point(readVec(), 2), rand)
val FX_PARTICLE = object : FxVecHandler() {
override fun handle(world: World, rand: Random, vec: Vec3d) {
PARTICLE_TICK.spawn(Point(vec, 2), rand)
}
}
}
@@ -95,7 +86,7 @@ class EnderEyeSpawnerParticles(private val entity: EntityBossEnderEye) : INBTSer
else -> 1.0
}
PacketClientFX(FX_PARTICLE, ParticleData(pos.addY(sqrt(progressCurvePoint) * 6.0))).sendToAllAround(entity.world, pos, 256.0)
PacketClientFX(FX_PARTICLE, FxVecData(pos.addY(sqrt(progressCurvePoint) * 6.0))).sendToAllAround(entity.world, pos, 256.0)
}
if (distSq > prevDistSq || distSq < square(0.15)) {

View File

@@ -29,6 +29,8 @@ import chylex.hee.network.client.PacketClientFX
import chylex.hee.network.fx.FxEntityData
import chylex.hee.network.fx.FxEntityHandler
import chylex.hee.system.color.IntColor.Companion.RGB
import chylex.hee.system.component.general.SerializableComponent
import chylex.hee.system.component.general.TickableComponent
import chylex.hee.system.math.Vec
import chylex.hee.system.math.Vec3
import chylex.hee.system.math.addY
@@ -53,13 +55,12 @@ import net.minecraft.util.SoundCategory
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import net.minecraftforge.common.util.INBTSerializable
import java.util.Random
import java.util.UUID
import kotlin.math.min
import kotlin.math.sqrt
class EndermanTeleportHandler(private val enderman: EntityMobAbstractEnderman) : INBTSerializable<TagCompound> {
class EndermanTeleportHandler(private val enderman: EntityMobAbstractEnderman) : TickableComponent, SerializableComponent {
companion object {
private const val DEFAULT_RESTORE_Y = -256.0
@@ -117,6 +118,9 @@ class EndermanTeleportHandler(private val enderman: EntityMobAbstractEnderman) :
}
}
override val serializationKey
get() = "Teleport"
val preventDespawn
get() = tpDelayTicks > 0
@@ -133,7 +137,7 @@ class EndermanTeleportHandler(private val enderman: EntityMobAbstractEnderman) :
private var lastDodged: UUID? = null
fun update() {
override fun tickServer() {
if (tpCooldown > 0) {
--tpCooldown
}

View File

@@ -4,15 +4,16 @@ import chylex.hee.game.entity.OPERATION_MUL_INCR_INDIVIDUAL
import chylex.hee.game.entity.living.EntityMobAbstractEnderman
import chylex.hee.game.entity.tryApplyModifier
import chylex.hee.game.entity.tryRemoveModifier
import chylex.hee.system.component.general.SerializableComponent
import chylex.hee.system.component.general.TickableComponent
import chylex.hee.system.random.nextInt
import chylex.hee.system.serialization.TagCompound
import chylex.hee.system.serialization.use
import net.minecraft.entity.SharedMonsterAttributes.ATTACK_DAMAGE
import net.minecraft.entity.ai.attributes.AttributeModifier
import net.minecraft.util.DamageSource
import net.minecraftforge.common.util.INBTSerializable
class EndermanWaterHandler(private val enderman: EntityMobAbstractEnderman, private val takeDamageAfterWetTicks: Int) : INBTSerializable<TagCompound> {
class EndermanWaterHandler(private val enderman: EntityMobAbstractEnderman, private val takeDamageAfterWetTicks: Int) : TickableComponent, SerializableComponent {
private companion object {
private val DEBUFF_WEAKNESS = AttributeModifier("Water weakness", -0.5, OPERATION_MUL_INCR_INDIVIDUAL)
@@ -20,10 +21,13 @@ class EndermanWaterHandler(private val enderman: EntityMobAbstractEnderman, priv
private const val DEBUFF_TICKS_TAG = "DebuffTicks"
}
override val serializationKey
get() = "Water"
private var wetCounter = 0
private var debuffTicks = 0
fun update() {
override fun tickServer() {
val isWet = enderman.isWet
if (isWet) {

View File

@@ -9,6 +9,7 @@ import chylex.hee.game.world.feature.stronghold.piece.StrongholdRoom_Main_Portal
import chylex.hee.game.world.feature.stronghold.piece.StrongholdRoom_Trap_CornerHoles
import chylex.hee.game.world.feature.stronghold.piece.StrongholdRoom_Trap_Prison
import chylex.hee.game.world.feature.stronghold.piece.StrongholdRoom_Trap_TallIntersection
import chylex.hee.game.world.feature.tombdungeon.piece.TombDungeonRoom_Tomb
import chylex.hee.init.ModEntities
import chylex.hee.system.delegate.NotifyOnChange
import chylex.hee.system.serialization.TagCompound
@@ -28,6 +29,10 @@ class EntityTechnicalTrigger(type: EntityType<EntityTechnicalTrigger>, world: Wo
this.type = type
}
constructor(world: World, type: Types, nbt: TagCompound) : this(world, type) {
handler.deserializeNBT(nbt)
}
private companion object {
private const val TYPE_TAG = "Type"
private const val DATA_TAG = "Data"
@@ -62,6 +67,7 @@ class EntityTechnicalTrigger(type: EntityType<EntityTechnicalTrigger>, world: Wo
STRONGHOLD_TRAP_TALL_INTERSECTION({ StrongholdRoom_Trap_TallIntersection.Trigger }),
ENERGY_SHRINE_GENERATOR({ EnergyShrineGenerator.GeneratorTrigger }),
ENERGY_SHRINE_GLOBAL({ EnergyShrineRoom_Main_Start.Particles }),
TOMB_DUNGEON_UNDREAD_SPAWNER(TombDungeonRoom_Tomb::MobSpawnerTrigger),
OBSIDIAN_TOWER_TOP_GLOWSTONE(ObsidianTowerLevel_Top::GlowstoneTrigger),
OBSIDIAN_TOWER_DEATH_ANIMATION(ObsidianTowerLevel_Top::DeathAnimationTrigger)
}

View File

@@ -3,6 +3,7 @@ package chylex.hee.game.item
import chylex.hee.game.block.entity.TileEntityEnergyCluster
import chylex.hee.game.inventory.heeTag
import chylex.hee.game.inventory.heeTagOrNull
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.mechanics.energy.IEnergyQuantity
import chylex.hee.game.mechanics.energy.IEnergyQuantity.Units
import chylex.hee.game.particle.ParticleEnergyTransferToPlayer
@@ -36,11 +37,11 @@ import chylex.hee.system.serialization.use
import chylex.hee.system.serialization.writePos
import net.minecraft.client.util.ITooltipFlag
import net.minecraft.entity.Entity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.network.PacketBuffer
import net.minecraft.util.ActionResultType
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.MathHelper
import net.minecraft.util.text.ITextComponent
import net.minecraft.util.text.TranslationTextComponent
@@ -50,7 +51,7 @@ import java.util.Random
import kotlin.math.max
import kotlin.math.pow
abstract class ItemAbstractEnergyUser(properties: Properties) : Item(properties) {
abstract class ItemAbstractEnergyUser(properties: Properties) : ItemWithComponents(properties), UseOnBlockComponent {
companion object {
private const val ENERGY_LEVEL_TAG = "EnergyLevel"
@@ -100,6 +101,8 @@ abstract class ItemAbstractEnergyUser(properties: Properties) : Item(properties)
init {
@Suppress("DEPRECATION")
require(maxStackSize == 1) { "energy item must have a maximum stack size of 1" }
components.attach(this)
}
protected abstract fun getEnergyCapacity(stack: ItemStack): Units
@@ -163,15 +166,11 @@ abstract class ItemAbstractEnergyUser(properties: Properties) : Item(properties)
// Energy charging
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType? {
val tile = pos.getTile<TileEntityEnergyCluster>(world)
val stack = player.getHeldItem(context.hand)
val stack = player.getHeldItem(ctx.hand)
if (tile == null || !player.canPlayerEdit(pos, context.face, stack)) {
if (tile == null || !player.canPlayerEdit(pos, ctx.face, stack)) {
return FAIL
}
else if (world.isRemote) {

View File

@@ -13,7 +13,7 @@ import net.minecraft.util.text.StringTextComponent
import net.minecraft.util.text.TranslationTextComponent
import net.minecraft.world.World
abstract class ItemAbstractInfusable(properties: Properties) : Item(properties), IInfusableItem {
abstract class ItemAbstractInfusable(properties: Properties) : ItemWithComponents(properties), IInfusableItem {
companion object {
fun onCanApplyInfusion(item: Item, infusion: Infusion): Boolean {
return infusion.targetItems.contains(item)

View File

@@ -1,6 +1,7 @@
package chylex.hee.game.item
import chylex.hee.game.inventory.size
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.world.BlockEditor
import chylex.hee.init.ModItems
import chylex.hee.network.client.PacketClientFX
@@ -15,7 +16,6 @@ import chylex.hee.system.migration.ItemBoneMeal
import net.minecraft.block.DispenserBlock.FACING
import net.minecraft.dispenser.IBlockSource
import net.minecraft.dispenser.OptionalDispenseBehavior
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.util.ActionResultType
@@ -25,7 +25,7 @@ import net.minecraft.world.server.ServerWorld
import net.minecraftforge.common.util.FakePlayerFactory
import java.util.Random
class ItemCompost(properties: Properties) : Item(properties) {
class ItemCompost(properties: Properties) : ItemWithComponents(properties) {
companion object {
private const val BONE_MEAL_EQUIVALENT = 2
@@ -61,6 +61,24 @@ class ItemCompost(properties: Properties) : Item(properties) {
}
init {
components.attach(object : UseOnBlockComponent {
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType {
if (!BlockEditor.canEdit(pos, player, item)) {
return FAIL
}
else if (world.isRemote) {
return SUCCESS
}
if (applyCompost(world, pos, player)) {
item.shrink(1)
return SUCCESS
}
return PASS
}
})
BlockDispenser.registerDispenseBehavior(this, object : OptionalDispenseBehavior() {
override fun dispenseStack(source: IBlockSource, stack: ItemStack): ItemStack {
val world = source.world
@@ -77,26 +95,4 @@ class ItemCompost(properties: Properties) : Item(properties) {
}
})
}
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
val heldItem = player.getHeldItem(context.hand)
if (!BlockEditor.canEdit(pos, player, heldItem)) {
return FAIL
}
else if (world.isRemote) {
return SUCCESS
}
if (applyCompost(world, pos, player)) {
heldItem.shrink(1)
return SUCCESS
}
return PASS
}
}

View File

@@ -2,27 +2,27 @@ package chylex.hee.game.item
import chylex.hee.game.block.IBlockDeathFlowerDecaying
import chylex.hee.game.entity.item.EntityItemCauldronTrigger
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.world.BlockEditor
import chylex.hee.game.world.getBlock
import chylex.hee.system.migration.ActionResult.FAIL
import chylex.hee.system.migration.ActionResult.PASS
import chylex.hee.system.migration.ActionResult.SUCCESS
import chylex.hee.system.migration.EntityPlayer
import net.minecraft.entity.Entity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.util.ActionResultType
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
class ItemEndPowder(properties: Properties) : Item(properties) {
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
val heldItem = player.getHeldItem(context.hand)
if (!BlockEditor.canEdit(pos, player, heldItem)) {
class ItemEndPowder(properties: Properties) : ItemWithComponents(properties), UseOnBlockComponent {
init {
components.attach(this)
}
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType? {
if (!BlockEditor.canEdit(pos, player, item)) {
return FAIL
}
@@ -33,7 +33,7 @@ class ItemEndPowder(properties: Properties) : Item(properties) {
block.healDeathFlower(world, pos)
}
heldItem.shrink(1)
item.shrink(1)
return SUCCESS
}

View File

@@ -28,7 +28,6 @@ import chylex.hee.system.math.angleBetween
import chylex.hee.system.math.floorToInt
import chylex.hee.system.math.over
import chylex.hee.system.math.toDegrees
import chylex.hee.system.migration.ActionResult.FAIL
import chylex.hee.system.migration.ActionResult.SUCCESS
import chylex.hee.system.migration.EntityLivingBase
import chylex.hee.system.migration.EntityPlayer
@@ -150,17 +149,13 @@ class ItemEnergyOracle(properties: Properties) : ItemAbstractEnergyUser(properti
return ItemAbstractInfusable.onCanApplyInfusion(this, infusion)
}
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType? {
if (player.isSneaking && pos.getTile<TileEntityEnergyCluster>(world) != null) {
if (world.isRemote) {
return SUCCESS
}
val heldItem = player.getHeldItem(context.hand)
val heldItem = player.getHeldItem(ctx.hand)
val entry = pos.toLong()
with(heldItem.heeTag) {
@@ -176,7 +171,7 @@ class ItemEnergyOracle(properties: Properties) : ItemAbstractEnergyUser(properti
return SUCCESS
}
return super.onItemUse(context)
return super.useOnBlock(world, pos, player, item, ctx)
}
override fun inventoryTick(stack: ItemStack, world: World, entity: Entity, itemSlot: Int, isSelected: Boolean) {

View File

@@ -4,6 +4,7 @@ import chylex.hee.client.color.NO_TINT
import chylex.hee.game.block.entity.TileEntityEnergyCluster
import chylex.hee.game.inventory.heeTag
import chylex.hee.game.inventory.heeTagOrNull
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.item.infusion.Infusion.SAFETY
import chylex.hee.game.item.infusion.Infusion.STABILITY
import chylex.hee.game.item.infusion.InfusionList
@@ -27,10 +28,10 @@ import chylex.hee.system.forge.Side
import chylex.hee.system.forge.Sided
import chylex.hee.system.migration.ActionResult.FAIL
import chylex.hee.system.migration.ActionResult.SUCCESS
import chylex.hee.system.migration.EntityPlayer
import chylex.hee.system.serialization.TagCompound
import chylex.hee.system.serialization.getIntegerOrNull
import chylex.hee.system.serialization.hasKey
import chylex.hee.system.serialization.use
import net.minecraft.client.renderer.color.IItemColor
import net.minecraft.client.util.ITooltipFlag
import net.minecraft.entity.Entity
@@ -43,7 +44,7 @@ import net.minecraft.util.text.TranslationTextComponent
import net.minecraft.world.World
import kotlin.math.pow
class ItemEnergyReceptacle(properties: Properties) : ItemAbstractInfusable(properties) {
class ItemEnergyReceptacle(properties: Properties) : ItemAbstractInfusable(properties), UseOnBlockComponent {
private companion object {
private const val CLUSTER_SNAPSHOT_TAG = "Cluster"
private const val UPDATE_TIME_TAG = "UpdateTime"
@@ -105,18 +106,14 @@ class ItemEnergyReceptacle(properties: Properties) : ItemAbstractInfusable(prope
}
// POLISH tweak animation
components.attach(this)
}
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
val stack = player.getHeldItem(context.hand)
stack.heeTag.use {
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType? {
with(item.heeTag) {
if (hasKey(CLUSTER_SNAPSHOT_TAG)) {
val finalPos = BlockEditor.place(ModBlocks.ENERGY_CLUSTER, player, stack, context)
val finalPos = BlockEditor.place(ModBlocks.ENERGY_CLUSTER, player, item, ctx)
if (world.isRemote) {
return SUCCESS
@@ -126,7 +123,7 @@ class ItemEnergyReceptacle(properties: Properties) : ItemAbstractInfusable(prope
finalPos.getTile<TileEntityEnergyCluster>(world)?.let {
it.loadClusterSnapshot(ClusterSnapshot(getCompound(CLUSTER_SNAPSHOT_TAG)), inactive = false)
if (shouldLoseHealth(it, this, InfusionTag.getList(stack))) {
if (shouldLoseHealth(it, this, InfusionTag.getList(item))) {
it.deteriorateHealth()
}
}
@@ -141,7 +138,7 @@ class ItemEnergyReceptacle(properties: Properties) : ItemAbstractInfusable(prope
return SUCCESS
}
}
else if (BlockEditor.canEdit(pos, player, stack)) {
else if (BlockEditor.canEdit(pos, player, item)) {
if (world.isRemote) {
return SUCCESS
}

View File

@@ -6,6 +6,7 @@ import chylex.hee.game.entity.heeTag
import chylex.hee.game.entity.heeTagOrNull
import chylex.hee.game.entity.posVec
import chylex.hee.game.inventory.doDamage
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.world.BlockEditor
import chylex.hee.game.world.FLAG_NONE
import chylex.hee.game.world.getBlock
@@ -14,7 +15,6 @@ import chylex.hee.game.world.playServer
import chylex.hee.game.world.removeBlock
import chylex.hee.game.world.setBlock
import chylex.hee.init.ModBlocks
import chylex.hee.system.compatibility.MinecraftForgeEventBus
import chylex.hee.system.forge.EventPriority
import chylex.hee.system.forge.SubscribeAllEvents
import chylex.hee.system.forge.SubscribeEvent
@@ -26,7 +26,6 @@ import chylex.hee.system.migration.EntityPlayer
import chylex.hee.system.migration.Sounds
import chylex.hee.system.random.nextFloat
import net.minecraft.block.Blocks
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.util.ActionResultType
@@ -36,7 +35,7 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import net.minecraftforge.event.world.ExplosionEvent
class ItemFlintAndInfernium(properties: Properties) : Item(properties) {
class ItemFlintAndInfernium(properties: Properties) : ItemWithComponents(properties), UseOnBlockComponent {
@SubscribeAllEvents(modid = HEE.ID)
companion object {
private const val CREEPER_INFERNIUM_TAG = "Infernium"
@@ -55,7 +54,7 @@ class ItemFlintAndInfernium(properties: Properties) : Item(properties) {
}
init {
MinecraftForgeEventBus.register(this)
components.attach(this)
}
fun igniteTNT(world: World, pos: BlockPos, player: EntityPlayer?, ignoreTrap: Boolean) {
@@ -67,14 +66,8 @@ class ItemFlintAndInfernium(properties: Properties) : Item(properties) {
pos.removeBlock(world)
}
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
val heldItem = player.getHeldItem(context.hand)
if (!BlockEditor.canEdit(pos, player, heldItem)) {
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType? {
if (!BlockEditor.canEdit(pos, player, item)) {
return FAIL
}
@@ -85,11 +78,11 @@ class ItemFlintAndInfernium(properties: Properties) : Item(properties) {
igniteTNT(world, pos, player, ignoreTrap = false)
}
else {
BlockEditor.place(ModBlocks.ETERNAL_FIRE, player, heldItem, context) ?: return FAIL
BlockEditor.place(ModBlocks.ETERNAL_FIRE, player, item, ctx) ?: return FAIL
}
Sounds.ITEM_FLINTANDSTEEL_USE.playServer(world, pos, SoundCategory.BLOCKS, volume = 1.1F, pitch = world.rand.nextFloat(0.4F, 0.5F))
heldItem.doDamage(1, player, context.hand)
item.doDamage(1, player, ctx.hand)
}
return SUCCESS

View File

@@ -9,6 +9,7 @@ import chylex.hee.game.entity.item.EntityTokenHolder
import chylex.hee.game.entity.selectExistingEntities
import chylex.hee.game.inventory.heeTag
import chylex.hee.game.inventory.heeTagOrNull
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.mechanics.portal.DimensionTeleporter
import chylex.hee.game.mechanics.portal.EntityPortalContact
import chylex.hee.game.world.BlockEditor
@@ -34,7 +35,6 @@ import chylex.hee.system.serialization.putEnum
import net.minecraft.client.renderer.color.IItemColor
import net.minecraft.client.util.ITooltipFlag
import net.minecraft.entity.Entity
import net.minecraft.item.Item
import net.minecraft.item.ItemGroup
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
@@ -43,13 +43,14 @@ import net.minecraft.util.ActionResultType
import net.minecraft.util.Hand
import net.minecraft.util.NonNullList
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.BlockPos
import net.minecraft.util.text.ITextComponent
import net.minecraft.util.text.StringTextComponent
import net.minecraft.util.text.TranslationTextComponent
import net.minecraft.world.World
import net.minecraft.world.dimension.DimensionType
class ItemPortalToken(properties: Properties) : Item(properties) {
class ItemPortalToken(properties: Properties) : ItemWithComponents(properties), UseOnBlockComponent {
companion object {
private const val TYPE_TAG = "Type"
private const val TERRITORY_TYPE_TAG = "Territory"
@@ -72,6 +73,8 @@ class ItemPortalToken(properties: Properties) : Item(properties) {
}
init {
components.attach(this)
addPropertyOverride(Resource.Custom("token_type")) { stack, _, _ ->
getTokenType(stack).propertyValue + (if (stack.heeTagOrNull.hasKey(IS_CORRUPTED_TAG)) 0.5F else 0F)
}
@@ -178,10 +181,7 @@ class ItemPortalToken(properties: Properties) : Item(properties) {
return ActionResult(SUCCESS, heldItem)
}
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType {
if (!player.isCreative || player.isSneaking) {
return PASS
}
@@ -190,16 +190,14 @@ class ItemPortalToken(properties: Properties) : Item(properties) {
return SUCCESS
}
val targetPos = context.pos.up()
val targetPos = pos.up()
val territoryType = getTerritoryType(item)
val heldItem = player.getHeldItem(context.hand)
val territoryType = getTerritoryType(heldItem)
if (territoryType == null || context.face != UP || !BlockEditor.canEdit(targetPos, player, heldItem) || world.selectExistingEntities.inBox<EntityTokenHolder>(AxisAlignedBB(targetPos)).any()) {
if (territoryType == null || ctx.face != UP || !BlockEditor.canEdit(targetPos, player, item) || world.selectExistingEntities.inBox<EntityTokenHolder>(AxisAlignedBB(targetPos)).any()) {
return FAIL
}
world.addEntity(EntityTokenHolder(world, targetPos, getTokenType(heldItem), territoryType))
world.addEntity(EntityTokenHolder(world, targetPos, getTokenType(item), territoryType))
return SUCCESS
}

View File

@@ -1,6 +1,7 @@
package chylex.hee.game.item
import chylex.hee.game.block.entity.TileEntityMinersBurialAltar
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.world.BlockEditor
import chylex.hee.game.world.getTile
import chylex.hee.game.world.playUniversal
@@ -8,33 +9,34 @@ import chylex.hee.init.ModSounds
import chylex.hee.system.migration.ActionResult.FAIL
import chylex.hee.system.migration.ActionResult.PASS
import chylex.hee.system.migration.ActionResult.SUCCESS
import net.minecraft.item.Item
import chylex.hee.system.migration.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.util.ActionResultType
import net.minecraft.util.SoundCategory
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
class ItemPuzzleMedallion(properties: Properties) : Item(properties) {
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
val heldItem = player.getHeldItem(context.hand)
if (!BlockEditor.canEdit(pos, player, heldItem)) {
return FAIL
}
val tile = pos.getTile<TileEntityMinersBurialAltar>(world)
if (tile != null && !tile.hasMedallion) {
tile.hasMedallion = true
heldItem.shrink(1)
ModSounds.ITEM_PUZZLE_MEDALLION_INSERT.playUniversal(player, pos, SoundCategory.BLOCKS, volume = 2F, pitch = 0.8F)
return SUCCESS
}
return PASS
class ItemPuzzleMedallion(properties: Properties) : ItemWithComponents(properties) {
init {
components.attach(object : UseOnBlockComponent {
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType {
if (!BlockEditor.canEdit(pos, player, item)) {
return FAIL
}
val tile = pos.getTile<TileEntityMinersBurialAltar>(world)
if (tile != null && !tile.hasMedallion) {
tile.hasMedallion = true
item.shrink(1)
ModSounds.ITEM_PUZZLE_MEDALLION_INSERT.playUniversal(player, pos, SoundCategory.BLOCKS, volume = 2F, pitch = 0.8F)
return SUCCESS
}
return PASS
}
})
}
}

View File

@@ -3,6 +3,7 @@ package chylex.hee.game.item
import chylex.hee.client.model.ModelHelper
import chylex.hee.game.block.entity.TileEntityEnergyCluster
import chylex.hee.game.entity.item.EntityItemRevitalizationSubstance
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.mechanics.energy.IClusterHealth.HealthOverride.REVITALIZING
import chylex.hee.game.particle.ParticleSmokeCustom
import chylex.hee.game.particle.spawner.ParticleSpawnerCustom
@@ -26,7 +27,6 @@ import chylex.hee.system.serialization.readPos
import chylex.hee.system.serialization.use
import chylex.hee.system.serialization.writePos
import net.minecraft.entity.Entity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.network.PacketBuffer
@@ -37,7 +37,7 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import java.util.Random
class ItemRevitalizationSubstance(properties: Properties) : Item(properties) {
class ItemRevitalizationSubstance(properties: Properties) : ItemWithComponents(properties), UseOnBlockComponent {
companion object {
private val PARTICLE_FAIL = ParticleSpawnerCustom(
type = ParticleSmokeCustom,
@@ -70,11 +70,11 @@ class ItemRevitalizationSubstance(properties: Properties) : Item(properties) {
}
}
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
init {
components.attach(this)
}
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType? {
if (world.isRemote) {
return FAIL // disable animation
}
@@ -83,11 +83,11 @@ class ItemRevitalizationSubstance(properties: Properties) : Item(properties) {
if (cluster.currentHealth != REVITALIZING) {
if (cluster.addRevitalizationSubstance()) {
player.getHeldItem(context.hand).shrink(1)
player.getHeldItem(ctx.hand).shrink(1)
ModSounds.ITEM_REVITALIZATION_SUBSTANCE_USE_SUCCESS.playServer(world, pos, SoundCategory.BLOCKS, volume = 0.5F)
}
else {
PacketClientFX(FX_FAIL, FxUseData(pos, player, context.hand)).sendToAllAround(player, 24.0)
PacketClientFX(FX_FAIL, FxUseData(pos, player, ctx.hand)).sendToAllAround(player, 24.0)
}
}

View File

@@ -2,33 +2,33 @@ package chylex.hee.game.item
import chylex.hee.game.block.BlockAbstractTableTile
import chylex.hee.game.block.BlockTableBase
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.world.BlockEditor
import chylex.hee.game.world.breakBlock
import chylex.hee.game.world.getBlock
import chylex.hee.game.world.setBlock
import chylex.hee.system.forge.Side
import chylex.hee.system.forge.Sided
import chylex.hee.system.migration.EntityPlayer
import net.minecraft.client.util.ITooltipFlag
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.util.ActionResultType
import net.minecraft.util.ActionResultType.FAIL
import net.minecraft.util.ActionResultType.PASS
import net.minecraft.util.ActionResultType.SUCCESS
import net.minecraft.util.math.BlockPos
import net.minecraft.util.text.ITextComponent
import net.minecraft.util.text.TranslationTextComponent
import net.minecraft.world.World
class ItemTableCore(private val tableBlocks: Array<BlockAbstractTableTile<*>>, properties: Properties) : Item(properties) {
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
val heldItem = player.getHeldItem(context.hand)
if (!BlockEditor.canEdit(pos, player, heldItem)) {
class ItemTableCore(private val tableBlocks: Array<BlockAbstractTableTile<*>>, properties: Properties) : ItemWithComponents(properties), UseOnBlockComponent {
init {
components.attach(this)
}
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType {
if (!BlockEditor.canEdit(pos, player, item)) {
return FAIL
}
@@ -42,7 +42,7 @@ class ItemTableCore(private val tableBlocks: Array<BlockAbstractTableTile<*>>, p
pos.setBlock(world, table)
}
heldItem.shrink(1)
item.shrink(1)
return SUCCESS
}

View File

@@ -12,6 +12,7 @@ import chylex.hee.game.item.ItemTableLink.Companion.SoundType.LINK_FAIL
import chylex.hee.game.item.ItemTableLink.Companion.SoundType.LINK_OUTPUT
import chylex.hee.game.item.ItemTableLink.Companion.SoundType.LINK_RESTART
import chylex.hee.game.item.ItemTableLink.Companion.SoundType.LINK_SUCCESS
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.game.world.getBlock
import chylex.hee.game.world.getTile
import chylex.hee.game.world.playClient
@@ -26,6 +27,7 @@ import chylex.hee.system.forge.Sided
import chylex.hee.system.migration.ActionResult.FAIL
import chylex.hee.system.migration.ActionResult.SUCCESS
import chylex.hee.system.migration.EntityItem
import chylex.hee.system.migration.EntityPlayer
import chylex.hee.system.random.nextFloat
import chylex.hee.system.serialization.getPos
import chylex.hee.system.serialization.hasKey
@@ -34,7 +36,6 @@ import chylex.hee.system.serialization.readPos
import chylex.hee.system.serialization.use
import chylex.hee.system.serialization.writePos
import net.minecraft.entity.Entity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.network.PacketBuffer
@@ -44,7 +45,7 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import java.util.Random
class ItemTableLink(properties: Properties) : Item(properties) {
class ItemTableLink(properties: Properties) : ItemWithComponents(properties), UseOnBlockComponent {
companion object {
private const val POS_TAG = "StoredPos"
private const val TIME_TAG = "StoredTime"
@@ -106,11 +107,11 @@ class ItemTableLink(properties: Properties) : Item(properties) {
}
}
override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
val world = context.world
val pos = context.pos
init {
components.attach(this)
}
override fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType? {
if (!player.isSneaking || !isValidTarget(world, pos)) {
val pedestal = pos.getTile<TileEntityTablePedestal>(world)
@@ -131,7 +132,7 @@ class ItemTableLink(properties: Properties) : Item(properties) {
return SUCCESS
}
val heldItem = player.getHeldItem(context.hand)
val heldItem = player.getHeldItem(ctx.hand)
var newStoredPos = pos
var soundType = LINK_RESTART

View File

@@ -21,6 +21,7 @@ import chylex.hee.game.world.structure.trigger.EntityStructureTrigger
import chylex.hee.game.world.territory.TerritoryType
import chylex.hee.init.ModBlocks
import chylex.hee.network.client.PacketClientFX
import chylex.hee.network.fx.FxVecData
import chylex.hee.system.facades.Facing4
import chylex.hee.system.math.addY
import chylex.hee.system.math.offsetTowards
@@ -145,9 +146,8 @@ abstract class ObsidianTowerLevel_Top(file: String) : ObsidianTowerLevel_General
val offsetProgress = chargeAnim.pow(0.8F).toDouble()
val particlePos = start.center.offsetTowards(tokenHolder.posVec.addY(tokenHolder.height * 0.5), offsetProgress).addY(progressCurvePoint * 6.0)
val particleData = EnderEyeSpawnerParticles.Companion.ParticleData(particlePos)
PacketClientFX(EnderEyeSpawnerParticles.FX_PARTICLE, particleData).sendToAllAround(entity.world, pos, 256.0)
PacketClientFX(EnderEyeSpawnerParticles.FX_PARTICLE, FxVecData(particlePos)).sendToAllAround(entity.world, pos, 256.0)
}
if (chargeAnim == 1F) {

View File

@@ -1,5 +1,7 @@
package chylex.hee.game.world.feature.tombdungeon
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount.HIGH
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount.MEDIUM
import chylex.hee.game.world.feature.tombdungeon.TombDungeonPieces.PIECE_TOMB_RANDOM_MASS_5X_BASIC
import chylex.hee.game.world.feature.tombdungeon.TombDungeonPieces.PIECE_TOMB_RANDOM_MASS_5X_BORDER
import chylex.hee.game.world.feature.tombdungeon.TombDungeonPieces.PIECE_TOMB_RANDOM_MASS_5X_SPLIT
@@ -19,6 +21,7 @@ import chylex.hee.game.world.feature.tombdungeon.piece.TombDungeonAbstractPiece
import chylex.hee.system.math.floorToInt
import chylex.hee.system.random.nextFloat
import chylex.hee.system.random.nextInt
import chylex.hee.system.random.nextRounded
import java.util.Random
import kotlin.math.max
import kotlin.math.min
@@ -101,4 +104,46 @@ enum class TombDungeonLevel(val isFancy: Boolean, private val corridorFactor: In
}
}
}
fun pickUndreadAndSpiderlingSpawns(rand: Random, amount: MobAmount): Pair<Int, Int> {
val m = if (amount >= MEDIUM) 1 else 0
val h = if (amount >= HIGH) 1 else 0
return when(this) {
FIRST -> when(rand.nextInt(0, 3)) {
0 -> MOBS(undreads = rand.nextRounded(0.2F), spiderlings = 1 + m + rand.nextInt(0, h))
1 -> MOBS(undreads = rand.nextRounded(0.3F * m), spiderlings = 1 + h + rand.nextInt(0, m))
2 -> MOBS(undreads = rand.nextRounded(0.3F * h), spiderlings = 2 + m)
else -> MOBS(undreads = 0, spiderlings = 1 + rand.nextInt(0, 1 + m))
}
SECOND -> when(rand.nextInt(0, 3)) {
0 -> MOBS(undreads = rand.nextRounded(0.3F), spiderlings = 1 + m + rand.nextInt(0, h))
1 -> MOBS(undreads = rand.nextRounded(0.4F * m), spiderlings = 1 + rand.nextInt(0, 2 * m))
else -> MOBS(undreads = 0, spiderlings = rand.nextRounded(1.8F + (m * 0.55F) + (h * 0.55F)))
}
THIRD -> when(rand.nextInt(0, 2)) {
0 -> MOBS(undreads = rand.nextInt(0, 1 + h), spiderlings = rand.nextRounded(1.4F + (m * 0.5F)) + rand.nextInt(0, m))
1 -> MOBS(undreads = rand.nextInt(0, 1 + m), spiderlings = rand.nextRounded(1.2F) + rand.nextInt(0, 2 * h))
else -> MOBS(undreads = rand.nextInt(0, 1 + m), spiderlings = 2 + rand.nextInt(0, m + h))
}
FOURTH -> when(rand.nextInt(0, 5)) {
in 0..1 -> MOBS(undreads = 1 + rand.nextInt(h, 3 + h), spiderlings = rand.nextRounded(0.4F * m) + rand.nextInt(0, m + h))
else -> MOBS(undreads = 2 + rand.nextInt(m, 2 * m), spiderlings = rand.nextRounded(0.3F + (m * 0.1F) + (h * 0.3F)))
}
else -> when(rand.nextInt(0, 2)) {
0 -> MOBS(undreads = 1 + h + rand.nextInt(m, 1 + (m * 2)) + rand.nextInt(h, 1 + h), spiderlings = rand.nextInt(0, m))
else -> MOBS(undreads = 2 + h + rand.nextInt(m, 2 * m), spiderlings = rand.nextRounded(0.2F + (m * 0.2F)))
}
}
}
private fun MOBS(undreads: Int, spiderlings: Int) = undreads to spiderlings
enum class MobAmount {
LOW, MEDIUM, HIGH
}
}

View File

@@ -1,10 +1,57 @@
package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.block.BlockGraveDirt
import chylex.hee.game.entity.living.EntityMobSpiderling
import chylex.hee.game.entity.living.EntityMobUndread
import chylex.hee.game.entity.posVec
import chylex.hee.game.entity.selectExistingEntities
import chylex.hee.game.entity.selectVulnerableEntities
import chylex.hee.game.entity.technical.EntityTechnicalTrigger
import chylex.hee.game.entity.technical.EntityTechnicalTrigger.ITriggerHandler
import chylex.hee.game.entity.technical.EntityTechnicalTrigger.Types.TOMB_DUNGEON_UNDREAD_SPAWNER
import chylex.hee.game.world.Pos
import chylex.hee.game.world.distanceSqTo
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
import chylex.hee.game.world.feature.tombdungeon.connection.TombDungeonConnection
import chylex.hee.game.world.feature.tombdungeon.connection.TombDungeonConnectionType.TOMB_ENTRANCE_INSIDE
import chylex.hee.game.world.getBlock
import chylex.hee.game.world.getState
import chylex.hee.game.world.isAir
import chylex.hee.game.world.offsetWhile
import chylex.hee.game.world.playClient
import chylex.hee.game.world.structure.IStructureWorld
import chylex.hee.game.world.structure.piece.IStructurePieceConnection
import chylex.hee.game.world.structure.trigger.EntityStructureTrigger
import chylex.hee.init.ModEntities
import chylex.hee.network.client.PacketClientFX
import chylex.hee.network.fx.FxVecData
import chylex.hee.network.fx.FxVecHandler
import chylex.hee.system.math.Vec
import chylex.hee.system.math.Vec3
import chylex.hee.system.math.addY
import chylex.hee.system.math.directionTowards
import chylex.hee.system.math.square
import chylex.hee.system.math.toYaw
import chylex.hee.system.migration.BlockWeb
import chylex.hee.system.migration.EntityLivingBase
import chylex.hee.system.migration.EntityPlayer
import chylex.hee.system.migration.Facing.DOWN
import chylex.hee.system.migration.Facing.NORTH
import chylex.hee.system.migration.Facing.SOUTH
import chylex.hee.system.random.nextFloat
import chylex.hee.system.random.nextInt
import chylex.hee.system.random.nextItem
import chylex.hee.system.serialization.TagCompound
import chylex.hee.system.serialization.use
import net.minecraft.entity.EntitySpawnPlacementRegistry
import net.minecraft.entity.SpawnReason.STRUCTURE
import net.minecraft.util.Direction.Axis
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import java.util.Random
abstract class TombDungeonRoom_Tomb(file: String, entranceY: Int, allowSecrets: Boolean, isFancy: Boolean) : TombDungeonRoom(file, isFancy) {
final override val secretAttachWeight = if (allowSecrets) 2 else 0
@@ -13,4 +60,176 @@ abstract class TombDungeonRoom_Tomb(file: String, entranceY: Int, allowSecrets:
final override val connections = arrayOf<IStructurePieceConnection>(
TombDungeonConnection(TOMB_ENTRANCE_INSIDE, Pos(centerX, entranceY, maxZ), SOUTH)
)
override fun generate(world: IStructureWorld, instance: Instance) {
super.generate(world, instance)
generateSpawnerTrigger(world, instance)
}
protected open fun generateSpawnerTrigger(world: IStructureWorld, instance: Instance) {
val rand = world.rand
val level = instance.context ?: return
val mobAmount = getSpawnerTriggerMobAmount(rand, level) ?: return
val (undreads, spiderlings) = level.pickUndreadAndSpiderlingSpawns(rand, mobAmount)
MobSpawnerTrigger.place(world, entrance = connections.first().offset, width = maxX, depth = maxZ, undreads, spiderlings)
}
protected abstract fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount?
class MobSpawnerTrigger() : ITriggerHandler {
companion object {
private const val WIDTH_TAG = "Width"
private const val DEPTH_TAG = "Depth"
private const val UNDREADS_TAG = "Undreads"
private const val SPIDERLINGS_TAG = "Spiderlings"
fun place(world: IStructureWorld, entrance: BlockPos, width: Int, depth: Int, undreads: Int, spiderlings: Int) {
val nbt = MobSpawnerTrigger(width - 1.0, depth - 1.0, undreads, spiderlings).serializeNBT()
world.addTrigger(entrance, EntityStructureTrigger({ wrld ->
EntityTechnicalTrigger(wrld, TOMB_DUNGEON_UNDREAD_SPAWNER, nbt).apply { rotationYaw = NORTH.horizontalAngle }
}, yOffset = 0.5))
}
val FX_SPAWN_UNDREAD = object : FxVecHandler() {
override fun handle(world: World, rand: Random, vec: Vec3d) {
EntityMobUndread(world).apply {
setLocationAndAngles(vec.x, vec.y, vec.z, 0F, 0F)
spawnExplosionParticle()
deathSound.playClient(vec, soundCategory, volume = 1.2F, pitch = soundPitch * 0.7F)
}
}
}
val FX_SPAWN_SPIDERLING = object : FxVecHandler() {
override fun handle(world: World, rand: Random, vec: Vec3d) {
EntityMobSpiderling(world).apply {
setLocationAndAngles(vec.x, vec.y, vec.z, 0F, 0F)
spawnExplosionParticle()
ambientSound.playClient(vec, soundCategory, volume = 1F, pitch = soundPitch)
}
}
}
}
private var width = 0.0
private var depth = 0.0
private var undreads = 0
private var spiderlings = 0
constructor(width: Double, depth: Double, undreads: Int, spiderlings: Int) : this() {
this.width = width
this.depth = depth
this.undreads = undreads
this.spiderlings = spiderlings
}
override fun check(world: World): Boolean {
return !world.isRemote
}
override fun update(entity: EntityTechnicalTrigger) {
val world = entity.world
val facing = entity.horizontalFacing
val vecF = Vec3.fromYaw(facing.horizontalAngle)
val vecL = Vec3.fromYaw(facing.rotateYCCW().horizontalAngle)
val vecR = Vec3.fromYaw(facing.rotateY().horizontalAngle)
val aabb = AxisAlignedBB(
vecL.scale(width * 0.5).add(vecF.scale(0.3)),
vecR.scale(width * 0.5).add(vecF.scale(depth)).addY(2.5)
).offset(entity.posVec)
val nearbyPlayers = world.selectVulnerableEntities.inBox<EntityPlayer>(aabb)
if (nearbyPlayers.isEmpty()) {
return
}
val rand = world.rand
val minPlayerDist = ((depth * 0.4) + (width * 0.16)).coerceIn(1.5, 3.5)
for((entityCount, entityType, spawnParticle) in listOf(
Triple(undreads, ModEntities.UNDREAD, FX_SPAWN_UNDREAD),
Triple(spiderlings, ModEntities.SPIDERLING, FX_SPAWN_SPIDERLING)
)) {
repeat(entityCount) {
var bestPos: BlockPos? = null
for(attempt in 1..75) {
val pos = Pos(
rand.nextFloat(aabb.minX, aabb.maxX),
rand.nextFloat(aabb.minY, aabb.maxY) + 0.5,
rand.nextFloat(aabb.minZ, aabb.maxZ),
).offsetWhile(DOWN, 1..3) {
it.isAir(world) || it.getBlock(world) is BlockWeb
}
val collisionCheckAABB = entityType.getBoundingBoxWithSizeApplied(pos.x + 0.5, pos.y.toDouble(), pos.z + 0.5).grow(0.2, 0.0, 0.2)
if (EntitySpawnPlacementRegistry.func_223515_a(entityType, world, STRUCTURE, pos, rand) &&
world.hasNoCollisions(collisionCheckAABB) &&
world.selectExistingEntities.inBox<EntityLivingBase>(collisionCheckAABB.grow(0.4, 0.0, 0.4)).isEmpty() &&
world.players.none { pos.distanceSqTo(it) < square(minPlayerDist) }
) {
bestPos = pos
if (entityType === ModEntities.UNDREAD) {
if (rand.nextInt(5) == 0 || pos.down().getBlock(world) is BlockGraveDirt) {
break
}
}
else {
break
}
}
}
if (bestPos != null) {
val x = bestPos.x + rand.nextFloat(0.35, 0.65)
val y = bestPos.y + bestPos.down().getState(world).getCollisionShape(world, bestPos).getEnd(Axis.Y) - 1.0
val z = bestPos.z + rand.nextFloat(0.35, 0.65)
val vec = Vec(x, y, z)
val target = rand.nextItem(nearbyPlayers)
entityType.create(world)?.apply {
setLocationAndAngles(x, y, z, vec.directionTowards(target.posVec).toYaw(), 0F)
rotationYawHead = rotationYaw
attackTarget = target
onGround = true // allow instant pathfinding in MeleeAttackGoal
if (this is EntityMobSpiderling) {
wakeUp(preventSleep = true, aiDelayTicks = if (rand.nextInt(10) == 0) rand.nextInt(4, 6) else rand.nextInt(11, 18))
}
onInitialSpawn(world, world.getDifficultyForLocation(bestPos), STRUCTURE, null, null)
world.addEntity(this)
PacketClientFX(spawnParticle, FxVecData(vec)).sendToAllAround(this, 24.0)
}
}
}
}
entity.remove()
}
override fun nextTimer(rand: Random): Int {
return 3
}
override fun serializeNBT() = TagCompound().apply {
putDouble(WIDTH_TAG, width)
putDouble(DEPTH_TAG, depth)
putInt(UNDREADS_TAG, undreads)
putInt(SPIDERLINGS_TAG, spiderlings)
}
override fun deserializeNBT(nbt: TagCompound) = nbt.use {
width = getDouble(WIDTH_TAG)
depth = getDouble(DEPTH_TAG)
undreads = getInt(UNDREADS_TAG)
spiderlings = getInt(SPIDERLINGS_TAG)
}
}
}

View File

@@ -2,9 +2,12 @@ package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.block.BlockGraveDirt
import chylex.hee.game.world.Pos
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
import chylex.hee.game.world.feature.tombdungeon.TombDungeonPieces
import chylex.hee.game.world.feature.tombdungeon.connection.TombDungeonConnection
import chylex.hee.game.world.feature.tombdungeon.connection.TombDungeonConnectionType.TOMB_ENTRANCE_INSIDE
import chylex.hee.game.world.feature.tombdungeon.piece.TombDungeonRoom_Tomb.MobSpawnerTrigger
import chylex.hee.game.world.generation.IBlockPicker.Single
import chylex.hee.game.world.generation.IBlockPicker.Single.Air
import chylex.hee.game.world.math.Size
@@ -32,6 +35,8 @@ class TombDungeonRoom_Tomb_Mass(width: Int, depth: Int, private val border: Bool
super.generate(world, instance)
val rand = world.rand
val level = instance.context
val centerX = size.centerX
val maxX = size.maxX
val maxY = size.maxY
@@ -56,8 +61,21 @@ class TombDungeonRoom_Tomb_Mass(width: Int, depth: Int, private val border: Bool
if (rand.nextInt(6) == 0 && (border || split)) {
placeJars(world, instance, listOf(Pos(centerX, 2, 1)))
if (level != null && rand.nextInt(9) != 0) {
placeSpawnerTrigger(world, level)
}
}
else if (level != null && rand.nextInt(3) != 0) {
placeSpawnerTrigger(world, level)
}
placeCobwebs(world, instance)
}
private fun placeSpawnerTrigger(world: IStructureWorld, level: TombDungeonLevel) {
val area = (size.x - 2) * (size.z - 2)
val (undreads, spiderlings) = level.pickUndreadAndSpiderlingSpawns(world.rand, if (area <= 30) MobAmount.LOW else MobAmount.MEDIUM)
MobSpawnerTrigger.place(world, entrance = connections.first().offset, width = size.maxX, depth = size.maxZ, undreads, spiderlings)
}
}

View File

@@ -2,9 +2,11 @@ package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.world.Pos
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
import chylex.hee.game.world.structure.IStructureWorld
import chylex.hee.system.migration.Facing.NORTH
import chylex.hee.system.random.nextInt
import java.util.Random
class TombDungeonRoom_Tomb_MassSpacious(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
override fun generate(world: IStructureWorld, instance: Instance) {
@@ -32,4 +34,8 @@ class TombDungeonRoom_Tomb_MassSpacious(file: String, entranceY: Int, isFancy: B
placeJars(world, instance, listOf(Pos(jarX, 3, maxZ - 2)))
}
}
override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
return MobAmount.HIGH.takeIf { rand.nextInt(11) < (if (level <= TombDungeonLevel.SECOND) 7 else 4) }
}
}

View File

@@ -2,11 +2,14 @@ package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.block.BlockGraveDirt
import chylex.hee.game.world.Pos
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
import chylex.hee.game.world.structure.IStructureWorld
import chylex.hee.system.migration.Facing.EAST
import chylex.hee.system.migration.Facing.WEST
import chylex.hee.system.random.nextInt
import chylex.hee.system.random.nextRounded
import java.util.Random
class TombDungeonRoom_Tomb_MultiDeep(file: String, private val tombsPerColumn: Int, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
override fun generate(world: IStructureWorld, instance: Instance) {
@@ -37,4 +40,15 @@ class TombDungeonRoom_Tomb_MultiDeep(file: String, private val tombsPerColumn: I
})
}
}
override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
if (rand.nextBoolean()) {
return null
}
return when {
tombsPerColumn <= 6 -> MobAmount.MEDIUM
else -> MobAmount.HIGH
}
}
}

View File

@@ -1,7 +1,10 @@
package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.world.Pos
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
import chylex.hee.game.world.structure.IStructureWorld
import java.util.Random
class TombDungeonRoom_Tomb_MultiNarrow(file: String, private val tombsPerColumn: Int, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
override fun generate(world: IStructureWorld, instance: Instance) {
@@ -18,4 +21,16 @@ class TombDungeonRoom_Tomb_MultiNarrow(file: String, private val tombsPerColumn:
})
}
}
override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
if (rand.nextInt(10) >= if (level.isFancy) 3 else 5) {
return null
}
return when {
tombsPerColumn <= 4 -> MobAmount.LOW
tombsPerColumn <= 6 -> MobAmount.MEDIUM
else -> MobAmount.HIGH
}
}
}

View File

@@ -2,11 +2,14 @@ package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.block.BlockGraveDirt
import chylex.hee.game.world.Pos
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
import chylex.hee.game.world.structure.IStructureWorld
import chylex.hee.system.migration.Facing.EAST
import chylex.hee.system.migration.Facing.WEST
import chylex.hee.system.random.nextInt
import chylex.hee.system.random.nextRounded
import java.util.Random
class TombDungeonRoom_Tomb_MultiSpacious(file: String, private val tombsPerColumn: Int, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
override fun generate(world: IStructureWorld, instance: Instance) {
@@ -37,4 +40,15 @@ class TombDungeonRoom_Tomb_MultiSpacious(file: String, private val tombsPerColum
})
}
}
override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
if (rand.nextInt(10) >= 6) {
return null
}
return when {
tombsPerColumn <= 6 -> MobAmount.MEDIUM
else -> MobAmount.HIGH
}
}
}

View File

@@ -0,0 +1,26 @@
package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.world.Pos
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
import chylex.hee.game.world.structure.IStructureWorld
import chylex.hee.system.migration.Facing.SOUTH
import java.util.Random
abstract class TombDungeonRoom_Tomb_Single(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
override fun generate(world: IStructureWorld, instance: Instance) {
super.generate(world, instance)
if (world.rand.nextInt(10) < 3) {
placeChest(world, instance, Pos(centerX, 1, maxZ - 4), SOUTH)
}
}
final override fun getSpawnerTriggerMobAmount(rand: Random, level: TombDungeonLevel): MobAmount? {
return null
}
protected fun placeSingleTombUndreadSpawner(world: IStructureWorld) {
MobSpawnerTrigger.place(world, entrance = connections.first().offset, width = maxX, depth = maxZ, undreads = 1, spiderlings = 0)
}
}

View File

@@ -1,15 +1,13 @@
package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.world.Pos
import chylex.hee.game.world.structure.IStructureWorld
import chylex.hee.system.migration.Facing.SOUTH
open class TombDungeonRoom_Tomb_SingleNarrow(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb(file, entranceY, allowSecrets = false, isFancy) {
class TombDungeonRoom_Tomb_SingleNarrow(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb_Single(file, entranceY, isFancy) {
override fun generate(world: IStructureWorld, instance: Instance) {
super.generate(world, instance)
if (world.rand.nextInt(10) < 3) {
placeChest(world, instance, Pos(centerX, 1, maxZ - 4), SOUTH)
if (world.rand.nextInt(5) == 0) {
placeSingleTombUndreadSpawner(world)
}
}
}

View File

@@ -3,7 +3,7 @@ package chylex.hee.game.world.feature.tombdungeon.piece
import chylex.hee.game.world.Pos
import chylex.hee.game.world.structure.IStructureWorld
class TombDungeonRoom_Tomb_SingleSpacious(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb_SingleNarrow(file, entranceY, isFancy) {
class TombDungeonRoom_Tomb_SingleSpacious(file: String, entranceY: Int, isFancy: Boolean) : TombDungeonRoom_Tomb_Single(file, entranceY, isFancy) {
override fun generate(world: IStructureWorld, instance: Instance) {
super.generate(world, instance)
@@ -14,6 +14,11 @@ class TombDungeonRoom_Tomb_SingleSpacious(file: String, entranceY: Int, isFancy:
Pos(centerX - 2, 4, if (rand.nextBoolean()) maxZ - 3 else maxZ - 4),
Pos(centerX + 2, 4, if (rand.nextBoolean()) maxZ - 3 else maxZ - 4),
))
placeSingleTombUndreadSpawner(world)
}
else if (rand.nextInt(10) < 4) {
placeSingleTombUndreadSpawner(world)
}
}
}

View File

@@ -28,6 +28,7 @@ import chylex.hee.game.item.ItemTableLink
import chylex.hee.game.mechanics.scorching.ScorchingHelper
import chylex.hee.game.mechanics.table.TableParticleHandler
import chylex.hee.game.potion.PotionBanishment
import chylex.hee.game.world.feature.tombdungeon.piece.TombDungeonRoom_Tomb
import chylex.hee.network.BaseClientPacket
import chylex.hee.network.fx.IFxData
import chylex.hee.network.fx.IFxHandler
@@ -79,7 +80,9 @@ class PacketClientFX<T : IFxData>() : BaseClientPacket() {
EnderEyeSpawnerParticles.FX_PARTICLE,
EndermanTeleportHandler.FX_TELEPORT_FAIL,
EndermanTeleportHandler.FX_TELEPORT_OUT_OF_WORLD,
PotionBanishment.FX_BANISH
PotionBanishment.FX_BANISH,
TombDungeonRoom_Tomb.MobSpawnerTrigger.FX_SPAWN_UNDREAD,
TombDungeonRoom_Tomb.MobSpawnerTrigger.FX_SPAWN_SPIDERLING,
)
}

View File

@@ -0,0 +1,18 @@
package chylex.hee.game.item
import chylex.hee.game.item.components.UseOnBlockComponent
import chylex.hee.system.component.EntityComponents
import net.minecraft.item.Item
import net.minecraft.item.ItemUseContext
import net.minecraft.util.ActionResultType
import net.minecraft.util.ActionResultType.FAIL
import net.minecraft.util.ActionResultType.PASS
open class ItemWithComponents(properties: Properties) : Item(properties) {
val components = EntityComponents()
final override fun onItemUse(context: ItemUseContext): ActionResultType {
val player = context.player ?: return FAIL
return components.handle<UseOnBlockComponent, ActionResultType> { useOnBlock(context.world, context.pos, player, context.item, context) } ?: PASS
}
}

View File

@@ -0,0 +1,12 @@
package chylex.hee.game.item.components
import chylex.hee.system.migration.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUseContext
import net.minecraft.util.ActionResultType
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
interface UseOnBlockComponent { // TODO use as objects instead of inheriting the interface on Item
fun useOnBlock(world: World, pos: BlockPos, player: EntityPlayer, item: ItemStack, ctx: ItemUseContext): ActionResultType?
}

View File

@@ -10,24 +10,6 @@ class RandomBiasedValueRange(range: ClosedFloatingPointRange<Float>, private val
require(highestChanceValue in min..max) { "highestChanceValue must be between min and max" }
}
/*
import java.util.Random
import chylex.hee.system.random.nextBiasedFloat
import kotlin.math.roundToInt
val rand = Random()
(1..100000).map {
val min = 3F
val max = 10F
val highestChanceValue = 6.5F
val biasSoftener = 3F
(highestChanceValue + (rand.nextBiasedFloat(biasSoftener) * (0.5F + max - highestChanceValue)) - (rand.nextBiasedFloat(biasSoftener) * (0.5F + highestChanceValue - min))).roundToInt()
}.groupBy { it }.mapValues { it.value.size }.toList().sortedBy { it.first }
*/
override fun generateInt(rand: Random): Int {
return (highestChanceValue + (rand.nextBiasedFloat(biasSoftener) * (0.5F + max - highestChanceValue)) - (rand.nextBiasedFloat(biasSoftener) * (0.5F + highestChanceValue - min))).roundToInt()
}

View File

@@ -0,0 +1,12 @@
package chylex.hee.network.fx
import chylex.hee.system.serialization.use
import chylex.hee.system.serialization.writeVec
import net.minecraft.network.PacketBuffer
import net.minecraft.util.math.Vec3d
class FxVecData(private val vec: Vec3d) : IFxData {
override fun write(buffer: PacketBuffer) = buffer.use {
writeVec(vec)
}
}

View File

@@ -0,0 +1,16 @@
package chylex.hee.network.fx
import chylex.hee.system.serialization.readVec
import chylex.hee.system.serialization.use
import net.minecraft.network.PacketBuffer
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import java.util.Random
abstract class FxVecHandler : IFxHandler<FxVecData> {
override fun handle(buffer: PacketBuffer, world: World, rand: Random) = buffer.use {
handle(world, rand, readVec())
}
abstract fun handle(world: World, rand: Random, vec: Vec3d)
}

View File

@@ -0,0 +1,8 @@
package chylex.hee.system.component
abstract class AbstractAwareComponent {
var entityComponents: EntityComponents? = null
open fun onComponentAttached() {}
open fun onComponentDetached() {}
}

View File

@@ -0,0 +1,56 @@
package chylex.hee.system.component
class EntityComponents {
private val mutableComponents = mutableListOf<Any>()
val components: List<Any>
get() = mutableComponents
fun attach(component: Any) {
require(!mutableComponents.contains(component)) { "[EntityComponents] component must not be registered twice" }
mutableComponents.add(component)
if (component is AbstractAwareComponent) {
require(component.entityComponents === null) { "[EntityComponents] component must not be registered in two entities" }
component.entityComponents = this
component.onComponentAttached()
}
}
fun detach(component: Any) {
if (!mutableComponents.remove(component)) {
return
}
if (component is AbstractAwareComponent) {
require(component.entityComponents === this) { "[EntityComponents] component was not registered correctly" }
component.onComponentDetached()
component.entityComponents = null
}
}
inline fun <reified T> on(f: T.() -> Unit) {
for(component in components) {
if (component is T) {
f(component)
}
}
}
inline fun <reified T, U> handle(f: T.() -> U?): U? {
for(component in components) {
if (component is T) {
val result = f(component)
if (result != null) {
return result
}
}
}
return null
}
inline fun <reified T> list(): List<T> {
return components.filterIsInstance<T>()
}
}

View File

@@ -0,0 +1,20 @@
package chylex.hee.system.component.general
import chylex.hee.system.component.EntityComponents
import chylex.hee.system.serialization.TagCompound
import net.minecraftforge.common.util.INBTSerializable
interface SerializableComponent : INBTSerializable<TagCompound> {
val serializationKey: String
}
fun EntityComponents.serializeTo(tag: TagCompound) {
this.on<SerializableComponent> {
require(!tag.contains(serializationKey)) { "[SerializableComponent] cannot serialize duplicate key: $serializationKey" }
tag.put(serializationKey, serializeNBT())
}
}
fun EntityComponents.deserializeFrom(tag: TagCompound) {
this.on<SerializableComponent> { deserializeNBT(tag.getCompound(serializationKey)) }
}

View File

@@ -0,0 +1,19 @@
package chylex.hee.system.component.general
import chylex.hee.system.component.EntityComponents
import net.minecraft.entity.Entity
interface TickableComponent {
@JvmDefault fun tickClient() {}
@JvmDefault fun tickServer() {}
}
@JvmName("tickEntity")
fun <T : Entity> EntityComponents.tick(entity: T) {
if (entity.world.isRemote) {
this.on(TickableComponent::tickClient)
}
else {
this.on(TickableComponent::tickServer)
}
}

View File

@@ -0,0 +1,28 @@
package chylex.hee.test.main
import chylex.hee.game.loot.rng.RandomBiasedValueRange
import java.util.Random
import kotlin.math.roundToInt
fun main() {
val min = 3F
val max = 10F
val highestChanceValue = 6.5F
val biasSoftener = 3F
val rand = Random()
val generator = RandomBiasedValueRange(min..max, highestChanceValue, biasSoftener)
val reps = 100000
val results = (1..reps).map { generator.generateInt(rand) }
.groupBy { it }
.mapValues { it.value.size }
.toList()
.sortedBy { it.first }
val pad1 = results.maxOf { it.first.toString().length }
val pad2 = results.maxOf { it.second.toString().length }
println(results.joinToString("\n") {
"${it.first.toString().padStart(pad1)} | ${it.second.toString().padStart(pad2)} | ${((it.second * 100.0) / reps).roundToInt().toString().padStart(2)} %"
})
}

View File

@@ -0,0 +1,35 @@
package chylex.hee.test.main
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel
import chylex.hee.game.world.feature.tombdungeon.TombDungeonLevel.MobAmount
import java.util.Locale.ROOT
import java.util.Random
import kotlin.math.roundToInt
fun main() {
val rand = Random()
for(level in TombDungeonLevel.values()) {
val reps = 100000
println()
println("== ${level.name} ${"=".repeat(24 - level.name.length)}")
for(amount in MobAmount.values()) {
println()
println(" ${amount.name}")
println()
val results = (1..reps)
.map { level.pickUndreadAndSpiderlingSpawns(rand, amount) }
.groupingBy { it }
.eachCount()
.entries
.map { ((it.value * 100.0) / reps).roundToInt() to it.key }
.sortedWith(compareBy({ -it.first }, { -it.second.first }, { -it.second.second }))
println(results.joinToString("\n") { " U = ${it.second.first} S = ${it.second.second} ${it.first.toString().padStart(2)} %" })
println(" U ~ ${"%.1f".format(ROOT, results.sumBy { it.second.first * it.first } * 0.01)} S ~ ${"%.1f".format(ROOT, results.sumBy { it.second.second * it.first } * 0.01)}")
}
}
}

View File

@@ -1,4 +1,4 @@
package chylex.hee.test.mechanics.damage
package chylex.hee.test.unit.mechanics.damage
import chylex.hee.game.mechanics.damage.DamageProperties
import chylex.hee.game.mechanics.damage.DamageType

View File

@@ -1,4 +1,4 @@
package chylex.hee.test.mechanics.energy
package chylex.hee.test.unit.mechanics.energy
import chylex.hee.game.mechanics.energy.IEnergyQuantity.Floating
import chylex.hee.game.mechanics.energy.IEnergyQuantity.Internal

View File

@@ -1,4 +1,4 @@
package chylex.hee.test.system.util
package chylex.hee.test.unit.system
import chylex.hee.game.world.Pos
import chylex.hee.system.math.Vec

View File

@@ -1,4 +1,4 @@
package chylex.hee.test.system.util
package chylex.hee.test.unit.system
import chylex.hee.game.inventory.cleanupNBT
import chylex.hee.game.inventory.heeTag