package com.hypixel.hytale.server.spawning.assets.spawns.config;
import com.hypixel.hytale.assetstore.AssetKeyValidator;
import com.hypixel.hytale.assetstore.AssetRegistry;
import com.hypixel.hytale.assetstore.AssetStore;
import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec;
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.codecs.EnumCodec;
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
import com.hypixel.hytale.codec.validation.ValidatorCache;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.server.core.asset.type.environment.config.Environment;
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import com.hypixel.hytale.server.core.asset.type.responsecurve.ScaledXYResponseCurve;
import com.hypixel.hytale.server.spawning.assets.spawnsuppression.SpawnSuppression;
import com.hypixel.hytale.server.spawning.util.FloodFillPositionSelector;
import java.time.Duration;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
publicclassBeaconNPCSpawnextendsNPCSpawnimplementsJsonAssetWithMap<String, IndexedLookupTableAssetMap<String, BeaconNPCSpawn>> {
publicstaticfinal AssetBuilderCodec<String, BeaconNPCSpawn> CODEC;
publicstaticfinalint[] DEFAULT_Y_RANGE;
publicstaticfinalint[] DEFAULT_CONCURRENT_SPAWNS_RANGE;
privatestaticfinal Duration[] DEFAULT_RESPAWN_TIME_RANGE;
protected String model;
protecteddoubletargetDistanceFromPlayer=15.0;
protecteddoubleminDistanceFromPlayer=5.0;
protectedint[] yRange;
protectedint maxSpawnedNpcs;
protectedint[] concurrentSpawnsRange;
protected Duration[] spawnAfterGameTime;
protected Duration[] spawnAfterRealTime;
protecteddouble[] initialSpawnDelay;
protecteddouble npcIdleDespawnTimeSeconds;
protecteddouble beaconRadius;
protecteddouble spawnRadius;
protected Duration beaconVacantDespawnTime;
protected String npcSpawnState;
protected String npcSpawnSubState;
protected String targetSlot;
protected String spawnSuppression;
protected ScaledXYResponseCurve maxSpawnsScalingCurve;
protected ScaledXYResponseCurve concurrentSpawnsScalingCurve;
protectedboolean overrideSpawnSuppressors;
protected FloodFillPositionSelector.Debug debug;
publicstaticfinal ValidatorCache<String> VALIDATOR_CACHE;
privatestatic AssetStore<String, BeaconNPCSpawn, IndexedLookupTableAssetMap<String, BeaconNPCSpawn>> ASSET_STORE;
publicBeaconNPCSpawn(String id) {
super(id);
this.yRange = DEFAULT_Y_RANGE;
this.maxSpawnedNpcs = 1;
this.concurrentSpawnsRange = DEFAULT_CONCURRENT_SPAWNS_RANGE;
this.npcIdleDespawnTimeSeconds = 10.0;
this.beaconRadius = 20.0;
this.spawnRadius = 15.0;
this.targetSlot = "LockedTarget";
this.debug = FloodFillPositionSelector.Debug.DISABLED;
}
protectedBeaconNPCSpawn() {
this.yRange = DEFAULT_Y_RANGE;
this.maxSpawnedNpcs = 1;
this.concurrentSpawnsRange = DEFAULT_CONCURRENT_SPAWNS_RANGE;
this.npcIdleDespawnTimeSeconds = 10.0;
this.beaconRadius = 20.0;
this.spawnRadius = 15.0;
this.targetSlot = "LockedTarget";
this.debug = FloodFillPositionSelector.Debug.DISABLED;
}
publicstatic AssetStore<String, BeaconNPCSpawn, IndexedLookupTableAssetMap<String, BeaconNPCSpawn>> getAssetStore() {
if (ASSET_STORE == null) {
ASSET_STORE = AssetRegistry.<String, BeaconNPCSpawn, IndexedLookupTableAssetMap<String, BeaconNPCSpawn>>getAssetStore(BeaconNPCSpawn.class);
}
return ASSET_STORE;
}
publicstatic IndexedLookupTableAssetMap<String, BeaconNPCSpawn> getAssetMap() {
return (IndexedLookupTableAssetMap)getAssetStore().getAssetMap();
}
public String getId() {
returnthis.id;
}
public String getModel() {
returnthis.model;
}
publicdoublegetTargetDistanceFromPlayer() {
returnthis.targetDistanceFromPlayer;
}
publicdoublegetMinDistanceFromPlayer() {
returnthis.minDistanceFromPlayer;
}
publicint[] getYRange() {
returnthis.yRange;
}
publicintgetMaxSpawnedNpcs() {
returnthis.maxSpawnedNpcs;
}
publicint[] getConcurrentSpawnsRange() {
returnthis.concurrentSpawnsRange;
}
public Duration[] getSpawnAfterGameTimeRange() {
returnthis.spawnAfterGameTime == null ? DEFAULT_RESPAWN_TIME_RANGE : this.spawnAfterGameTime;
}
public Duration[] getSpawnAfterRealTimeRange() {
returnthis.spawnAfterRealTime;
}
publicbooleanisRespawnRealtime() {
returnthis.spawnAfterRealTime != null && this.spawnAfterGameTime == null;
}
publicdouble[] getInitialSpawnDelay() {
returnthis.initialSpawnDelay;
}
publicdoublegetNpcIdleDespawnTimeSeconds() {
returnthis.npcIdleDespawnTimeSeconds;
}
public Duration getBeaconVacantDespawnTime() {
returnthis.beaconVacantDespawnTime;
}
publicdoublegetBeaconRadius() {
returnthis.beaconRadius;
}
publicdoublegetSpawnRadius() {
returnthis.spawnRadius;
}
public String getNpcSpawnState() {
returnthis.npcSpawnState;
}
public String getNpcSpawnSubState() {
returnthis.npcSpawnSubState;
}
public String getSpawnSuppression() {
returnthis.spawnSuppression;
}
publicbooleanisOverrideSpawnSuppressors() {
returnthis.overrideSpawnSuppressors;
}
public String getTargetSlot() {
returnthis.targetSlot;
}
public ScaledXYResponseCurve getMaxSpawnsScalingCurve() {
returnthis.maxSpawnsScalingCurve;
}
public ScaledXYResponseCurve getConcurrentSpawnsScalingCurve() {
returnthis.concurrentSpawnsScalingCurve;
}
public FloodFillPositionSelector.Debug getDebug() {
returnthis.debug;
}
@Nonnullpublic String toString() {
Stringvar10000=this.id;
return"BeaconNPCSpawn{id='" + var10000 + "', model=" + this.model + ", npcs=" + Arrays.deepToString(this.npcs) + ", despawnParameters=" + (this.despawnParameters != null ? this.despawnParameters.toString() : "Null") + ", environments=" + Arrays.toString(this.environments) + ", dayTimeRange=" + Arrays.toString(this.dayTimeRange) + ", moonPhaseRange=" + Arrays.toString(this.moonPhaseRange) + ", lightTypeMap=" + (this.lightTypeMap != null ? (String)this.lightTypeMap.entrySet().stream().map((entry) -> {
Stringvar10000= String.valueOf(entry.getKey());
return var10000 + "=" + Arrays.toString((double[])entry.getValue());
}).collect(Collectors.joining(", ", "{", "}")) : "Null") + ", targetDistanceFromPlayer=" + this.targetDistanceFromPlayer + ", minDistanceFromPlayer=" + this.minDistanceFromPlayer + ", yRange=" + Arrays.toString(this.yRange) + ", maxSpawnedNpcs=" + this.maxSpawnedNpcs + ", concurrentSpawnsRange=" + Arrays.toString(this.concurrentSpawnsRange) + ", spawnAfterGameTimeRange=" + Arrays.toString(this.spawnAfterGameTime) + ", spawnAfterRealTime=" + Arrays.toString(this.spawnAfterRealTime) + ", initialSpawnDelay=" + Arrays.toString(this.initialSpawnDelay) + ", npcIdleDespawnTimeSeconds=" + this.npcIdleDespawnTimeSeconds + ", beaconVacantDespawnTime=" + String.valueOf(this.beaconVacantDespawnTime) + ", beaconRadius=" + this.beaconRadius + ", spawnRadius=" + this.spawnRadius + ", npcSpawnState=" + this.npcSpawnState + ", npcSpawnSubState=" + this.npcSpawnSubState + ", spawnSuppression=" + this.spawnSuppression + ", overrideSpawnSuppressors=" + this.overrideSpawnSuppressors + ", targetSlot=" + this.targetSlot + ", scaleDayTimeRange=" + this.scaleDayTimeRange + ", maxSpawnsScalingCurve=" + String.valueOf(this.maxSpawnsScalingCurve) + ", concurrentSpawnsScalingCurve=" + String.valueOf(this.concurrentSpawnsScalingCurve) + ", debug=" + String.valueOf(this.debug) + "}";
}
static {
CODEC = ((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)((AssetBuilderCodec.Builder)AssetBuilderCodec.builder(BeaconNPCSpawn.class, BeaconNPCSpawn::new, NPCSpawn.BASE_CODEC, Codec.STRING, (t, k) -> t.id = k, (t) -> t.id, (asset, data) -> asset.data = data, (asset) -> asset.data).documentation("A spawning configuration used to spawn NPCs around the player when the player is within a specific radius from the beacon. When **Environments** are defined for the beacon, beacons of that type will be dynamically created while the player is in one of the specified environments.")).appendInherited(newKeyedCodec("Model", Codec.STRING), (spawn, s) -> spawn.model = s, (spawn) -> spawn.model, (spawn, parent) -> spawn.model = parent.model).documentation("An optional model to represent the beacon in the world.").addValidator(ModelAsset.VALIDATOR_CACHE.getValidator()).add()).appendInherited(newKeyedCodec("Environments", Codec.STRING_ARRAY), (spawn, o) -> spawn.environments = o, (spawn) -> spawn.environments, (spawn, parent) -> spawn.environments = parent.environments).documentation("A required list of environments that this configuration covers. Each combination of environment and NPC in this configuration should be unique.\n\nFor Beacon NPC Spawn configurations, this can be left empty. In this case, these define the environments this beacon can be dynamically spawned in. If left empty, the beacon will not be dynamically spawned (e.g. if it should only be spawned by an objective).").addValidator(Validators.nonNull()).addValidator(Validators.uniqueInArray()).addValidator(Environment.VALIDATOR_CACHE.getArrayValidator()).add()).appendInherited(newKeyedCodec("TargetDistanceFromPlayer", Codec.DOUBLE), (spawn, d) -> spawn.targetDistanceFromPlayer = d, (spawn) -> spawn.targetDistanceFromPlayer, (spawn, parent) -> spawn.targetDistanceFromPlayer = parent.targetDistanceFromPlayer).documentation("Roughly how far the NPC should be spawned away from the player (this is a guideline and not an absolute).").addValidator(Validators.greaterThan(0.0)).add()).appendInherited(newKeyedCodec("MinDistanceFromPlayer", Codec.DOUBLE), (spawn, d) -> spawn.minDistanceFromPlayer = d, (spawn) -> spawn.minDistanceFromPlayer, (spawn, parent) -> spawn.minDistanceFromPlayer = parent.minDistanceFromPlayer).documentation("A hard cutoff for how close an NPC can be spawned to the player to prevent the guideline distance above resulting in an NPC spawning too close to them.").addValidator(Validators.greaterThan(0.0)).add()).appendInherited(newKeyedCodec("YRange", Codec.INT_ARRAY), (spawn, o) -> spawn.yRange = o, (spawn) -> spawn.yRange, (spawn, parent) -> spawn.yRange = parent.yRange).documentation("The acceptable y range within which NPCs can be spawned from the beacon. This is defined as offsets from the beacon. With [ -5, 5 ], NPCs can be spawned from five blocks below the beacon up to five blocks above.").addValidator(Validators.intArraySize(2)).add()).appendInherited(newKeyedCodec("MaxSpawnedNPCs", Codec.INTEGER), (spawn, i) -> spawn.maxSpawnedNpcs = i, (spawn) -> spawn.maxSpawnedNpcs, (spawn, parent) -> spawn.maxSpawnedNpcs = parent.maxSpawnedNpcs).documentation("The maximum number of NPCs this beacon can have spawned at once.").addValidator(Validators.greaterThan(0)).add()).appendInherited(newKeyedCodec("ConcurrentSpawnsRange", Codec.INT_ARRAY), (spawn, i) -> spawn.concurrentSpawnsRange = i, (spawn) -> spawn.concurrentSpawnsRange, (spawn, parent) -> spawn.concurrentSpawnsRange = parent.concurrentSpawnsRange).documentation("The range from which a random number will be chosen that will represent the number of NPCs to be spawned in the next round between cooldowns.").addValidator(Validators.intArraySize(2)).add()).appendInherited(newKeyedCodec("SpawnAfterGameTimeRange", newArrayCodec(Codec.DURATION, (x$0) -> newDuration[x$0])), (spawn, s) -> spawn.spawnAfterGameTime = s, (spawn) -> spawn.spawnAfterGameTime, (spawn, parent) -> spawn.spawnAfterGameTime = parent.spawnAfterGameTime).documentation("The random range from which to pick the next game-time based cooldown between spawns. This should be a duration string, e.g. [ \"PT5M\", \"PT10M\" ] which will spawn between 5 and 10 in-game minutes after the last spawn.").addValidator(Validators.arraySize(2)).add()).appendInherited(newKeyedCodec("SpawnAfterRealTimeRange", newArrayCodec(Codec.DURATION, (x$0) -> newDuration[x$0])), (spawn, s) -> spawn.spawnAfterRealTime = s, (spawn) -> spawn.spawnAfterRealTime, (spawn, parent) -> spawn.spawnAfterRealTime = parent.spawnAfterRealTime).documentation("The random range from which to pick the next real-time based cooldown between spawns. This should be a duration string, e.g. [ \"PT30S\", \"PT80S\" ] which will spawn between 30 and 80 seconds IRL after the last spawn.").addValidator(Validators.arraySize(2)).add()).appendInherited(newKeyedCodec("InitialSpawnDelayRange", Codec.DOUBLE_ARRAY), (spawn, s) -> spawn.initialSpawnDelay = s, (spawn) -> spawn.initialSpawnDelay, (spawn, parent) -> spawn.initialSpawnDelay = parent.initialSpawnDelay).documentation("An optional range from which to pick an initial delay in real time seconds before which the first round of NPCs will be spawned after a beacon is created.").addValidator(Validators.doubleArraySize(2)).add()).appendInherited(newKeyedCodec("NPCIdleDespawnTime", Codec.DOUBLE), (spawn, d) -> spawn.npcIdleDespawnTimeSeconds = d, (spawn) -> spawn.npcIdleDespawnTimeSeconds, (spawn, parent) -> spawn.npcIdleDespawnTimeSeconds = parent.npcIdleDespawnTimeSeconds).documentation("The number of seconds an NPC spawned by this beacon needs to spend idle before it will be despawned due to having no target. If **NPCSpawnState** is omitted, this will be ignored.").addValidator(Validators.greaterThan(0.0)).add()).appendInherited(newKeyedCodec("BeaconVacantDespawnGameTime", Codec.DURATION), (spawn, d) -> spawn.beaconVacantDespawnTime = d, (spawn) -> spawn.beaconVacantDespawnTime, (spawn, parent) -> spawn.beaconVacantDespawnTime = parent.beaconVacantDespawnTime).documentation("The amount of game time that needs to pass with no players present within the **SpawnRadius** before this beacon will remove itself. This should be a duration string.").add()).appendInherited(newKeyedCodec("BeaconRadius", Codec.DOUBLE), (spawn, d) -> spawn.beaconRadius = d, (spawn) -> spawn.beaconRadius, (spawn, parent) -> spawn.beaconRadius = parent.beaconRadius).documentation("The radius within which a spawned NPC is considered to be under the influence of the beacon and NPCs will be spawned for a player. If an NPC spawned by the beacon moves outside this radius and is not in a busy state, it will begin to tick down the **NPCIdleDespawnTime** (if being considered). It is recommended that this be ~25% larger than the **SpawnRadius**.").addValidator(Validators.greaterThan(0.0)).add()).appendInherited(newKeyedCodec("SpawnRadius", Codec.DOUBLE), (spawn, d) -> spawn.spawnRadius = d, (spawn) -> spawn.spawnRadius, (spawn, parent) -> spawn.spawnRadius = parent.spawnRadius).documentation("The radius within which NPCs spawns can physically happen (i.e. where their spawn points will be).").addValidator(Validators.greaterThan(0.0)).add()).appendInherited(newKeyedCodec("NPCSpawnState", Codec.STRING), (spawn, s) -> spawn.npcSpawnState = s, (spawn) -> spawn.npcSpawnState, (spawn, parent) -> spawn.npcSpawnState = parent.npcSpawnState).documentation("An optional state to force the NPC into upon spawn. If this state exists on the NPC, it will immediately enter the state upon spawn. For example, setting this to **Chase** will result in most NPCs immediately going for the player they were spawned around. If omitted, this beacon will allow idle NPCs.").add()).appendInherited(newKeyedCodec("NPCSpawnSubState", Codec.STRING), (spawn, s) -> spawn.npcSpawnSubState = s, (spawn) -> spawn.npcSpawnSubState, (spawn, parent) -> spawn.npcSpawnSubState = parent.npcSpawnSubState).documentation("As with **NPCSpawnStat**, but acts as an additional qualifier to define the desired substate.").add()).appendInherited(newKeyedCodec("TargetSlot", Codec.STRING), (spawn, s) -> spawn.targetSlot = s, (spawn) -> spawn.targetSlot, (spawn, parent) -> spawn.targetSlot = parent.targetSlot).documentation("The locked target slot to set the player to in the NPC.").addValidator(Validators.nonNull()).addValidator(Validators.nonEmptyString()).add()).appendInherited(newKeyedCodec("SpawnSuppression", Codec.STRING), (spawn, s) -> spawn.spawnSuppression = s, (spawn) -> spawn.spawnSuppression, (spawn, parent) -> spawn.spawnSuppression = parent.spawnSuppression).documentation("An optional spawn suppression that will be tied to this beacon.").addValidator(SpawnSuppression.VALIDATOR_CACHE.getValidator()).add()).appendInherited(newKeyedCodec("OverrideSpawnSuppressors", Codec.BOOLEAN), (spawn, b) -> spawn.overrideSpawnSuppressors = b, (spawn) -> spawn.overrideSpawnSuppressors, (spawn, parent) -> spawn.overrideSpawnSuppressors = parent.overrideSpawnSuppressors).documentation("Whether this beacon should ignore any spawn suppressions.").add()).appendInherited(newKeyedCodec("MaxSpawnsScalingCurve", ScaledXYResponseCurve.CODEC), (spawn, s) -> spawn.maxSpawnsScalingCurve = s, (spawn) -> spawn.maxSpawnsScalingCurve, (spawn, parent) -> spawn.maxSpawnsScalingCurve = parent.maxSpawnsScalingCurve).documentation("A scaled response curve that represents the number of additional mobs to be added to the total **MaxSpawnedNPCs** based on the number of players within the beacon's max **DistanceRange**.").add()).appendInherited(newKeyedCodec("ConcurrentSpawnsScalingCurve", ScaledXYResponseCurve.CODEC), (spawn, s) -> spawn.concurrentSpawnsScalingCurve = s, (spawn) -> spawn.concurrentSpawnsScalingCurve, (spawn, parent) -> spawn.concurrentSpawnsScalingCurve = parent.concurrentSpawnsScalingCurve).documentation("A scaled response curve that represents the number of additional mobs to be added to the total **MaxConcurrentSpawns** based on the number of players within the beacon's max **DistanceRange**.").add()).appendInherited(newKeyedCodec("Debug", (newEnumCodec(FloodFillPositionSelector.Debug.class)).documentKey(FloodFillPositionSelector.Debug.ALL, "Print all maps.").documentKey(FloodFillPositionSelector.Debug.IRREGULARITIES, "Print only irregular maps.").documentKey(FloodFillPositionSelector.Debug.DISABLED, "Disable map printing.")), (spawn, b) -> spawn.debug = b, (spawn) -> spawn.debug, (spawn, parent) -> spawn.debug = parent.debug).documentation("The debug mode. Can be used to print 2d maps of evaluated spawn regions.").add()).build();
DEFAULT_Y_RANGE = newint[]{-5, 5};
DEFAULT_CONCURRENT_SPAWNS_RANGE = newint[]{1, 1};
DEFAULT_RESPAWN_TIME_RANGE = newDuration[]{Duration.ofSeconds(5L), Duration.ofSeconds(10L)};
VALIDATOR_CACHE = newValidatorCache<String>(newAssetKeyValidator(BeaconNPCSpawn::getAssetStore));
}
}