2018-11-13 11:39:28 +01:00
package de.ellpeck.naturesaura.chunk ;
2018-10-21 12:51:13 +02:00
2022-05-20 19:03:44 +02:00
import com.google.common.collect.HashBasedTable ;
import com.google.common.collect.Table ;
2018-11-12 22:04:40 +01:00
import de.ellpeck.naturesaura.api.NaturesAuraAPI ;
2018-11-11 13:26:19 +01:00
import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk ;
import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect ;
2020-01-23 16:05:52 +01:00
import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect.ActiveType ;
2018-11-12 01:29:33 +01:00
import de.ellpeck.naturesaura.api.aura.type.IAuraType ;
2021-12-04 15:40:09 +01:00
import de.ellpeck.naturesaura.api.misc.ILevelData ;
import de.ellpeck.naturesaura.misc.LevelData ;
2020-01-22 23:21:52 +01:00
import de.ellpeck.naturesaura.packet.PacketAuraChunk ;
import de.ellpeck.naturesaura.packet.PacketHandler ;
2021-12-04 15:40:09 +01:00
import net.minecraft.core.BlockPos ;
import net.minecraft.nbt.CompoundTag ;
import net.minecraft.nbt.ListTag ;
import net.minecraft.resources.ResourceLocation ;
2019-02-09 21:55:40 +01:00
import net.minecraft.util.Tuple ;
2021-12-04 15:40:09 +01:00
import net.minecraft.world.entity.player.Player ;
import net.minecraft.world.item.ItemStack ;
import net.minecraft.world.level.chunk.LevelChunk ;
2018-10-21 12:51:13 +02:00
import org.apache.commons.lang3.mutable.MutableInt ;
2022-05-20 20:29:40 +02:00
import org.apache.commons.lang3.mutable.MutableObject ;
2022-05-20 19:03:44 +02:00
import org.apache.commons.lang3.tuple.Pair ;
2018-10-21 12:51:13 +02:00
2018-12-03 00:45:34 +01:00
import java.util.ArrayList ;
import java.util.List ;
import java.util.Map ;
import java.util.concurrent.ConcurrentHashMap ;
2018-10-21 12:51:13 +02:00
import java.util.function.BiConsumer ;
2018-11-11 13:26:19 +01:00
public class AuraChunk implements IAuraChunk {
2018-10-21 12:51:13 +02:00
2021-12-04 15:40:09 +01:00
private final LevelChunk chunk ;
2018-11-12 01:29:33 +01:00
private final IAuraType type ;
2018-12-03 00:45:34 +01:00
private final Map < BlockPos , MutableInt > drainSpots = new ConcurrentHashMap < > ( ) ;
2022-05-20 19:03:44 +02:00
private final Table < BlockPos , Integer , Pair < Integer , Integer > > auraAndSpotAmountCache = HashBasedTable . create ( ) ;
2022-05-20 20:29:40 +02:00
private final Table < BlockPos , Integer , Pair < BlockPos , Integer > [ ] > limitSpotCache = HashBasedTable . create ( ) ;
2018-10-25 18:49:42 +02:00
private final List < IDrainSpotEffect > effects = new ArrayList < > ( ) ;
2018-10-21 12:51:13 +02:00
private boolean needsSync ;
2021-12-04 15:40:09 +01:00
public AuraChunk ( LevelChunk chunk , IAuraType type ) {
2018-10-21 12:51:13 +02:00
this . chunk = chunk ;
2018-11-07 23:42:13 +01:00
this . type = type ;
2021-12-04 19:17:21 +01:00
for ( var supplier : NaturesAuraAPI . DRAIN_SPOT_EFFECTS . values ( ) ) {
var effect = supplier . get ( ) ;
2018-11-13 18:21:41 +01:00
if ( effect . appliesHere ( this . chunk , this , this . type ) )
2018-11-13 11:39:28 +01:00
this . effects . add ( effect ) ;
}
2018-10-21 12:51:13 +02:00
}
2018-11-11 13:26:19 +01:00
@Override
2018-11-24 17:32:39 +01:00
public int drainAura ( BlockPos pos , int amount , boolean aimForZero , boolean simulate ) {
2018-11-18 19:49:30 +01:00
if ( amount < = 0 )
2018-11-18 20:34:57 +01:00
return 0 ;
2021-12-04 19:17:21 +01:00
var spot = this . getActualDrainSpot ( pos , ! simulate ) ;
var curr = spot ! = null ? spot . intValue ( ) : 0 ;
2019-01-27 19:12:48 +01:00
if ( curr < 0 & & curr - amount > 0 ) // Underflow protection
2021-12-04 15:40:09 +01:00
return this . drainAura ( pos . above ( ) , amount , aimForZero , simulate ) ;
2018-11-18 20:34:57 +01:00
if ( aimForZero ) {
if ( curr > 0 & & curr - amount < 0 )
amount = curr ;
}
2018-11-24 17:32:39 +01:00
if ( ! simulate ) {
spot . subtract ( amount ) ;
2018-12-03 00:45:34 +01:00
if ( spot . intValue ( ) = = 0 )
this . drainSpots . remove ( pos ) ;
2018-11-24 17:32:39 +01:00
this . markDirty ( ) ;
}
2018-11-18 20:34:57 +01:00
return amount ;
2018-10-21 12:51:13 +02:00
}
2018-11-11 13:26:19 +01:00
@Override
2018-11-18 20:34:57 +01:00
public int drainAura ( BlockPos pos , int amount ) {
2018-11-24 17:32:39 +01:00
return this . drainAura ( pos , amount , false , false ) ;
2018-11-18 20:34:57 +01:00
}
@Override
2018-11-24 17:32:39 +01:00
public int storeAura ( BlockPos pos , int amount , boolean aimForZero , boolean simulate ) {
2018-11-18 19:49:30 +01:00
if ( amount < = 0 )
2018-11-18 20:34:57 +01:00
return 0 ;
2021-12-04 19:17:21 +01:00
var spot = this . getActualDrainSpot ( pos , ! simulate ) ;
var curr = spot ! = null ? spot . intValue ( ) : 0 ;
2018-12-02 01:36:41 +01:00
if ( curr > 0 & & curr + amount < 0 ) // Overflow protection
2021-12-04 15:40:09 +01:00
return this . storeAura ( pos . above ( ) , amount , aimForZero , simulate ) ;
2018-11-18 20:34:57 +01:00
if ( aimForZero ) {
if ( curr < 0 & & curr + amount > 0 ) {
amount = - curr ;
}
}
2018-11-24 17:32:39 +01:00
if ( ! simulate ) {
spot . add ( amount ) ;
2018-12-03 00:45:34 +01:00
if ( spot . intValue ( ) = = 0 )
this . drainSpots . remove ( pos ) ;
2018-11-24 17:32:39 +01:00
this . markDirty ( ) ;
}
2018-11-18 20:34:57 +01:00
return amount ;
}
@Override
public int storeAura ( BlockPos pos , int amount ) {
2018-11-24 17:32:39 +01:00
return this . storeAura ( pos , amount , true , false ) ;
2018-10-21 12:51:13 +02:00
}
2018-12-02 01:36:41 +01:00
private MutableInt getActualDrainSpot ( BlockPos pos , boolean make ) {
2021-12-04 19:17:21 +01:00
var spot = this . drainSpots . get ( pos ) ;
2018-12-02 01:36:41 +01:00
if ( spot = = null & & make ) {
2018-10-24 13:06:24 +02:00
spot = new MutableInt ( ) ;
2018-11-23 20:36:31 +01:00
this . addDrainSpot ( pos , spot ) ;
2018-10-21 12:51:13 +02:00
}
return spot ;
}
2018-12-02 01:36:41 +01:00
@Override
public int getDrainSpot ( BlockPos pos ) {
2021-12-04 19:17:21 +01:00
var spot = this . getActualDrainSpot ( pos , false ) ;
2018-12-02 01:36:41 +01:00
return spot = = null ? 0 : spot . intValue ( ) ;
}
2018-11-23 20:36:31 +01:00
private void addDrainSpot ( BlockPos pos , MutableInt spot ) {
2021-12-04 19:17:21 +01:00
var expX = pos . getX ( ) > > 4 ;
var expZ = pos . getZ ( ) > > 4 ;
var myPos = this . chunk . getPos ( ) ;
2020-01-22 23:21:52 +01:00
if ( expX ! = myPos . x | | expZ ! = myPos . z )
throw new IllegalArgumentException ( " Tried to add drain spot " + pos + " to chunk at " + myPos . x + " , " + myPos . z + " when it should've been added to chunk at " + expX + " , " + expZ ) ;
2018-11-23 20:36:31 +01:00
this . drainSpots . put ( pos , spot ) ;
}
2018-10-24 13:06:24 +02:00
public void setSpots ( Map < BlockPos , MutableInt > spots ) {
2018-10-21 12:51:13 +02:00
this . drainSpots . clear ( ) ;
2021-12-04 19:17:21 +01:00
for ( var entry : spots . entrySet ( ) )
2018-11-23 20:36:31 +01:00
this . addDrainSpot ( entry . getKey ( ) , entry . getValue ( ) ) ;
2021-01-30 16:43:46 +01:00
this . addOrRemoveAsActive ( ) ;
2018-10-21 12:51:13 +02:00
}
2018-11-11 13:26:19 +01:00
@Override
2018-11-12 01:29:33 +01:00
public IAuraType getType ( ) {
2018-11-07 23:42:13 +01:00
return this . type ;
}
2018-11-11 13:26:19 +01:00
@Override
2018-10-21 12:51:13 +02:00
public void markDirty ( ) {
2021-12-04 19:17:21 +01:00
this . chunk . setUnsaved ( true ) ;
2018-10-21 12:51:13 +02:00
this . needsSync = true ;
2022-05-20 19:03:44 +02:00
this . auraAndSpotAmountCache . clear ( ) ;
2022-05-20 20:29:40 +02:00
this . limitSpotCache . clear ( ) ;
2021-01-30 16:43:46 +01:00
this . addOrRemoveAsActive ( ) ;
2018-10-21 12:51:13 +02:00
}
public void update ( ) {
2021-12-04 19:17:21 +01:00
var level = this . chunk . getLevel ( ) ;
2018-10-21 12:51:13 +02:00
2021-12-04 19:17:21 +01:00
for ( var entry : this . drainSpots . entrySet ( ) ) {
var pos = entry . getKey ( ) ;
var amount = entry . getValue ( ) ;
for ( var effect : this . effects )
2021-12-04 15:40:09 +01:00
effect . update ( level , this . chunk , this , pos , amount . intValue ( ) ) ;
2018-11-18 19:49:30 +01:00
}
if ( this . needsSync ) {
2021-12-04 19:17:21 +01:00
var pos = this . chunk . getPos ( ) ;
2021-12-04 15:40:09 +01:00
PacketHandler . sendToAllLoaded ( level ,
2020-01-22 23:21:52 +01:00
new BlockPos ( pos . x * 16 , 0 , pos . z * 16 ) ,
this . makePacket ( ) ) ;
2018-11-18 19:49:30 +01:00
this . needsSync = false ;
2018-10-21 12:51:13 +02:00
}
}
2020-01-22 23:21:52 +01:00
public PacketAuraChunk makePacket ( ) {
2021-12-04 19:17:21 +01:00
var pos = this . chunk . getPos ( ) ;
2020-01-22 23:21:52 +01:00
return new PacketAuraChunk ( pos . x , pos . z , this . drainSpots ) ;
2018-10-21 13:12:03 +02:00
}
2022-05-20 19:03:44 +02:00
public void getSpots ( BlockPos pos , int radius , BiConsumer < BlockPos , Integer > consumer ) {
2021-12-04 19:17:21 +01:00
for ( var entry : this . drainSpots . entrySet ( ) ) {
var drainPos = entry . getKey ( ) ;
2021-12-04 15:40:09 +01:00
if ( drainPos . distSqr ( pos ) < = radius * radius ) {
2019-02-09 21:55:40 +01:00
consumer . accept ( drainPos , entry . getValue ( ) . intValue ( ) ) ;
}
}
}
2022-05-20 19:03:44 +02:00
public Pair < Integer , Integer > getAuraAndSpotAmount ( BlockPos pos , int radius ) {
var ret = this . auraAndSpotAmountCache . get ( pos , radius ) ;
if ( ret = = null ) {
var aura = new MutableInt ( ) ;
var spots = new MutableInt ( ) ;
this . getSpots ( pos , radius , ( p , i ) - > {
aura . add ( i ) ;
spots . increment ( ) ;
} ) ;
ret = Pair . of ( aura . intValue ( ) , spots . intValue ( ) ) ;
this . auraAndSpotAmountCache . put ( pos , radius , ret ) ;
}
return ret ;
}
2022-05-20 20:29:40 +02:00
public Pair < BlockPos , Integer > [ ] getLowestAndHighestSpots ( BlockPos pos , int radius ) {
var ret = this . limitSpotCache . get ( pos , radius ) ;
if ( ret = = null ) {
var lowestSpot = new MutableObject < BlockPos > ( ) ;
var highestSpot = new MutableObject < BlockPos > ( ) ;
var lowestAmount = new MutableInt ( Integer . MAX_VALUE ) ;
var highestAmount = new MutableInt ( Integer . MIN_VALUE ) ;
this . getSpots ( pos , radius , ( p , i ) - > {
if ( i > highestAmount . intValue ( ) ) {
highestAmount . setValue ( i ) ;
highestSpot . setValue ( p ) ;
}
if ( i < lowestAmount . intValue ( ) ) {
lowestAmount . setValue ( i ) ;
lowestSpot . setValue ( p ) ;
}
} ) ;
ret = new Pair [ ] {
Pair . of ( lowestSpot . getValue ( ) , lowestAmount . intValue ( ) ) ,
Pair . of ( highestSpot . getValue ( ) , highestAmount . intValue ( ) ) } ;
this . limitSpotCache . put ( pos , radius , ret ) ;
}
return ret ;
}
2021-12-04 15:40:09 +01:00
public void getActiveEffectIcons ( Player player , Map < ResourceLocation , Tuple < ItemStack , Boolean > > icons ) {
2021-12-04 19:17:21 +01:00
for ( var effect : this . effects ) {
var alreadyThere = icons . get ( effect . getName ( ) ) ;
2020-01-21 21:04:44 +01:00
if ( alreadyThere ! = null & & alreadyThere . getB ( ) )
2019-02-09 21:55:40 +01:00
continue ;
2021-12-04 19:17:21 +01:00
for ( var entry : this . drainSpots . entrySet ( ) ) {
var pos = entry . getKey ( ) ;
var amount = entry . getValue ( ) ;
var state = effect . isActiveHere ( player , this . chunk , this , pos , amount . intValue ( ) ) ;
2020-01-23 16:05:52 +01:00
if ( state = = ActiveType . INACTIVE )
2019-02-09 21:55:40 +01:00
continue ;
2021-12-04 19:17:21 +01:00
var stack = effect . getDisplayIcon ( ) ;
2019-02-09 21:55:40 +01:00
if ( stack . isEmpty ( ) )
continue ;
2020-01-23 16:05:52 +01:00
icons . put ( effect . getName ( ) , new Tuple < > ( stack , state = = ActiveType . INHIBITED ) ) ;
2019-02-09 21:55:40 +01:00
}
}
}
2018-10-21 12:51:13 +02:00
@Override
2021-12-04 15:40:09 +01:00
public CompoundTag serializeNBT ( ) {
2021-12-04 19:17:21 +01:00
var list = new ListTag ( ) ;
for ( var entry : this . drainSpots . entrySet ( ) ) {
var tag = new CompoundTag ( ) ;
2021-12-04 15:40:09 +01:00
tag . putLong ( " pos " , entry . getKey ( ) . asLong ( ) ) ;
2020-01-21 21:04:44 +01:00
tag . putInt ( " amount " , entry . getValue ( ) . intValue ( ) ) ;
list . add ( tag ) ;
2018-10-21 12:51:13 +02:00
}
2021-12-04 19:17:21 +01:00
var compound = new CompoundTag ( ) ;
2020-01-21 21:04:44 +01:00
compound . put ( " drain_spots " , list ) ;
2018-10-21 12:51:13 +02:00
return compound ;
}
@Override
2021-12-04 15:40:09 +01:00
public void deserializeNBT ( CompoundTag compound ) {
2018-10-21 12:51:13 +02:00
this . drainSpots . clear ( ) ;
2021-12-04 19:17:21 +01:00
var list = compound . getList ( " drain_spots " , 10 ) ;
for ( var base : list ) {
var tag = ( CompoundTag ) base ;
2018-11-23 20:36:31 +01:00
this . addDrainSpot (
2021-12-04 15:40:09 +01:00
BlockPos . of ( tag . getLong ( " pos " ) ) ,
2020-01-21 21:04:44 +01:00
new MutableInt ( tag . getInt ( " amount " ) ) ) ;
2018-10-21 12:51:13 +02:00
}
2021-01-30 16:43:46 +01:00
this . addOrRemoveAsActive ( ) ;
2020-12-07 01:06:22 +01:00
}
2021-01-30 16:43:46 +01:00
private void addOrRemoveAsActive ( ) {
2021-12-04 19:17:21 +01:00
var chunkPos = this . chunk . getPos ( ) . toLong ( ) ;
var data = ( LevelData ) ILevelData . getLevelData ( this . chunk . getLevel ( ) ) ;
2021-01-30 16:43:46 +01:00
if ( this . drainSpots . size ( ) > 0 ) {
data . auraChunksWithSpots . put ( chunkPos , this ) ;
} else {
data . auraChunksWithSpots . remove ( chunkPos ) ;
}
}
2018-10-21 12:51:13 +02:00
}