Config implementation & bestiary rework

This commit is contained in:
FatherToast 2022-07-10 16:07:07 -05:00
parent 86ef90e201
commit 22cfb5616b
243 changed files with 4971 additions and 4198 deletions

View file

@ -10,7 +10,6 @@ import fathertoast.specialmobs.common.entity.witherskeleton.NinjaWitherSkeletonE
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.RenderTypeLookup;
import net.minecraft.client.renderer.entity.SpriteRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
@ -58,12 +57,12 @@ public class ClientRegister {
registerSpeciesRenderer( NinjaSkeletonEntity.SPECIES, NinjaSkeletonRenderer::new );
registerSpeciesRenderer( NinjaWitherSkeletonEntity.SPECIES, NinjaSkeletonRenderer::new );
registerSpeciesRenderer( CorporealShiftGhastEntity.SPECIES, CorporealShiftGhastRenderer::new );
// Other
registerSpriteRenderer( SMEntities.CORPOREAL_FIREBALL.get(), game, 3.0F, true );
}
private static <T extends LivingEntity> void registerFamilyRenderers( MobFamily<T> family, IRenderFactory<? super T> renderFactory ) {
private static <T extends LivingEntity> void registerFamilyRenderers( MobFamily<T, ?> family, IRenderFactory<? super T> renderFactory ) {
RenderingRegistry.registerEntityRenderingHandler( family.vanillaReplacement.entityType.get(), renderFactory );
for( MobFamily.Species<? extends T> species : family.variants )
registerSpeciesRenderer( species, renderFactory );
@ -72,9 +71,9 @@ public class ClientRegister {
private static <T extends LivingEntity> void registerSpeciesRenderer( MobFamily.Species<T> species, IRenderFactory<? super T> renderFactory ) {
RenderingRegistry.registerEntityRenderingHandler( species.entityType.get(), renderFactory );
}
private static <T extends Entity & IRendersAsItem> void registerSpriteRenderer(EntityType<T> entityType, Supplier<Minecraft> minecraftSupplier, float scale, boolean fullBright) {
private static <T extends Entity & IRendersAsItem> void registerSpriteRenderer( EntityType<T> entityType, Supplier<Minecraft> minecraftSupplier, float scale, boolean fullBright ) {
ItemRenderer itemRenderer = minecraftSupplier.get().getItemRenderer();
RenderingRegistry.registerEntityRenderingHandler(entityType, (renderManager) -> new SpriteRenderer<>(renderManager, itemRenderer, scale, fullBright));
RenderingRegistry.registerEntityRenderingHandler( entityType, ( renderManager ) -> new SpriteRenderer<>( renderManager, itemRenderer, scale, fullBright ) );
}
}

View file

@ -1,8 +1,30 @@
package fathertoast.specialmobs.common.bestiary;
import fathertoast.specialmobs.common.config.util.AttributeEntry;
import fathertoast.specialmobs.common.config.util.AttributeList;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import fathertoast.specialmobs.common.config.util.RegistryEntryList;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.util.References;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.potion.Effect;
import net.minecraft.potion.Effects;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.*;
/**
* This class serves solely to store data for mob species in an organized way, providing builder methods as applicable.
* The bulk of bestiary info is default config settings.
*/
public class BestiaryInfo {
public enum BaseWeight {
public enum DefaultWeight {
DEFAULT( 600 ),
DISABLED( 0 ),
LOWEST( DEFAULT.value / 8 ),
@ -12,103 +34,555 @@ public class BestiaryInfo {
public final int value;
BaseWeight( int v ) { value = v; }
DefaultWeight( int v ) { value = v; }
}
//TODO default themes
// public static final EnvironmentListConfig DEFAULT_THEME_FIRE = new EnvironmentListConfig(
// new TargetEnvironment.TargetBiomeGroup( "desert", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "savanna", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "mesa", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetDimension( DimensionType.NETHER, BestiaryInfo.BASE_WEIGHT_COMMON ),
//
// new TargetEnvironment.TargetBiomeGroup( "ice", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "frozen", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "cold", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "taiga_cold", BestiaryInfo.BASE_WEIGHT_RARE )
// );
//
// public static final EnvironmentListConfig DEFAULT_THEME_ICE = new EnvironmentListConfig(
// new TargetEnvironment.TargetBiomeGroup( "ice", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "frozen", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "cold", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "taiga_cold", BestiaryInfo.BASE_WEIGHT_COMMON ),
//
// new TargetEnvironment.TargetBiomeGroup( "desert", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "savanna", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "mesa", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetDimension( DimensionType.NETHER, BestiaryInfo.BASE_WEIGHT_RARE )
// );
//
// public static final EnvironmentListConfig DEFAULT_THEME_FOREST = new EnvironmentListConfig(
// new TargetEnvironment.TargetBiomeGroup( "swamp", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "forest", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "birch_forest", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "roofed_forest", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "jungle", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "taiga", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "redwood_taiga", BestiaryInfo.BASE_WEIGHT_COMMON ),
//
// new TargetEnvironment.TargetBiomeGroup( "desert", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "ice", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "stone", BestiaryInfo.BASE_WEIGHT_RARE )
// );
//
// public static final EnvironmentListConfig DEFAULT_THEME_MOUNTAIN = new EnvironmentListConfig(
// new TargetEnvironment.TargetBiomeGroup( "extreme", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "smaller_extreme", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "mesa", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "ice_mountains", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiomeGroup( "stone", BestiaryInfo.BASE_WEIGHT_COMMON ),
//
// new TargetEnvironment.TargetBiomeGroup( "plains", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "desert", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "swamp", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "beach", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "cold_beach", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "savanna", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "ice_flats", BestiaryInfo.BASE_WEIGHT_RARE )
// );
//
// public static final EnvironmentListConfig DEFAULT_THEME_WATER = new EnvironmentListConfig(
// new TargetEnvironment.TargetBiomeGroup( "swamp", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiome( Biomes.BEACH, BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiome( Biomes.OCEAN, BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiome( Biomes.DEEP_OCEAN, BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiome( Biomes.FROZEN_OCEAN, BestiaryInfo.BASE_WEIGHT_COMMON ),
//
// new TargetEnvironment.TargetBiomeGroup( "desert", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetBiomeGroup( "savanna", BestiaryInfo.BASE_WEIGHT_RARE ),
// new TargetEnvironment.TargetDimension( DimensionType.NETHER, 0 )
// );
//
// public static final EnvironmentListConfig DEFAULT_THEME_FISHING = new EnvironmentListConfig(
// new TargetEnvironment.TargetBiomeGroup( "swamp", BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiome( Biomes.BEACH, BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiome( Biomes.OCEAN, BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiome( Biomes.DEEP_OCEAN, BestiaryInfo.BASE_WEIGHT_COMMON ),
// new TargetEnvironment.TargetBiome( Biomes.FROZEN_OCEAN, BestiaryInfo.BASE_WEIGHT_COMMON ),
//
// new TargetEnvironment.TargetBiomeGroup( "desert", BestiaryInfo.BASE_WEIGHT_UNCOMMON ),
// new TargetEnvironment.TargetBiomeGroup( "savanna", BestiaryInfo.BASE_WEIGHT_UNCOMMON ),
// new TargetEnvironment.TargetBiomeGroup( "mesa", BestiaryInfo.BASE_WEIGHT_UNCOMMON )
// );
// These can be set in the entity class to change these default values.
//public EnvironmentListConfig weightExceptions = new EnvironmentListConfig( );
public enum Theme {
NONE,
FIRE, ICE,
DESERT, WATER,
FOREST, MOUNTAIN,
FISHING
}
/** The spot color for spawn eggs of this species. The base color is determined by the family. */
public final int eggSpotsColor;
public final BaseWeight weight;
/** The base render scale of this species. */
public final float baseScale;
public BestiaryInfo( int eggColor ) { this( eggColor, BaseWeight.DEFAULT ); }
/** The default species weight. */
public final DefaultWeight defaultWeight;
/** The species theme; determines default environment weight exceptions. */
public final Theme theme;
/** The default species attribute differences compared to their vanilla counterpart. */
public final AttributeList defaultAttributes;
public BestiaryInfo( int eggColor, BaseWeight baseWeight ) {
eggSpotsColor = eggColor;
weight = baseWeight;
/** The default texture. */
public final ResourceLocation texture;
/** The default glowing eyes texture. Not applicable for ghasts or families normally rendered at max brightness. */
public final ResourceLocation eyesTexture;
/**
* The default overlay texture.
* Generally only applicable for bipedal mobs, though this is used as the "shooting" texture for ghasts.
*/
public final ResourceLocation overlayTexture;
// Special Mob Data defaults
public final int experience;
public final int healTime;
public final double fallDamageMultiplier;
public final boolean isImmuneToFire;
public final boolean isImmuneToBurning;
public final boolean canBreatheInWater;
public final boolean ignoreWaterPush;
public final boolean isDamagedByWater;
public final boolean allowLeashing;
public final boolean ignorePressurePlates;
public final RegistryEntryList<Block> immuneToStickyBlocks;
public final RegistryEntryList<Effect> immuneToPotions;
public final double rangedAttackDamage;
public final double rangedAttackSpread;
public final double rangedWalkSpeed;
public final int rangedAttackCooldown;
public final int rangedAttackMaxCooldown;
public final double rangedAttackMaxRange;
//--------------- Builder Implementation ----------------
public static BestiaryInfo.Builder of( MobFamily.Species<?> species, EntityType.Builder<?> typeBuilder ) {
return new Builder( species, typeBuilder );
}
public BestiaryInfo setTheme() {
return this;//TODO theme builder
private BestiaryInfo( int eggColor, float scale, DefaultWeight weight, Theme spawnTheme, List<AttributeEntry> attributes,
ResourceLocation tex, ResourceLocation eyeTex, ResourceLocation ovrTex,
int xp, int regen, double fallDmg, boolean fireImm, boolean burnImm, boolean drownImm, boolean pushImm,
boolean waterDmg, boolean leash, boolean plateImm, Block[] blockImm, Effect[] effectImm,
double raDmg, double raVar, double raSpd, int raCD, int raMCD, double raRng ) {
eggSpotsColor = eggColor;
baseScale = scale;
defaultWeight = weight;
theme = spawnTheme;
defaultAttributes = new AttributeList( attributes );
texture = tex;
eyesTexture = eyeTex;
overlayTexture = ovrTex;
experience = xp;
healTime = regen;
fallDamageMultiplier = fallDmg;
isImmuneToFire = fireImm;
isImmuneToBurning = burnImm;
canBreatheInWater = drownImm;
ignoreWaterPush = pushImm;
isDamagedByWater = waterDmg;
allowLeashing = leash;
ignorePressurePlates = plateImm;
immuneToStickyBlocks = new RegistryEntryList<>( ForgeRegistries.BLOCKS, blockImm );
immuneToPotions = new RegistryEntryList<>( ForgeRegistries.POTIONS, effectImm );
rangedAttackDamage = raDmg;
rangedAttackSpread = raVar;
rangedWalkSpeed = raSpd;
rangedAttackCooldown = raCD;
rangedAttackMaxCooldown = raMCD;
rangedAttackMaxRange = raRng;
}
@SuppressWarnings( { "UnusedReturnValue", "unused" } )
public static final class Builder {
private final MobFamily.Species<?> owningSpecies;
private final EntityType.Builder<?> entityTypeBuilder;
// Fields NOT inherited from vanilla replacement
private boolean colorSet;
private int eggSpotsColor;
private DefaultWeight defaultWeight = DefaultWeight.DEFAULT;
private Theme spawnTheme = Theme.NONE;
private final List<AttributeEntry> attributes = new ArrayList<>();
// Fields inherited from vanilla replacement (technically also SM Data)
private float baseScale = 1.0F;
private ResourceLocation texture;
private ResourceLocation eyesTexture;
private ResourceLocation overlayTexture;
// Special Mob Data fields (also inherited)
private int experience = -1;
private int healTime;
private double fallDamageMultiplier = 1.0;
private boolean isImmuneToFire;
private boolean isImmuneToBurning;
private boolean canBreatheInWater;
private boolean ignoreWaterPush;
private boolean isDamagedByWater;
private boolean allowLeashing;
private boolean ignorePressurePlates;
private final ArrayList<Block> immuneToStickyBlocks = new ArrayList<>();
private final ArrayList<Effect> immuneToPotions = new ArrayList<>();
private double rangedAttackDamage = -1.0;
private double rangedAttackSpread = -1.0;
private double rangedWalkSpeed = -1.0;
private int rangedAttackCooldown = -1;
private int rangedAttackMaxCooldown = -1;
private double rangedAttackMaxRange = -1.0;
private Builder( MobFamily.Species<?> species, EntityType.Builder<?> typeBuilder ) {
owningSpecies = species;
entityTypeBuilder = typeBuilder;
// Special variants should copy many of the vanilla replacement's stats
if( species.specialVariantName != null ) {
final BestiaryInfo parent = species.family.vanillaReplacement.bestiaryInfo;
baseScale = parent.baseScale;
texture = parent.texture;
eyesTexture = parent.eyesTexture;
overlayTexture = parent.overlayTexture;
experience = parent.experience;
healTime = parent.healTime;
fallDamageMultiplier = parent.fallDamageMultiplier;
isImmuneToFire = parent.isImmuneToFire;
isImmuneToBurning = parent.isImmuneToBurning;
canBreatheInWater = parent.canBreatheInWater;
ignoreWaterPush = parent.ignoreWaterPush;
isDamagedByWater = parent.isDamagedByWater;
allowLeashing = parent.allowLeashing;
ignorePressurePlates = parent.ignorePressurePlates;
immuneToStickyBlocks.addAll( parent.immuneToStickyBlocks.getEntries() );
immuneToPotions.addAll( parent.immuneToPotions.getEntries() );
rangedAttackDamage = parent.rangedAttackDamage;
rangedAttackSpread = parent.rangedAttackSpread;
rangedWalkSpeed = parent.rangedWalkSpeed;
rangedAttackCooldown = parent.rangedAttackCooldown;
rangedAttackMaxCooldown = parent.rangedAttackMaxCooldown;
rangedAttackMaxRange = parent.rangedAttackMaxRange;
}
}
BestiaryInfo build() {
// Perform a little verification
if( !colorSet )
throw new IllegalStateException( "Species " + owningSpecies.name + " has not assigned egg spots color!" );
if( experience < 0 )
throw new IllegalStateException( "Family " + owningSpecies.family.name + " has not set the base experience value!" );
return new BestiaryInfo( eggSpotsColor, baseScale, defaultWeight, spawnTheme, attributes, texture, eyesTexture, overlayTexture,
experience, healTime, fallDamageMultiplier, isImmuneToFire, isImmuneToBurning, canBreatheInWater, ignoreWaterPush, isDamagedByWater,
allowLeashing, ignorePressurePlates, immuneToStickyBlocks.toArray( new Block[0] ), immuneToPotions.toArray( new Effect[0] ),
rangedAttackDamage, rangedAttackSpread, rangedWalkSpeed, rangedAttackCooldown, rangedAttackMaxCooldown, rangedAttackMaxRange );
}
//--------------- Bestiary ----------------
/** Sets the species spawn egg spots color. This MUST be called or the build will throw an exception! */
public Builder color( int eggColor ) {
eggSpotsColor = eggColor;
colorSet = true;
return this;
}
/** Sets the species default weight. */
public Builder weight( DefaultWeight weight ) {
defaultWeight = weight;
return this;
}
/** Sets the species default environment weight exceptions by theme. */
public Builder theme( Theme theme ) {
spawnTheme = theme;
return this;
}
//--------------- Size ----------------
/** Sets the family base render size. Throws an exception if called for a special variant. */
public Builder familySize( float renderScale ) {
// Do NOT use for special variants; if the render scale is changed, the bounding box should match!
if( owningSpecies.specialVariantName != null )
throw new IllegalStateException( "Special variant " + owningSpecies.specialVariantName + " cannot set family render scale!" );
baseScale = renderScale;
return this;
}
/** Sets the species size - both render scale and bounding box dimensions. */
public Builder size( float renderScale, float width, float height ) {
baseScale = renderScale;
entityTypeBuilder.sized( width, height );
return this;
}
//--------------- Textures (Vanilla) ----------------
/** Sets the species default base, glowing eyes, and overlay textures. */
public Builder vanillaTexturesAll( String tex, String eyeTex, String ovrTex ) {
return vanillaBaseTexture( tex ).vanillaEyesTexture( eyeTex ).vanillaOverlayTexture( ovrTex );
}
/** Sets the species default base and glowing eyes textures. Removes all other textures. */
public Builder vanillaTextureWithEyes( String tex, String eyeTex ) {
return vanillaBaseTexture( tex ).vanillaEyesTexture( eyeTex ).noOverlayTexture();
}
/** Sets the species default base and overlay textures. Removes all other textures. */
public Builder vanillaTextureWithOverlay( String tex, String ovrTex ) {
return vanillaBaseTexture( tex ).noEyesTexture().vanillaOverlayTexture( ovrTex );
}
/** Sets the species default base and animation (overlay) textures. Removes all other textures. */
public Builder vanillaTextureWithAnimation( String tex, String aniTex ) { return vanillaTextureWithOverlay( tex, aniTex ); }
/** Sets the species default base texture. Removes all other textures. */
public Builder vanillaTextureBaseOnly( String tex ) { return vanillaBaseTexture( tex ).noEyesTexture().noOverlayTexture(); }
/** Sets the species default base texture. */
private Builder vanillaBaseTexture( String tex ) { return baseTexture( tex == null ? null : new ResourceLocation( tex ) ); }
/** Sets the species default glowing eyes texture. */
private Builder vanillaEyesTexture( String eyeTex ) { return eyesTexture( eyeTex == null ? null : new ResourceLocation( eyeTex ) ); }
/** Sets the species default overlay texture. */
private Builder vanillaOverlayTexture( String ovrTex ) { return overlayTexture( ovrTex == null ? null : new ResourceLocation( ovrTex ) ); }
//--------------- Textures (Auto-selected) ----------------
/** Sets the species default base, glowing eyes, and overlay textures. */
public Builder uniqueTexturesAll() { return uniqueBaseTexture().uniqueEyesTexture().uniqueOverlayTexture(); }
/** Sets the species default base and glowing eyes textures. Removes all other textures. */
public Builder uniqueTextureWithEyes() { return uniqueBaseTexture().uniqueEyesTexture().noOverlayTexture(); }
/** Sets the species default base and overlay textures. Removes all other textures. */
public Builder uniqueTextureWithOverlay() { return uniqueBaseTexture().noEyesTexture().uniqueOverlayTexture(); }
/** Sets the species default base and animation (overlay) textures. Removes all other textures. */
public Builder uniqueTextureWithAnimation() { return uniqueBaseTexture().noEyesTexture().uniqueAnimationTexture(); }
/** Sets the species default base texture. Removes all other textures. */
public Builder uniqueTextureBaseOnly() { return uniqueBaseTexture().noEyesTexture().noOverlayTexture(); }
/** Sets the species default base texture. */
// Private because we always want to replace all textures when using a unique base
private Builder uniqueBaseTexture() { return baseTexture( getBaseTexture() ); }
/** Sets the species default glowing eyes texture. */
public Builder uniqueEyesTexture() { return eyesTexture( getEyesTexture() ); }
/** Sets the species default overlay texture. */
public Builder uniqueOverlayTexture() { return overlayTexture( getOverlayTexture() ); }
/** Sets the species default animation texture (uses the overlay slot). */
public Builder uniqueAnimationTexture() { return overlayTexture( getShootingTexture() ); }
/** @return The expected base texture for this builder. */
private ResourceLocation getBaseTexture() { return toTexture( References.TEXTURE_BASE_SUFFIX ); }
/** @return The expected eyes texture for this builder. */
private ResourceLocation getEyesTexture() { return toTexture( References.TEXTURE_EYES_SUFFIX ); }
/** @return The expected overlay texture for this builder. */
private ResourceLocation getOverlayTexture() { return toTexture( References.TEXTURE_OVERLAY_SUFFIX ); }
/** @return The given strings converted to a texture resource location. */
private ResourceLocation getShootingTexture() { return toTexture( References.TEXTURE_SHOOTING_SUFFIX ); }
/** @return The given strings converted to a texture resource location. */
private ResourceLocation toTexture( String suffix ) {
return SpecialMobs.resourceLoc( String.format( References.TEXTURE_FORMAT,
ConfigUtil.camelCaseToLowerUnderscore( owningSpecies.family.name ),
ConfigUtil.camelCaseToLowerUnderscore( owningSpecies.specialVariantName ), suffix ) );
}
//--------------- Textures (Misc/Internal) ----------------
/** Removes the species default glowing eyes texture. */
public Builder noEyesTexture() { return eyesTexture( null ); }
/** Removes the species default overlay texture. */
public Builder noOverlayTexture() { return overlayTexture( null ); }
/** Removes the species default animation texture (uses the overlay slot). */
public Builder noAnimationTexture() { return noOverlayTexture(); }
/** Sets the species default base texture. */
private Builder baseTexture( ResourceLocation tex ) {
texture = tex;
return this;
}
/** Sets the species default glowing eyes texture. */
private Builder eyesTexture( ResourceLocation eyeTex ) {
eyesTexture = eyeTex;
return this;
}
/** Sets the species default overlay texture. */
private Builder overlayTexture( ResourceLocation ovrTex ) {
overlayTexture = ovrTex;
return this;
}
//--------------- Creature Type Templates ----------------
/** Sets the standard species stats implied by being undead. */
public Builder undead() { return effectImmune( Effects.REGENERATION, Effects.POISON ); }
/** Sets the standard species stats implied by being a spider. */
public Builder spider() { return webImmune().effectImmune( Effects.POISON ); }
//--------------- Special Mob Data ----------------
/** Sets the species experience value. */
public Builder experience( int xp ) {
experience = xp;
return this;
}
/** Adds a flat amount to the species experience value. */
public Builder addExperience( int xp ) { return experience( experience + xp ); }
/** Sets the species heal time. */
public Builder regen( int time ) {
healTime = time;
return this;
}
/** Sets the species as fall damage immune. */
public Builder fallImmune() { return fallDamage( 0.0 ); }
/** Sets the species fall damage multiplier. */
public Builder fallDamage( double multiplier ) {
fallDamageMultiplier = multiplier;
return this;
}
/** Sets the species as fire immune. */
public Builder fireImmune() {
entityTypeBuilder.fireImmune();
isImmuneToFire = true;
return this;
}
/** Sets the species as burning immune. */
public Builder burnImmune() {
isImmuneToBurning = true;
return this;
}
/** Sets the species as drowning immune. */
public Builder drownImmune() {
canBreatheInWater = true;
return this;
}
/** Sets the species as fluid-pushing immune. */
public Builder fluidPushImmune() {
ignoreWaterPush = true;
return this;
}
/** Sets the species as damaged by water. */
public Builder waterSensitive() {
isDamagedByWater = true;
return this;
}
/** Sets the species as leashable (can have a lead attached). */
public Builder leashable() {
allowLeashing = true;
return this;
}
/** Sets the species as pressure plate immune. */
public Builder pressurePlateImmune() {
ignorePressurePlates = true;
return this;
}
/** Sets the block hazards (damaging blocks) the species is immune to. */
public Builder hazardImmune( Block... hazards ) {
entityTypeBuilder.immuneTo( hazards );
return this;
}
/** Sets the species as cobweb immune. */
public Builder webImmune() { return stickyBlockImmune( Blocks.COBWEB ); }
/** Sets the species as immune to a specific list of sticky blocks. */
public Builder stickyBlockImmune( Block... blocks ) {
immuneToStickyBlocks.addAll( Arrays.asList( blocks ) );
return this;
}
/** Sets the species as immune to a specific list of effects. */
public Builder effectImmune( Effect... effects ) {
immuneToPotions.addAll( Arrays.asList( effects ) );
return this;
}
//--------------- Ranged Attacks (Special Mob Data) ----------------
/** Sets the species ranged attack stats (for a bow user). */
public Builder bowAttack( double damage, double spread, double walkSpeed, int cooldown, double range ) {
return rangedDamage( damage ).rangedSpread( spread ).rangedWalkSpeed( walkSpeed ).rangedCooldown( cooldown ).rangedMaxRange( range );
}
/** Sets the species ranged attack stats (for a fireball shooter). */
public Builder fireballAttack( double spread, int charge, int cooldown, double range ) {
return rangedSpread( spread ).rangedCooldown( charge ).rangedMaxCooldown( charge + cooldown ).rangedMaxRange( range );
}
/** Sets the species ranged attack stats (for a spit shooter). */
public Builder spitAttack( double damage, double spread, int cooldown, int extraCooldown, double range ) {
return rangedDamage( damage ).rangedSpread( spread )
.rangedCooldown( cooldown ).rangedMaxCooldown( cooldown + extraCooldown ).rangedMaxRange( range );
}
/** Applies multipliers to the species ranged attack stats (for a spit shooter). */
public Builder spitAttackMultiplied( double damage, double spread, float cooldown, double range ) {
return multiplyRangedDamage( damage ).multiplyRangedSpread( spread )
.multiplyRangedCooldown( cooldown ).multiplyRangedMaxCooldown( cooldown ).multiplyRangedMaxRange( range );
}
/** Sets the species as unable to use ranged attacks (for any ranged user). */
public Builder disableRangedAttack() { return rangedMaxRange( 0.0 ); }
/** Applies a flat modifier to the species ranged attack damage. */
public Builder addToRangedDamage( double value ) { return rangedDamage( rangedAttackDamage + value ); }
/** Applies a multiplier to the species ranged attack damage. */
public Builder multiplyRangedDamage( double value ) { return rangedDamage( rangedAttackDamage * value ); }
/** Sets the species ranged attack damage. */
public Builder rangedDamage( double value ) {
if( owningSpecies.specialVariantName != null && rangedAttackDamage < 0.0 )
throw new IllegalStateException( "Attempted to add inapplicable ranged attack stat!" );
rangedAttackDamage = value;
return this;
}
/** Applies a multiplier to the species ranged attack spread. */
public Builder multiplyRangedSpread( double value ) { return rangedSpread( rangedAttackSpread * value ); }
/** Sets the species ranged attack spread. */
public Builder rangedSpread( double value ) {
if( owningSpecies.specialVariantName != null && rangedAttackSpread < 0.0 )
throw new IllegalStateException( "Attempted to add inapplicable ranged attack stat!" );
rangedAttackSpread = value;
return this;
}
/** Applies a multiplier to the species ranged attack walk speed. */
public Builder multiplyRangedWalkSpeed( double value ) { return rangedWalkSpeed( rangedWalkSpeed * value ); }
/** Sets the species ranged attack walk speed. */
public Builder rangedWalkSpeed( double value ) {
if( owningSpecies.specialVariantName != null && rangedWalkSpeed < 0.0 )
throw new IllegalStateException( "Attempted to add inapplicable ranged attack stat!" );
rangedWalkSpeed = value;
return this;
}
/** Applies a multiplier to the species ranged attack cooldown. */
public Builder multiplyRangedCooldown( float value ) { return rangedCooldown( Math.round( rangedAttackCooldown * value ) ); }
/** Sets the species ranged attack cooldown. */
public Builder rangedCooldown( int value ) {
if( owningSpecies.specialVariantName != null && rangedAttackCooldown < 0 )
throw new IllegalStateException( "Attempted to add inapplicable ranged attack stat!" );
rangedAttackCooldown = value;
return this;
}
/** Applies a multiplier to the species ranged attack max cooldown. */
public Builder multiplyRangedMaxCooldown( float value ) { return rangedCooldown( Math.round( rangedAttackMaxCooldown * value ) ); }
/** Sets the species ranged attack max cooldown. */
public Builder rangedMaxCooldown( int value ) {
if( owningSpecies.specialVariantName != null && rangedAttackMaxCooldown < 0 )
throw new IllegalStateException( "Attempted to add inapplicable ranged attack stat!" );
rangedAttackMaxCooldown = value;
return this;
}
/** Applies a multiplier to the species ranged attack max range. */
public Builder multiplyRangedMaxRange( double value ) { return rangedMaxRange( rangedAttackMaxRange * value ); }
/** Sets the species ranged attack max range. */
public Builder rangedMaxRange( double value ) {
if( owningSpecies.specialVariantName != null && rangedAttackMaxRange < 0.0 )
throw new IllegalStateException( "Attempted to add inapplicable ranged attack stat!" );
rangedAttackMaxRange = value;
return this;
}
//--------------- Attribute Changes ----------------
/** Adds a flat value to the base attribute. Not applicable for the movement speed attribute, use a multiplier instead. */
public Builder addToAttribute( Attribute attribute, double value ) {
if( attribute == Attributes.MOVEMENT_SPEED )
throw new IllegalArgumentException( "Do not add flat movement speed!" );
attributes.add( AttributeEntry.add( attribute, value ) );
return this;
}
/** Adds a value multiplier to the base attribute. */
public Builder multiplyAttribute( Attribute attribute, double value ) {
attributes.add( AttributeEntry.mult( attribute, value ) );
return this;
}
}
}

View file

@ -1,5 +1,7 @@
package fathertoast.specialmobs.common.bestiary;
import fathertoast.specialmobs.common.config.family.*;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.core.register.SMEntities;
import fathertoast.specialmobs.common.core.register.SMItems;
import fathertoast.specialmobs.common.util.AnnotationHelper;
@ -17,6 +19,7 @@ import net.minecraftforge.fml.RegistryObject;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.*;
import java.util.function.Function;
/**
* Special mobs are broken up into distinct 'families', each of which correspond to a type of vanilla mob that can be
@ -26,90 +29,90 @@ import java.util.*;
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public class MobFamily<T extends LivingEntity> {
public class MobFamily<T extends LivingEntity, V extends FamilyConfig> {
/** List of all families, generated to make iteration possible. */
private static final List<MobFamily<?>> FAMILY_LIST = new ArrayList<>();
private static final List<MobFamily<?, ?>> FAMILY_LIST = new ArrayList<>();
/** List of all species, generated to make iteration more convenient. */
private static final List<Species<?>> SPECIES_LIST;
/** Maps each replaceable entity type to the family that replaces it. */
private static final Map<EntityType<?>, MobFamily<?>> TYPE_TO_FAMILY_MAP;
private static final Map<EntityType<?>, MobFamily<?, ?>> TYPE_TO_FAMILY_MAP;
// NOTE: When adding a new mob family, do not forget to also register its renderer in the client register!
public static final MobFamily<CreeperEntity> CREEPER = new MobFamily<>(
public static final MobFamily<CreeperEntity, CreeperFamilyConfig> CREEPER = new MobFamily<>( CreeperFamilyConfig::new,
"Creeper", "creepers", 0x0DA70B, new EntityType[] { EntityType.CREEPER },
"Dark", "Death", "Dirt", "Doom", "Drowning", "Ender", "Fire", "Gravel", "Jumping", "Lightning",
"Mini", /*"Scope",*/ "Skeleton", "Splitting"
);
public static final MobFamily<ZombieEntity> ZOMBIE = new MobFamily<>(
public static final MobFamily<ZombieEntity, FamilyConfig> ZOMBIE = new MobFamily<>( FamilyConfig::new,
"Zombie", "zombies", 0x00AFAF, new EntityType[] { EntityType.ZOMBIE, EntityType.HUSK },
"Brute", "Fire", /*"Fishing",*/ "Giant", "Hungry", "Husk", "MadScientist", "Plague"
);
// TODO Drowned family and zombie transform mechanic
public static final MobFamily<ZombifiedPiglinEntity> ZOMBIFIED_PIGLIN = new MobFamily<>(//TODO figure out crossbows
public static final MobFamily<ZombifiedPiglinEntity, FamilyConfig> ZOMBIFIED_PIGLIN = new MobFamily<>( FamilyConfig::new,
"ZombifiedPiglin", "zombified piglins", 0xEA9393, new EntityType[] { EntityType.ZOMBIFIED_PIGLIN },
"Brute", /*"Fishing",*/ "Giant", "Hungry", "Knight", "Plague", "Vampire"
"Brute", /*"Fishing",*/ "Giant", "Hungry", "Knight", "Plague", "Vampire"//TODO figure out crossbows
);
public static final MobFamily<AbstractSkeletonEntity> SKELETON = new MobFamily<>(
public static final MobFamily<AbstractSkeletonEntity, SkeletonFamilyConfig> SKELETON = new MobFamily<>( SkeletonFamilyConfig::new,
"Skeleton", "skeletons", 0xC1C1C1, new EntityType[] { EntityType.SKELETON, EntityType.STRAY },
"Brute", "Fire", "Gatling", "Giant", "Knight", "Ninja", "Poison", "Sniper", "Spitfire", "Stray"
);
public static final MobFamily<AbstractSkeletonEntity> WITHER_SKELETON = new MobFamily<>(
public static final MobFamily<AbstractSkeletonEntity, SkeletonFamilyConfig> WITHER_SKELETON = new MobFamily<>( SkeletonFamilyConfig::new,
"WitherSkeleton", "wither skeletons", 0x141414, new EntityType[] { EntityType.WITHER_SKELETON },
"Brute", "Gatling", "Giant", "Knight", "Ninja", "Sniper", "Spitfire"
);
public static final MobFamily<SlimeEntity> SLIME = new MobFamily<>(
public static final MobFamily<SlimeEntity, SlimeFamilyConfig> SLIME = new MobFamily<>( SlimeFamilyConfig::new,
"Slime", "slimes", 0x51A03E, new EntityType[] { EntityType.SLIME },
"Blackberry", "Blueberry", "Caramel", "Grape", "Lemon", "Strawberry", "Watermelon"
);
public static final MobFamily<MagmaCubeEntity> MAGMA_CUBE = new MobFamily<>(
public static final MobFamily<MagmaCubeEntity, FamilyConfig> MAGMA_CUBE = new MobFamily<>( FamilyConfig::new,
"MagmaCube", "magma cubes", 0x340000, new EntityType[] { EntityType.MAGMA_CUBE },
"Bouncing", "Hardened", "Sticky", "Volatile"
);
public static final MobFamily<SpiderEntity> SPIDER = new MobFamily<>(
public static final MobFamily<SpiderEntity, FamilyConfig> SPIDER = new MobFamily<>( FamilyConfig::new,
"Spider", "spiders", 0x342D27, new EntityType[] { EntityType.SPIDER },
"Baby", "Desert", "Flying", "Giant", "Hungry", "Mother", "Pale", "Poison", /*"Water",*/ "Web", "Witch"
);
public static final MobFamily<CaveSpiderEntity> CAVE_SPIDER = new MobFamily<>(
public static final MobFamily<CaveSpiderEntity, FamilyConfig> CAVE_SPIDER = new MobFamily<>( FamilyConfig::new,
"CaveSpider", "cave spiders", 0x0C424E, new EntityType[] { EntityType.CAVE_SPIDER },
"Baby", "Flying", "Mother", /*"Water",*/ "Web", "Witch"
);
public static final MobFamily<SilverfishEntity> SILVERFISH = new MobFamily<>(
public static final MobFamily<SilverfishEntity, SilverfishFamilyConfig> SILVERFISH = new MobFamily<>( SilverfishFamilyConfig::new,
"Silverfish", "silverfish", 0x6E6E6E, new EntityType[] { EntityType.SILVERFISH },
"Blinding", /*"Fishing",*/ "Flying", "Poison", /*"Puffer",*/ "Tough"
);
public static final MobFamily<EndermanEntity> ENDERMAN = new MobFamily<>(
public static final MobFamily<EndermanEntity, FamilyConfig> ENDERMAN = new MobFamily<>( FamilyConfig::new,
"Enderman", "endermen", 0x161616, new EntityType[] { EntityType.ENDERMAN },
"Blinding", "Icy", "Lightning", "Mini", "Mirage", "Thief"
);
public static final MobFamily<WitchEntity> WITCH = new MobFamily<>(
public static final MobFamily<WitchEntity, WitchFamilyConfig> WITCH = new MobFamily<>( WitchFamilyConfig::new,
"Witch", "witches", 0x340000, new EntityType[] { EntityType.WITCH },
"Domination", "Shadows", "Undead", "Wilds", "Wind"
);
public static final MobFamily<GhastEntity> GHAST = new MobFamily<>(
public static final MobFamily<GhastEntity, GhastFamilyConfig> GHAST = new MobFamily<>( GhastFamilyConfig::new,
"Ghast", "ghasts", 0xF9F9F9, new EntityType[] { EntityType.GHAST },
"Baby", "Fighter", "King", "Queen", "Unholy", "CorporealShift"
);
public static final MobFamily<BlazeEntity> BLAZE = new MobFamily<>(
public static final MobFamily<BlazeEntity, FamilyConfig> BLAZE = new MobFamily<>( FamilyConfig::new,
"Blaze", "blazes", 0xF6B201, new EntityType[] { EntityType.BLAZE },
"Cinder", "Conflagration", "Ember", "Hellfire", "Inferno", "Jolt", "Wildfire"
);
static {
final HashMap<EntityType<?>, MobFamily<?>> classToFamilyMap = new HashMap<>();
final HashMap<EntityType<?>, MobFamily<?, ?>> classToFamilyMap = new HashMap<>();
final ArrayList<Species<?>> allSpecies = new ArrayList<>();
for( MobFamily<?> family : FAMILY_LIST ) {
for( MobFamily<?, ?> family : FAMILY_LIST ) {
for( EntityType<?> replaceable : family.replaceableTypes )
classToFamilyMap.put( replaceable, family );
@ -126,23 +129,23 @@ public class MobFamily<T extends LivingEntity> {
public static void initBestiary() { }
/** @return A list of all families. */
public static List<MobFamily<?>> getAll() { return Collections.unmodifiableList( FAMILY_LIST ); }
public static List<MobFamily<?, ?>> getAll() { return Collections.unmodifiableList( FAMILY_LIST ); }
/** @return A list of all species. */
public static List<Species<?>> getAllSpecies() { return SPECIES_LIST; }
/** @return The family of mobs that can replace the passed entity; returns null if the entity is not replaceable. */
@Nullable
public static MobFamily<?> getReplacementFamily( LivingEntity entity ) {
public static MobFamily<?, ?> getReplacementFamily( LivingEntity entity ) {
return TYPE_TO_FAMILY_MAP.get( entity.getType() );
}
/** The technical name that refers to this family. Note that this is UpperCamelCase, but is often used in lowercase. */
public final String name;
/** The name used to refer to this family in unlocalized situations; e.g. config comments. */
/** The plural name used to refer to this family in unlocalized situations; e.g. config comments. */
public final String configName;
/** The base egg color for species in this family. Species' eggs differ visually only by spot color. */
/** The base color for spawn eggs of species in this family. Species' eggs differ visually only by spot color. */
public final int eggBaseColor;
/** List of replaceable entity types. The vanilla replacement's entity type is based on the first entry in this array. */
@ -156,25 +159,31 @@ public class MobFamily<T extends LivingEntity> {
/** Array of all special variant species in this family. In practice, these are the true "special mobs". */
public final Species<? extends T>[] variants;
//public Config.FamilyConfig config;
/** This family's config. */
public final V config;
private MobFamily( String familyName, String familyKey, int eggColor, EntityType<?>[] replaceable,
private MobFamily( Function<MobFamily<?, ?>, V> configSupplier,
String familyName, String readableName, int eggColor, EntityType<?>[] replaceable,
String... variantNames ) {
name = familyName;
configName = familyKey;
configName = readableName.toLowerCase( Locale.ROOT );
eggBaseColor = eggColor;
replaceableTypes = replaceable;
if( replaceable.length < 1 )
throw new IllegalArgumentException( familyName + " family must have at least one replaceable type!" );
final String packageRoot = References.ENTITY_PACKAGE + name.toLowerCase() + ".";
final String packageRoot = References.ENTITY_PACKAGE + name.toLowerCase();
vanillaReplacement = new Species<>( this, packageRoot, null );
//noinspection unchecked
variants = new Species[variantNames.length];
for( int i = 0; i < variants.length; i++ ) {
variants[i] = new Species<>( this, packageRoot, variantNames[i] );
}
config = configSupplier.apply( this );
config.SPEC.initialize();
// We register here because otherwise there's no way to find all families
FAMILY_LIST.add( this );
}
@ -223,7 +232,7 @@ public class MobFamily<T extends LivingEntity> {
@MethodsReturnNonnullByDefault
public static class Species<T extends LivingEntity> {
/** The special mob family this species belongs to. */
public final MobFamily<? super T> family;
public final MobFamily<? super T, ?> family;
/** The name of this special variant; or null for vanilla replacement species. */
public final String specialVariantName;
/** The technical name that refers to this species. Note that this is UpperCamelCase, but is often used in lowercase. */
@ -231,7 +240,7 @@ public class MobFamily<T extends LivingEntity> {
/** The entity class for this species. */
public final Class<T> entityClass;
/** The bestiary info describing this species */
/** The bestiary info describing this species. */
public final BestiaryInfo bestiaryInfo;
/** This species's entity type, wrapped in its registry object. */
@ -239,8 +248,11 @@ public class MobFamily<T extends LivingEntity> {
/** This species's spawn egg item, wrapped in its registry object. */
public final RegistryObject<ForgeSpawnEggItem> spawnEgg;
/** This species's config. */
public final SpeciesConfig config;
/** Constructs a new mob species. For vanilla replacements, the variant name is null. */
private Species( MobFamily<? super T> parentFamily, String packageRoot, @Nullable String variantName ) {
private Species( MobFamily<? super T, ?> parentFamily, String packageRoot, @Nullable String variantName ) {
final boolean vanillaReplacement = variantName == null;
family = parentFamily;
@ -253,14 +265,19 @@ public class MobFamily<T extends LivingEntity> {
// Below require variant class to be defined
final EntityType.Builder<T> entityTypeBuilder = makeEntityTypeBuilder( parentFamily.replaceableTypes[0] );
bestiaryInfo = AnnotationHelper.getBestiaryInfo( this, entityTypeBuilder );
bestiaryInfo = AnnotationHelper.getBestiaryInfo( this, BestiaryInfo.of( this, entityTypeBuilder ) ).build();
// Initialize deferred registry objects
entityType = SMEntities.register( name.toLowerCase( Locale.ROOT ), entityTypeBuilder );
spawnEgg = SMItems.registerSpawnEgg( entityType, parentFamily.eggBaseColor, bestiaryInfo.eggSpotsColor );
// Config uses bestiary info for default values
config = AnnotationHelper.createConfig( this );
config.SPEC.initialize();
// Register this species with the entity class
AnnotationHelper.injectSpeciesReference( this );
AnnotationHelper.verifySpeciesSupplier( this );
}
/** Finds the entity class based on a standard format. */

View file

@ -1,5 +1,7 @@
package fathertoast.specialmobs.common.bestiary;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -14,50 +16,77 @@ import java.lang.annotation.Target;
*/
@Target( ElementType.TYPE )
public @interface SpecialMob {
/**
* REQUIRED. This is injected with a reference to the species during registration so you may access it later, as needed.
* <p>
* The annotated field must have a signature that follows the pattern:
* <p>
* public static MobFamily.Species<T> FIELD_NAME;
* {@code public static MobFamily.Species<T> FIELD_NAME;}
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.FIELD )
@interface SpeciesReference { }
/**
* REQUIRED. This is called during registration to collect static properties of the mob needed for the bestiary
* and for building the species's entity type.
* This is not 'overridable' because all species must have unique info in the bestiary.
* REQUIRED. This is used to enforce overriding {@link ISpecialMob#getSpecies()} in every species entity class.
* <p>
* The annotated method must have a signature that follows the pattern:
* <p>
* public static BestiaryInfo METHOD_NAME( EntityType.Builder<LivingEntity> entityType )
* {@code public MobFamily.Species<? extends T> getSpecies();}
* <p>
* The returned bestiary info will be used to describe the mob species.
* The builder passed in is a copy of the vanilla 'base' entity type. Common uses of the entity type builder are
* modifying entity size and fire/hazard immunities.
* The returned species should be the field annotated with {@link SpecialMob.SpeciesReference}.
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )
@interface SpeciesSupplier { }
/**
* REQUIRED. This is called during registration to collect static properties of the mob needed for the bestiary
* and for building the species's entity type.
* This is not 'overridable' because all species must have unique info in the bestiary, however some info in the
* builder is automatically inherited.
* <p>
* The annotated method must have a signature that follows the pattern:
* <p>
* {@code public static void METHOD_NAME( BestiaryInfo.Builder bestiaryInfo )}
* <p>
* The provided bestiary info builder is initialized with as much data from the parent entity as possible.
* The only builder method that absolutely must be called is #color( int ). This will likely later expand to desc.
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )
@interface BestiaryInfoSupplier { }
/**
* OVERRIDABLE. This is called during registration to build the base attributes for the species.
* 'Overridable' static methods inherit from their superclass if not defined in a subclass, but must be defined somewhere.
* This is 'overridable' because not all species need to have different attributes from their parent vanilla mob.
* OPTIONAL-OVERRIDABLE. This is called during registration to generate the species config.
* 'Overridable' static methods inherit from their superclass if not defined in a subclass.
* This is 'overridable' because not all species (or even families) require unique config categories.
* <p>
* The annotated method must have a signature that follows the pattern:
* <p>
* public static AttributeModifierMap.MutableAttribute METHOD_NAME()
* {@code public static SpeciesConfig METHOD_NAME( MobFamily.Species<?> species )}
* <p>
* The returned config will be loaded immediately after the call and a reference will be stored in the species.
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )
@interface ConfigSupplier { }
/**
* OVERRIDABLE. This is called during registration to build the base attributes for the species.
* 'Overridable' static methods inherit from their superclass if not defined in a subclass, but must be defined somewhere.
* This is 'overridable' because a few species have a different parent vanilla mob from the rest of their family.
* <p>
* The annotated method must have a signature that follows the pattern:
* <p>
* {@code public static AttributeModifierMap.MutableAttribute METHOD_NAME()}
* <p>
* The returned attribute modifier map builder will be 'built' immediately after the call and registered to be
* applied to all entity class instances of the mob species.
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )
@interface AttributeCreator { }
@interface AttributeSupplier { }
/**
* REQUIRED. This is called during data generation to build the mod's default lang files.
@ -65,7 +94,7 @@ public @interface SpecialMob {
* <p>
* The annotated method must have a signature that follows the pattern:
* <p>
* public static String[] METHOD_NAME( String langKey )
* {@code public static String[] METHOD_NAME( String langKey )}
* <p>
* The returned string array should be created by References#translations using the given lang key as the first
* argument. Always be sure that any non-ASCII characters used are properly handled by the translations method.
@ -83,7 +112,7 @@ public @interface SpecialMob {
* <p>
* The annotated method must have a signature that follows the pattern:
* <p>
* public static void METHOD_NAME( LootTableBuilder loot )
* {@code public static void METHOD_NAME( LootTableBuilder loot )}
* <p>
* The builder passed in is a new empty instance and will be 'built' immediately after the call.
*/
@ -98,7 +127,7 @@ public @interface SpecialMob {
* <p>
* The annotated method must have a signature that follows the pattern:
* <p>
* public static EntityType.IFactory<T> METHOD_NAME()
* {@code public static EntityType.IFactory<T> METHOD_NAME()}
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )

View file

@ -35,7 +35,7 @@ public class Config {
/** The spec used by this config that defines the file's format. */
public final ToastConfigSpec SPEC;
AbstractConfig( File dir, String fileName, String... fileDescription ) {
public AbstractConfig( File dir, String fileName, String... fileDescription ) {
AbstractConfigField.loadingCategory = "";
SPEC = new ToastConfigSpec( dir, fileName );
SPEC.header( TomlHelper.newComment( fileDescription ) );
@ -50,17 +50,10 @@ public class Config {
/** The spec used by this config that defines the file's format. */
protected final ToastConfigSpec SPEC;
AbstractCategory( ToastConfigSpec parent, String name, String... categoryDescription ) {
public AbstractCategory( ToastConfigSpec parent, String name, String... categoryDescription ) {
AbstractConfigField.loadingCategory = name + ".";
SPEC = parent;
SPEC.category( name, TomlHelper.newComment( categoryDescription ) );
}
}
/** The plus or minus symbol (+/-). */
public static final String PLUS_OR_MINUS = "\u00b1";
/** The less than or equal to symbol (<=). */
public static final String LESS_OR_EQUAL = "\u2264";
/** The greater than or equal to symbol (>=). */
public static final String GREATER_OR_EQUAL = "\u2265";
}

View file

@ -2,6 +2,7 @@ package fathertoast.specialmobs.common.config;
import fathertoast.specialmobs.common.config.field.*;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import java.io.File;
@ -12,58 +13,61 @@ public class MainConfig extends Config.AbstractConfig {
/** Builds the config spec that should be used for this config. */
MainConfig( File dir, String fileName ) {
super( dir, fileName,
"This config contains options that apply to the mod as a whole, including some master disable",
"toggles for convenience."
);
"This config contains options that apply to the mod as a whole, including some master settings",
"toggles for convenience.",
"",
"Terminology used in Special Mobs configs:",
" * Mob - An entity that is 'alive', short for \"Mobile\" or MobEntity.",
" * Family - The group of mobs based on (but not including) a particular vanilla mob; e.g., Creepers.",
" * Species - A specific type of mob within a family; e.g., Fire Creepers or vanilla-replacement Creepers.",
" * Vanilla Replacement - The one species within a family that is intended to be a replica of the base vanilla mob.",
" * Special Variant - Any species that is not the family's vanilla replacement. Includes species that are",
" replicas of 'vanilla special variants'; i.e. Husks and Strays.",
" * Mob Replacer - The tool that watches vanilla mob spawns and cancels them to spawn this mod's entities." );
GENERAL = new General( SPEC );
}
public static class General extends Config.AbstractCategory {
public final IntField updateTime;
public final BooleanField enableMobReplacement;
public final InjectionWrapperField<BooleanField> defaultGameRuleNoRegen;
public final BooleanField masterVanillaReplacement;
public final DoubleField masterRandomScaling;
public final BooleanField foodExtraTooltipInfo;
public final BooleanField disableAbsorptionFeatures;
public final BooleanField disableHealthFeatures;
//public final BooleanField disableHungerFeatures; // NOTE: If/when hunger features are added, also move food configs from health
public final BooleanField enableNausea;
public final BooleanField fancyFishingMobs;
General( ToastConfigSpec parent ) {
super( parent, "general",
"Options for customizing the mod as a whole." );
"Options that apply to the Special Mobs mod as a whole.",
"Also includes several 'master toggles' for convenience." );
updateTime = SPEC.define( new IntField( "update_time", 5, IntField.Range.POSITIVE,
"The number of ticks between this mod's logic/recovery updates (20 ticks = 1 second)." ) );
enableMobReplacement = SPEC.define( new BooleanField( "enable_mob_replacer", true,
"Whether the Mob Replacer is enabled. This 'hijacks' vanilla mob spawns to use as its own.",
"The Mob Replacer is the traditional spawn method for Special Mobs which allows everything that spawns",
"valid vanilla mobs (e.g. dungeon spawners) to spawn this mod's mobs based on your configs instead." ) );
SPEC.newLine();
defaultGameRuleNoRegen = SPEC.define( new InjectionWrapperField<>(
new BooleanField( "default_regen_game_rule_disabled", true,
"When set to true, this mod will alter the vanilla regeneration game rule \"naturalRegeneration\" to",
"be \"false\" by default when creating new worlds.",
"Regardless of this config setting, you can still create a world with vanilla health regen ON or OFF",
"by using the Game Rules button on the new world options screen or by using commands in-game." ),
( wrapped ) -> {
// Note, we are assuming the default is always true without this mod (ie, no other mod changes the default)
//GameRules.GAME_RULE_TYPES.put( GameRules.RULE_NATURAL_REGENERATION,
// GameRules.BooleanValue.create( !wrapped.get() ) );
} ) );
masterVanillaReplacement = SPEC.define( new BooleanField( "master_vanilla_replacement", true,
"Whether the mod uses Special Mobs entities in place of vanilla entities for non-special species.",
"This allows your config options to apply to non-special species and allows them to benefit from",
"Special Mob Data and any improvements made to the entity (for example, zombies can use bows & shields).",
"If false, vanilla replacements are disabled for all families; if true, it is determined by the family's config." ) );
masterRandomScaling = SPEC.define( new DoubleField( "master_random_scaling", 0.07, DoubleField.Range.PERCENT,
"When greater than 0, mobs will have a random render scale applied. This is a visual effect only.",
"For example, with a value of 0.07, mob scale will vary " + ConfigUtil.PLUS_OR_MINUS + "7% of normal size.",
"By default, this applies to all mobs in the mod; but family and species configs can override it." ) );
SPEC.newLine();
foodExtraTooltipInfo = SPEC.define( new BooleanField( "food.extra_tooltip_info", true,
"Set to true to display nutritional value on the tooltips of food items.",
"Lists hunger and saturation that can be restored from eating. (See health config for healing display.)" ) );
SPEC.newLine();
disableAbsorptionFeatures = SPEC.define( new BooleanField( "disable_absorption_features", false,
"If set to 'true', disables all features in this mod related to absorption (yellow hearts)." ) );
disableHealthFeatures = SPEC.define( new BooleanField( "disable_health_features", false,
"If set to 'true', disables all features in this mod related to health (red hearts)." ) );
enableNausea = SPEC.define( new BooleanField( "enable_nausea_effects", true,
"Set to false to prevent any of this mod's mobs from applying nausea (aka confusion).",
"Use this if the screen warping from nausea hurts your face or makes you sick." ) );
fancyFishingMobs = SPEC.define( new BooleanField( "fancy_fishing_mobs", true,
"Overrides the default fishing rod item animation so that it is compatible with fishing mobs from this mod.",
"Set to false if it causes problems with another mod. Fishing mobs will instead render a stick while casting." ) );
}
}
}

View file

@ -0,0 +1,43 @@
package fathertoast.specialmobs.common.config.family;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the family config for creepers.
*/
public class CreeperFamilyConfig extends FamilyConfig {
public final Creepers CREEPERS;
/** Builds the config spec that should be used for this config. */
public CreeperFamilyConfig( MobFamily<?, ?> family ) {
super( family );
CREEPERS = new Creepers( SPEC, family );
}
public static class Creepers extends Config.AbstractCategory {
public final DoubleField familyStormChargeChance;
public final DoubleField superchargeChance;
Creepers( ToastConfigSpec parent, MobFamily<?, ?> family ) {
super( parent, ConfigUtil.noSpaces( family.configName ),
"Options specific to the family of " + family.configName + "." );
familyStormChargeChance = SPEC.define( new DoubleField( "family_storm_charge_chance", 0.01, DoubleField.Range.PERCENT,
"Chance for " + family.configName + " to spawn charged during thunderstorms.",
"By default, this applies to all " + family.configName + "; but species configs can override it." ) );
SPEC.newLine();
superchargeChance = SPEC.define( new DoubleField( "supercharge_chance", 0.1, DoubleField.Range.PERCENT,
"Chance for " + family.configName + " to become supercharged when charged in any way." ) );
}
}
}

View file

@ -0,0 +1,87 @@
package fathertoast.specialmobs.common.config.family;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.BooleanField;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import java.io.File;
import java.util.List;
/**
* This is the base config for mob families. This may be extended to add categories specific to the family, but all
* options that are used by all families should be defined in this class.
*/
public class FamilyConfig extends Config.AbstractConfig {
public static File dir( MobFamily<?, ?> family ) { return new File( Config.CONFIG_DIR, ConfigUtil.noSpaces( family.configName ) ); }
protected static String fileName( MobFamily<?, ?> family ) { return "_family_of_" + ConfigUtil.noSpaces( family.configName ); }
/** Category containing all options applicable to mob families as a whole; i.e. not specific to any particular family. */
public final General GENERAL;
/** Builds the config spec that should be used for this config. */
public FamilyConfig( MobFamily<?, ?> family ) {
super( dir( family ), fileName( family ),
"This config contains options that apply to the family of " + family.configName + " as a whole;",
"that is, the vanilla replacement and all special variants." );
GENERAL = new General( SPEC, family );
}
public static class General extends Config.AbstractCategory {
public final BooleanField vanillaReplacement;
public final DoubleField familyRandomScaling;
public final DoubleField specialVariantChance;
public final IntField[] specialVariantWeights;
General( ToastConfigSpec parent, MobFamily<?, ?> family ) {
super( parent, "general",
"Options standard to all mob families (that is, not specific to any particular mob family)." );
vanillaReplacement = SPEC.define( new BooleanField( "vanilla_replacement", true,
"Whether this mob family replaces vanilla " + family.configName + " with its vanilla replacement species.",
"The \"master_vanilla_replacement\" setting in the mod's main config must also be true for this to work." ) );
SPEC.newLine();
familyRandomScaling = SPEC.define( new DoubleField( "family_random_scaling", -1.0, DoubleField.Range.SIGNED_PERCENT,
"When greater than 0, " + family.configName + " will have a random render scale applied. This is a visual effect only.",
"If this is set to a non-negative value, it overrides the value set for \"master_random_scaling\", though",
"species configs can override this value." ) );
SPEC.newLine();
specialVariantChance = SPEC.define( new DoubleField( "special_variant_chance", 0.25, DoubleField.Range.PERCENT,
"The chance for " + family.configName + " to spawn as special variants." ) );
// TODO special variant chance exceptions
SPEC.newLine();
List<String> comment;
// TODO consider wrapping this all up into an 'environment sensitive weighted list' config object? seems handy
comment = TomlHelper.newComment(
"The weight of each " + ConfigUtil.camelCaseToLowerSpace( family.name ) + " species to be chosen as the replacement when",
family.configName + " spawn as special variants. Higher weight is more common." );
comment.add( TomlHelper.multiFieldInfo( IntField.Range.NON_NEGATIVE ) );
SPEC.comment( comment );
specialVariantWeights = new IntField[family.variants.length];
for( int i = 0; i < specialVariantWeights.length; i++ ) {
specialVariantWeights[i] = SPEC.define( new IntField(
"weight." + ConfigUtil.camelCaseToLowerUnderscore( family.variants[i].specialVariantName ),
family.variants[i].bestiaryInfo.defaultWeight.value, IntField.Range.NON_NEGATIVE, (String[]) null ) );
}
// TODO special variant weights exceptions
}
}
}

View file

@ -0,0 +1,36 @@
package fathertoast.specialmobs.common.config.family;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.BooleanField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the family config for ghasts.
*/
public class GhastFamilyConfig extends FamilyConfig {
public final Ghasts GHASTS;
/** Builds the config spec that should be used for this config. */
public GhastFamilyConfig( MobFamily<?, ?> family ) {
super( family );
GHASTS = new Ghasts( SPEC, family );
}
public static class Ghasts extends Config.AbstractCategory {
public final BooleanField allowVerticalTargeting;
Ghasts( ToastConfigSpec parent, MobFamily<?, ?> family ) {
super( parent, ConfigUtil.noSpaces( family.configName ),
"Options specific to the family of " + family.configName + "." );
allowVerticalTargeting = SPEC.define( new BooleanField( "allow_vertical_targeting", true,
"If true, " + family.configName + " will be allowed to target any visible player in their follow range.",
"As of MC 1.8, vanilla ghasts can only start targeting players nearly completely horizontal from them." ) );
}
}
}

View file

@ -0,0 +1,36 @@
package fathertoast.specialmobs.common.config.family;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the family config for silverfish.
*/
public class SilverfishFamilyConfig extends FamilyConfig {
public final Silverfish SILVERFISH;
/** Builds the config spec that should be used for this config. */
public SilverfishFamilyConfig( MobFamily<?, ?> family ) {
super( family );
SILVERFISH = new Silverfish( SPEC, family );
}
public static class Silverfish extends Config.AbstractCategory {
public final DoubleField familyAggressiveChance;
Silverfish( ToastConfigSpec parent, MobFamily<?, ?> family ) {
super( parent, ConfigUtil.noSpaces( family.configName ),
"Options specific to the family of " + family.configName + "." );
familyAggressiveChance = SPEC.define( new DoubleField( "family_aggressive_chance", 0.05, DoubleField.Range.PERCENT,
"Chance for " + family.configName + " to spawn already calling for reinforcements.",
"By default, this applies to all " + family.configName + "; but species configs can override it." ) );
}
}
}

View file

@ -0,0 +1,36 @@
package fathertoast.specialmobs.common.config.family;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the family config for skeletons and wither skeletons.
*/
public class SkeletonFamilyConfig extends FamilyConfig {
public final Skeletons SKELETONS;
/** Builds the config spec that should be used for this config. */
public SkeletonFamilyConfig( MobFamily<?, ?> family ) {
super( family );
SKELETONS = new Skeletons( SPEC, family );
}
public static class Skeletons extends Config.AbstractCategory {
public final DoubleField babyChance;
Skeletons( ToastConfigSpec parent, MobFamily<?, ?> family ) {
super( parent, ConfigUtil.noSpaces( family.configName ),
"Options specific to the family of " + family.configName + "." );
babyChance = SPEC.define( new DoubleField( "baby_chance", 0.05, DoubleField.Range.PERCENT,
"Chance for valid " + family.configName + " to spawn as babies. Baby mobs are about half-sized,",
"move 50% faster, drop 150% more experience, and are 50% cuter." ) );
}
}
}

View file

@ -0,0 +1,35 @@
package fathertoast.specialmobs.common.config.family;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.BooleanField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the family config for slimes (NOT magma cubes).
*/
public class SlimeFamilyConfig extends FamilyConfig {
public final Slimes SLIMES;
/** Builds the config spec that should be used for this config. */
public SlimeFamilyConfig( MobFamily<?, ?> family ) {
super( family );
SLIMES = new Slimes( SPEC, family );
}
public static class Slimes extends Config.AbstractCategory {
public final BooleanField tinySlimesDealDamage;
Slimes( ToastConfigSpec parent, MobFamily<?, ?> family ) {
super( parent, ConfigUtil.noSpaces( family.configName ),
"Options specific to the family of " + family.configName + "." );
tinySlimesDealDamage = SPEC.define( new BooleanField( "tiny_slimes_deal_damage", true,
"If true, the smallest size " + family.configName + " will be allowed to deal damage (unlike vanilla)." ) );
}
}
}

View file

@ -0,0 +1,36 @@
package fathertoast.specialmobs.common.config.family;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.BooleanField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the family config for witches.
*/
public class WitchFamilyConfig extends FamilyConfig {
public final Witches WITCHES;
/** Builds the config spec that should be used for this config. */
public WitchFamilyConfig( MobFamily<?, ?> family ) {
super( family );
WITCHES = new Witches( SPEC, family );
}
public static class Witches extends Config.AbstractCategory {
public final BooleanField useSplashSwiftness;
Witches( ToastConfigSpec parent, MobFamily<?, ?> family ) {
super( parent, ConfigUtil.noSpaces( family.configName ),
"Options specific to the family of " + family.configName + "." );
useSplashSwiftness = SPEC.define( new BooleanField( "use_splash_swiftness", true,
"If true, " + family.configName + " will use splash potions of swiftness when trying to keep up,",
"rather than drinking a swiftness potion. Helps them keep up just a little better than vanilla." ) );
}
}
}

View file

@ -0,0 +1,146 @@
package fathertoast.specialmobs.common.config.field;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.config.util.AttributeEntry;
import fathertoast.specialmobs.common.config.util.AttributeList;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a config field with an entity list value.
*/
public class AttributeListField extends GenericField<AttributeList> {
/** Provides a detailed description of how to use attribute lists. Recommended to put at the top of any file using attribute lists. */
public static List<String> verboseDescription() {
List<String> comment = new ArrayList<>();
comment.add( "Attribute List fields: General format = [ \"namespace:attribute_name operation value\", ... ]" );
comment.add( " Attribute lists are arrays of base attribute changes. Attributes are defined by their key in the attribute registry," );
comment.add( " usually following the pattern 'namespace:attribute_name'. The operations that can be performed are +, -, and *. The + and -" );
comment.add( " operators change the attribute by a flat value by addition/subtraction. The * operator changes the attribute by multiplication." );
comment.add( " Each entry in the attribute list is applied in the exact order listed." );
comment.add( " As an example, the entry \"minecraft:generic.max_health + 10.0\" will increase a mob's max health by 10. By convention, never" );
comment.add( " use the + or - operators for movement speed (minecraft:generic.movement_speed)." );
return comment;
}
/** Creates a new field. */
public AttributeListField( String key, AttributeList defaultValue, String... description ) {
super( key, defaultValue, description );
}
/** Applies all attribute changes in this list to the entity attribute builder. */
public void apply( AttributeModifierMap.MutableAttribute builder ) { get().apply( builder ); }
/** Applies all attribute changes in this list to the entity. */
public void apply( LivingEntity entity ) { get().apply( entity ); }
/** Adds info about the field type, format, and bounds to the end of a field's description. */
public void appendFieldInfo( List<String> comment ) {
comment.add( TomlHelper.fieldInfoFormat( "Attribute List", valueDefault,
"[ \"namespace:attribute_name operation value\", ... ]" ) );
comment.add( " Range for Values: " + TomlHelper.fieldRange( DoubleField.Range.ANY.MIN, DoubleField.Range.ANY.MAX ) );
}
/**
* Loads this field's value from the given raw toml value. If anything goes wrong, correct it at the lowest level possible.
* <p>
* For example, a missing value should be set to the default, while an out-of-range value should be adjusted to the
* nearest in-range value
*/
@Override
public void load( @Nullable Object raw ) {
if( raw == null ) {
value = valueDefault;
return;
}
List<String> list = TomlHelper.parseStringList( raw );
List<AttributeEntry> entryList = new ArrayList<>();
for( String line : list ) {
AttributeEntry entry = parseEntry( line );
if( entry != null ) {
entryList.add( entry );
}
}
value = new AttributeList( entryList );
}
/** Parses a single entry line and returns a valid result if possible, or null if the entry is completely invalid. */
@Nullable
private AttributeEntry parseEntry( final String line ) {
// Parse the attribute-operation-value array
final String[] args = line.split( " ", 4 );
if( args.length > 3 ) {
SpecialMobs.LOG.warn( "Entry has for {} \"{}\" is too long! Deleting excess. Invalid entry: {}",
getClass(), getKey(), line );
}
final Attribute attribute;
final ResourceLocation regKey = new ResourceLocation( args[0].trim() );
if( !ForgeRegistries.ATTRIBUTES.containsKey( regKey ) ) {
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Deleting entry. Invalid entry: {}",
getClass(), getKey(), line ); // TODO note: I don't know if attributes will be registered at this point
return null;
}
attribute = ForgeRegistries.ATTRIBUTES.getValue( regKey );
final int operator;
if( args.length < 2 ) {
SpecialMobs.LOG.warn( "Entry has no operator for {} \"{}\"! Replacing missing operator with +. Invalid entry: {}",
getClass(), getKey(), line );
operator = 1;
}
else {
switch( args[1] ) {
case "*": operator = 0;
break;
case "+": operator = 1;
break;
case "-": operator = -1;
break;
default: operator = 1;
SpecialMobs.LOG.warn( "Entry has invalid operator {} for {} \"{}\"! Replacing operator with +. " +
"Invalid entry: {}", args[1], getClass(), getKey(), line );
break;
}
}
final int identityValue = operator == 0 ? 1 : 0;
final double value;
if( args.length < 3 ) {
SpecialMobs.LOG.warn( "Entry has no value for {} \"{}\"! Replacing missing value with {}. Invalid entry: {}",
getClass(), getKey(), identityValue, line );
value = identityValue;
}
else {
value = parseValue( args[2], line, identityValue );
}
//noinspection ConstantConditions
return operator == 0 ? AttributeEntry.mult( attribute, value ) : AttributeEntry.add( attribute, value * operator );
}
/** Parses a single value argument and returns a valid result. */
private double parseValue( final String arg, final String line, final int identity ) {
// Try to parse the value
double value;
try {
value = Double.parseDouble( arg );
}
catch( NumberFormatException ex ) {
// This is thrown if the string is not a parsable number
SpecialMobs.LOG.warn( "Invalid value for {} \"{}\"! Falling back to {}. Invalid entry: {}",
getClass(), getKey(), identity, line );
value = identity;
}
return value;
}
}

View file

@ -14,9 +14,8 @@ import java.util.List;
@SuppressWarnings( "unused" )
public class BlockListField extends GenericField<BlockList> {
/** Provides a detailed description of how to use entity lists. Recommended putting at the top of any file using entity lists. */
@Override
public List<String> verboseDescription() {
/** Provides a detailed description of how to use block lists. Recommended putting at the top of any file using block lists. */
public static List<String> verboseDescription() {
List<String> comment = new ArrayList<>();
comment.add( "Block List fields: General format = [ \"namespace:block_name[property1=value1,...]\", ... ]" );
comment.add( " Block lists are arrays of blocks and partial block states." );

View file

@ -103,6 +103,8 @@ public class DoubleField extends AbstractConfigField {
NON_NEGATIVE( 0.0, Double.POSITIVE_INFINITY ),
/** Accepts any value between 0 and 1. */
PERCENT( 0.0, 1.0 ),
/** Accepts any value between -1 and 1. */
SIGNED_PERCENT( -1.0, 1.0 ),
/** Accepts any value between -1 and 2. */
DROP_CHANCE( -1.0, 2.0 );

View file

@ -22,8 +22,7 @@ public class EntityListField extends GenericField<EntityList> {
public static final String REG_KEY_DEFAULT = "default";
/** Provides a detailed description of how to use entity lists. Recommended to put at the top of any file using entity lists. */
@Override
public List<String> verboseDescription() {
public static List<String> verboseDescription() {
List<String> comment = new ArrayList<>();
comment.add( "Entity List fields: General format = [ \"namespace:entity_type value1 value2 ...\", ... ]" );
comment.add( " Entity lists are arrays of entity types. Some entity lists specify a number of values linked to each entity type." );

View file

@ -1,8 +1,5 @@
package fathertoast.specialmobs.common.config.field;
import javax.annotation.Nullable;
import java.util.List;
/**
* Represents a config field with an object value.
* <p>
@ -23,11 +20,6 @@ public abstract class GenericField<T> extends AbstractConfigField {
super( key, description );
valueDefault = defaultValue;
}
@Nullable
public List<String> verboseDescription() {
return null;
}
/** @return Returns the config field's value. */
public T get() { return value; }

View file

@ -0,0 +1,57 @@
package fathertoast.specialmobs.common.config.field;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.config.util.RegistryEntryList;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a config field with a registry entry list value.
* <p>
* See also: {@link net.minecraftforge.registries.ForgeRegistries}
*/
@SuppressWarnings( "unused" )
public class RegistryEntryListField<T extends IForgeRegistryEntry<T>> extends GenericField<RegistryEntryList<T>> {
/** Provides a detailed description of how to use registry entry lists. Recommended putting at the top of any file using registry entry lists. */
public static List<String> verboseDescription() {
List<String> comment = new ArrayList<>();
comment.add( "Registry Entry List fields: General format = [ \"namespace:entry_name\", ... ]" );
comment.add( " Registry entry lists are arrays of registry keys. Many things in the game, such as blocks or potions, are defined" );
comment.add( " by their registry key within a registry. For example, all items are registered in the \"minecraft:item\" registry." );
comment.add( " An asterisk '*' can be used to match multiple registry keys. For example, 'minecraft:*' will match all vanilla entries" );
comment.add( " within the registry entry list's target registry." );
return comment;
}
/** Creates a new field. */
public RegistryEntryListField( String key, RegistryEntryList<T> defaultValue, String... description ) {
super( key, defaultValue, description );
}
/** Adds info about the field type, format, and bounds to the end of a field's description. */
public void appendFieldInfo( List<String> comment ) {
comment.add( TomlHelper.fieldInfoFormat( "\"" + SpecialMobs.toString( valueDefault.getRegistry().getRegistryName() ) +
"\" Registry List", valueDefault, "[ \"namespace:entry_name\", ... ]" ) );
}
/**
* Loads this field's value from the given raw toml value. If anything goes wrong, correct it at the lowest level possible.
* <p>
* For example, a missing value should be set to the default, while an out-of-range value should be adjusted to the
* nearest in-range value
*/
@Override
public void load( @Nullable Object raw ) {
if( raw == null ) {
value = valueDefault;
return;
}
// All the actual loading is done through the objects
value = new RegistryEntryList<>( this, valueDefault.getRegistry(), TomlHelper.parseStringList( raw ) );
}
}

View file

@ -6,8 +6,7 @@ import com.electronwill.nightconfig.core.file.FileWatcher;
import com.electronwill.nightconfig.core.io.CharacterOutput;
import com.electronwill.nightconfig.core.io.ParsingException;
import com.electronwill.nightconfig.core.io.WritingException;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.GenericField;
import fathertoast.specialmobs.common.config.field.*;
import fathertoast.specialmobs.common.core.SpecialMobs;
import java.io.File;
@ -338,13 +337,17 @@ public class ToastConfigSpec {
/** @param comment The file comment to insert. */
public void header( List<String> comment ) { ACTIONS.add( new Header( this, comment ) ); }
/** Inserts a detailed description of how to use the given field. */
public void verboseFieldDesc( GenericField<?> field ) {
final List<String> description = field.verboseDescription();
if( description != null && !description.isEmpty() )
ACTIONS.add( new Comment( field.verboseDescription() ) );
}
/** Inserts a detailed description of how to use the registry entry list field. */
public void describeRegistryEntryList() { ACTIONS.add( new Comment( RegistryEntryListField.verboseDescription() ) ); }
/** Inserts a detailed description of how to use the entity list field. */
public void describeEntityList() { ACTIONS.add( new Comment( EntityListField.verboseDescription() ) ); }
/** Inserts a detailed description of how to use the attribute list field. */
public void describeAttributeList() { ACTIONS.add( new Comment( AttributeListField.verboseDescription() ) ); }
/** Inserts a detailed description of how to use the block list field. */
public void describeBlockList() { ACTIONS.add( new Comment( BlockListField.verboseDescription() ) ); }
/**
* @param name The category name.

View file

@ -2,16 +2,16 @@ package fathertoast.specialmobs.common.config.file;
import com.electronwill.nightconfig.core.NullObject;
import com.electronwill.nightconfig.core.utils.StringUtils;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class TomlHelper {
private TomlHelper() {} // This is a static access only class that cannot be instantiated
private TomlHelper() { } // This is a static access only class that cannot be instantiated
/** Attempts to convert a toml literal to a string list. May or may not be accurate. */
public static List<String> parseStringList( Object value ) {
@ -184,10 +184,10 @@ public final class TomlHelper {
private static String fieldRangeNoLimit() { return "Any Value"; }
/** @return A range representation of toml literals with only an upper limit. */
private static String fieldRangeUpperLimit( Number max ) { return Config.LESS_OR_EQUAL + " " + toLiteral( max ); }
private static String fieldRangeUpperLimit( Number max ) { return ConfigUtil.LESS_OR_EQUAL + " " + toLiteral( max ); }
/** @return A range representation of toml literals with only a lower limit. */
private static String fieldRangeLowerLimit( Number min ) { return Config.GREATER_OR_EQUAL + " " + toLiteral( min ); }
private static String fieldRangeLowerLimit( Number min ) { return ConfigUtil.GREATER_OR_EQUAL + " " + toLiteral( min ); }
/** @return A range representation of toml literals with both a lower and upper limit. */
private static String fieldRangeInterval( Number min, Number max ) { return toLiteral( min ) + " ~ " + toLiteral( max ); }

View file

@ -0,0 +1,38 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the base species config for blazes.
*/
public class BlazeSpeciesConfig extends SpeciesConfig {
public final Blazes BLAZES;
/** Builds the config spec that should be used for this config. */
public BlazeSpeciesConfig( MobFamily.Species<?> species, int fireballBurstCount, int fireballBurstDelay ) {
super( species );
BLAZES = new Blazes( SPEC, species, speciesName, fireballBurstCount, fireballBurstDelay );
}
public static class Blazes extends Config.AbstractCategory {
public final IntField fireballBurstCount;
public final IntField fireballBurstDelay;
Blazes( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName, int burstCount, int burstDelay ) {
super( parent, ConfigUtil.noSpaces( species.family.configName ),
"Options standard to all " + species.family.configName + "." );
fireballBurstCount = SPEC.define( new IntField( "fireball_attack.burst_count", burstCount, IntField.Range.NON_NEGATIVE,
"The number of fireballs " + speciesName + " launch with each burst." ) );
fireballBurstDelay = SPEC.define( new IntField( "fireball_attack.burst_delay", burstDelay, IntField.Range.NON_NEGATIVE,
"The time (in ticks) " + speciesName + " wait between each fireball in a burst. (20 ticks = 1 second)" ) );
}
}
}

View file

@ -0,0 +1,54 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.BooleanField;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the base species config for creepers.
*/
public class CreeperSpeciesConfig extends SpeciesConfig {
public final Creepers CREEPERS;
/** Builds the config spec that should be used for this config. */
public CreeperSpeciesConfig( MobFamily.Species<?> species,
boolean cannotExplodeWhileWet, boolean explodeWhileBurning, boolean explodeWhenShot ) {
super( species );
CREEPERS = new Creepers( SPEC, species, speciesName, cannotExplodeWhileWet, explodeWhileBurning, explodeWhenShot );
}
public static class Creepers extends Config.AbstractCategory {
public final DoubleField stormChargeChance;
/** Note that this is inverted from how it is normally seen and used elsewhere. This is to avoid double-negatives in the config. */
public final BooleanField canExplodeWhileWet;
public final BooleanField explodesWhileBurning;
public final BooleanField explodesWhenShot;
Creepers( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName,
boolean cannotExplodeWhileWet, boolean explodeWhileBurning, boolean explodeWhenShot ) {
super( parent, ConfigUtil.noSpaces( species.family.configName ),
"Options standard to all " + species.family.configName + "." );
stormChargeChance = SPEC.define( new DoubleField( "storm_charge_chance", -1.0, DoubleField.Range.SIGNED_PERCENT,
"Chance for " + speciesName + " to spawn charged during thunderstorms.",
"If this is set to a non-negative value, it overrides the value set for \"family_storm_charge_chance\"." ) );
SPEC.newLine();
canExplodeWhileWet = SPEC.define( new BooleanField( "can_explode_while_wet", !cannotExplodeWhileWet,
"If true, " + speciesName + " can explode while wet (normal creeper behavior)." ) );
explodesWhileBurning = SPEC.define( new BooleanField( "explodes_while_burning", explodeWhileBurning,
"If true, " + speciesName + " will explode while burning. If extinguished before the fuse runs",
"out, they will resume normal behavior." ) );
explodesWhenShot = SPEC.define( new BooleanField( "explodes_when_shot", explodeWhenShot,
"If true, " + speciesName + " will explode when hit by an indirect attack (e.g. an arrow)." ) );
}
}
}

View file

@ -0,0 +1,49 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
public class MotherSpiderSpeciesConfig extends SpiderSpeciesConfig {
public final Mother MOTHER;
/** Builds the config spec that should be used for this config. */
public MotherSpiderSpeciesConfig( MobFamily.Species<?> species, double spitChance,
int minBabies, int maxBabies, int minExtraBabies, int maxExtraBabies ) {
super( species, spitChance );
MOTHER = new Mother( SPEC, species, speciesName, minBabies, maxBabies, minExtraBabies, maxExtraBabies );
}
public static class Mother extends Config.AbstractCategory {
public final IntField.RandomRange babies;
public final IntField.RandomRange extraBabies;
Mother( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName,
int minBabies, int maxBabies, int minExtraBabies, int maxExtraBabies ) {
super( parent, ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ),
"Options specific to " + speciesName + "." );
babies = new IntField.RandomRange(
SPEC.define( new IntField( "babies.min", minBabies, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of babies " + speciesName + " spawn on death.",
"Any remaining 'extra babies' will added to the amount spawned on death (see below)." ) ),
SPEC.define( new IntField( "babies.max", maxBabies, IntField.Range.NON_NEGATIVE ) )
);
SPEC.newLine();
extraBabies = new IntField.RandomRange(
SPEC.define( new IntField( "extra_babies.min", minExtraBabies, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of babies that " + speciesName + " can spawn from hits before death.",
"Any remaining 'extra babies' will added to the amount spawned on death (see above)." ) ),
SPEC.define( new IntField( "extra_babies.max", maxExtraBabies, IntField.Range.NON_NEGATIVE ) )
);
}
}
}

View file

@ -0,0 +1,46 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
public class QueenGhastSpeciesConfig extends SpeciesConfig {
public final Queen QUEEN;
/** Builds the config spec that should be used for this config. */
public QueenGhastSpeciesConfig( MobFamily.Species<?> species, int minBabies, int maxBabies, int minSummons, int maxSummons ) {
super( species );
QUEEN = new Queen( SPEC, species, speciesName, minBabies, maxBabies, minSummons, maxSummons );
}
public static class Queen extends Config.AbstractCategory {
public final IntField.RandomRange babies;
public final IntField.RandomRange summons;
Queen( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName,
int minBabies, int maxBabies, int minSummons, int maxSummons ) {
super( parent, ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ),
"Options specific to " + speciesName + "." );
babies = new IntField.RandomRange(
SPEC.define( new IntField( "babies.min", minBabies, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of babies " + speciesName + " spawn on death." ) ),
SPEC.define( new IntField( "babies.max", maxBabies, IntField.Range.NON_NEGATIVE ) )
);
SPEC.newLine();
summons = new IntField.RandomRange(
SPEC.define( new IntField( "summons.min", minSummons, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of times " + speciesName + " can summon minions." ) ),
SPEC.define( new IntField( "summons.max", maxSummons, IntField.Range.NON_NEGATIVE ) )
);
}
}
}

View file

@ -0,0 +1,45 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the base species config for silverfish.
*/
public class SilverfishSpeciesConfig extends SpeciesConfig {
public final Silverfish SILVERFISH;
/** Builds the config spec that should be used for this config. */
public SilverfishSpeciesConfig( MobFamily.Species<?> species, double spitChance ) {
super( species );
SILVERFISH = new Silverfish( SPEC, species, speciesName, spitChance );
}
public static class Silverfish extends Config.AbstractCategory {
public final DoubleField aggressiveChance;
public final DoubleField spitterChance;
Silverfish( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName, double spitChance ) {
super( parent, ConfigUtil.noSpaces( species.family.configName ),
"Options standard to all " + species.family.configName + "." );
aggressiveChance = SPEC.define( new DoubleField( "aggressive_chance", -1.0, DoubleField.Range.SIGNED_PERCENT,
"Chance for " + speciesName + " to spawn already calling for reinforcements.",
"If this is set to a non-negative value, it overrides the value set for \"family_aggressive_chance\"." ) );
SPEC.newLine();
// Automatically set the default spitter chance to 0% if the mob has ranged attacks disabled by default
final double effectiveDefault = species.bestiaryInfo.rangedAttackMaxRange > 0.0F ? spitChance : 0.0;
spitterChance = SPEC.define( new DoubleField( "spitter_chance", effectiveDefault, DoubleField.Range.PERCENT,
"Chance for " + speciesName + " to spawn as a 'spitter', which enables their ranged attack (if max range > 0)." ) );
}
}
}

View file

@ -0,0 +1,45 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the base species config for skeletons and wither skeletons.
*/
public class SkeletonSpeciesConfig extends SpeciesConfig {
public final Skeletons SKELETONS;
/** Builds the config spec that should be used for this config. */
public SkeletonSpeciesConfig( MobFamily.Species<?> species, double bowChance, double shieldChance ) {
super( species );
SKELETONS = new Skeletons( SPEC, species, speciesName, bowChance, shieldChance );
}
public static class Skeletons extends Config.AbstractCategory {
public final DoubleField bowEquipChance;
public final DoubleField shieldEquipChance;
Skeletons( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName, double bowChance, double shieldChance ) {
super( parent, ConfigUtil.noSpaces( species.family.configName ),
"Options standard to all " + species.family.configName + "." );
// Automatically set the default bow chance to 0% if the mob has ranged attacks disabled by default
final double effectiveDefault = species.bestiaryInfo.rangedAttackMaxRange > 0.0F ? bowChance : 0.0;
bowEquipChance = SPEC.define( new DoubleField( "bow_chance", effectiveDefault, DoubleField.Range.PERCENT,
"Chance for " + speciesName + " to spawn with a bow, which enables their ranged attack (if max range > 0)." ) );
SPEC.newLine();
shieldEquipChance = SPEC.define( new DoubleField( "shield_chance", shieldChance, DoubleField.Range.PERCENT,
"Chance for " + speciesName + " to spawn with a shield if they did not spawn with a bow.",
"Shield users have a 33% chance to block frontal attacks (100% vs. long range attacks) and can be broken by axes." ) );
}
}
}

View file

@ -0,0 +1,143 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.family.FamilyConfig;
import fathertoast.specialmobs.common.config.field.*;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import net.minecraft.block.Block;
import net.minecraft.potion.Effect;
/**
* This is the base config for mob species. This may be extended to add categories specific to the species, but all
* options that are used by all species should be defined in this class.
*/
public class SpeciesConfig extends Config.AbstractConfig {
public static final String SPECIAL_DATA_SUBCAT = "special_data.";
protected static String fileName( MobFamily.Species<?> species ) {
return (species.specialVariantName == null ? "_normal" : ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ))
+ "_" + ConfigUtil.noSpaces( species.family.configName );
}
protected static String variantName( MobFamily.Species<?> species ) {
return species.specialVariantName == null ? "vanilla replacement" : ConfigUtil.camelCaseToLowerSpace( species.specialVariantName );
}
/** The full readable name for the species in lower space case; e.g., "baby cave spiders". */
protected final String speciesName;
/** Category containing all options applicable to mob species as a whole; i.e. not specific to any particular species. */
public final General GENERAL;
/** Builds the config spec that should be used for this config. */
public SpeciesConfig( MobFamily.Species<?> species ) {
super( FamilyConfig.dir( species.family ), fileName( species ),
String.format( "This config contains options that apply only to the %s %s species.",
species.specialVariantName, ConfigUtil.camelCaseToLowerSpace( species.family.name ) ) );
SPEC.newLine();
SPEC.describeAttributeList();
SPEC.describeRegistryEntryList();
speciesName = variantName( species ) + " " + species.family.configName;
GENERAL = new General( SPEC, species, speciesName );
}
public static class General extends Config.AbstractCategory {
public final DoubleField randomScaling;
public final AttributeListField attributeChanges;
public final IntField experience;
public final IntField healTime;
public final DoubleField fallDamageMultiplier;
public final BooleanField isImmuneToFire;
public final BooleanField isImmuneToBurning;
public final BooleanField canBreatheInWater;
public final BooleanField ignoreWaterPush;
public final BooleanField isDamagedByWater;
public final BooleanField allowLeashing;
public final BooleanField ignorePressurePlates;
public final RegistryEntryListField<Block> immuneToStickyBlocks;
public final RegistryEntryListField<Effect> immuneToPotions;
// These are at the end because they may or may not be present (not applicable for all families)
public final DoubleField rangedAttackDamage;
public final DoubleField rangedAttackSpread;
public final DoubleField rangedWalkSpeed;
public final IntField rangedAttackCooldown;
public final IntField rangedAttackMaxCooldown;
public final DoubleField rangedAttackMaxRange;
General( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName ) {
super( parent, "general",
"Options standard to all mob species (that is, not specific to any particular mob species)." );
final BestiaryInfo info = species.bestiaryInfo;
randomScaling = SPEC.define( new DoubleField( "random_scaling", -1.0, DoubleField.Range.SIGNED_PERCENT,
"When greater than 0, " + speciesName + " will have a random render scale applied. This is a visual effect only.",
"If this is set to a non-negative value, it overrides the value set for both \"master_random_scaling\" and",
"\"family_random_scaling\". The priority is species value > family value > master value." ) );
SPEC.newLine();
attributeChanges = SPEC.define( new AttributeListField( "attributes", info.defaultAttributes,
"Attribute modifiers for " + speciesName + ". If no attribute changes are defined here, " + speciesName,
"will have the exact same attributes as their parent vanilla mob." ) );
SPEC.newLine();
experience = SPEC.define( new IntField( SPECIAL_DATA_SUBCAT + "experience", info.experience, IntField.Range.NON_NEGATIVE,
"The amount of experience " + speciesName + " drop when killed by a player. Multiplied by 2.5 for babies.",
"Extra experience may drop based on equipment. Slime-style mobs also drop experience equal to slime size." ) );
healTime = SPEC.define( new IntField( SPECIAL_DATA_SUBCAT + "heal_time", info.healTime, IntField.Range.NON_NEGATIVE,
"If greater than 0, " + speciesName + " will heal 1 half-heart of health every \"heal_time\" ticks. (20 ticks = 1 second)" ) );
fallDamageMultiplier = SPEC.define( new DoubleField( SPECIAL_DATA_SUBCAT + "fall_damage_multiplier", info.fallDamageMultiplier, DoubleField.Range.NON_NEGATIVE,
"Fall damage taken by " + speciesName + " is multiplied by this value. 0 is fall damage immunity." ) );
isImmuneToFire = SPEC.define( new BooleanField( SPECIAL_DATA_SUBCAT + "immune_to_fire", info.isImmuneToFire,
"If true, " + speciesName + " will take no fire damage. Does not affect spawn restrictions." ) );
isImmuneToBurning = SPEC.define( new BooleanField( SPECIAL_DATA_SUBCAT + "immune_to_burning", info.isImmuneToBurning,
"If true, " + speciesName + " cannot be set on fire (this setting only matters if not immune to fire)." ) );
canBreatheInWater = SPEC.define( new BooleanField( SPECIAL_DATA_SUBCAT + "immune_to_drowning", info.canBreatheInWater,
"If true, " + speciesName + " can breathe in water." ) );
ignoreWaterPush = SPEC.define( new BooleanField( SPECIAL_DATA_SUBCAT + "immune_to_fluid_push", info.ignoreWaterPush,
"If true, " + speciesName + " will ignore forces applied by flowing fluids." ) );
isDamagedByWater = SPEC.define( new BooleanField( SPECIAL_DATA_SUBCAT + "sensitive_to_water", info.isDamagedByWater,
"If true, " + speciesName + " will be continuously damaged while wet." ) );
allowLeashing = SPEC.define( new BooleanField( SPECIAL_DATA_SUBCAT + "allow_leashing", info.allowLeashing,
"If true, " + speciesName + " can be leashed. (Note: Leashed mobs can still attack you.)" ) );
ignorePressurePlates = SPEC.define( new BooleanField( SPECIAL_DATA_SUBCAT + "immune_to_pressure_plates", info.ignorePressurePlates,
"If true, " + speciesName + " will not trigger pressure plates." ) );
immuneToStickyBlocks = SPEC.define( new RegistryEntryListField<>( SPECIAL_DATA_SUBCAT + "immune_to_sticky_blocks", info.immuneToStickyBlocks,
ConfigUtil.properCase( speciesName ) + " will not be 'trapped' in any blocks specified here (e.g. \"cobweb\" or \"sweet_berry_bush\")." ) );
immuneToPotions = SPEC.define( new RegistryEntryListField<>( SPECIAL_DATA_SUBCAT + "immune_to_effects", info.immuneToPotions,
ConfigUtil.properCase( speciesName ) + " cannot be inflicted with any effects specified here (e.g. \"instant_damage\" or \"regeneration\")." ) );
SPEC.newLine();
rangedAttackDamage = info.rangedAttackDamage < 0.0F ? null :
SPEC.define( new DoubleField( SPECIAL_DATA_SUBCAT + "ranged_attack.damage", info.rangedAttackDamage, DoubleField.Range.NON_NEGATIVE,
"" ) );
rangedAttackSpread = info.rangedAttackSpread < 0.0F ? null :
SPEC.define( new DoubleField( SPECIAL_DATA_SUBCAT + "ranged_attack.spread", info.rangedAttackSpread, DoubleField.Range.NON_NEGATIVE,
"." ) );
rangedWalkSpeed = info.rangedWalkSpeed < 0.0F ? null :
SPEC.define( new DoubleField( SPECIAL_DATA_SUBCAT + "ranged_attack.walk_speed", info.rangedWalkSpeed, DoubleField.Range.NON_NEGATIVE,
"." ) );
rangedAttackCooldown = info.rangedAttackCooldown < 0 ? null :
SPEC.define( new IntField( SPECIAL_DATA_SUBCAT + "ranged_attack.charge_time", info.rangedAttackCooldown, IntField.Range.NON_NEGATIVE,
"The delay (in ticks) to 'charge up' and perform a ranged attack. (20 ticks = 1 second)" ) );
rangedAttackMaxCooldown = info.rangedAttackMaxCooldown < 0 ? null :
SPEC.define( new IntField( SPECIAL_DATA_SUBCAT + "ranged_attack.refire_time", info.rangedAttackMaxCooldown, IntField.Range.NON_NEGATIVE,
"The total delay (in ticks) between each ranged attack. (20 ticks = 1 second)" ) );
rangedAttackMaxRange = info.rangedAttackMaxRange < 0.0F ? null :
SPEC.define( new DoubleField( SPECIAL_DATA_SUBCAT + "ranged_attack.max_range", info.rangedAttackMaxRange, DoubleField.Range.NON_NEGATIVE,
"" ) );
}
}
}

View file

@ -0,0 +1,37 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the base species config for spiders and cave spiders.
*/
public class SpiderSpeciesConfig extends SpeciesConfig {
public final Spiders SPIDERS;
/** Builds the config spec that should be used for this config. */
public SpiderSpeciesConfig( MobFamily.Species<?> species, double spitChance ) {
super( species );
SPIDERS = new Spiders( SPEC, species, speciesName, spitChance );
}
public static class Spiders extends Config.AbstractCategory {
public final DoubleField spitterChance;
Spiders( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName, double spitChance ) {
super( parent, ConfigUtil.noSpaces( species.family.configName ),
"Options standard to all " + species.family.configName + "." );
// Automatically set the default spitter chance to 0% if the mob has ranged attacks disabled by default
final double effectiveDefault = species.bestiaryInfo.rangedAttackMaxRange > 0.0F ? spitChance : 0.0;
spitterChance = SPEC.define( new DoubleField( "spitter_chance", effectiveDefault, DoubleField.Range.PERCENT,
"Chance for " + speciesName + " to spawn as a 'spitter', which enables their ranged attack (if max range > 0)." ) );
}
}
}

View file

@ -0,0 +1,38 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
public class SplittingCreeperSpeciesConfig extends CreeperSpeciesConfig {
public final Splitting SPLITTING;
/** Builds the config spec that should be used for this config. */
public SplittingCreeperSpeciesConfig( MobFamily.Species<?> species,
boolean cannotExplodeWhileWet, boolean explodeWhileBurning, boolean explodeWhenShot,
int minExtraBabies, int maxExtraBabies ) {
super( species, cannotExplodeWhileWet, explodeWhileBurning, explodeWhenShot );
SPLITTING = new Splitting( SPEC, species, speciesName, minExtraBabies, maxExtraBabies );
}
public static class Splitting extends Config.AbstractCategory {
public final IntField.RandomRange extraBabies;
Splitting( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName, int minExtraBabies, int maxExtraBabies ) {
super( parent, ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ),
"Options specific to " + speciesName + "." );
extraBabies = new IntField.RandomRange(
SPEC.define( new IntField( "extra_babies.min", minExtraBabies, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of extra babies that " + speciesName + " spawn with their explosion.",
"This is in addition to the number spawned based on explosion power." ) ),
SPEC.define( new IntField( "extra_babies.max", maxExtraBabies, IntField.Range.NON_NEGATIVE ) )
);
}
}
}

View file

@ -0,0 +1,35 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
public class UndeadWitchSpeciesConfig extends SpeciesConfig {
public final Undead UNDEAD;
/** Builds the config spec that should be used for this config. */
public UndeadWitchSpeciesConfig( MobFamily.Species<?> species, int minSummons, int maxSummons ) {
super( species );
UNDEAD = new Undead( SPEC, species, speciesName, minSummons, maxSummons );
}
public static class Undead extends Config.AbstractCategory {
public final IntField.RandomRange summons;
Undead( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName, int minSummons, int maxSummons ) {
super( parent, ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ),
"Options specific to " + speciesName + "." );
summons = new IntField.RandomRange(
SPEC.define( new IntField( "summons.min", minSummons, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of times " + speciesName + " can summon minions." ) ),
SPEC.define( new IntField( "summons.max", maxSummons, IntField.Range.NON_NEGATIVE ) )
);
}
}
}

View file

@ -0,0 +1,35 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
public class WebSpiderSpeciesConfig extends SpiderSpeciesConfig {
public final Web WEB;
/** Builds the config spec that should be used for this config. */
public WebSpiderSpeciesConfig( MobFamily.Species<?> species, double spitChance, int minWebs, int maxWebs ) {
super( species, spitChance );
WEB = new Web( SPEC, species, speciesName, minWebs, maxWebs );
}
public static class Web extends Config.AbstractCategory {
public final IntField.RandomRange webCount;
Web( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName, int minWebs, int maxWebs ) {
super( parent, ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ),
"Options specific to " + speciesName + "." );
webCount = new IntField.RandomRange(
SPEC.define( new IntField( "webs.min", minWebs, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of cobwebs " + speciesName + " can place." ) ),
SPEC.define( new IntField( "webs.max", maxWebs, IntField.Range.NON_NEGATIVE ) )
);
}
}
}

View file

@ -0,0 +1,47 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
public class WildfireBlazeSpeciesConfig extends BlazeSpeciesConfig {
public final Wildfire WILDFIRE;
/** Builds the config spec that should be used for this config. */
public WildfireBlazeSpeciesConfig( MobFamily.Species<?> species, int fireballBurstCount, int fireballBurstDelay,
int minBabies, int maxBabies, int minSummons, int maxSummons ) {
super( species, fireballBurstCount, fireballBurstDelay );
WILDFIRE = new Wildfire( SPEC, species, speciesName, minBabies, maxBabies, minSummons, maxSummons );
}
public static class Wildfire extends Config.AbstractCategory {
public final IntField.RandomRange babies;
public final IntField.RandomRange summons;
Wildfire( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName,
int minBabies, int maxBabies, int minSummons, int maxSummons ) {
super( parent, ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ),
"Options specific to " + speciesName + "." );
babies = new IntField.RandomRange(
SPEC.define( new IntField( "babies.min", minBabies, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of babies " + speciesName + " spawn on death." ) ),
SPEC.define( new IntField( "babies.max", maxBabies, IntField.Range.NON_NEGATIVE ) )
);
SPEC.newLine();
summons = new IntField.RandomRange(
SPEC.define( new IntField( "summons.min", minSummons, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of times " + speciesName + " can summon minions." ) ),
SPEC.define( new IntField( "summons.max", maxSummons, IntField.Range.NON_NEGATIVE ) )
);
}
}
}

View file

@ -0,0 +1,58 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
public class WildsWitchSpeciesConfig extends SpeciesConfig {
public final Wilds WILDS;
/** Builds the config spec that should be used for this config. */
public WildsWitchSpeciesConfig( MobFamily.Species<?> species, int minMounts, int maxMounts,
int minSwarms, int maxSwarms, int minSwarmSize, int maxSwarmSize ) {
super( species );
WILDS = new Wilds( SPEC, species, speciesName, minMounts, maxMounts, minSwarms, maxSwarms, minSwarmSize, maxSwarmSize );
}
public static class Wilds extends Config.AbstractCategory {
public final IntField.RandomRange mounts;
public final IntField.RandomRange swarms;
public final IntField.RandomRange swarmSize;
Wilds( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName,
int minMounts, int maxMounts, int minSwarms, int maxSwarms, int minSwarmSize, int maxSwarmSize ) {
super( parent, ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ),
"Options specific to " + speciesName + "." );
mounts = new IntField.RandomRange(
SPEC.define( new IntField( "mounts.min", minMounts, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of times " + speciesName + " can summon a spider mount." ) ),
SPEC.define( new IntField( "mounts.max", maxMounts, IntField.Range.NON_NEGATIVE ) )
);
SPEC.newLine();
swarms = new IntField.RandomRange(
SPEC.define( new IntField( "swarms.min", minSwarms, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of times " + speciesName + " can summon a spider swarm." ) ),
SPEC.define( new IntField( "swarms.max", maxSwarms, IntField.Range.NON_NEGATIVE ) )
);
SPEC.newLine();
swarmSize = new IntField.RandomRange(
SPEC.define( new IntField( "swarm_size.min", minSwarmSize, IntField.Range.NON_NEGATIVE,
"The minimum and maximum (inclusive) number of spiders " + speciesName + " spawn with each swarm.",
"Note that this is rolled on the summoner's spawn, not each time a swarm is summoned." ) ),
SPEC.define( new IntField( "swarm_size.max", maxSwarmSize, IntField.Range.NON_NEGATIVE ) )
);
}
}
}

View file

@ -0,0 +1,45 @@
package fathertoast.specialmobs.common.config.species;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
/**
* This is the base species config for zombies, drowned, and zombified piglins.
*/
public class ZombieSpeciesConfig extends SpeciesConfig {
public final Zombies ZOMBIES;
/** Builds the config spec that should be used for this config. */
public ZombieSpeciesConfig( MobFamily.Species<?> species, double bowChance, double shieldChance ) {
super( species );
ZOMBIES = new Zombies( SPEC, species, speciesName, bowChance, shieldChance );
}
public static class Zombies extends Config.AbstractCategory {
public final DoubleField bowEquipChance;
public final DoubleField shieldEquipChance;
Zombies( ToastConfigSpec parent, MobFamily.Species<?> species, String speciesName, double bowChance, double shieldChance ) {
super( parent, ConfigUtil.noSpaces( species.family.configName ),
"Options standard to all " + species.family.configName + "." );
// Automatically set the default bow chance to 0% if the mob has ranged attacks disabled by default
final double effectiveDefault = species.bestiaryInfo.rangedAttackMaxRange > 0.0F ? bowChance : 0.0;
bowEquipChance = SPEC.define( new DoubleField( "bow_chance", effectiveDefault, DoubleField.Range.PERCENT,
"Chance for " + speciesName + " to spawn with a bow, which enables their ranged attack (if max range > 0)." ) );
SPEC.newLine();
shieldEquipChance = SPEC.define( new DoubleField( "shield_chance", shieldChance, DoubleField.Range.PERCENT,
"Chance for " + speciesName + " to spawn with a shield if they did not spawn with a bow.",
"Shield users have a 33% chance to block frontal attacks (100% vs. long range attacks) and can be broken by axes." ) );
}
}
}

View file

@ -0,0 +1,75 @@
package fathertoast.specialmobs.common.config.util;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistries;
/**
* One attribute-operation-value entry in an attribute list.
*/
@SuppressWarnings( "unused" )
public class AttributeEntry {
/** The attribute this entry is defined for. */
public final Attribute ATTRIBUTE;
/** True if the value should be multiplied to the base attribute value (as opposed to added). */
public final boolean MULTIPLY;
/** The value given to this entry. */
public final double VALUE;
/** The class this entry is defined for. This is not assigned until a world has been loaded. */
Class<? extends Entity> entityClass;
/** Creates an entry with the specified values using the addition operation. Incompatible with move speed. Used for creating default configs. */
public static AttributeEntry add( Attribute attribute, double value ) {
if( attribute.equals( Attributes.MOVEMENT_SPEED ) )
throw new IllegalArgumentException( "Move speed should not be added!" );
return new AttributeEntry( attribute, false, value );
}
/** Creates an entry with the specified values using the multiplication operation. Used for creating default configs. */
public static AttributeEntry mult( Attribute attribute, double value ) { return new AttributeEntry( attribute, true, value ); }
/** Creates an entry with the specified values. */
private AttributeEntry( Attribute attribute, boolean multiply, double value ) {
ATTRIBUTE = attribute;
MULTIPLY = multiply;
VALUE = value;
}
/**
* @return The string representation of this entity list entry, as it would appear in a config file.
* <p>
* Format is "registry_key operation value", operation may be +, -, or *.
*/
@Override
public String toString() {
// Start with the attribute registry key
ResourceLocation resource = ForgeRegistries.ATTRIBUTES.getKey( ATTRIBUTE );
StringBuilder str = new StringBuilder( resource == null ? "null" : resource.toString() ).append( ' ' );
// Append operation and value
if( MULTIPLY ) str.append( "* " ).append( VALUE );
else if( VALUE < 0.0 ) str.append( "- " ).append( -VALUE );
else str.append( "+ " ).append( VALUE );
return str.toString();
}
/** Applies this attribute change to the entity attribute builder. */
public void apply( AttributeModifierMap.MutableAttribute builder ) { apply( builder.builder.get( ATTRIBUTE ) ); }
/** Applies this attribute change to the entity. */
public void apply( LivingEntity entity ) { apply( entity.getAttribute( ATTRIBUTE ) ); }
/** Applies this attribute change to the attribute instance. Assumes that the instance is for this entry's target attribute. */
private void apply( ModifiableAttributeInstance attributeInstance ) {
if( attributeInstance == null )
throw new IllegalStateException( "Attempted to modify non-registered attribute " + ATTRIBUTE.getDescriptionId() );
if( MULTIPLY ) attributeInstance.setBaseValue( attributeInstance.getBaseValue() * VALUE );
else attributeInstance.setBaseValue( attributeInstance.getBaseValue() + VALUE );
}
}

View file

@ -0,0 +1,57 @@
package fathertoast.specialmobs.common.config.util;
import fathertoast.specialmobs.common.config.field.IStringArray;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import java.util.ArrayList;
import java.util.List;
public class AttributeList implements IStringArray {
/** The attribute-operation-value entries in this list. */
private final AttributeEntry[] ENTRIES;
/**
* Create a new attribute list from a list of entries.
*/
public AttributeList( List<AttributeEntry> entries ) { this( entries.toArray( new AttributeEntry[0] ) ); }
/**
* Create a new entity list from an array of entries. Used for creating default configs.
*/
public AttributeList( AttributeEntry... entries ) { ENTRIES = entries; }
/** @return A string representation of this object. */
@Override
public String toString() { return TomlHelper.toLiteral( toStringList().toArray() ); }
/** @return Returns true if this object has the same value as another object. */
@Override
public boolean equals( Object other ) {
if( !(other instanceof AttributeList) ) return false;
// Compare by the string list view of the object
return toStringList().equals( ((AttributeList) other).toStringList() );
}
/** @return A list of strings that will represent this object when written to a toml file. */
@Override
public List<String> toStringList() {
// Create a list of the entries in string format
final List<String> list = new ArrayList<>( ENTRIES.length );
for( AttributeEntry entry : ENTRIES ) {
list.add( entry.toString() );
}
return list;
}
/** Applies all attribute changes in this list to the entity attribute builder. */
public void apply( AttributeModifierMap.MutableAttribute builder ) {
for( AttributeEntry entry : ENTRIES ) entry.apply( builder );
}
/** Applies all attribute changes in this list to the entity. */
public void apply( LivingEntity entity ) {
for( AttributeEntry entry : ENTRIES ) entry.apply( entity );
}
}

View file

@ -14,7 +14,7 @@ import net.minecraftforge.registries.ForgeRegistries;
import java.util.*;
/**
* One entity-value entry in an entity list.
* One block state entry in a block list.
*/
@SuppressWarnings( "unused" )
public class BlockEntry implements Cloneable {

View file

@ -0,0 +1,36 @@
package fathertoast.specialmobs.common.config.util;
public abstract class ConfigUtil {
/** The plus or minus symbol (+/-). */
public static final String PLUS_OR_MINUS = "\u00b1";
/** The less than or equal to symbol (<=). */
public static final String LESS_OR_EQUAL = "\u2264";
/** The greater than or equal to symbol (>=). */
public static final String GREATER_OR_EQUAL = "\u2265";
/** @return The string with all spaces replaced by underscores. Useful for file names. */
public static String noSpaces( String str ) { return str.replace( ' ', '_' ); }
/** @return The string converted from camel case to lower space case; e.g., "UpperCamelCase" returns "upper camel case". */
public static String camelCaseToLowerSpace( String str ) {
final StringBuilder spacedStr = new StringBuilder();
for( int i = 0; i < str.length(); i++ ) {
final char c = str.charAt( i );
if( Character.isUpperCase( c ) ) {
if( i > 0 ) spacedStr.append( ' ' );
spacedStr.append( Character.toLowerCase( c ) );
}
else {
spacedStr.append( c );
}
}
return spacedStr.toString();
}
/** @return The string converted from camel case to lower underscore case; e.g., "UpperCamelCase" returns "upper_camel_case". */
public static String camelCaseToLowerUnderscore( String str ) { return noSpaces( camelCaseToLowerSpace( str ) ); }
/** @return The string, but with the first character changed to upper case. */
public static String properCase( String str ) { return str.substring( 0, 1 ).toUpperCase() + str.substring( 1 ); }
}

View file

@ -0,0 +1,121 @@
package fathertoast.specialmobs.common.config.util;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.IStringArray;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import java.util.*;
/**
* A list of entries used to match registry entries. Can safely be loaded before its target registry is loaded, but
* is not able to error check as well.
* <p>
* See also: {@link net.minecraftforge.registries.ForgeRegistries}
*/
@SuppressWarnings( "unused" )
public class LazyRegistryEntryList<T extends IForgeRegistryEntry<T>> implements IStringArray {
/** The registry this list acts as a subset of. */
private final IForgeRegistry<T> REGISTRY;
/** The entries in this list. */
private final Set<T> UNDERLYING_SET = new HashSet<>();
/** The list used to write back to file. */
private final List<String> PRINT_LIST = new ArrayList<>();
/**
* Create a new registry entry list from an array of entries. Used for creating default configs.
* <p>
* This method of creation can not take advantage of the * notation.
*/
@SafeVarargs
public LazyRegistryEntryList( IForgeRegistry<T> registry, T... entries ) {
REGISTRY = registry;
for( T entry : entries ) {
if( UNDERLYING_SET.add( entry ) ) PRINT_LIST.add( SpecialMobs.toString( registry.getKey( entry ) ) );
}
}
/**
* Create a new registry entry list from a list of registry key strings.
*/
public LazyRegistryEntryList( AbstractConfigField field, IForgeRegistry<T> registry, List<String> entries ) {
REGISTRY = registry;
for( String line : entries ) {
if( line.endsWith( "*" ) ) {
// Handle special case; add all entries in namespace
if( !mergeFromNamespace( line.substring( 0, line.length() - 1 ) ) ) {
// Don't delete this kind of entry
SpecialMobs.LOG.warn( "Namespace entry for {} \"{}\" did not match anything! Questionable entry: {}",
field.getClass(), field.getKey(), line );
}
PRINT_LIST.add( line );
}
else {
// Add a single registry entry
final ResourceLocation regKey = new ResourceLocation( line );
if( mergeFrom( regKey ) ) {
PRINT_LIST.add( regKey.toString() );
}
else {
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Deleting entry. Invalid entry: {}",
field.getClass(), field.getKey(), line );
}
}
}
}
/** @return The registry this list draws from. */
public IForgeRegistry<T> getRegistry() { return REGISTRY; }
/** @return The entries in this list. */
public Set<T> getEntries() { return Collections.unmodifiableSet( UNDERLYING_SET ); }
/** @return A string representation of this object. */
@Override
public String toString() {
return TomlHelper.toLiteral( PRINT_LIST.toArray() );
}
/** @return Returns true if this object has the same value as another object. */
@Override
public boolean equals( Object other ) {
if( !(other instanceof LazyRegistryEntryList) ) return false;
// Compare by the registries used and string list view of the object
return getRegistry() == ((LazyRegistryEntryList<?>) other).getRegistry() &&
toStringList().equals( ((LazyRegistryEntryList<?>) other).toStringList() );
}
/** @return A list of strings that will represent this object when written to a toml file. */
@Override
public List<String> toStringList() { return PRINT_LIST; }
/** @return Returns true if there are no entries in this list. */
public boolean isEmpty() { return UNDERLYING_SET.isEmpty(); }
/** @return Returns true if the entry is contained in this list. */
public boolean contains( T entry ) { return UNDERLYING_SET.contains( entry ); }
/** @return Adds the registry entry if it exists and isn't already present, returns true if successful. */
private boolean mergeFrom( ResourceLocation regKey ) {
final T entry = REGISTRY.getValue( regKey );
return entry != null && UNDERLYING_SET.add( entry );
}
/**
* @param namespace Merges all registry entries with keys that start with a namespace into this list.
* @return True if any registry entries were actually added.
*/
private boolean mergeFromNamespace( String namespace ) {
boolean foundAny = false;
for( ResourceLocation regKey : REGISTRY.getKeys() ) {
if( regKey.toString().startsWith( namespace ) ) {
if( mergeFrom( regKey ) ) foundAny = true;
}
}
return foundAny;
}
}

View file

@ -0,0 +1,120 @@
package fathertoast.specialmobs.common.config.util;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.IStringArray;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import java.util.*;
/**
* A list of entries used to match registry entries.
* <p>
* See also: {@link net.minecraftforge.registries.ForgeRegistries}
*/
@SuppressWarnings( "unused" )
public class RegistryEntryList<T extends IForgeRegistryEntry<T>> implements IStringArray {
/** The registry this list acts as a subset of. */
private final IForgeRegistry<T> REGISTRY;
/** The entries in this list. */
private final Set<T> UNDERLYING_SET = new HashSet<>();
/** The list used to write back to file. */
private final List<String> PRINT_LIST = new ArrayList<>();
/**
* Create a new registry entry list from an array of entries. Used for creating default configs.
* <p>
* This method of creation can not take advantage of the * notation.
*/
@SafeVarargs
public RegistryEntryList( IForgeRegistry<T> registry, T... entries ) {
REGISTRY = registry;
for( T entry : entries ) {
if( UNDERLYING_SET.add( entry ) ) PRINT_LIST.add( SpecialMobs.toString( registry.getKey( entry ) ) );
}
}
/**
* Create a new registry entry list from a list of registry key strings.
*/
public RegistryEntryList( AbstractConfigField field, IForgeRegistry<T> registry, List<String> entries ) {
REGISTRY = registry;
for( String line : entries ) {
if( line.endsWith( "*" ) ) {
// Handle special case; add all entries in namespace
if( !mergeFromNamespace( line.substring( 0, line.length() - 1 ) ) ) {
// Don't delete this kind of entry
SpecialMobs.LOG.warn( "Namespace entry for {} \"{}\" did not match anything! Questionable entry: {}",
field.getClass(), field.getKey(), line );
}
PRINT_LIST.add( line );
}
else {
// Add a single registry entry
final ResourceLocation regKey = new ResourceLocation( line );
if( mergeFrom( regKey ) ) {
PRINT_LIST.add( regKey.toString() );
}
else {
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Deleting entry. Invalid entry: {}",
field.getClass(), field.getKey(), line );
}
}
}
}
/** @return The registry this list draws from. */
public IForgeRegistry<T> getRegistry() { return REGISTRY; }
/** @return The entries in this list. */
public Set<T> getEntries() { return Collections.unmodifiableSet( UNDERLYING_SET ); }
/** @return A string representation of this object. */
@Override
public String toString() {
return TomlHelper.toLiteral( PRINT_LIST.toArray() );
}
/** @return Returns true if this object has the same value as another object. */
@Override
public boolean equals( Object other ) {
if( !(other instanceof RegistryEntryList) ) return false;
// Compare by the registries used and string list view of the object
return getRegistry() == ((RegistryEntryList<?>) other).getRegistry() &&
toStringList().equals( ((RegistryEntryList<?>) other).toStringList() );
}
/** @return A list of strings that will represent this object when written to a toml file. */
@Override
public List<String> toStringList() { return PRINT_LIST; }
/** @return Returns true if there are no entries in this list. */
public boolean isEmpty() { return UNDERLYING_SET.isEmpty(); }
/** @return Returns true if the entry is contained in this list. */
public boolean contains( T entry ) { return UNDERLYING_SET.contains( entry ); }
/** @return Adds the registry entry if it exists and isn't already present, returns true if successful. */
private boolean mergeFrom( ResourceLocation regKey ) {
final T entry = REGISTRY.getValue( regKey );
return entry != null && UNDERLYING_SET.add( entry );
}
/**
* @param namespace Merges all registry entries with keys that start with a namespace into this list.
* @return True if any registry entries were actually added.
*/
private boolean mergeFromNamespace( String namespace ) {
boolean foundAny = false;
for( ResourceLocation regKey : REGISTRY.getKeys() ) {
if( regKey.toString().startsWith( namespace ) ) {
if( mergeFrom( regKey ) ) foundAny = true;
}
}
return foundAny;
}
}

View file

@ -39,17 +39,19 @@ public class SpecialMobs {
* o dimension-sensitive configs
* o environment-sensitive configs
* ? natural spawning
* + potions
* + vulnerability (opposite of resistance)
* o entities
* - nbt-driven capabilities (special mob data)
* o fish hook
* o bug projectile
* o bug spit
* + bestiary
* ? configurable stats
* - configurable stats
* - monster families (see doc for specifics)
* - creepers
* - chance to spawn charged during thunderstorms
* + scope
* - zombies
* - zombies TODO zombie villager renderer
* o villager infection
* + transformations
* - ranged attack AI (using bow)
@ -57,25 +59,28 @@ public class SpecialMobs {
* + drowned
* - zombified piglins
* - ranged attack AI (using bow)
* + ranged attack AI (using crossbow)
* - use shields
* - skeletons
* - use shields
* - melee chance
* - babies
* - wither skeletons
* - use shields
* - bow chance
* - babies
* - slimes
* - smallest size can deal damage
* - magma cubes
* - spiders
* o ranged attack AI
* o ranged attack AI (spitter)
* - cave spiders
* o ranged attack AI
* o ranged attack AI (spitter)
* - silverfish
* ? ranged attack AI
* + ranged attack AI (spitter)
* + puffer
* - endermen
* - witches
* - witches TODO inject ranged attack stats
* - ability to equip held items
* - uses splash speed instead of regular
* - ghasts
@ -109,6 +114,8 @@ public class SpecialMobs {
public SpecialMobs() {
Config.initialize();
MobFamily.initBestiary();
packetHandler.registerMessages();
//MinecraftForge.EVENT_BUS.register( new SMEventListener() );
@ -121,8 +128,6 @@ public class SpecialMobs {
SMEntities.REGISTRY.register( eventBus );
SMItems.REGISTRY.register( eventBus );
MobFamily.initBestiary();
}
// TODO - This could very well help out the config malformation issue

View file

@ -7,6 +7,7 @@ import fathertoast.specialmobs.common.util.AnnotationHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraftforge.event.entity.EntityAttributeCreationEvent;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
@ -15,10 +16,10 @@ import net.minecraftforge.registries.ForgeRegistries;
public class SMEntities {
public static final DeferredRegister<EntityType<?>> REGISTRY = DeferredRegister.create( ForgeRegistries.ENTITIES, SpecialMobs.MOD_ID );
/** Misc entities */
public static final RegistryObject<EntityType<CorporealShiftFireballEntity>> CORPOREAL_FIREBALL = register("corporeal_shift_fireball",
EntityType.Builder.<CorporealShiftFireballEntity>of(CorporealShiftFireballEntity::new, EntityClassification.MISC).sized(1.0F, 1.0F).clientTrackingRange(4).updateInterval(3));
public static final RegistryObject<EntityType<CorporealShiftFireballEntity>> CORPOREAL_FIREBALL = register( "corporeal_shift_fireball",
EntityType.Builder.<CorporealShiftFireballEntity>of( CorporealShiftFireballEntity::new, EntityClassification.MISC ).sized( 1.0F, 1.0F ).clientTrackingRange( 4 ).updateInterval( 3 ) );
/** Registers an entity type to the deferred register. */
public static <T extends Entity> RegistryObject<EntityType<T>> register( String name, EntityType.Builder<T> builder ) {
@ -28,8 +29,11 @@ public class SMEntities {
/** Sets the default attributes for entity types, such as max health, attack damage etc. */
public static void createAttributes( EntityAttributeCreationEvent event ) {
// Bestiary-generated entities
for( MobFamily.Species<?> species : MobFamily.getAllSpecies() )
event.put( species.entityType.get(), AnnotationHelper.createAttributes( species ) );
for( MobFamily.Species<?> species : MobFamily.getAllSpecies() ) {
final AttributeModifierMap.MutableAttribute attributes = AnnotationHelper.createAttributes( species );
species.config.GENERAL.attributeChanges.apply( attributes );
event.put( species.entityType.get(), attributes.build() );
}
}
/** Sets the natural spawn placement rules for entity types. */

View file

@ -1,13 +1,17 @@
package fathertoast.specialmobs.common.entity;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import net.minecraft.entity.LivingEntity;
import net.minecraft.pathfinding.PathNodeType;
import net.minecraft.util.ResourceLocation;
public interface ISpecialMob<T extends LivingEntity & ISpecialMob<T>> {
/** @return This mob's special data. */
SpecialMobData<T> getSpecialData();
/** @return This mob's species. */
MobFamily.Species<? extends T> getSpecies();
/** @return The experience that should be dropped by this entity. */
int getExperience();
@ -16,7 +20,4 @@ public interface ISpecialMob<T extends LivingEntity & ISpecialMob<T>> {
/** Sets the entity's pathfinding malus for a particular node type; negative value is un-walkable. */
void setPathfindingMalus( PathNodeType nodeType, float malus );
/** @return All default textures for this entity. */
ResourceLocation[] getDefaultTextures();
}

View file

@ -1,11 +1,13 @@
package fathertoast.specialmobs.common.entity;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.entity.creeper._SpecialCreeperEntity;
import net.minecraft.block.FlowingFluidBlock;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.CreatureAttribute;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.monster.CreeperEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.AbstractArrowEntity;
import net.minecraft.fluid.Fluid;
@ -52,6 +54,16 @@ public final class MobHelper {
new EffectInstance( Effects.POISON, 1, 0 ) // Keep this option last for easy disable (by cave spiders)
};
/** Charges a creeper, potentially supercharging it. */
public static void charge( CreeperEntity creeper ) {
if( creeper instanceof _SpecialCreeperEntity ) {
((_SpecialCreeperEntity) creeper).charge();
}
else {
creeper.getEntityData().set( CreeperEntity.DATA_IS_POWERED, true );
}
}
/** @return True if the damage source can deal normal damage to vampire-type mobs (e.g., wooden or smiting weapons). */
public static boolean isDamageSourceIneffectiveAgainstVampires( DamageSource source ) {
if( source != null ) {
@ -184,8 +196,8 @@ public final class MobHelper {
public static EffectInstance nextPlagueEffect( Random random, World world ) {
final int duration = MobHelper.getDebuffDuration( world.getDifficulty() );
//final EffectInstance potion = PLAGUE_EFFECTS[random.nextInt( PLAGUE_EFFECTS.length - (Config.get().GENERAL.DISABLE_NAUSEA ? 1 : 0) )]; TODO config
final EffectInstance potion = PLAGUE_EFFECTS[random.nextInt( PLAGUE_EFFECTS.length )];
final EffectInstance potion = PLAGUE_EFFECTS[random.nextInt( PLAGUE_EFFECTS.length -
(Config.MAIN.GENERAL.enableNausea.get() ? 0 : 1) )];
return new EffectInstance( potion.getEffect(), duration * potion.getDuration(), potion.getAmplifier() );
}
@ -248,7 +260,7 @@ public final class MobHelper {
Vector3d targetVec = sourcePos.vectorTo( blocker.position() ).normalize();
targetVec = new Vector3d( targetVec.x, 0.0, targetVec.z );
if( targetVec.dot( lookVec ) < 0.0 ) {
blocker.level.playSound(null, blocker.getX() + 0.5D, blocker.getY(), blocker.getZ() + 0.5D, SoundEvents.SHIELD_BLOCK, SoundCategory.NEUTRAL, 0.9F, 1.0F);
blocker.level.playSound( null, blocker.getX() + 0.5D, blocker.getY(), blocker.getZ() + 0.5D, SoundEvents.SHIELD_BLOCK, SoundCategory.NEUTRAL, 0.9F, 1.0F );
if( needsShield && entity instanceof PlayerEntity ) {
maybeDestroyShield( blocker, shield, shieldHand, ((PlayerEntity) entity).getMainHandItem() );
}
@ -268,7 +280,7 @@ public final class MobHelper {
private static void maybeDestroyShield( LivingEntity blocker, ItemStack shield, Hand shieldHand, ItemStack weapon ) {
if( !weapon.isEmpty() && !shield.isEmpty() && weapon.getItem() instanceof AxeItem && shield.getItem() == Items.SHIELD &&
blocker.getRandom().nextFloat() < 0.25F - EnchantmentHelper.getBlockEfficiency( blocker ) * 0.05F ) {
blocker.level.playSound(null, blocker.getX() + 0.5D, blocker.getY(), blocker.getZ() + 0.5D, SoundEvents.SHIELD_BREAK, SoundCategory.NEUTRAL, 0.9F, 1.0F);
blocker.level.playSound( null, blocker.getX() + 0.5D, blocker.getY(), blocker.getZ() + 0.5D, SoundEvents.SHIELD_BREAK, SoundCategory.NEUTRAL, 0.9F, 1.0F );
blocker.broadcastBreakEvent( shieldHand );
blocker.setItemInHand( shieldHand, ItemStack.EMPTY );
}

View file

@ -1,12 +1,14 @@
package fathertoast.specialmobs.common.entity;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.CreatureAttribute;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.monster.SpiderEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.StringNBT;
@ -14,11 +16,12 @@ import net.minecraft.network.datasync.DataParameter;
import net.minecraft.pathfinding.PathNodeType;
import net.minecraft.potion.Effect;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.living.PotionEvent;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.Collection;
import java.util.HashSet;
import static fathertoast.specialmobs.common.util.References.*;
@ -47,11 +50,6 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
/** Data manager parameter for render scale. */
private final DataParameter<Float> renderScale;
/** The base collision box scale of this variant's family. */
private final float familyScale;
/** The base collision box scale of this variant. */
private float baseScale;
/** The base texture of the entity. */
private ResourceLocation texture;
/** The glowing eyes texture of the entity. */
@ -61,43 +59,17 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
/** True if the textures need to be sent to the client. */
private boolean updateTextures;
/** The damage the entity uses for its ranged attacks, when applicable. */
public float rangedAttackDamage;
/** The spread (inaccuracy) of the entity's ranged attacks. */
public float rangedAttackSpread = 1.0F;
/** The movement speed multiplier the entity uses during its ranged attack ai. Requires an AI reload to take effect. */
public float rangedWalkSpeed = 1.0F;
/** The delay (in ticks) before a new ranged attack can begin after firing. Requires an AI reload to take effect. */
public int rangedAttackCooldown;
/**
* The delay (in ticks) between each ranged attack at maximum delay. Requires an AI reload to take effect.
* Unused for bow attacks. For fireball attacks, this is the cooldown + charge time.
* For all other attacks, this is the cooldown at maximum range (scaled down to the minimum cooldown at point-blank).
*/
public int rangedAttackMaxCooldown;
/**
* The maximum distance (in blocks) the entity can fire ranged attacks from. Requires an ai reload to take effect.
* Ranged ai can only be used if this stat is greater than 0. Does not change aggro range.
*/
public float rangedAttackMaxRange;
/** The rate this mob regenerates health (ticks per 1 health). Off if 0 or less. */
private int healTimeMax;
/** Counter to the next heal, if healTimeMax is greater than 0. */
private int healTime;
/** Proportion of fall damage taken. */
private float fallDamageMultiplier = 1.0F;
private float fallDamageMultiplier;
/** Whether the entity is immune to fire damage. */
private boolean isImmuneToFire;
/** Whether the entity is immune to being set on fire. */
private boolean isImmuneToBurning;
/** Whether the entity can be leashed. */
private boolean allowLeashing;
/** Whether the entity does not trigger pressure plates. */
private boolean ignorePressurePlates;
/** Whether the entity can breathe under water. */
private boolean canBreatheInWater;
/** Whether the entity can ignore pushing from flowing water. */
@ -105,44 +77,79 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
/** Whether the entity is damaged when wet. */
private boolean isDamagedByWater;
/** List of blocks that the entity cannot be stuck in. */
private final HashSet<String> immuneToStickyBlocks = new HashSet<>();
/** List of potions that cannot be applied to the entity. */
private final HashSet<String> immuneToPotions = new HashSet<>();
/** Whether the entity can be leashed. */
private boolean allowLeashing;
/** Whether the entity does not trigger pressure plates. */
private boolean ignorePressurePlates;
/** Set of blocks that the entity cannot be stuck in. */
private final HashSet<Block> immuneToStickyBlocks = new HashSet<>();
/** Set of potions that cannot be applied to the entity. */
private final HashSet<Effect> immuneToPotions = new HashSet<>();
/** The damage the entity uses for its ranged attacks, when applicable. */
private float rangedAttackDamage;
/** The spread (inaccuracy) of the entity's ranged attacks. */
private float rangedAttackSpread;
/** The movement speed multiplier the entity uses during its ranged attack ai. Requires an AI reload to take effect. */
private float rangedWalkSpeed;
/** The delay (in ticks) before a ranged attack can be used. Requires an AI reload to take effect. */
private int rangedAttackCooldown;
/**
* The delay (in ticks) between each ranged attack at maximum delay. Requires an AI reload to take effect.
* Unused for bow attacks. For fireball attacks, this is "refire" time.
* For spit attacks, this is the cooldown at maximum range (scaled down to the minimum cooldown at point-blank).
*/
private int rangedAttackMaxCooldown;
/**
* The maximum distance (in blocks) the entity can fire ranged attacks from. Requires an ai reload to take effect.
* Ranged ai can only be used if this stat is greater than 0. Does not change aggro range.
*/
private float rangedAttackMaxRange;
/**
* Constructs a SpecialMobData to store generic data about a mob.
* <p>
* This constructor should be called during data watcher definitions, and defining the 'render scale' data watcher
* parameter is the only thing actually done while constructing.
* parameter and setting up AI stats are the only things actually done while constructing.
* <p>
* The #initialize() method must be called later on to complete initialization (e.g. in the entity constructor).
* The #initialize() method must be called later on to complete initialization (in the entity constructor).
*
* @param entity The entity to store data for.
* @param scale Data parameter for storing the render scale.
* @param familyBaseScale Base render scale. Typically 1.0F.
* @param entity The entity to store data for.
* @param scale Data parameter for storing the render scale.
*/
public SpecialMobData( T entity, DataParameter<Float> scale, float familyBaseScale ) {
public SpecialMobData( T entity, DataParameter<Float> scale ) {
theEntity = entity;
renderScale = scale;
familyScale = baseScale = familyBaseScale;
setTextures( entity.getDefaultTextures() );
entity.getEntityData().define( renderScale, nextScale() );
final SpeciesConfig.General config = theEntity.getSpecies().config.GENERAL;
setRangedAttackDamage( config.rangedAttackDamage == null ? -1.0F : (float) config.rangedAttackDamage.get() );
setRangedAttackSpread( config.rangedAttackSpread == null ? -1.0F : (float) config.rangedAttackSpread.get() );
setRangedWalkSpeed( config.rangedWalkSpeed == null ? -1.0F : (float) config.rangedWalkSpeed.get() );
setRangedAttackCooldown( config.rangedAttackCooldown == null ? -1 : config.rangedAttackCooldown.get() );
setRangedAttackMaxCooldown( config.rangedAttackMaxCooldown == null ? -1 : config.rangedAttackMaxCooldown.get() );
setRangedAttackMaxRange( config.rangedAttackMaxRange == null ? -1.0F : (float) config.rangedAttackMaxRange.get() );
}
/** Called to finish initialization, since we can only define data watcher params in the constructor. */
public void initialize() {
setImmuneToFire( theEntity.getType().fireImmune() );
if( theEntity.getMobType() == CreatureAttribute.UNDEAD ) {
addPotionImmunity( Effects.REGENERATION, Effects.POISON );
}
if( theEntity instanceof SpiderEntity ) {
addStickyBlockImmunity( Blocks.COBWEB );
addPotionImmunity( Effects.POISON );
}
final BestiaryInfo info = theEntity.getSpecies().bestiaryInfo;
texture = info.texture;
textureEyes = info.eyesTexture;
textureOverlay = info.overlayTexture;
final SpeciesConfig.General config = theEntity.getSpecies().config.GENERAL;
theEntity.setExperience( config.experience.get() );
setRegenerationTime( config.healTime.get() );
setFallDamageMultiplier( (float) config.fallDamageMultiplier.get() );
setImmuneToFire( config.isImmuneToFire.get() );
setImmuneToBurning( config.isImmuneToBurning.get() );
setCanBreatheInWater( config.canBreatheInWater.get() );
setIgnoreWaterPush( config.ignoreWaterPush.get() );
setDamagedByWater( config.isDamagedByWater.get() );
setAllowLeashing( config.allowLeashing.get() );
setIgnorePressurePlates( config.ignorePressurePlates.get() );
addStickyBlockImmunity( config.immuneToStickyBlocks.get().getEntries() );
addPotionImmunity( config.immuneToPotions.get().getEntries() );
}
/** Copies all of the data from another mob, optionally copying texture(s). */
@ -177,43 +184,29 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
}
}
/**
* @return Whether this entity has a glowing eyes texture.
*/
/** @return Whether this entity has a glowing eyes texture. */
public boolean hasEyesTexture() { return textureEyes != null; }
/**
* @return Whether this entity has an overlay texture.
*/
/** @return Whether this entity has an overlay texture. */
public boolean hasOverlayTexture() { return textureOverlay != null; }
/**
* @return The base texture for the entity.
*/
/** @return The base texture for the entity. */
public ResourceLocation getTexture() { return texture; }
/**
* @return The glowing eyes texture for the entity.
*/
/** @return The glowing eyes texture for the entity. */
public ResourceLocation getTextureEyes() { return textureEyes; }
/**
* @return The overlay texture for the entity.
*/
/** @return The overlay texture for the entity. */
public ResourceLocation getTextureOverlay() { return textureOverlay; }
/**
* @param textures The new texture(s) to set for the entity.
*/
/** @param textures The new texture(s) to set for the entity. */
private void setTextures( ResourceLocation[] textures ) {
texture = textures[0];
textureEyes = textures.length > 1 ? textures[1] : null;
textureOverlay = textures.length > 2 ? textures[2] : null;
}
/**
* @param textures The new texture(s) to load for the entity. Called when loaded from a packet.
*/
/** @param textures The new texture(s) to load for the entity. Called when loaded from a packet. */
public void loadTextures( String[] textures ) {
try {
loadTexture( textures[0] );
@ -275,42 +268,51 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
}
}
/** @return The render scale for the entity. */
/** @return The render scale for the entity, including any applied random scaling. */
public float getRenderScale() { return theEntity.getEntityData().get( renderScale ); }
/** Sets the overall render scale for the entity. */
public void setRenderScale( float scale ) {
if( !theEntity.level.isClientSide ) {
theEntity.getEntityData().set( renderScale, scale );
}
if( !theEntity.level.isClientSide ) theEntity.getEntityData().set( renderScale, scale );
}
public float getFamilyBaseScale() { return familyScale; }
//** @return The base render scale for the entity's mob family. */
//public float getFamilyBaseScale() { return familyScale; }
public float getBaseScaleForPreScaledValues() { return getBaseScale() / getFamilyBaseScale(); }
//** @return The render scale for the entity without its family scale factored in; used to correct scaling for pre-scaled vanilla values. */
//public float getBaseScaleForPreScaledValues() { return getBaseScale() / getFamilyBaseScale(); }
public float getBaseScale() { return baseScale; }
public void setBaseScale( float newBaseScale ) {
baseScale = newBaseScale;
setRenderScale( nextScale() );
}
/** @return The base render scale for the entity, which is a property of the mob species. */
public float getBaseScale() { return theEntity.getSpecies().bestiaryInfo.baseScale; }
/** @return A random render scale based on config settings. */
private float nextScale() {
// if( Config.get().GENERAL.RANDOM_SCALING > 0.0F ) { TODO configs
// return baseScale * (1.0F + (theEntity.getRNG().nextFloat() - 0.5F) * Config.get().GENERAL.RANDOM_SCALING);
// }
return baseScale;
// Don't do random on client side stuff
if( theEntity.level == null || theEntity.level.isClientSide() ) return getBaseScale();
// Prioritize most specific value available
final MobFamily.Species<? extends T> species = theEntity.getSpecies();
final double randomScaling;
if( species.config.GENERAL.randomScaling.get() >= 0.0 )
randomScaling = species.config.GENERAL.randomScaling.get();
else if( species.family.config.GENERAL.familyRandomScaling.get() >= 0.0 )
randomScaling = species.family.config.GENERAL.familyRandomScaling.get();
else
randomScaling = Config.MAIN.GENERAL.masterRandomScaling.get();
return randomScaling <= 0.0 ? getBaseScale() :
getBaseScale() * (1.0F + (theEntity.getRandom().nextFloat() - 0.5F) * 2.0F * (float) randomScaling);
}
public void setRegenerationTime( int ticks ) { healTimeMax = ticks; }
private void setRegenerationTime( int ticks ) { healTimeMax = ticks; }
public float getFallDamageMultiplier() { return fallDamageMultiplier; }
public void setFallDamageMultiplier( float value ) { fallDamageMultiplier = value; }
private void setFallDamageMultiplier( float value ) { fallDamageMultiplier = value; }
public boolean isImmuneToFire() { return isImmuneToFire; }
public void setImmuneToFire( boolean value ) {
private void setImmuneToFire( boolean value ) {
isImmuneToFire = value;
if( value ) {
theEntity.setPathfindingMalus( PathNodeType.LAVA, PathNodeType.WATER.getMalus() );
@ -326,7 +328,7 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
public boolean isImmuneToBurning() { return isImmuneToBurning; }
public void setImmuneToBurning( boolean value ) {
private void setImmuneToBurning( boolean value ) {
theEntity.clearFire();
isImmuneToBurning = value;
if( value ) {
@ -341,23 +343,23 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
public boolean allowLeashing() { return allowLeashing; }
public void setAllowLeashing( boolean value ) { allowLeashing = value; }
private void setAllowLeashing( boolean value ) { allowLeashing = value; }
public boolean ignorePressurePlates() { return ignorePressurePlates; }
public void setIgnorePressurePlates( boolean value ) { ignorePressurePlates = value; }
private void setIgnorePressurePlates( boolean value ) { ignorePressurePlates = value; }
public boolean canBreatheInWater() { return canBreatheInWater; }
public void setCanBreatheInWater( boolean value ) { canBreatheInWater = value; }
private void setCanBreatheInWater( boolean value ) { canBreatheInWater = value; }
public boolean ignoreWaterPush() { return ignoreWaterPush; }
public void setIgnoreWaterPush( boolean value ) { ignoreWaterPush = value; }
private void setIgnoreWaterPush( boolean value ) { ignoreWaterPush = value; }
public boolean isDamagedByWater() { return isDamagedByWater; }
public void setDamagedByWater( boolean value ) {
private void setDamagedByWater( boolean value ) {
isDamagedByWater = value;
theEntity.setPathfindingMalus( PathNodeType.WATER, value ? PathNodeType.LAVA.getMalus() : PathNodeType.WATER.getMalus() );
}
@ -368,12 +370,10 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
* @param block The block state to test.
* @return True if the block is allowed to apply its stuck speed multiplier.
*/
public boolean canBeStuckIn( BlockState block ) { return !immuneToStickyBlocks.contains( block.getBlock().getDescriptionId() ); }
public boolean canBeStuckIn( BlockState block ) { return !immuneToStickyBlocks.contains( block.getBlock() ); }
/** @param blocks The sticky block(s) to grant immunity from. */
public void addStickyBlockImmunity( Block... blocks ) {
for( Block block : blocks ) immuneToStickyBlocks.add( block.getDescriptionId() );
}
private void addStickyBlockImmunity( Collection<Block> blocks ) { immuneToStickyBlocks.addAll( blocks ); }
/**
* Tests a potion effect to see if it is applicable to the entity.
@ -387,14 +387,38 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
switch( event.getResult() ) {
case DENY: return false;
case ALLOW: return true;
default: return !immuneToPotions.contains( effect.getDescriptionId() );
default: return !immuneToPotions.contains( effect.getEffect() );
}
}
/** @param effects The effect(s) to grant immunity from. */
public void addPotionImmunity( Effect... effects ) {
for( Effect effect : effects ) immuneToPotions.add( effect.getDescriptionId() );
}
private void addPotionImmunity( Collection<Effect> effects ) { immuneToPotions.addAll( effects ); }
public float getRangedAttackDamage() { return rangedAttackDamage; }
public void setRangedAttackDamage( float value ) { rangedAttackDamage = value; }
public float getRangedAttackSpread() { return rangedAttackSpread; }
public void setRangedAttackSpread( float value ) { rangedAttackSpread = value; }
public float getRangedWalkSpeed() { return rangedWalkSpeed; }
public void setRangedWalkSpeed( float value ) { rangedWalkSpeed = value; }
public int getRangedAttackCooldown() { return rangedAttackCooldown; }
public void setRangedAttackCooldown( int value ) { rangedAttackCooldown = value; }
public int getRangedAttackMaxCooldown() { return rangedAttackMaxCooldown; }
public void setRangedAttackMaxCooldown( int value ) { rangedAttackMaxCooldown = value; }
public float getRangedAttackMaxRange() { return rangedAttackMaxRange; }
public void setRangedAttackMaxRange( float value ) { rangedAttackMaxRange = value; }
public void disableRangedAttack() { setRangedAttackMaxRange( 0.0F ); }
/**
* Saves this data to NBT.
@ -403,22 +427,14 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
*/
public void writeToNBT( CompoundNBT tag ) {
tag.putFloat( TAG_RENDER_SCALE, getRenderScale() );
tag.putInt( TAG_EXPERIENCE, theEntity.getExperience() );
tag.putByte( TAG_REGENERATION, (byte) healTimeMax );
tag.putString( TAG_TEXTURE, texture.toString() );
tag.putString( TAG_TEXTURE_EYES, textureEyes == null ? "" : textureEyes.toString() );
tag.putString( TAG_TEXTURE_OVER, textureOverlay == null ? "" : textureOverlay.toString() );
// Arrow AI
tag.putFloat( TAG_ARROW_DAMAGE, rangedAttackDamage );
tag.putFloat( TAG_ARROW_SPREAD, rangedAttackSpread );
tag.putFloat( TAG_ARROW_WALK_SPEED, rangedWalkSpeed );
tag.putShort( TAG_ARROW_REFIRE_MIN, (short) rangedAttackCooldown );
tag.putShort( TAG_ARROW_REFIRE_MAX, (short) rangedAttackMaxCooldown );
tag.putFloat( TAG_ARROW_RANGE, rangedAttackMaxRange );
// Abilities
// Capabilities
tag.putInt( TAG_EXPERIENCE, theEntity.getExperience() );
tag.putByte( TAG_REGENERATION, (byte) healTimeMax );
tag.putFloat( TAG_FALL_MULTI, getFallDamageMultiplier() );
tag.putBoolean( TAG_FIRE_IMMUNE, isImmuneToFire() );
tag.putBoolean( TAG_BURN_IMMUNE, isImmuneToBurning() );
@ -429,16 +445,32 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
tag.putBoolean( TAG_WATER_DAMAGE, isDamagedByWater() );
final ListNBT stickyBlocksTag = new ListNBT();
for( String blockName : immuneToStickyBlocks ) {
stickyBlocksTag.add( StringNBT.valueOf( blockName ) );
for( Block block : immuneToStickyBlocks ) {
final ResourceLocation regKey = ForgeRegistries.BLOCKS.getKey( block );
if( regKey != null ) stickyBlocksTag.add( StringNBT.valueOf( SpecialMobs.toString( regKey ) ) );
}
tag.put( TAG_STICKY_IMMUNE, stickyBlocksTag );
final ListNBT potionsTag = new ListNBT();
for( String potionName : immuneToPotions ) {
potionsTag.add( StringNBT.valueOf( potionName ) );
for( Effect effect : immuneToPotions ) {
final ResourceLocation regKey = ForgeRegistries.POTIONS.getKey( effect );
if( regKey != null ) potionsTag.add( StringNBT.valueOf( SpecialMobs.toString( regKey ) ) );
}
tag.put( TAG_POTION_IMMUNE, potionsTag );
// Ranged attack stats (optional)
if( getRangedAttackDamage() >= 0.0F )
tag.putFloat( TAG_ARROW_DAMAGE, getRangedAttackDamage() );
if( getRangedAttackSpread() >= 0.0F )
tag.putFloat( TAG_ARROW_SPREAD, getRangedAttackSpread() );
if( getRangedWalkSpeed() >= 0.0F )
tag.putFloat( TAG_ARROW_WALK_SPEED, getRangedWalkSpeed() );
if( getRangedAttackCooldown() >= 0 )
tag.putShort( TAG_ARROW_REFIRE_MIN, (short) getRangedAttackCooldown() );
if( getRangedAttackMaxCooldown() >= 0 )
tag.putShort( TAG_ARROW_REFIRE_MAX, (short) getRangedAttackMaxCooldown() );
if( getRangedAttackMaxRange() >= 0.0F )
tag.putFloat( TAG_ARROW_RANGE, getRangedAttackMaxRange() );
}
/**
@ -450,12 +482,6 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
if( tag.contains( TAG_RENDER_SCALE, NBT_TYPE_NUMERICAL ) ) {
setRenderScale( tag.getFloat( TAG_RENDER_SCALE ) );
}
if( tag.contains( TAG_EXPERIENCE, NBT_TYPE_NUMERICAL ) ) {
theEntity.setExperience( tag.getInt( TAG_EXPERIENCE ) );
}
if( tag.contains( TAG_REGENERATION, NBT_TYPE_NUMERICAL ) ) {
healTimeMax = tag.getByte( TAG_REGENERATION );
}
try {
if( tag.contains( TAG_TEXTURE, NBT_TYPE_STRING ) ) {
@ -472,27 +498,13 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
SpecialMobs.LOG.warn( "Failed to load textures from NBT! " + theEntity.toString() );
}
// Arrow AI
if( tag.contains( TAG_ARROW_DAMAGE, NBT_TYPE_NUMERICAL ) ) {
rangedAttackDamage = tag.getFloat( TAG_ARROW_DAMAGE );
// Capabilities
if( tag.contains( TAG_EXPERIENCE, NBT_TYPE_NUMERICAL ) ) {
theEntity.setExperience( tag.getInt( TAG_EXPERIENCE ) );
}
if( tag.contains( TAG_ARROW_SPREAD, NBT_TYPE_NUMERICAL ) ) {
rangedAttackSpread = tag.getFloat( TAG_ARROW_SPREAD );
if( tag.contains( TAG_REGENERATION, NBT_TYPE_NUMERICAL ) ) {
healTimeMax = tag.getByte( TAG_REGENERATION );
}
if( tag.contains( TAG_ARROW_WALK_SPEED, NBT_TYPE_NUMERICAL ) ) {
rangedWalkSpeed = tag.getFloat( TAG_ARROW_WALK_SPEED );
}
if( tag.contains( TAG_ARROW_REFIRE_MIN, NBT_TYPE_NUMERICAL ) ) {
rangedAttackCooldown = tag.getShort( TAG_ARROW_REFIRE_MIN );
}
if( tag.contains( TAG_ARROW_REFIRE_MAX, NBT_TYPE_NUMERICAL ) ) {
rangedAttackMaxCooldown = tag.getShort( TAG_ARROW_REFIRE_MAX );
}
if( tag.contains( TAG_ARROW_RANGE, NBT_TYPE_NUMERICAL ) ) {
rangedAttackMaxRange = tag.getFloat( TAG_ARROW_RANGE );
}
// Abilities
if( tag.contains( TAG_FALL_MULTI, NBT_TYPE_NUMERICAL ) ) {
setFallDamageMultiplier( tag.getFloat( TAG_FALL_MULTI ) );
}
@ -502,12 +514,6 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
if( tag.contains( TAG_BURN_IMMUNE, NBT_TYPE_NUMERICAL ) ) {
setImmuneToBurning( tag.getBoolean( TAG_BURN_IMMUNE ) );
}
if( tag.contains( TAG_LEASHABLE, NBT_TYPE_NUMERICAL ) ) {
setAllowLeashing( tag.getBoolean( TAG_LEASHABLE ) );
}
if( tag.contains( TAG_TRAP_IMMUNE, NBT_TYPE_NUMERICAL ) ) {
setIgnorePressurePlates( tag.getBoolean( TAG_TRAP_IMMUNE ) );
}
if( tag.contains( TAG_DROWN_IMMUNE, NBT_TYPE_NUMERICAL ) ) {
setCanBreatheInWater( tag.getBoolean( TAG_DROWN_IMMUNE ) );
}
@ -517,19 +523,49 @@ public class SpecialMobData<T extends LivingEntity & ISpecialMob<T>> {
if( tag.contains( TAG_WATER_DAMAGE, NBT_TYPE_NUMERICAL ) ) {
setDamagedByWater( tag.getBoolean( TAG_WATER_DAMAGE ) );
}
if( tag.contains( TAG_LEASHABLE, NBT_TYPE_NUMERICAL ) ) {
setAllowLeashing( tag.getBoolean( TAG_LEASHABLE ) );
}
if( tag.contains( TAG_TRAP_IMMUNE, NBT_TYPE_NUMERICAL ) ) {
setIgnorePressurePlates( tag.getBoolean( TAG_TRAP_IMMUNE ) );
}
if( tag.contains( TAG_STICKY_IMMUNE, NBT_TYPE_LIST ) ) {
final ListNBT stickyBlocksTag = tag.getList( TAG_STICKY_IMMUNE, NBT_TYPE_STRING );
immuneToStickyBlocks.clear();
for( int i = 0; i < stickyBlocksTag.size(); i++ ) {
immuneToStickyBlocks.add( stickyBlocksTag.getString( i ) );
final Block block = ForgeRegistries.BLOCKS.getValue( new ResourceLocation( stickyBlocksTag.getString( i ) ) );
if( block != null && !block.is( Blocks.AIR ) )
immuneToStickyBlocks.add( block );
}
}
if( tag.contains( TAG_POTION_IMMUNE, NBT_TYPE_LIST ) ) {
final ListNBT potionsTag = tag.getList( TAG_POTION_IMMUNE, NBT_TYPE_STRING );
immuneToPotions.clear();
for( int i = 0; i < potionsTag.size(); i++ ) {
immuneToPotions.add( potionsTag.getString( i ) );
final Effect effect = ForgeRegistries.POTIONS.getValue( new ResourceLocation( potionsTag.getString( i ) ) );
if( effect != null )
immuneToPotions.add( effect );
}
}
// Ranged attack stats
if( tag.contains( TAG_ARROW_DAMAGE, NBT_TYPE_NUMERICAL ) ) {
setRangedAttackDamage( tag.getFloat( TAG_ARROW_DAMAGE ) );
}
if( tag.contains( TAG_ARROW_SPREAD, NBT_TYPE_NUMERICAL ) ) {
setRangedAttackSpread( tag.getFloat( TAG_ARROW_SPREAD ) );
}
if( tag.contains( TAG_ARROW_WALK_SPEED, NBT_TYPE_NUMERICAL ) ) {
setRangedWalkSpeed( tag.getFloat( TAG_ARROW_WALK_SPEED ) );
}
if( tag.contains( TAG_ARROW_REFIRE_MIN, NBT_TYPE_NUMERICAL ) ) {
setRangedAttackCooldown( tag.getShort( TAG_ARROW_REFIRE_MIN ) );
}
if( tag.contains( TAG_ARROW_REFIRE_MAX, NBT_TYPE_NUMERICAL ) ) {
setRangedAttackMaxCooldown( tag.getShort( TAG_ARROW_REFIRE_MAX ) );
}
if( tag.contains( TAG_ARROW_RANGE, NBT_TYPE_NUMERICAL ) ) {
setRangedAttackMaxRange( tag.getFloat( TAG_ARROW_RANGE ) );
}
}
}

View file

@ -0,0 +1,12 @@
package fathertoast.specialmobs.common.entity.ai;
public interface IExplodingMob {
/** Sets this exploding entity's swell direction. */
void setSwellDir( int value );
/** @return This exploding entity's swell direction. */
int getSwellDir();
/** @return Additional range from its target at which this entity will start to explode. */
default double getExtraRange() { return 0.0; }
}

View file

@ -1,6 +1,6 @@
package fathertoast.specialmobs.common.entity.ai.goal;
import fathertoast.specialmobs.common.entity.creeper._SpecialCreeperEntity;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.entity.zombie.MadScientistZombieEntity;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.monster.CreeperEntity;
@ -85,12 +85,7 @@ public class ChargeCreeperGoal<T extends MadScientistZombieEntity> extends Goal
madman.getLookControl().setLookAt( this.creeper.getX(), this.creeper.getEyeY(), this.creeper.getZ() );
if( madman.distanceTo( creeper ) < 1.5D ) {
creeper.getEntityData().set( CreeperEntity.DATA_IS_POWERED, true );
// HEE HEE HEE HAW
if( creeper instanceof _SpecialCreeperEntity && creeper.level.random.nextDouble() < 0.1 ) { // TODO config
((_SpecialCreeperEntity) creeper).setSupercharged( true );
}
MobHelper.charge( creeper );
madman.level.playSound( null, creeper.getX() + 0.5D, creeper.getY(), creeper.getZ() + 0.5D, SoundEvents.BEE_STING, SoundCategory.HOSTILE, 0.9F, 1.0F );
creeper = null;
}

View file

@ -58,7 +58,7 @@ public class SpecialBlazeAttackGoal extends Goal {
else lastSeen++;
final double distanceSqr = blaze.distanceToSqr( target );
final float rangeSq = data.rangedAttackMaxRange * data.rangedAttackMaxRange;
final float rangeSq = data.getRangedAttackMaxRange() * data.getRangedAttackMaxRange();
if( distanceSqr < getAttackReachSqr( target ) ) {
if( canSee && attackTime <= 0 ) {
attackTime = 20;
@ -71,14 +71,14 @@ public class SpecialBlazeAttackGoal extends Goal {
attackStep++;
if( attackStep == 1 ) {
attackTime = data.rangedAttackMaxCooldown - data.rangedAttackCooldown;
attackTime = data.getRangedAttackCooldown();
blaze.setCharged( true );
}
else if( attackStep <= 1 + blaze.fireballBurstCount ) {
attackTime = blaze.fireballBurstDelay;
}
else {
attackTime = data.rangedAttackCooldown;
attackTime = data.getRangedAttackMaxCooldown() - data.getRangedAttackCooldown();
attackStep = 0;
blaze.setCharged( false );
}

View file

@ -38,19 +38,19 @@ public class SpecialGhastFireballAttackGoal extends Goal {
final SpecialMobData<_SpecialGhastEntity> data = ghast.getSpecialData();
if( target.distanceToSqr( ghast ) < data.rangedAttackMaxRange * data.rangedAttackMaxRange && ghast.canSee( target ) ) {
if( target.distanceToSqr( ghast ) < data.getRangedAttackMaxRange() * data.getRangedAttackMaxRange() && ghast.canSee( target ) ) {
chargeTime++;
if( chargeTime == (data.rangedAttackCooldown >> 1) && !ghast.isSilent() ) {
if( chargeTime == (data.getRangedAttackCooldown() >> 1) && !ghast.isSilent() ) {
ghast.level.levelEvent( null, References.EVENT_GHAST_WARN, ghast.blockPosition(), 0 );
}
if( chargeTime >= data.rangedAttackCooldown ) {
if( chargeTime >= data.getRangedAttackCooldown() ) {
ghast.performRangedAttack( target, 1.0F );
chargeTime = data.rangedAttackCooldown - data.rangedAttackMaxCooldown;
chargeTime = data.getRangedAttackCooldown() - data.getRangedAttackMaxCooldown();
}
}
else if( chargeTime > 0 ) {
chargeTime--;
}
ghast.setCharging( chargeTime > (data.rangedAttackCooldown >> 1) );
ghast.setCharging( chargeTime > (data.getRangedAttackCooldown() >> 1) );
}
}

View file

@ -28,8 +28,8 @@ public class SpecialGhastLookAroundGoal extends Goal {
public void tick() {
final LivingEntity target = ghast.getTarget();
if( target != null ) {
final float range = ghast.getSpecialData().rangedAttackMaxRange > 0.0F ?
ghast.getSpecialData().rangedAttackMaxRange :
final float range = ghast.getSpecialData().getRangedAttackMaxRange() > 0.0F ?
ghast.getSpecialData().getRangedAttackMaxRange() :
16.0F; // Range for melee ghast to face target
if( target.distanceToSqr( ghast ) < range * range ) {

View file

@ -0,0 +1,49 @@
package fathertoast.specialmobs.common.entity.ai.goal;
import fathertoast.specialmobs.common.entity.ai.IExplodingMob;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MobEntity;
import net.minecraft.entity.ai.goal.Goal;
import java.util.EnumSet;
/**
* The "creeper swell" goal repurposed for use on other mobs.
* <p>
* {@link net.minecraft.entity.ai.goal.CreeperSwellGoal}
*/
public class SpecialSwellGoal<T extends MobEntity & IExplodingMob> extends Goal {
private final T mob;
private LivingEntity target;
public SpecialSwellGoal( T entity ) {
mob = entity;
setFlags( EnumSet.of( Flag.MOVE ) );
}
public boolean canUse() {
final LivingEntity target = mob.getTarget();
return mob.getSwellDir() > 0 || target != null && mob.distanceToSqr( target ) < 9.0F + mob.getExtraRange();
}
public void start() {
mob.getNavigation().stop();
target = mob.getTarget();
}
public void stop() {
mob.setSwellDir( -1 );
target = null;
}
public void tick() {
if( target == null || mob.distanceToSqr( target ) > 49.0 || !mob.getSensing().canSee( target ) ) {
mob.setSwellDir( -1 );
}
else {
mob.setSwellDir( 1 );
}
}
}

View file

@ -3,14 +3,11 @@ package fathertoast.specialmobs.common.entity.blaze;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.world.World;
@ -28,17 +25,12 @@ public class CinderBlazeEntity extends _SpecialBlazeEntity {
public static MobFamily.Species<CinderBlazeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.5F, 0.9F );
return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.DISABLED );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() )
.addAttribute( Attributes.ATTACK_DAMAGE, -2.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.3 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFFC0CB ).weight( BestiaryInfo.DefaultWeight.DISABLED )
.size( 0.5F, 0.5F, 0.9F )
.experience( 2 ).disableRangedAttack()
.addToAttribute( Attributes.ATTACK_DAMAGE, -2.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.3 );
}
@SpecialMob.LanguageProvider
@ -55,23 +47,18 @@ public class CinderBlazeEntity extends _SpecialBlazeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<CinderBlazeEntity> getVariantFactory() { return CinderBlazeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends CinderBlazeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public CinderBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.5F );
xpReward = 1;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackDamage -= 1.0F;
disableRangedAI();
}
public CinderBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
target.setSecondsOnFire( 4 );
}

View file

@ -20,7 +20,7 @@ import net.minecraft.nbt.CompoundNBT;
import net.minecraft.potion.PotionUtils;
import net.minecraft.potion.Potions;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -37,9 +37,11 @@ public class ConflagrationBlazeEntity extends _SpecialBlazeEntity {
public static MobFamily.Species<ConflagrationBlazeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.9F, 2.7F );
return new BestiaryInfo( 0xFFF87E, BestiaryInfo.BaseWeight.LOW );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFFF87E ).weight( BestiaryInfo.DefaultWeight.LOW )
.uniqueTextureBaseOnly()
.size( 1.5F, 0.9F, 2.7F )
.addExperience( 4 );
}
@SpecialMob.LanguageProvider
@ -58,6 +60,11 @@ public class ConflagrationBlazeEntity extends _SpecialBlazeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<ConflagrationBlazeEntity> getVariantFactory() { return ConflagrationBlazeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends ConflagrationBlazeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
@ -68,13 +75,10 @@ public class ConflagrationBlazeEntity extends _SpecialBlazeEntity {
/** The level of increased attack damage gained. */
private int growthLevel;
public ConflagrationBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.5F );
xpReward += 4;
}
public ConflagrationBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
if( target instanceof LivingEntity ) {
MobHelper.stealLife( this, (LivingEntity) target, 2.0F );
@ -91,12 +95,6 @@ public class ConflagrationBlazeEntity extends _SpecialBlazeEntity {
if( !level.isClientSide() && growthLevel < 7 ) {
growthLevel++;
getSpecialData().rangedAttackDamage += 0.5F;
getSpecialData().rangedAttackCooldown -= 4;
getSpecialData().rangedAttackMaxCooldown -= 4;
if( growthLevel == 7 ) fireballBurstCount++;
updateFeedingLevels();
}
amount /= 2.0F;
@ -107,6 +105,13 @@ public class ConflagrationBlazeEntity extends _SpecialBlazeEntity {
/** Recalculates the modifiers associated with this entity's feeding level counters. */
private void updateFeedingLevels() {
if( level != null && !level.isClientSide ) {
final int cooldownReduction = MathHelper.floor( getConfig().GENERAL.rangedAttackCooldown.get() * growthLevel * 0.1 );
getSpecialData().setRangedAttackCooldown( getConfig().GENERAL.rangedAttackCooldown.get() - cooldownReduction );
getSpecialData().setRangedAttackMaxCooldown( getConfig().GENERAL.rangedAttackMaxCooldown.get() - cooldownReduction );
fireballBurstCount = getConfig().BLAZES.fireballBurstCount.get();
if( growthLevel >= 7 ) fireballBurstCount++;
final ModifiableAttributeInstance damage = getAttribute( Attributes.ATTACK_DAMAGE );
//noinspection ConstantConditions
damage.removeModifier( DAMAGE_BOOST.getId() );
@ -130,12 +135,4 @@ public class ConflagrationBlazeEntity extends _SpecialBlazeEntity {
growthLevel = saveTag.getByte( References.TAG_GROWTH_LEVEL );
updateFeedingLevels();
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "conflagration" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -4,17 +4,14 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -30,17 +27,12 @@ public class EmberBlazeEntity extends _SpecialBlazeEntity {
public static MobFamily.Species<EmberBlazeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x000000 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 10.0 )
.addAttribute( Attributes.ARMOR, 10.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.3 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x000000 )
.uniqueTextureBaseOnly()
.addExperience( 2 ).disableRangedAttack()
.addToAttribute( Attributes.MAX_HEALTH, 10.0 ).addToAttribute( Attributes.ARMOR, 10.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.3 );
}
@SpecialMob.LanguageProvider
@ -58,32 +50,21 @@ public class EmberBlazeEntity extends _SpecialBlazeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<EmberBlazeEntity> getVariantFactory() { return EmberBlazeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends EmberBlazeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public EmberBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
disableRangedAI();
}
public EmberBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
if( target instanceof LivingEntity ) {
MobHelper.causeLifeLoss( (LivingEntity) target, 2.0F );
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "ember" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,18 +3,17 @@ package fathertoast.specialmobs.common.entity.blaze;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.config.species.BlazeSpeciesConfig;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.projectile.FireballEntity;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
@ -31,16 +30,18 @@ public class HellfireBlazeEntity extends _SpecialBlazeEntity {
public static MobFamily.Species<HellfireBlazeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.7F, 1.99F );
return new BestiaryInfo( 0xDDDDDD );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xDDDDDD )
.uniqueTextureBaseOnly()
.size( 1.1F, 0.7F, 1.99F )
.addExperience( 2 )
.fireballAttack( 0.05, 60, 100, 40.0 )
.addToAttribute( Attributes.MAX_HEALTH, 10.0 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 10.0 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new BlazeSpeciesConfig( species, 1, 0 );
}
@SpecialMob.LanguageProvider
@ -58,31 +59,25 @@ public class HellfireBlazeEntity extends _SpecialBlazeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<HellfireBlazeEntity> getVariantFactory() { return HellfireBlazeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends HellfireBlazeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
/** The base explosion strength of this blaze's fireballs. */
private int explosionPower = 2;
public HellfireBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.1F );
xpReward += 2;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackSpread *= 0.05F;
setRangedAI( 1, 0, 60, 100, 40.0F );
}
public HellfireBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) { super( entityType, world ); }
/** Called to attack the target with a ranged attack. */
@Override
public void performRangedAttack( LivingEntity target, float damageMulti ) {
if( !isSilent() ) level.levelEvent( null, References.EVENT_BLAZE_SHOOT, blockPosition(), 0 );
final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().rangedAttackSpread;
final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().getRangedAttackSpread();
final double dX = target.getX() - getX() + getRandom().nextGaussian() * accelVariance;
final double dY = target.getY( 0.5 ) - getY( 0.5 );
final double dZ = target.getZ() - getZ() + getRandom().nextGaussian() * accelVariance;
@ -105,12 +100,4 @@ public class HellfireBlazeEntity extends _SpecialBlazeEntity {
if( saveTag.contains( References.TAG_EXPLOSION_POWER, References.NBT_TYPE_NUMERICAL ) )
explosionPower = saveTag.getByte( References.TAG_EXPLOSION_POWER );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "hellfire" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,18 +3,17 @@ package fathertoast.specialmobs.common.entity.blaze;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.config.species.BlazeSpeciesConfig;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -30,15 +29,17 @@ public class InfernoBlazeEntity extends _SpecialBlazeEntity {
public static MobFamily.Species<InfernoBlazeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xF14F00 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xF14F00 )
.uniqueTextureBaseOnly()
.addExperience( 2 )
.fireballAttack( 3.0, 80, 100, 20.0 )
.addToAttribute( Attributes.MAX_HEALTH, 10.0 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 10.0 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new BlazeSpeciesConfig( species, 12, 2 );
}
@SpecialMob.LanguageProvider
@ -57,33 +58,21 @@ public class InfernoBlazeEntity extends _SpecialBlazeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<InfernoBlazeEntity> getVariantFactory() { return InfernoBlazeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends InfernoBlazeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public InfernoBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackSpread *= 3.0F;
setRangedAI( 12, 2, 80, 100, 20.0F );
}
public InfernoBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
if( target instanceof LivingEntity ) {
MobHelper.causeLifeLoss( (LivingEntity) target, 2.0F );
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "inferno" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -4,7 +4,6 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
@ -13,7 +12,6 @@ import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.effect.LightningBoltEntity;
import net.minecraft.item.Items;
@ -21,7 +19,6 @@ import net.minecraft.tags.FluidTags;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.IndirectEntityDamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
@ -42,17 +39,12 @@ public class JoltBlazeEntity extends _SpecialBlazeEntity {
public static MobFamily.Species<JoltBlazeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x499CAE );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 10.0 )
.addAttribute( Attributes.ARMOR, 10.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.3 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x499CAE )
.uniqueTextureBaseOnly()
.addExperience( 2 ).disableRangedAttack()
.addToAttribute( Attributes.MAX_HEALTH, 10.0 ).addToAttribute( Attributes.ARMOR, 10.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.3 );
}
@SpecialMob.LanguageProvider
@ -70,21 +62,18 @@ public class JoltBlazeEntity extends _SpecialBlazeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<JoltBlazeEntity> getVariantFactory() { return JoltBlazeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends JoltBlazeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public JoltBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
disableRangedAI();
}
public JoltBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
if( target instanceof LivingEntity ) {
MobHelper.causeLifeLoss( (LivingEntity) target, 2.0F );
@ -129,6 +118,10 @@ public class JoltBlazeEntity extends _SpecialBlazeEntity {
return success;
}
/** Called when this entity is struck by lightning. */
@Override
public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { }
/** @return Teleports this "enderman" to a random nearby position; returns true if successful. */
protected boolean teleport() {
if( level.isClientSide() || !isAlive() ) return false;
@ -174,16 +167,4 @@ public class JoltBlazeEntity extends _SpecialBlazeEntity {
}
return success;
}
/** Called when this entity is struck by lightning. */
@Override
public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { }
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "jolt" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,16 +3,15 @@ package fathertoast.specialmobs.common.entity.blaze;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.config.species.WildfireBlazeSpeciesConfig;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.*;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
@ -30,16 +29,19 @@ public class WildfireBlazeEntity extends _SpecialBlazeEntity {
public static MobFamily.Species<WildfireBlazeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.9F, 2.7F );
return new BestiaryInfo( 0xF4EE32 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xF4EE32 )
.uniqueTextureBaseOnly()
.size( 1.5F, 0.9F, 2.7F )
.addExperience( 2 ).regen( 40 )
.fireballAttack( 0.1, 30, 50, 20.0 )
.addToAttribute( Attributes.MAX_HEALTH, 20.0 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 20.0 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new WildfireBlazeSpeciesConfig( species, 1, 0,
3, 6, 4, 10 );
}
@SpecialMob.LanguageProvider
@ -58,6 +60,11 @@ public class WildfireBlazeEntity extends _SpecialBlazeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<WildfireBlazeEntity> getVariantFactory() { return WildfireBlazeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends WildfireBlazeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
@ -68,22 +75,16 @@ public class WildfireBlazeEntity extends _SpecialBlazeEntity {
public WildfireBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.5F );
getSpecialData().setRegenerationTime( 40 );
xpReward += 2;
babies = 3 + random.nextInt( 4 );
summons = 4 + random.nextInt( 7 );
babies = getConfig().WILDFIRE.babies.next( random );
summons = getConfig().WILDFIRE.summons.next( random );
}
/** Override to change this entity's AI goals. */
/** @return This entity's species config. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackSpread *= 0.1F;
setRangedAI( 1, 0, 30, 50, 20.0F );
}
public WildfireBlazeSpeciesConfig getConfig() { return (WildfireBlazeSpeciesConfig) getSpecies().config; }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
target.setSecondsOnFire( 8 );
}
@ -157,12 +158,4 @@ public class WildfireBlazeEntity extends _SpecialBlazeEntity {
if( saveTag.contains( References.TAG_SUMMONS, References.NBT_TYPE_NUMERICAL ) )
summons = saveTag.getByte( References.TAG_SUMMONS );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "wildfire" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,7 +3,8 @@ package fathertoast.specialmobs.common.entity.blaze;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.config.species.BlazeSpeciesConfig;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.entity.ai.AIHelper;
@ -25,14 +26,10 @@ import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@ -46,15 +43,24 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob
public static MobFamily.Species<_SpecialBlazeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xFFF87E );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFFF87E )
.vanillaTextureBaseOnly( "textures/entity/blaze.png" )
.experience( 10 ).fireImmune().waterSensitive()
.fireballAttack( 1.0, 60, 100, 48.0 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return BlazeEntity.createAttributes();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new BlazeSpeciesConfig( species, 3, 6 );
}
/** @return This entity's species config. */
public BlazeSpeciesConfig getConfig() { return (BlazeSpeciesConfig) getSpecies().config; }
@SpecialMob.AttributeSupplier
public static AttributeModifierMap.MutableAttribute createAttributes() { return BlazeEntity.createAttributes(); }
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Blaze",
@ -72,12 +78,6 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob
//--------------- Variant-Specific Breakouts ----------------
public _SpecialBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().initialize();
getSpecialData().setDamagedByWater( true );
}
/** Called in the MobEntity.class constructor to initialize AI goals. */
@Override
protected void registerGoals() {
@ -86,29 +86,12 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob
goalSelector.addGoal( 4, new SpecialBlazeAttackGoal( this ) );
AIHelper.replaceHurtByTarget( this, new SpecialHurtByTargetGoal( this, BlazeEntity.class ).setAlertOthers() );
getSpecialData().rangedAttackDamage = 2.0F;
setRangedAI( 3, 6, 60, 100, 48.0F );
registerVariantGoals();
}
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Helper method to set the ranged attack AI more easily. */
protected void disableRangedAI() {
setRangedAI( 0, 6, 60, 100, 0.0F );
}
/** Helper method to set the ranged attack AI more easily. */
protected void setRangedAI( int burstCount, int burstDelay, int chargeTime, int cooldownTime, float range ) {
fireballBurstCount = burstCount;
fireballBurstDelay = burstDelay;
getSpecialData().rangedAttackCooldown = cooldownTime;
getSpecialData().rangedAttackMaxCooldown = cooldownTime + chargeTime;
getSpecialData().rangedAttackMaxRange = range;
}
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
@ -119,6 +102,21 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob
/** Override to apply effects when this entity hits a target with a melee attack. */
protected void onVariantAttack( Entity target ) { }
/** Called to attack the target with a ranged attack. */
@Override
public void performRangedAttack( LivingEntity target, float damageMulti ) {
if( !isSilent() ) level.levelEvent( null, References.EVENT_BLAZE_SHOOT, blockPosition(), 0 );
final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().getRangedAttackSpread();
final double dX = target.getX() - getX() + getRandom().nextGaussian() * accelVariance;
final double dY = target.getY( 0.5 ) - getY( 0.5 );
final double dZ = target.getZ() - getZ() + getRandom().nextGaussian() * accelVariance;
final SmallFireballEntity fireball = new SmallFireballEntity( level, this, dX, dY, dZ );
fireball.setPos( getX(), getY( 0.5 ) + 0.5, getZ() );
level.addFreshEntity( fireball );
}
/** Override to save data to this entity's NBT data. */
public void addVariantSaveData( CompoundNBT saveTag ) { }
@ -136,35 +134,19 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob
/** The ticks between each shot in a burst. */
public int fireballBurstDelay;
public _SpecialBlazeEntity( EntityType<? extends _SpecialBlazeEntity> entityType, World world ) {
super( entityType, world );
fireballBurstCount = getConfig().BLAZES.fireballBurstCount.get();
fireballBurstDelay = getConfig().BLAZES.fireballBurstDelay.get();
getSpecialData().initialize();
}
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
specialData = new SpecialMobData<>( this, SCALE, 1.0F );
}
/** Called to attack the target with a ranged attack. */
@Override
public void performRangedAttack( LivingEntity target, float damageMulti ) {
if( !isSilent() ) level.levelEvent( null, References.EVENT_BLAZE_SHOOT, blockPosition(), 0 );
final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().rangedAttackSpread;
final double dX = target.getX() - getX() + getRandom().nextGaussian() * accelVariance;
final double dY = target.getY( 0.5 ) - getY( 0.5 );
final double dZ = target.getZ() - getZ() + getRandom().nextGaussian() * accelVariance;
final SmallFireballEntity fireball = new SmallFireballEntity( level, this, dX, dY, dZ );
fireball.setPos( getX(), getY( 0.5 ) + 0.5, getZ() );
level.addFreshEntity( fireball );
}
/** Called on spawn to initialize properties based on the world, difficulty, and the group it spawns with. */
@Nullable
public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason,
@Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) {
groupData = super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag );
// TODO ranged attack
return groupData;
specialData = new SpecialMobData<>( this, SCALE );
}
@ -174,7 +156,12 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob
/** @return This mob's special data. */
@Override
public SpecialMobData<_SpecialBlazeEntity> getSpecialData() { return specialData; }
public final SpecialMobData<_SpecialBlazeEntity> getSpecialData() { return specialData; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends _SpecialBlazeEntity> getSpecies() { return SPECIES; }
/** @return The experience that should be dropped by this entity. */
@Override
@ -184,16 +171,6 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob
@Override
public final void setExperience( int xp ) { xpReward = xp; }
static ResourceLocation GET_TEXTURE_PATH( String type ) {
return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "blaze/" + type + ".png" );
}
private static final ResourceLocation[] TEXTURES = { new ResourceLocation( "textures/entity/blaze.png" ) };
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- SpecialMobData Hooks ----------------

View file

@ -3,13 +3,10 @@ package fathertoast.specialmobs.common.entity.cavespider;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.world.World;
@ -27,18 +24,13 @@ public class BabyCaveSpiderEntity extends _SpecialCaveSpiderEntity {
public static MobFamily.Species<BabyCaveSpiderEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.6F, 0.4F );
return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.DISABLED );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCaveSpiderEntity.createAttributes() )
.multAttribute( Attributes.MAX_HEALTH, 1.0 / 3.0 )
.addAttribute( Attributes.ATTACK_DAMAGE, -1.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.3 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFFC0CB ).weight( BestiaryInfo.DefaultWeight.DISABLED )
.size( 0.4F, 0.6F, 0.4F )
.experience( 1 ).disableRangedAttack()
.multiplyAttribute( Attributes.MAX_HEALTH, 0.33 )
.addToAttribute( Attributes.ATTACK_DAMAGE, -1.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.3 );
}
@SpecialMob.LanguageProvider
@ -55,21 +47,13 @@ public class BabyCaveSpiderEntity extends _SpecialCaveSpiderEntity {
@SpecialMob.Factory
public static EntityType.IFactory<BabyCaveSpiderEntity> getVariantFactory() { return BabyCaveSpiderEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends BabyCaveSpiderEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public BabyCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.4F );
getSpecialData().rangedAttackDamage -= 1.0F;
getSpecialData().rangedAttackMaxRange = 0.0F;
xpReward = 1;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackDamage -= 1.0F;
getSpecialData().rangedAttackMaxRange = 0.0F;
}
public BabyCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) { super( entityType, world ); }
}

View file

@ -4,16 +4,12 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.goal.SpecialLeapAtTargetGoal;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -29,16 +25,11 @@ public class FlyingCaveSpiderEntity extends _SpecialCaveSpiderEntity {
public static MobFamily.Species<FlyingCaveSpiderEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x6388B2 );
//TODO theme - mountain
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCaveSpiderEntity.createAttributes() )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.2 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x6388B2 ).theme( BestiaryInfo.Theme.MOUNTAIN )
.uniqueTextureWithEyes()
.addExperience( 2 ).fallImmune().disableRangedAttack()
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.2 );
}
@SpecialMob.LanguageProvider
@ -56,30 +47,20 @@ public class FlyingCaveSpiderEntity extends _SpecialCaveSpiderEntity {
@SpecialMob.Factory
public static EntityType.IFactory<FlyingCaveSpiderEntity> getVariantFactory() { return FlyingCaveSpiderEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends FlyingCaveSpiderEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public FlyingCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setFallDamageMultiplier( 0.0F );
xpReward += 2;
}
public FlyingCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackMaxRange = 0.0F;
goalSelector.addGoal( 3, new SpecialLeapAtTargetGoal(
this, 10, 6.0F, 12.0F, 2.0F, 2.0F ) );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "flying" ),
GET_TEXTURE_PATH( "flying_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,20 +3,18 @@ package fathertoast.specialmobs.common.entity.cavespider;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.config.species.MotherSpiderSpeciesConfig;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ILivingEntityData;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvents;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
@ -35,20 +33,25 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
public static MobFamily.Species<MotherCaveSpiderEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.9F, 0.6F );
return new BestiaryInfo( 0xB300B3 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xB300B3 )
.uniqueTextureWithEyes()
.size( 0.8F, 0.9F, 0.6F )
.addExperience( 1 ).regen( 30 )
.addToAttribute( Attributes.MAX_HEALTH, 16.0 ).addToAttribute( Attributes.ARMOR, 6.0 )
.addToAttribute( Attributes.ATTACK_DAMAGE, 3.0 ).addToRangedDamage( 1.5 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCaveSpiderEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 16.0 )
.addAttribute( Attributes.ARMOR, 6.0 )
.addAttribute( Attributes.ATTACK_DAMAGE, 3.0 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new MotherSpiderSpeciesConfig( species, DEFAULT_SPIT_CHANCE,
2, 4, 2, 4 );
}
/** @return This entity's species config. */
@Override
public MotherSpiderSpeciesConfig getConfig() { return (MotherSpiderSpeciesConfig) getSpecies().config; }
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Mother Cave Spider",
@ -64,28 +67,23 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
@SpecialMob.Factory
public static EntityType.IFactory<MotherCaveSpiderEntity> getVariantFactory() { return MotherCaveSpiderEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends MotherCaveSpiderEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public MotherCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.8F );
getSpecialData().setRegenerationTime( 30 );
xpReward += 1;
babies = 2 + random.nextInt( 3 );
extraBabies = 2 + random.nextInt( 3 );
}
/** The number of babies spawned on death. */
private int babies;
/** The number of extra babies that can be spawned from hits. */
private int extraBabies;
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackDamage += 1.5F;
public MotherCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
babies = getConfig().MOTHER.babies.next( random );
extraBabies = getConfig().MOTHER.extraBabies.next( random );
}
/** @return Attempts to damage this entity; returns true if the hit was successful. */
@ -159,13 +157,4 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
if( saveTag.contains( References.TAG_EXTRA_BABIES, References.NBT_TYPE_NUMERICAL ) )
extraBabies = saveTag.getByte( References.TAG_EXTRA_BABIES );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "mother" ),
GET_TEXTURE_PATH( "mother_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,7 +3,8 @@ package fathertoast.specialmobs.common.entity.cavespider;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.config.species.WebSpiderSpeciesConfig;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
@ -11,12 +12,10 @@ import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.monster.SpiderEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
@ -33,19 +32,24 @@ public class WebCaveSpiderEntity extends _SpecialCaveSpiderEntity {
public static MobFamily.Species<WebCaveSpiderEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xE7E7E7, BestiaryInfo.BaseWeight.LOW );
//TODO theme - forest
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xE7E7E7 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FOREST )
.uniqueTextureWithEyes()
.addExperience( 2 )
.spitAttackMultiplied( 0.1, 1.0, 2.0F, 1.0 )
.addToAttribute( Attributes.MAX_HEALTH, 4.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.2 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCaveSpiderEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 4.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.2 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new WebSpiderSpeciesConfig( species, 0.01, 2, 6 );
}
/** @return This entity's species config. */
@Override
public WebSpiderSpeciesConfig getConfig() { return (WebSpiderSpeciesConfig) getSpecies().config; }
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Cave Weaver",
@ -61,18 +65,22 @@ public class WebCaveSpiderEntity extends _SpecialCaveSpiderEntity {
@SpecialMob.Factory
public static EntityType.IFactory<WebCaveSpiderEntity> getVariantFactory() { return WebCaveSpiderEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends WebCaveSpiderEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public WebCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
webCount = 2 + random.nextInt( 5 );
}
/** The number of cobwebs this spider can place. */
private int webCount;
public WebCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
webCount = getConfig().WEB.webCount.next( random );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
@ -113,13 +121,4 @@ public class WebCaveSpiderEntity extends _SpecialCaveSpiderEntity {
if( saveTag.contains( References.TAG_AMMO, References.NBT_TYPE_NUMERICAL ) )
webCount = saveTag.getByte( References.TAG_AMMO );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "web" ),
GET_TEXTURE_PATH( "web_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -4,18 +4,15 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.EffectType;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -31,16 +28,12 @@ public class WitchCaveSpiderEntity extends _SpecialCaveSpiderEntity {
public static MobFamily.Species<WitchCaveSpiderEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xDD0E0E, BestiaryInfo.BaseWeight.LOW );
//TODO theme - forest
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCaveSpiderEntity.createAttributes() )
.addAttribute( Attributes.ARMOR, 15.0 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xDD0E0E ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FOREST )
.uniqueTextureWithEyes()
.addExperience( 2 )
.spitAttackMultiplied( 0.1, 1.0, 2.0F, 1.0 )
.addToAttribute( Attributes.ARMOR, 15.0 );
}
@SpecialMob.LanguageProvider
@ -58,13 +51,15 @@ public class WitchCaveSpiderEntity extends _SpecialCaveSpiderEntity {
@SpecialMob.Factory
public static EntityType.IFactory<WitchCaveSpiderEntity> getVariantFactory() { return WitchCaveSpiderEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends WitchCaveSpiderEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public WitchCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
public WitchCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
@ -81,13 +76,4 @@ public class WitchCaveSpiderEntity extends _SpecialCaveSpiderEntity {
// Witch spider is immune to debuffs
return effect.getEffect().getCategory() != EffectType.HARMFUL && super.canBeAffected( effect );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "witch" ),
GET_TEXTURE_PATH( "witch_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,7 +3,8 @@ package fathertoast.specialmobs.common.entity.cavespider;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.config.species.SpiderSpeciesConfig;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.util.References;
@ -21,13 +22,9 @@ import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@ -41,15 +38,26 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
public static MobFamily.Species<_SpecialCaveSpiderEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xA80E0E );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xA80E0E ).familySize( 0.7F )
.vanillaTextureWithEyes( "textures/entity/spider/cave_spider.png", "textures/entity/spider_eyes.png" )
.experience( 5 ).spider()
.spitAttack( 1.0, 1.3, 40, 40, 10.0 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return CaveSpiderEntity.createAttributes();
protected static final double DEFAULT_SPIT_CHANCE = 0.05;
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new SpiderSpeciesConfig( species, DEFAULT_SPIT_CHANCE );
}
/** @return This entity's species config. */
public SpiderSpeciesConfig getConfig() { return (SpiderSpeciesConfig) getSpecies().config; }
@SpecialMob.AttributeSupplier
public static AttributeModifierMap.MutableAttribute createAttributes() { return CaveSpiderEntity.createAttributes(); }
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Cave Spider",
@ -67,21 +75,10 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
//--------------- Variant-Specific Breakouts ----------------
public _SpecialCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().initialize();
}
/** Called in the MobEntity.class constructor to initialize AI goals. */
@Override
protected void registerGoals() {
super.registerGoals();
getSpecialData().rangedAttackDamage = 1.0F;
getSpecialData().rangedAttackSpread = 1.3F;
getSpecialData().rangedAttackCooldown = 40;
getSpecialData().rangedAttackMaxCooldown = getSpecialData().rangedAttackCooldown;
getSpecialData().rangedAttackMaxRange = 10.0F;
registerVariantGoals();
}
@ -110,20 +107,18 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
/** The parameter for special mob render scale. */
private static final DataParameter<Float> SCALE = EntityDataManager.defineId( _SpecialCaveSpiderEntity.class, DataSerializers.FLOAT );
public _SpecialCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
if( !getConfig().SPIDERS.spitterChance.rollChance( random ) ) getSpecialData().disableRangedAttack();
getSpecialData().initialize();
}
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
specialData = new SpecialMobData<>( this, SCALE, 0.7F );
}
/** Called on spawn to initialize properties based on the world, difficulty, and the group it spawns with. */
@Nullable
public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason,
@Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) {
groupData = super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag );
// TODO ranged attack
return groupData;
specialData = new SpecialMobData<>( this, SCALE );
}
@ -133,7 +128,12 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
/** @return This mob's special data. */
@Override
public SpecialMobData<_SpecialCaveSpiderEntity> getSpecialData() { return specialData; }
public final SpecialMobData<_SpecialCaveSpiderEntity> getSpecialData() { return specialData; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends _SpecialCaveSpiderEntity> getSpecies() { return SPECIES; }
/** @return The experience that should be dropped by this entity. */
@Override
@ -143,19 +143,6 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
@Override
public final void setExperience( int xp ) { xpReward = xp; }
static ResourceLocation GET_TEXTURE_PATH( String type ) {
return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "cavespider/" + type + ".png" );
}
private static final ResourceLocation[] TEXTURES = {
new ResourceLocation( "textures/entity/spider/cave_spider.png" ),
new ResourceLocation( "textures/entity/spider_eyes.png" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- SpecialMobData Hooks ----------------

View file

@ -10,14 +10,12 @@ import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.potion.PotionUtils;
import net.minecraft.potion.Potions;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
@ -36,8 +34,10 @@ public class DarkCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<DarkCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xF9FF3A );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xF9FF3A )
.uniqueTextureWithEyes()
.addExperience( 1 );
}
@SpecialMob.LanguageProvider
@ -56,13 +56,15 @@ public class DarkCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<DarkCreeperEntity> getVariantFactory() { return DarkCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends DarkCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public DarkCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
xpReward += 1;
}
public DarkCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion power multiplier. */
protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius / 2.0F ); }
@ -121,13 +123,4 @@ public class DarkCreeperEntity extends _SpecialCreeperEntity {
protected void modifyVariantLingeringCloudEffects( List<EffectInstance> potions ) {
potions.add( new EffectInstance( Effects.BLINDNESS, 100 ) );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "dark" ),
GET_TEXTURE_PATH( "dark_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,17 +3,15 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.config.species.CreeperSpeciesConfig;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -29,16 +27,17 @@ public class DeathCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<DeathCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.9F, 2.6F );
return new BestiaryInfo( 0xCD0000 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xCD0000 )
.uniqueTextureWithEyes()
.size( 1.5F, 0.9F, 2.6F )
.addExperience( 2 )
.addToAttribute( Attributes.MAX_HEALTH, 10.0 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCreeperEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 10.0 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new CreeperSpeciesConfig( species, false, true, false );
}
@SpecialMob.LanguageProvider
@ -57,25 +56,16 @@ public class DeathCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<DeathCreeperEntity> getVariantFactory() { return DeathCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends DeathCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public DeathCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.5F );
setExplodesWhileBurning( true );
xpReward += 2;
}
public DeathCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion power multiplier. */
protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius + 1.0F ); }
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "death" ),
GET_TEXTURE_PATH( "death_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,7 +3,6 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
@ -11,11 +10,8 @@ import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.Explosion;
import net.minecraft.world.World;
@ -33,15 +29,11 @@ public class DirtCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<DirtCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x78553B );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCreeperEntity.createAttributes() )
.addAttribute( Attributes.ARMOR, 6.0 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x78553B )
.uniqueTextureBaseOnly()
.addExperience( 1 ).burnImmune()
.addToAttribute( Attributes.ARMOR, 6.0 );
}
@SpecialMob.LanguageProvider
@ -61,14 +53,15 @@ public class DirtCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<DirtCreeperEntity> getVariantFactory() { return DirtCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends DirtCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public DirtCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setImmuneToBurning( true );
xpReward += 1;
}
public DirtCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion. */
@Override
@ -97,12 +90,4 @@ public class DirtCreeperEntity extends _SpecialCreeperEntity {
}
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "dirt" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -9,10 +9,8 @@ import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.AreaEffectCloudEntity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -29,9 +27,10 @@ public class DoomCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<DoomCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x494949 );
//TODO theme - forest
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x494949 ).theme( BestiaryInfo.Theme.FOREST )
.uniqueTextureWithOverlay()
.addExperience( 1 ).effectImmune( Effects.HARM, Effects.WITHER );
}
@SpecialMob.LanguageProvider
@ -49,14 +48,15 @@ public class DoomCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<DoomCreeperEntity> getVariantFactory() { return DoomCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends DoomCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public DoomCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().addPotionImmunity( Effects.HARM, Effects.WITHER );
xpReward += 1;
}
public DoomCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion power multiplier. */
protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius / 2.0F ); }
@ -82,14 +82,4 @@ public class DoomCreeperEntity extends _SpecialCreeperEntity {
protected void modifyVariantLingeringCloud( AreaEffectCloudEntity potionCloud ) {
potionCloud.setRadius( (float) explosionRadius * (isPowered() ? 2.0F : 1.0F) );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "doom" ),
null,
GET_TEXTURE_PATH( "doom_overlay" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,7 +3,6 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder;
@ -13,14 +12,11 @@ import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.passive.fish.PufferfishEntity;
import net.minecraft.item.Items;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.Explosion;
import net.minecraft.world.IServerWorld;
@ -39,16 +35,11 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<DrowningCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x2D41F4, BestiaryInfo.BaseWeight.LOW );
//TODO theme - water
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCreeperEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 10.0 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x2D41F4 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.WATER )
.uniqueTextureWithEyes()
.addExperience( 2 ).drownImmune().fluidPushImmune()
.addToAttribute( Attributes.MAX_HEALTH, 10.0 );
}
@SpecialMob.LanguageProvider
@ -69,15 +60,15 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<DrowningCreeperEntity> getVariantFactory() { return DrowningCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends DrowningCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public DrowningCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setCanBreatheInWater( true );
getSpecialData().setIgnoreWaterPush( true );
xpReward += 2;
}
public DrowningCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion power multiplier. */
protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius ) + 3.0F; }
@ -147,7 +138,6 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity {
}
}
/** Helper method to simplify spawning pufferfish. */
private void spawnPufferfish( BlockPos pos ) {
if( !(level instanceof IServerWorld) ) return;
@ -164,13 +154,4 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity {
@Override
public boolean isInWaterRainOrBubble() { return true; }
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "drowning" ),
GET_TEXTURE_PATH( "drowning_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,15 +3,15 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.config.species.CreeperSpeciesConfig;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.entity.ai.AIHelper;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.entity.*;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.ai.goal.NearestAttackableTargetGoal;
@ -49,16 +49,16 @@ public class EnderCreeperEntity extends _SpecialCreeperEntity implements IAngera
public static MobFamily.Species<EnderCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xCC00FA, BestiaryInfo.BaseWeight.LOW );
//TODO theme - the end
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xCC00FA ).weight( BestiaryInfo.DefaultWeight.LOW )
.uniqueTextureWithEyes()
.addExperience( 2 ).waterSensitive()
.addToAttribute( Attributes.MAX_HEALTH, 20.0 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCreeperEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 20.0 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new CreeperSpeciesConfig( species, true, false, false );
}
@SpecialMob.LanguageProvider
@ -76,15 +76,15 @@ public class EnderCreeperEntity extends _SpecialCreeperEntity implements IAngera
@SpecialMob.Factory
public static EntityType.IFactory<EnderCreeperEntity> getVariantFactory() { return EnderCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends EnderCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public EnderCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setDamagedByWater( true );
setCannotExplodeWhileWet( true );
xpReward += 2;
}
public EnderCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this entity's AI goals. */
@Override
@ -107,15 +107,6 @@ public class EnderCreeperEntity extends _SpecialCreeperEntity implements IAngera
readPersistentAngerSaveData( (ServerWorld) level, saveTag );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "ender" ),
GET_TEXTURE_PATH( "ender_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- IAngerable Implementations ----------------

View file

@ -3,14 +3,14 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.config.species.CreeperSpeciesConfig;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -26,10 +26,15 @@ public class FireCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<FireCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.fireImmune();
return new BestiaryInfo( 0xE13916 );
//TODO theme - fire
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xE13916 ).theme( BestiaryInfo.Theme.FIRE )
.uniqueTextureBaseOnly()
.addExperience( 1 ).fireImmune().waterSensitive();
}
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new CreeperSpeciesConfig( species, true, false, false );
}
@SpecialMob.LanguageProvider
@ -48,27 +53,19 @@ public class FireCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<FireCreeperEntity> getVariantFactory() { return FireCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends FireCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public FireCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setDamagedByWater( true );
setCannotExplodeWhileWet( true );
xpReward += 1;
}
public FireCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion. */
@Override
protected void makeVariantExplosion( float explosionPower ) {
ExplosionHelper.explode( this, explosionPower, true, true );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "fire" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -9,11 +9,9 @@ import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.item.FallingBlockEntity;
import net.minecraft.item.Items;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.Explosion;
@ -32,8 +30,10 @@ public class GravelCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<GravelCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x908884 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x908884 )
.uniqueTextureBaseOnly()
.addExperience( 1 ).burnImmune();
}
@SpecialMob.LanguageProvider
@ -52,14 +52,15 @@ public class GravelCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<GravelCreeperEntity> getVariantFactory() { return GravelCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends GravelCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public GravelCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setImmuneToBurning( true );
xpReward += 1;
}
public GravelCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion power multiplier. */
protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius / 2.0F ); }
@ -106,12 +107,4 @@ public class GravelCreeperEntity extends _SpecialCreeperEntity {
}
return super.hurt( source, amount );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "gravel" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -5,16 +5,12 @@ import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.AIHelper;
import fathertoast.specialmobs.common.entity.ai.goal.SpecialLeapAtTargetGoal;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -30,16 +26,11 @@ public class JumpingCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<JumpingCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x7D6097 );
//TODO theme - mountain
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCreeperEntity.createAttributes() )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.2 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x7D6097 ).theme( BestiaryInfo.Theme.MOUNTAIN )
.uniqueTextureBaseOnly()
.addExperience( 2 ).fallImmune()
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.2 );
}
@SpecialMob.LanguageProvider
@ -57,14 +48,15 @@ public class JumpingCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<JumpingCreeperEntity> getVariantFactory() { return JumpingCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends JumpingCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public JumpingCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setFallDamageMultiplier( 0.0F );
xpReward += 2;
}
public JumpingCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this entity's AI goals. */
@Override
@ -72,12 +64,4 @@ public class JumpingCreeperEntity extends _SpecialCreeperEntity {
AIHelper.insertGoal( goalSelector, 4, new SpecialLeapAtTargetGoal(
this, 10, 6.0F, 10.0F, 1.3F, 2.0F ) );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "jumping" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -8,9 +8,7 @@ import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraft.world.storage.IServerWorldInfo;
@ -27,9 +25,10 @@ public class LightningCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<LightningCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.fireImmune();
return new BestiaryInfo( 0x499CAE );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x499CAE )
.uniqueTextureBaseOnly()
.addExperience( 1 ).fireImmune();
}
@SpecialMob.LanguageProvider
@ -47,13 +46,15 @@ public class LightningCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<LightningCreeperEntity> getVariantFactory() { return LightningCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends LightningCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public LightningCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
xpReward += 1;
}
public LightningCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion power multiplier. */
@Override
@ -95,12 +96,4 @@ public class LightningCreeperEntity extends _SpecialCreeperEntity {
}
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "lightning" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,13 +3,10 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.world.World;
@ -26,16 +23,10 @@ public class MiniCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<MiniCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.5F, 0.9F );
return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.LOW );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCreeperEntity.createAttributes() )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.3 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFFC0CB ).weight( BestiaryInfo.DefaultWeight.LOW )
.size( 0.5F, 0.5F, 0.9F )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.3 );
}
@SpecialMob.LanguageProvider
@ -52,13 +43,15 @@ public class MiniCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<MiniCreeperEntity> getVariantFactory() { return MiniCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends MiniCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public MiniCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.5F );
}
public MiniCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this creeper's explosion power multiplier. */
protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius / 2.0F ); }

View file

@ -4,15 +4,12 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.AIHelper;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.CreatureAttribute;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.ai.goal.FleeSunGoal;
import net.minecraft.entity.ai.goal.RestrictSunGoal;
@ -21,7 +18,6 @@ import net.minecraft.entity.projectile.ProjectileHelper;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.BowItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d;
@ -40,17 +36,12 @@ public class SkeletonCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<SkeletonCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xC1C1C1 );
//TODO theme - forest
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCreeperEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, -4.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.2 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xC1C1C1 ).theme( BestiaryInfo.Theme.FOREST )
.uniqueTextureBaseOnly()
.addExperience( 1 )
.addToAttribute( Attributes.MAX_HEALTH, -4.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.2 );
}
@SpecialMob.LanguageProvider
@ -68,13 +59,15 @@ public class SkeletonCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<SkeletonCreeperEntity> getVariantFactory() { return SkeletonCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends SkeletonCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public SkeletonCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
xpReward += 1;
}
public SkeletonCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this entity's AI goals. */
@Override
@ -140,12 +133,4 @@ public class SkeletonCreeperEntity extends _SpecialCreeperEntity {
}
super.aiStep();
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "skeleton" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,20 +3,18 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.config.species.SplittingCreeperSpeciesConfig;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ILivingEntityData;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvents;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
@ -35,18 +33,23 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity {
public static MobFamily.Species<SplittingCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.7F, 1.99F );
return new BestiaryInfo( 0x5F9D22, BestiaryInfo.BaseWeight.LOW );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x5F9D22 ).weight( BestiaryInfo.DefaultWeight.LOW )
.uniqueTextureWithEyes()
.size( 1.2F, 0.7F, 1.99F )
.addExperience( 2 )
.addToAttribute( Attributes.MAX_HEALTH, 20.0 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialCreeperEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 20.0 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new SplittingCreeperSpeciesConfig( species, false, false, true,
1, 3 );
}
/** @return This entity's species config. */
public SplittingCreeperSpeciesConfig getConfig() { return (SplittingCreeperSpeciesConfig) getSpecies().config; }
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Splitting Creeper",
@ -62,21 +65,22 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Factory
public static EntityType.IFactory<SplittingCreeperEntity> getVariantFactory() { return SplittingCreeperEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends SplittingCreeperEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public SplittingCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.2F );
getSpecialData().setImmuneToBurning( true );
setExplodesWhenShot( true );
xpReward += 2;
extraBabies = random.nextInt( 4 );
}
/** The number of extra mini creepers spawned on explosion (in addition to the amount based on explosion power). */
private int extraBabies;
public SplittingCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
extraBabies = getConfig().SPLITTING.extraBabies.next( random );
}
/** Override to change this creeper's explosion. */
@Override
protected void makeVariantExplosion( float explosionPower ) {
@ -104,9 +108,8 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity {
baby.yBodyRot = yRot;
groupData = baby.finalizeSpawn( (IServerWorld) level, level.getCurrentDifficultyAt( blockPosition() ),
SpawnReason.MOB_SUMMONED, groupData, null );
baby.copyChargedState( this );
baby.setTarget( getTarget() );
if( isPowered() ) baby.getEntityData().set( DATA_IS_POWERED, true );
if ( isSupercharged() ) baby.getEntityData().set( IS_SUPERCHARGED, true );
baby.setDeltaMovement(
(random.nextDouble() - 0.5) * speed,
@ -130,13 +133,4 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity {
if( saveTag.contains( References.TAG_EXTRA_BABIES, References.NBT_TYPE_NUMERICAL ) )
extraBabies = saveTag.getByte( References.TAG_EXTRA_BABIES );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "splitting" ),
GET_TEXTURE_PATH( "splitting_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,9 +3,11 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.config.species.CreeperSpeciesConfig;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.entity.ai.IExplodingMob;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
@ -24,7 +26,6 @@ import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.pathfinding.PathNodeType;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.IServerWorld;
@ -39,7 +40,7 @@ import java.util.List;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<_SpecialCreeperEntity> {
public class _SpecialCreeperEntity extends CreeperEntity implements IExplodingMob, ISpecialMob<_SpecialCreeperEntity> {
//--------------- Static Special Mob Hooks ----------------
@ -47,15 +48,23 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
public static MobFamily.Species<_SpecialCreeperEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x000000 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x000000 )
.vanillaTextureBaseOnly( "textures/entity/creeper/creeper.png" )
.experience( 5 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return CreeperEntity.createAttributes();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new CreeperSpeciesConfig( species, false, false, false );
}
/** @return This entity's species config. */
public CreeperSpeciesConfig getConfig() { return (CreeperSpeciesConfig) getSpecies().config; }
@SpecialMob.AttributeSupplier
public static AttributeModifierMap.MutableAttribute createAttributes() { return CreeperEntity.createAttributes(); }
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Creeper",
@ -73,11 +82,6 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
//--------------- Variant-Specific Breakouts ----------------
public _SpecialCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().initialize();
}
/** Called in the MobEntity.class constructor to initialize AI goals. */
@Override
protected void registerGoals() {
@ -96,6 +100,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@SuppressWarnings( "unused" ) // Not normally used for creepers
protected void onVariantAttack( Entity target ) { }
/** Called to perform this creeper's explosion 'attack'. */
@ -159,13 +164,21 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
/** The parameter for special mob render scale. */
private static final DataParameter<Float> SCALE = EntityDataManager.defineId( _SpecialCreeperEntity.class, DataSerializers.FLOAT );
public _SpecialCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
setCannotExplodeWhileWet( !getConfig().CREEPERS.canExplodeWhileWet.get() );
setExplodesWhileBurning( getConfig().CREEPERS.explodesWhileBurning.get() );
setExplodesWhenShot( getConfig().CREEPERS.explodesWhenShot.get() );
getSpecialData().initialize();
}
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
specialData = new SpecialMobData<>( this, SCALE, 1.0F );
specialData = new SpecialMobData<>( this, SCALE );
entityData.define( EXPLODE_FLAGS, (byte) 0 );
entityData.define( IS_SUPERCHARGED, false );
}
/** Called each tick to update this entity. */
@ -188,9 +201,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
/** Called when this entity is struck by lightning. */
@Override
public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) {
if( !isPowered() && random.nextDouble() < 0.1 ) // TODO config
setSupercharged( true );
charge();
super.thunderHit( world, lightningBolt );
// Make it less likely for charged "explode while burning" creepers to immediately explode
@ -202,8 +213,13 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason,
@Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) {
groupData = super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag );
if( world.getLevelData().isThundering() && random.nextDouble() < 0.01 ) { //TODO config
getEntityData().set( DATA_IS_POWERED, true );
if( world.getLevelData().isThundering() ) {
final double chargedChance = getConfig().CREEPERS.stormChargeChance.get() < 0.0 ?
MobFamily.CREEPER.config.CREEPERS.familyStormChargeChance.get() :
getConfig().CREEPERS.stormChargeChance.get();
if( random.nextDouble() < chargedChance ) charge();
}
return groupData;
}
@ -213,31 +229,48 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
/** The parameter for creeper explosion properties. This is a combination of boolean flags. */
private static final DataParameter<Byte> EXPLODE_FLAGS = EntityDataManager.defineId( _SpecialCreeperEntity.class, DataSerializers.BYTE );
protected static final DataParameter<Boolean> IS_SUPERCHARGED = EntityDataManager.defineId( _SpecialCreeperEntity.class, DataSerializers.BOOLEAN );
/** The bit for "is supercharged". */
private static final byte EXPLODE_FLAG_SUPERCHARGED = 0b0001;
/** The bit for "cannot explode while wet". */
private static final byte EXPLODE_FLAG_DEFUSE_IN_WATER = 0b0001;
private static final byte EXPLODE_FLAG_DEFUSE_IN_WATER = 0b0010;
/** The bit for "explodes while burning". */
private static final byte EXPLODE_FLAG_ON_FIRE = 0b0010;
private static final byte EXPLODE_FLAG_ON_FIRE = 0b0100;
/** The bit for "explodes when shot". */
private static final byte EXPLODE_FLAG_WHEN_SHOT = 0b0100;
private static final byte EXPLODE_FLAG_WHEN_SHOT = 0b1000;
/** @return True if this creeper is super charged. */
public boolean isSupercharged() {
return entityData.get( IS_SUPERCHARGED );
/** Called to charge this creeper, potentially supercharging it. */
public void charge() {
if( !isPowered() ) {
setPowered( true );
if( MobFamily.CREEPER.config.CREEPERS.superchargeChance.rollChance( random ) )
setSupercharged( true );
}
}
/** Copy another creeper's charged state. */
public void copyChargedState( _SpecialCreeperEntity other ) {
setPowered( other.isPowered() );
setSupercharged( other.isSupercharged() );
}
/** Sets this creeper's charged state to the given value. */
private void setPowered( boolean charged ) { entityData.set( DATA_IS_POWERED, charged ); }
/** @return True if this creeper is super charged. */
public boolean isSupercharged() { return getExplodeFlag( EXPLODE_FLAG_SUPERCHARGED ); }
/** Sets this creeper's supercharged state to the given value. */
public void setSupercharged( boolean superCharged ) {
entityData.set( IS_SUPERCHARGED, superCharged );
private void setSupercharged( boolean value ) {
if( value && !isPowered() ) setPowered( true );
setExplodeFlag( EXPLODE_FLAG_SUPERCHARGED, value );
}
/** @return True if this creeper is unable to explode while wet. */
public boolean cannotExplodeWhileWet() { return getExplodeFlag( EXPLODE_FLAG_DEFUSE_IN_WATER ); }
/** Sets this creeper's capability to explode while wet. */
public void setCannotExplodeWhileWet( boolean value ) {
private void setCannotExplodeWhileWet( boolean value ) {
setExplodeFlag( EXPLODE_FLAG_DEFUSE_IN_WATER, value );
setPathfindingMalus( PathNodeType.WATER, value ? PathNodeType.LAVA.getMalus() : PathNodeType.WATER.getMalus() );
}
@ -246,7 +279,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
public boolean explodesWhileBurning() { return getExplodeFlag( EXPLODE_FLAG_ON_FIRE ); }
/** Sets this creeper's property to explode while burning. */
public void setExplodesWhileBurning( boolean value ) {
private void setExplodesWhileBurning( boolean value ) {
setExplodeFlag( EXPLODE_FLAG_ON_FIRE, value );
if( value ) {
setPathfindingMalus( PathNodeType.DANGER_FIRE, PathNodeType.DAMAGE_FIRE.getMalus() );
@ -262,7 +295,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
public boolean explodesWhenShot() { return getExplodeFlag( EXPLODE_FLAG_WHEN_SHOT ); }
/** Sets this creeper's property to explode when shot. */
public void setExplodesWhenShot( boolean value ) { setExplodeFlag( EXPLODE_FLAG_WHEN_SHOT, value ); }
private void setExplodesWhenShot( boolean value ) { setExplodeFlag( EXPLODE_FLAG_WHEN_SHOT, value ); }
/** @return The value for a specific explode flag. */
private boolean getExplodeFlag( byte flag ) { return (entityData.get( EXPLODE_FLAGS ) & flag) != 0; }
@ -284,6 +317,11 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
@Override
public SpecialMobData<_SpecialCreeperEntity> getSpecialData() { return specialData; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends _SpecialCreeperEntity> getSpecies() { return SPECIES; }
/** @return The experience that should be dropped by this entity. */
@Override
public final int getExperience() { return xpReward; }
@ -292,16 +330,6 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
@Override
public final void setExperience( int xp ) { xpReward = xp; }
static ResourceLocation GET_TEXTURE_PATH( String type ) {
return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "creeper/" + type + ".png" );
}
private static final ResourceLocation[] TEXTURES = { new ResourceLocation( "textures/entity/creeper/creeper.png" ) };
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- SpecialMobData Hooks ----------------
@ -388,7 +416,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
saveTag.putBoolean( References.TAG_SUPERCHARGED, isSupercharged() );
saveTag.putBoolean( References.TAG_DRY_EXPLODE, cannotExplodeWhileWet() );
saveTag.putBoolean( References.TAG_WHEN_BURNING_EXPLODE, explodesWhileBurning() );
saveTag.putBoolean( References.TAG_WHILE_BURNING_EXPLODE, explodesWhileBurning() );
saveTag.putBoolean( References.TAG_WHEN_SHOT_EXPLODE, explodesWhenShot() );
getSpecialData().writeToNBT( saveTag );
@ -407,8 +435,8 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
if( saveTag.contains( References.TAG_DRY_EXPLODE, References.NBT_TYPE_NUMERICAL ) )
setCannotExplodeWhileWet( saveTag.getBoolean( References.TAG_DRY_EXPLODE ) );
if( saveTag.contains( References.TAG_WHEN_BURNING_EXPLODE, References.NBT_TYPE_NUMERICAL ) )
setExplodesWhileBurning( saveTag.getBoolean( References.TAG_WHEN_BURNING_EXPLODE ) );
if( saveTag.contains( References.TAG_WHILE_BURNING_EXPLODE, References.NBT_TYPE_NUMERICAL ) )
setExplodesWhileBurning( saveTag.getBoolean( References.TAG_WHILE_BURNING_EXPLODE ) );
if( saveTag.contains( References.TAG_WHEN_SHOT_EXPLODE, References.NBT_TYPE_NUMERICAL ) )
setExplodesWhenShot( saveTag.getBoolean( References.TAG_WHEN_SHOT_EXPLODE ) );

View file

@ -11,7 +11,6 @@ import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Items;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -27,9 +26,10 @@ public class BlindingEndermanEntity extends _SpecialEndermanEntity {
public static MobFamily.Species<BlindingEndermanEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xFFFFFF );
//TODO theme - forest
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFFFFFF ).theme( BestiaryInfo.Theme.FOREST )
.uniqueTextureWithEyes()
.addExperience( 1 ).effectImmune( Effects.BLINDNESS );
}
@SpecialMob.LanguageProvider
@ -47,14 +47,15 @@ public class BlindingEndermanEntity extends _SpecialEndermanEntity {
@SpecialMob.Factory
public static EntityType.IFactory<BlindingEndermanEntity> getVariantFactory() { return BlindingEndermanEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends BlindingEndermanEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public BlindingEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().addPotionImmunity( Effects.BLINDNESS );
xpReward += 1;
}
public BlindingEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) { super( entityType, world ); }
/** Called each tick to update this entity's movement. */
@Override
@ -68,13 +69,4 @@ public class BlindingEndermanEntity extends _SpecialEndermanEntity {
target.removeEffect( Effects.NIGHT_VISION ); // Prevent blind + night vision combo (black screen)
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "blinding" ),
GET_TEXTURE_PATH( "blinding_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -25,7 +25,6 @@ import net.minecraft.potion.Effects;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
@ -45,9 +44,11 @@ public class IcyEndermanEntity extends _SpecialEndermanEntity {
public static MobFamily.Species<IcyEndermanEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x72959C );
//TODO theme - ice
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x72959C ).theme( BestiaryInfo.Theme.ICE )
.uniqueTextureWithEyes()
.addExperience( 1 ).effectImmune( Effects.MOVEMENT_SLOWDOWN );
}
@SpecialMob.LanguageProvider
@ -66,14 +67,16 @@ public class IcyEndermanEntity extends _SpecialEndermanEntity {
@SpecialMob.Factory
public static EntityType.IFactory<IcyEndermanEntity> getVariantFactory() { return IcyEndermanEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends IcyEndermanEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public IcyEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().addPotionImmunity( Effects.MOVEMENT_SLOWDOWN );
xpReward += 1;
setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() );
}
@ -189,13 +192,4 @@ public class IcyEndermanEntity extends _SpecialEndermanEntity {
FrostWalkerEnchantment.onEntityMoved( this, level, pos, 1 );
onGround = actualOnGround;
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "icy" ),
GET_TEXTURE_PATH( "icy_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -12,7 +12,6 @@ import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.LightningBoltEntity;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
@ -29,9 +28,10 @@ public class LightningEndermanEntity extends _SpecialEndermanEntity {
public static MobFamily.Species<LightningEndermanEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.fireImmune();
return new BestiaryInfo( 0x4BB4B5 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x4BB4B5 )
.uniqueTextureWithEyes()
.addExperience( 2 ).fireImmune();
}
@SpecialMob.LanguageProvider
@ -49,13 +49,15 @@ public class LightningEndermanEntity extends _SpecialEndermanEntity {
@SpecialMob.Factory
public static EntityType.IFactory<LightningEndermanEntity> getVariantFactory() { return LightningEndermanEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends LightningEndermanEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public LightningEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
public LightningEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
@ -71,13 +73,4 @@ public class LightningEndermanEntity extends _SpecialEndermanEntity {
/** Called when this entity is struck by lightning. */
@Override
public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { }
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "lightning" ),
GET_TEXTURE_PATH( "lightning_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,13 +3,10 @@ package fathertoast.specialmobs.common.entity.enderman;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.world.World;
@ -26,17 +23,12 @@ public class MiniEndermanEntity extends _SpecialEndermanEntity {
public static MobFamily.Species<MiniEndermanEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.5F, 0.99F );
return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.LOW );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialEndermanEntity.createAttributes() )
.addAttribute( Attributes.ATTACK_DAMAGE, -2.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.3 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFFC0CB ).weight( BestiaryInfo.DefaultWeight.LOW )
.size( 0.35F, 0.5F, 0.99F )
.addExperience( 1 )
.addToAttribute( Attributes.ATTACK_DAMAGE, -2.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.3 );
}
@SpecialMob.LanguageProvider
@ -53,15 +45,16 @@ public class MiniEndermanEntity extends _SpecialEndermanEntity {
@SpecialMob.Factory
public static EntityType.IFactory<MiniEndermanEntity> getVariantFactory() { return MiniEndermanEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends MiniEndermanEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public MiniEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.35F );
maxUpStep = 0.5F;
xpReward += 1;
}
// None
}

View file

@ -3,19 +3,15 @@ package fathertoast.specialmobs.common.entity.enderman;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -31,16 +27,11 @@ public class MirageEndermanEntity extends _SpecialEndermanEntity {
public static MobFamily.Species<MirageEndermanEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xC2BC84, BestiaryInfo.BaseWeight.LOW );
//TODO theme - desert
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialEndermanEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 20.0 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xC2BC84 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.DESERT )
.uniqueTextureWithEyes()
.addExperience( 2 )
.addToAttribute( Attributes.MAX_HEALTH, 20.0 );
}
@SpecialMob.LanguageProvider
@ -60,21 +51,23 @@ public class MirageEndermanEntity extends _SpecialEndermanEntity {
@SpecialMob.Factory
public static EntityType.IFactory<MirageEndermanEntity> getVariantFactory() { return MirageEndermanEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends MirageEndermanEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public MirageEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
/** Whether this mirage enderman is fake. */
public boolean isFake = false;
public MirageEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) { super( entityType, world ); }
/** Sets this mirage enderman as fake. */
public void setFake() {
isFake = true;
xpReward = 0;
setExperience( 0 );
//noinspection ConstantConditions
getAttribute( Attributes.ATTACK_DAMAGE ).setBaseValue( 0.0 );
}
@ -112,18 +105,14 @@ public class MirageEndermanEntity extends _SpecialEndermanEntity {
protected boolean teleport( double x, double y, double z ) {
if( isFake ) return false;
double xI = getX();
double yI = getY();
double zI = getZ();
if( super.teleport( x, y, z ) ) {
mirage( xI, yI, zI );
mirage();
return true;
}
return false;
}
private void mirage( double xI, double yI, double zI ) {
private void mirage() {
if( !isFake && getTarget() != null ) {
final MirageEndermanEntity mirage = SPECIES.entityType.get().create( level );
if( mirage == null ) return;
@ -134,10 +123,10 @@ public class MirageEndermanEntity extends _SpecialEndermanEntity {
// Return one of the endermen to the initial position (either the real or the fake)
if( random.nextInt( 4 ) == 0 ) {
moveTo( xI, yI, zI );
moveTo( xo, yo, zo );
}
else {
mirage.moveTo( xI, yI, zI );
mirage.moveTo( xo, yo, zo );
}
mirage.setHealth( getHealth() );
@ -163,13 +152,4 @@ public class MirageEndermanEntity extends _SpecialEndermanEntity {
if( saveTag.contains( References.TAG_IS_FAKE, References.NBT_TYPE_NUMERICAL ) )
isFake = saveTag.getBoolean( References.TAG_IS_FAKE );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "mirage" ),
GET_TEXTURE_PATH( "mirage_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,17 +3,14 @@ package fathertoast.specialmobs.common.entity.enderman;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.world.World;
@ -33,15 +30,11 @@ public class ThiefEndermanEntity extends _SpecialEndermanEntity {
public static MobFamily.Species<ThiefEndermanEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x04FA00, BestiaryInfo.BaseWeight.LOW );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialEndermanEntity.createAttributes() )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.2 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x04FA00 ).weight( BestiaryInfo.DefaultWeight.LOW )
.uniqueTextureWithEyes()
.addExperience( 2 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.2 );
}
@SpecialMob.LanguageProvider
@ -59,16 +52,18 @@ public class ThiefEndermanEntity extends _SpecialEndermanEntity {
@SpecialMob.Factory
public static EntityType.IFactory<ThiefEndermanEntity> getVariantFactory() { return ThiefEndermanEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends ThiefEndermanEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public ThiefEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
private int teleportTargetDelay;
public ThiefEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
@ -118,13 +113,4 @@ public class ThiefEndermanEntity extends _SpecialEndermanEntity {
super.aiStep();
if( !level.isClientSide() ) teleportTargetDelay--;
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "thief" ),
GET_TEXTURE_PATH( "thief_eyes" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,7 +3,6 @@ package fathertoast.specialmobs.common.entity.enderman;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.util.References;
@ -19,13 +18,9 @@ import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@ -39,14 +34,14 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
public static MobFamily.Species<_SpecialEndermanEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x000000 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x000000 )
.vanillaTextureWithEyes( "textures/entity/enderman/enderman.png", "textures/entity/enderman/enderman_eyes.png" )
.experience( 5 ).waterSensitive();
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return EndermanEntity.createAttributes();
}
@SpecialMob.AttributeSupplier
public static AttributeModifierMap.MutableAttribute createAttributes() { return EndermanEntity.createAttributes(); }
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
@ -65,12 +60,6 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
//--------------- Variant-Specific Breakouts ----------------
public _SpecialEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().initialize();
getSpecialData().setDamagedByWater( true );
}
/** Called in the MobEntity.class constructor to initialize AI goals. */
@Override
protected void registerGoals() {
@ -103,20 +92,16 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
/** The parameter for special mob render scale. */
private static final DataParameter<Float> SCALE = EntityDataManager.defineId( _SpecialEndermanEntity.class, DataSerializers.FLOAT );
public _SpecialEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().initialize();
}
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
specialData = new SpecialMobData<>( this, SCALE, 1.0F );
}
/** Called on spawn to initialize properties based on the world, difficulty, and the group it spawns with. */
@Nullable
public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason,
@Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) {
groupData = super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag );
// TODO anything?
return groupData;
specialData = new SpecialMobData<>( this, SCALE );
}
@ -128,6 +113,11 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
@Override
public SpecialMobData<_SpecialEndermanEntity> getSpecialData() { return specialData; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends _SpecialEndermanEntity> getSpecies() { return SPECIES; }
/** @return The experience that should be dropped by this entity. */
@Override
public final int getExperience() { return xpReward; }
@ -136,19 +126,6 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
@Override
public final void setExperience( int xp ) { xpReward = xp; }
static ResourceLocation GET_TEXTURE_PATH( String type ) {
return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "enderman/" + type + ".png" );
}
private static final ResourceLocation[] TEXTURES = {
new ResourceLocation( "textures/entity/enderman/enderman.png" ),
new ResourceLocation( "textures/entity/enderman/enderman_eyes.png" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- SpecialMobData Hooks ----------------

View file

@ -3,13 +3,10 @@ package fathertoast.specialmobs.common.entity.ghast;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.SoundEvent;
@ -28,16 +25,11 @@ public class BabyGhastEntity extends _SpecialGhastEntity {
public static MobFamily.Species<BabyGhastEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 1.0F, 1.0F );
return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.DISABLED );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialGhastEntity.createAttributes() )
.addAttribute( Attributes.ATTACK_DAMAGE, -1.0 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFFC0CB ).weight( BestiaryInfo.DefaultWeight.DISABLED )
.size( 0.25F, 1.0F, 1.0F )
.experience( 1 ).disableRangedAttack()
.addToAttribute( Attributes.ATTACK_DAMAGE, -1.0 );
}
@SpecialMob.LanguageProvider
@ -54,21 +46,15 @@ public class BabyGhastEntity extends _SpecialGhastEntity {
@SpecialMob.Factory
public static EntityType.IFactory<BabyGhastEntity> getVariantFactory() { return BabyGhastEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends BabyGhastEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public BabyGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.25F );
xpReward = 1;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackDamage -= 1.0F;
disableRangedAI();
}
public BabyGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) { super( entityType, world ); }
/** @return The sound this entity makes idly. */
@Override

View file

@ -4,126 +4,117 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.core.register.SMItems;
import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.entity.projectile.CorporealShiftFireballEntity;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.util.Constants;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class CorporealShiftGhastEntity extends _SpecialGhastEntity {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.SpeciesReference
public static MobFamily.Species<CorporealShiftGhastEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo(EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 6.0F, 6.0F );
return new BestiaryInfo( 0xA7FF9B, BestiaryInfo.BaseWeight.LOW );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xA7FF9B ).weight( BestiaryInfo.DefaultWeight.LOW )
.uniqueTextureWithAnimation()
.addExperience( 4 ).regen( 80 )
.addToAttribute( Attributes.MAX_HEALTH, 20.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialGhastEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 20.0 )
.addAttribute( Attributes.ARMOR, 0.0 )
.addAttribute( Attributes.ATTACK_DAMAGE, 0.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 0.8 )
.build();
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Corporeal Shift Ghast",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
// TODO - Uh uhm uhhhhh hmmm..
loot.addSemicommonDrop( "semicommon", SMItems.INCORPOREAL_FIREBALL.get() );
}
@SpecialMob.Factory
public static EntityType.IFactory<CorporealShiftGhastEntity> getVariantFactory() { return CorporealShiftGhastEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends CorporealShiftGhastEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public static final DataParameter<Boolean> CORPOREAL = EntityDataManager.defineId(CorporealShiftGhastEntity.class, DataSerializers.BOOLEAN);
public static final DataParameter<Boolean> CORPOREAL = EntityDataManager.defineId( CorporealShiftGhastEntity.class, DataSerializers.BOOLEAN );
private final int maxShiftTime = 600;
private int shiftTime = maxShiftTime;
public CorporealShiftGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setRegenerationTime( 80 );
getSpecialData().setBaseScale( 1.0F );
xpReward += 2;
}
public CorporealShiftGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) { super( entityType, world ); }
@Override
protected void defineSynchedData() {
super.defineSynchedData();
entityData.define(CORPOREAL, false);
entityData.define( CORPOREAL, false );
}
@Override
public void tick() {
super.tick();
if ( --shiftTime <= 0 ) {
if ( !level.isClientSide ) {
if( --shiftTime <= 0 ) {
if( !level.isClientSide ) {
shiftTime = maxShiftTime;
entityData.set(CORPOREAL, !entityData.get(CORPOREAL));
spawnShiftSmoke((ServerWorld)level);
setCorporeal( !isCorporeal() );
spawnShiftSmoke( (ServerWorld) level );
}
}
}
private void spawnShiftSmoke(ServerWorld world) {
world.sendParticles(ParticleTypes.CLOUD, this.getX(), this.getY(), this.getZ(), 25, 0.0, 0.0, 0.0, 0.4);
private void spawnShiftSmoke( ServerWorld world ) {
world.sendParticles( ParticleTypes.CLOUD, this.getX(), this.getY(), this.getZ(), 25, 0.0, 0.0, 0.0, 0.4 );
}
public boolean isCorporeal() {
return entityData.get(CORPOREAL);
}
public boolean isCorporeal() { return entityData.get( CORPOREAL ); }
private void setCorporeal( boolean value ) { entityData.set( CORPOREAL, value ); }
/** Override to change this ghast's explosion power multiplier. */
@Override
protected int getVariantExplosionPower( int radius ) { return Math.round( radius * 2.5F ); }
/** Called to attack the target with a ranged attack. */
@Override
public void performRangedAttack( LivingEntity target, float damageMulti ) {
if( !isSilent() ) level.levelEvent( null, References.EVENT_GHAST_SHOOT, blockPosition(), 0 );
final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().rangedAttackSpread;
final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().getRangedAttackSpread();
final Vector3d lookVec = getViewVector( 1.0F ).scale( getBbWidth() );
double dX = target.getX() - (getX() + lookVec.x) + getRandom().nextGaussian() * accelVariance;
double dY = target.getY( 0.5 ) - (0.5 + getY( 0.5 ));
double dZ = target.getZ() - (getZ() + lookVec.z) + getRandom().nextGaussian() * accelVariance;
final CorporealShiftFireballEntity fireball = new CorporealShiftFireballEntity( level, this, dX, dY, dZ );
fireball.explosionPower = getVariantExplosionPower( getExplosionPower() );
fireball.setPos(
@ -132,40 +123,29 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity {
getZ() + lookVec.z );
level.addFreshEntity( fireball );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "corporeal_shift" ),
null,
GET_TEXTURE_PATH( "corporeal_shift_shoot" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- SpecialMobData Hooks ----------------
/** @return Attempts to damage this entity; returns true if the hit was successful. */
@Override
public boolean hurt(DamageSource source, float amount ) {
return isCorporeal() && super.hurt(source, amount);
public boolean hurt( DamageSource source, float amount ) {
return isCorporeal() && super.hurt( source, amount );
}
/** Saves data to this entity's base NBT compound that is specific to its subclass. */
/** Override to save data to this entity's NBT data. */
@Override
public void addAdditionalSaveData( CompoundNBT tag ) {
super.addAdditionalSaveData( tag );
tag.putInt("ShiftTime", shiftTime);
public void addVariantSaveData( CompoundNBT saveTag ) {
saveTag.putBoolean( References.TAG_IS_SHIFTED, isCorporeal() );
saveTag.putShort( References.TAG_SHIFT_TIME, (short) shiftTime );
}
/** Loads data from this entity's base NBT compound that is specific to its subclass. */
/** Override to load data from this entity's NBT data. */
@Override
public void readAdditionalSaveData( CompoundNBT tag ) {
super.readAdditionalSaveData( tag );
if (tag.contains("ShiftTime", Constants.NBT.TAG_ANY_NUMERIC)) {
shiftTime = tag.getInt("ShiftTime");
}
public void readVariantSaveData( CompoundNBT saveTag ) {
if( saveTag.contains( References.TAG_IS_SHIFTED, References.NBT_TYPE_NUMERICAL ) )
setCorporeal( saveTag.getBoolean( References.TAG_IS_SHIFTED ) );
if( saveTag.contains( References.TAG_SHIFT_TIME, References.NBT_TYPE_NUMERICAL ) )
shiftTime = saveTag.getShort( References.TAG_SHIFT_TIME );
}
}
}

View file

@ -4,17 +4,14 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -30,19 +27,14 @@ public class FighterGhastEntity extends _SpecialGhastEntity {
public static MobFamily.Species<FighterGhastEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 2.0F, 2.0F );
return new BestiaryInfo( 0x7A1300 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialGhastEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 20.0 )
.addAttribute( Attributes.ARMOR, 10.0 )
.addAttribute( Attributes.ATTACK_DAMAGE, 2.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 0.8 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x7A1300 )
.uniqueTextureWithAnimation()
.size( 0.5F, .0F, 2.0F )
.addExperience( 1 ).disableRangedAttack()
.addToAttribute( Attributes.MAX_HEALTH, 20.0 ).addToAttribute( Attributes.ARMOR, 10.0 )
.addToAttribute( Attributes.ATTACK_DAMAGE, 2.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 );
}
@SpecialMob.LanguageProvider
@ -60,21 +52,15 @@ public class FighterGhastEntity extends _SpecialGhastEntity {
@SpecialMob.Factory
public static EntityType.IFactory<FighterGhastEntity> getVariantFactory() { return FighterGhastEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends FighterGhastEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public FighterGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.5F );
xpReward += 1;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackDamage += 2.0F;
disableRangedAI();
}
public FighterGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
protected void onVariantAttack( Entity target ) {
@ -82,14 +68,4 @@ public class FighterGhastEntity extends _SpecialGhastEntity {
MobHelper.causeLifeLoss( (LivingEntity) target, 2.0F );
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "fighter" ),
null,
GET_TEXTURE_PATH( "fighter_shooting" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,16 +3,12 @@ package fathertoast.specialmobs.common.entity.ghast;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -28,19 +24,14 @@ public class KingGhastEntity extends _SpecialGhastEntity {
public static MobFamily.Species<KingGhastEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 6.0F, 6.0F );
return new BestiaryInfo( 0xE8C51A, BestiaryInfo.BaseWeight.LOW );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialGhastEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 20.0 )
.addAttribute( Attributes.ARMOR, 10.0 )
.addAttribute( Attributes.ATTACK_DAMAGE, 4.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 0.6 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xE8C51A ).weight( BestiaryInfo.DefaultWeight.LOW )
.uniqueTextureWithAnimation()
.size( 1.5F, 6.0F, 6.0F )
.addExperience( 4 ).regen( 30 )
.addToAttribute( Attributes.MAX_HEALTH, 20.0 ).addToAttribute( Attributes.ARMOR, 10.0 )
.addToAttribute( Attributes.ATTACK_DAMAGE, 4.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.6 );
}
@SpecialMob.LanguageProvider
@ -59,33 +50,17 @@ public class KingGhastEntity extends _SpecialGhastEntity {
@SpecialMob.Factory
public static EntityType.IFactory<KingGhastEntity> getVariantFactory() { return KingGhastEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends KingGhastEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public KingGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.5F );
getSpecialData().setRegenerationTime( 30 );
xpReward += 4;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackDamage += 4.0F;
}
public KingGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this ghast's explosion power multiplier. */
@Override
protected int getVariantExplosionPower( int radius ) { return Math.round( radius * 2.5F ); }
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "king" ),
null,
GET_TEXTURE_PATH( "king_shooting" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,7 +3,8 @@ package fathertoast.specialmobs.common.entity.ghast;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.config.species.QueenGhastSpeciesConfig;
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
@ -11,11 +12,9 @@ import net.minecraft.entity.EntityType;
import net.minecraft.entity.ILivingEntityData;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
@ -33,20 +32,24 @@ public class QueenGhastEntity extends _SpecialGhastEntity {
public static MobFamily.Species<QueenGhastEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 5.0F, 5.0F );
return new BestiaryInfo( 0xCE0Aff );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xCE0Aff )
.uniqueTextureWithAnimation()
.size( 1.25F, 5.0F, 5.0F )
.addExperience( 2 ).regen( 20 )
.addToAttribute( Attributes.MAX_HEALTH, 20.0 )
.addToAttribute( Attributes.ATTACK_DAMAGE, 2.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.6 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialGhastEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 20.0 )
.addAttribute( Attributes.ATTACK_DAMAGE, 2.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 0.6 )
.build();
@SpecialMob.ConfigSupplier
public static SpeciesConfig createConfig( MobFamily.Species<?> species ) {
return new QueenGhastSpeciesConfig( species, 3, 6, 4, 10 );
}
/** @return This entity's species config. */
public QueenGhastSpeciesConfig getConfig() { return (QueenGhastSpeciesConfig) getSpecies().config; }
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Queen Ghast",
@ -63,6 +66,11 @@ public class QueenGhastEntity extends _SpecialGhastEntity {
@SpecialMob.Factory
public static EntityType.IFactory<QueenGhastEntity> getVariantFactory() { return QueenGhastEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends QueenGhastEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
@ -73,18 +81,8 @@ public class QueenGhastEntity extends _SpecialGhastEntity {
public QueenGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.25F );
getSpecialData().setRegenerationTime( 20 );
xpReward += 2;
babies = 3 + random.nextInt( 4 );
summons = 4 + random.nextInt( 7 );
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackDamage += 2.0F;
babies = getConfig().QUEEN.babies.next( random );
summons = getConfig().QUEEN.summons.next( random );
}
/** Called to attack the target with a ranged attack. */
@ -160,14 +158,4 @@ public class QueenGhastEntity extends _SpecialGhastEntity {
if( saveTag.contains( References.TAG_SUMMONS, References.NBT_TYPE_NUMERICAL ) )
summons = saveTag.getByte( References.TAG_SUMMONS );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "queen" ),
null,
GET_TEXTURE_PATH( "queen_shooting" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -4,7 +4,6 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
@ -12,13 +11,11 @@ import net.minecraft.entity.CreatureAttribute;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -34,18 +31,14 @@ public class UnholyGhastEntity extends _SpecialGhastEntity {
public static MobFamily.Species<UnholyGhastEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 2.0F, 2.0F );
return new BestiaryInfo( 0x7AC754, BestiaryInfo.BaseWeight.LOW );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialGhastEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 10.0 )
.addAttribute( Attributes.ATTACK_DAMAGE, 2.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 0.7 )
.build();
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x7AC754 ).weight( BestiaryInfo.DefaultWeight.LOW )
.uniqueTextureWithAnimation()
.size( 0.5F, 2.0F, 2.0F )
.addExperience( 4 ).undead().disableRangedAttack()
.addToAttribute( Attributes.MAX_HEALTH, 10.0 )
.addToAttribute( Attributes.ATTACK_DAMAGE, 2.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.7 );
}
@SpecialMob.LanguageProvider
@ -64,21 +57,15 @@ public class UnholyGhastEntity extends _SpecialGhastEntity {
@SpecialMob.Factory
public static EntityType.IFactory<UnholyGhastEntity> getVariantFactory() { return UnholyGhastEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends UnholyGhastEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public UnholyGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.5F );
xpReward += 4;
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackDamage += 2.0F;
disableRangedAI();
}
public UnholyGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
protected void onVariantAttack( Entity target ) {
@ -120,14 +107,4 @@ public class UnholyGhastEntity extends _SpecialGhastEntity {
}
super.aiStep();
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "unholy" ),
null,
GET_TEXTURE_PATH( "unholy_shooting" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,7 +3,6 @@ package fathertoast.specialmobs.common.entity.ghast;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.entity.ai.AIHelper;
@ -32,7 +31,6 @@ import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance;
@ -53,11 +51,14 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob
public static MobFamily.Species<_SpecialGhastEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xBCBCBC );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xBCBCBC )
.vanillaTextureWithAnimation( "textures/entity/ghast/ghast.png", "textures/entity/ghast/ghast_shooting.png" )
.experience( 5 ).fireImmune()
.fireballAttack( 0.0, 20, 40, 64.0 );
}
@SpecialMob.AttributeCreator
@SpecialMob.AttributeSupplier
public static AttributeModifierMap.MutableAttribute createAttributes() {
return GhastEntity.createAttributes().add( Attributes.ATTACK_DAMAGE, 4.0 );
}
@ -79,13 +80,6 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob
//--------------- Variant-Specific Breakouts ----------------
public _SpecialGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) {
super( entityType, world );
moveControl = new SimpleFlyingMovementController( this );
reassessAttackGoal();
getSpecialData().initialize();
}
/** Called in the MobEntity.class constructor to initialize AI goals. */
@Override
protected void registerGoals() {
@ -94,24 +88,18 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob
AIHelper.removeGoals( goalSelector, 7 ); // GhastEntity.LookAroundGoal & GhastEntity.FireballAttackGoal
goalSelector.addGoal( 7, new SpecialGhastLookAroundGoal( this ) );
// Allow ghasts to target things not directly horizontal to them (why was this ever added?) TODO config
AIHelper.removeGoals( targetSelector, NearestAttackableTargetGoal.class );
targetSelector.addGoal( 1, new NearestAttackableTargetGoal<>( this, PlayerEntity.class, true ) );
// Allow ghasts to target things not directly horizontal to them (why was this ever added?)
if( MobFamily.GHAST.config.GHASTS.allowVerticalTargeting.get() ) {
AIHelper.removeGoals( targetSelector, NearestAttackableTargetGoal.class );
targetSelector.addGoal( 1, new NearestAttackableTargetGoal<>( this, PlayerEntity.class, true ) );
}
getSpecialData().rangedAttackDamage = 2.0F;
getSpecialData().rangedAttackSpread = 0.0F;
getSpecialData().rangedAttackCooldown = 20;
getSpecialData().rangedAttackMaxCooldown = 60;
getSpecialData().rangedAttackMaxRange = 64.0F;
registerVariantGoals();
}
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Helper method to set the ranged attack AI more easily. */
protected void disableRangedAI() { getSpecialData().rangedAttackMaxRange = 0.0F; }
/** Override to change this entity's attack goal priority. */
protected int getVariantAttackPriority() { return 4; }
@ -120,7 +108,7 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob
public void performRangedAttack( LivingEntity target, float damageMulti ) {
if( !isSilent() ) level.levelEvent( null, References.EVENT_GHAST_SHOOT, blockPosition(), 0 );
final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().rangedAttackSpread;
final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().getRangedAttackSpread();
final Vector3d lookVec = getViewVector( 1.0F ).scale( getBbWidth() );
double dX = target.getX() - (getX() + lookVec.x) + getRandom().nextGaussian() * accelVariance;
double dY = target.getY( 0.5 ) - (0.5 + getY( 0.5 ));
@ -163,11 +151,18 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob
/** This entity's attack AI. */
private Goal currentAttackAI;
public _SpecialGhastEntity( EntityType<? extends _SpecialGhastEntity> entityType, World world ) {
super( entityType, world );
moveControl = new SimpleFlyingMovementController( this );
reassessAttackGoal();
getSpecialData().initialize();
}
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
specialData = new SpecialMobData<>( this, SCALE, 1.0F );
specialData = new SpecialMobData<>( this, SCALE );
}
/** Called on spawn to initialize properties based on the world, difficulty, and the group it spawns with. */
@ -191,7 +186,7 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob
if( level != null && !level.isClientSide ) {
if( currentAttackAI != null ) goalSelector.removeGoal( currentAttackAI );
currentAttackAI = getSpecialData().rangedAttackMaxRange > 0.0F ?
currentAttackAI = getSpecialData().getRangedAttackMaxRange() > 0.0F ?
new SpecialGhastFireballAttackGoal( this ) :
new SpecialGhastMeleeAttackGoal( this );
@ -208,6 +203,11 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob
@Override
public SpecialMobData<_SpecialGhastEntity> getSpecialData() { return specialData; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends _SpecialGhastEntity> getSpecies() { return SPECIES; }
/** @return The experience that should be dropped by this entity. */
@Override
public final int getExperience() { return xpReward; }
@ -216,20 +216,6 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob
@Override
public final void setExperience( int xp ) { xpReward = xp; }
static ResourceLocation GET_TEXTURE_PATH( String type ) {
return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "ghast/" + type + ".png" );
}
private static final ResourceLocation[] TEXTURES = {
new ResourceLocation( "textures/entity/ghast/ghast.png" ),
null,
new ResourceLocation( "textures/entity/ghast/ghast_shooting.png" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- SpecialMobData Hooks ----------------

View file

@ -11,7 +11,6 @@ import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.Items;
@ -19,7 +18,6 @@ import net.minecraft.nbt.CompoundNBT;
import net.minecraft.pathfinding.PathNavigator;
import net.minecraft.pathfinding.PathNodeType;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -35,10 +33,12 @@ public class BouncingMagmaCubeEntity extends _SpecialMagmaCubeEntity {
public static MobFamily.Species<BouncingMagmaCubeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.fireImmune();
return new BestiaryInfo( 0xB333B3 );
//TODO theme - mountain
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xB333B3 ).theme( BestiaryInfo.Theme.MOUNTAIN )
.uniqueTextureBaseOnly()
.addExperience( 1 ).fallImmune()
.addToAttribute( Attributes.MAX_HEALTH, 4.0 )
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.2 );
}
@SpecialMob.LanguageProvider
@ -56,24 +56,19 @@ public class BouncingMagmaCubeEntity extends _SpecialMagmaCubeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<BouncingMagmaCubeEntity> getVariantFactory() { return BouncingMagmaCubeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends BouncingMagmaCubeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public BouncingMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setFallDamageMultiplier( 0.0F );
slimeExperienceValue += 1;
setPathfindingMalus( PathNodeType.LAVA, PathNodeType.WALKABLE.getMalus() );
}
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 2.0 * size );
multAttribute( Attributes.MOVEMENT_SPEED, 1.2 );
}
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() {
AIHelper.removeGoals( goalSelector, 1 ); // SlimeEntity.FloatGoal
@ -104,12 +99,4 @@ public class BouncingMagmaCubeEntity extends _SpecialMagmaCubeEntity {
public void readVariantSaveData( CompoundNBT saveTag ) {
setPathfindingMalus( PathNodeType.LAVA, PathNodeType.WALKABLE.getMalus() );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "bouncing" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -12,7 +12,6 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
@ -29,9 +28,13 @@ public class HardenedMagmaCubeEntity extends _SpecialMagmaCubeEntity {
public static MobFamily.Species<HardenedMagmaCubeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 3.06F, 3.06F );
return new BestiaryInfo( 0xDF7679, BestiaryInfo.BaseWeight.LOW );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xDF7679 ).weight( BestiaryInfo.DefaultWeight.LOW )
.uniqueTextureBaseOnly()
.size( 1.5F, 3.06F, 3.06F )
.addExperience( 2 )
.addToAttribute( Attributes.MAX_HEALTH, 8.0 ).addToAttribute( Attributes.ARMOR, 16.0 )
.addToAttribute( Attributes.ATTACK_DAMAGE, 1.0 );
}
@SpecialMob.LanguageProvider
@ -49,21 +52,15 @@ public class HardenedMagmaCubeEntity extends _SpecialMagmaCubeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<HardenedMagmaCubeEntity> getVariantFactory() { return HardenedMagmaCubeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends HardenedMagmaCubeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public HardenedMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.5F );
slimeExperienceValue += 2;
}
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 2.0 * size + 8.0 );
addAttribute( Attributes.ARMOR, 8.0 );
}
public HardenedMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this entity's AI goals. */
@Override
@ -86,12 +83,4 @@ public class HardenedMagmaCubeEntity extends _SpecialMagmaCubeEntity {
setDeltaMovement( getDeltaMovement().multiply( 0.2, 1.0, 0.2 ) );
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "hardened" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -12,7 +12,6 @@ import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ -29,8 +28,11 @@ public class StickyMagmaCubeEntity extends _SpecialMagmaCubeEntity {
public static MobFamily.Species<StickyMagmaCubeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x9D733F );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x9D733F )
.uniqueTextureBaseOnly()
.addExperience( 2 )
.addToAttribute( Attributes.MAX_HEALTH, 8.0 );
}
@SpecialMob.LanguageProvider
@ -48,23 +50,19 @@ public class StickyMagmaCubeEntity extends _SpecialMagmaCubeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<StickyMagmaCubeEntity> getVariantFactory() { return StickyMagmaCubeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends StickyMagmaCubeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public StickyMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) {
super( entityType, world );
slimeExperienceValue += 2;
}
private final DamageSource grabDamageSource = DamageSource.mobAttack( this ).bypassArmor().bypassMagic();
private int grabTime;
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 4.0 * size );
}
public StickyMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) { super( entityType, world ); }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
@ -90,12 +88,4 @@ public class StickyMagmaCubeEntity extends _SpecialMagmaCubeEntity {
}
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "sticky" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -3,15 +3,15 @@ package fathertoast.specialmobs.common.entity.magmacube;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.IExplodingMob;
import fathertoast.specialmobs.common.entity.ai.goal.SpecialSwellGoal;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.AreaEffectCloudEntity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
@ -21,19 +21,17 @@ import net.minecraft.particles.ParticleTypes;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvents;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class VolatileMagmaCubeEntity extends _SpecialMagmaCubeEntity {
public class VolatileMagmaCubeEntity extends _SpecialMagmaCubeEntity implements IExplodingMob {
//--------------- Static Special Mob Hooks ----------------
@ -41,8 +39,11 @@ public class VolatileMagmaCubeEntity extends _SpecialMagmaCubeEntity {
public static MobFamily.Species<VolatileMagmaCubeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x331133 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0x331133 )
.uniqueTextureBaseOnly()
.addExperience( 2 )
.addToAttribute( Attributes.MAX_HEALTH, 2.0 );
}
@SpecialMob.LanguageProvider
@ -60,39 +61,34 @@ public class VolatileMagmaCubeEntity extends _SpecialMagmaCubeEntity {
@SpecialMob.Factory
public static EntityType.IFactory<VolatileMagmaCubeEntity> getVariantFactory() { return VolatileMagmaCubeEntity::new; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends VolatileMagmaCubeEntity> getSpecies() { return SPECIES; }
//--------------- Variant-Specific Implementations ----------------
public VolatileMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) {
super( entityType, world );
slimeExperienceValue += 2;
}
private static final byte MAX_FUSE = 30;
private int fuse = 0;
private int swellDir = 0;
private boolean ignited = false;
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 2.0 * size );
}
public VolatileMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) { super( entityType, world ); }
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
goalSelector.addGoal( 0, new SlimeSwellGoal( this ) );
goalSelector.addGoal( 0, new SpecialSwellGoal<>( this ) );
}
/** Called each tick to update this entity. */
@Override
public void tick() {
if( isAlive() && !level.isClientSide() ) {
if( ignited ) swellDir = 1;
if( ignited ) setSwellDir( 1 );
if( swellDir > 0 ) {
if( getSwellDir() > 0 ) {
if( fuse == 0 ) {
playSound( SoundEvents.CREEPER_PRIMED, 1.0F, 0.5F );
}
@ -106,9 +102,9 @@ public class VolatileMagmaCubeEntity extends _SpecialMagmaCubeEntity {
changeFuse( +1 );
}
}
else if( swellDir < 0 && fuse > 0 ) {
else if( getSwellDir() < 0 && fuse > 0 ) {
changeFuse( -1 );
if( fuse <= 0 ) swellDir = 0;
if( fuse <= 0 ) setSwellDir( 0 );
}
}
super.tick();
@ -187,51 +183,20 @@ public class VolatileMagmaCubeEntity extends _SpecialMagmaCubeEntity {
@Override
protected IParticleData getParticleType() { return ParticleTypes.SMOKE; }
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "volatile" )
};
/** @return All default textures for this entity. */
//--------------- IExplodingEntity Implementations ----------------
private int swellDir = 0;
/** Sets this exploding entity's swell direction. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
public void setSwellDir( int value ) { swellDir = value; }
/** @return This exploding entity's swell direction. */
@Override
public int getSwellDir() { return swellDir; }
//--------------- Nested Classes ----------------
/** The "creeper swell" goal repurposed for use on a slime. */
private static class SlimeSwellGoal extends Goal {
private final VolatileMagmaCubeEntity slime;
private LivingEntity target;
public SlimeSwellGoal( VolatileMagmaCubeEntity entity ) {
slime = entity;
setFlags( EnumSet.of( Flag.MOVE ) );
}
public boolean canUse() {
final LivingEntity target = slime.getTarget();
return slime.swellDir > 0 || target != null && slime.distanceToSqr( target ) < 9.0F + (slime.getSize() - 1) * 2.0F;
}
public void start() {
slime.getNavigation().stop();
target = slime.getTarget();
}
public void stop() {
slime.swellDir = -1;
target = null;
}
public void tick() {
if( target == null || slime.distanceToSqr( target ) > 49.0 || !slime.getSensing().canSee( target ) ) {
slime.swellDir = -1;
}
else {
slime.swellDir = 1;
}
}
}
/** @return Additional range from its target at which this entity will start to explode. */
@Override
public double getExtraRange() { return (getSize() - 1) * 2.0F; }
}

View file

@ -3,7 +3,6 @@ package fathertoast.specialmobs.common.entity.magmacube;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.util.References;
@ -11,20 +10,18 @@ import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.entity.*;
import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.AttributeModifierManager;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance;
import net.minecraft.entity.monster.MagmaCubeEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.SnowballEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
@ -41,11 +38,13 @@ public class _SpecialMagmaCubeEntity extends MagmaCubeEntity implements ISpecial
public static MobFamily.Species<_SpecialMagmaCubeEntity> SPECIES;
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xFCFC00 );
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
bestiaryInfo.color( 0xFCFC00 )
.vanillaTextureBaseOnly( "textures/entity/slime/magmacube.png" )
.experience( 0 );
}
@SpecialMob.AttributeCreator
@SpecialMob.AttributeSupplier
public static AttributeModifierMap.MutableAttribute createAttributes() {
return MagmaCubeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size
}
@ -67,14 +66,6 @@ public class _SpecialMagmaCubeEntity extends MagmaCubeEntity implements ISpecial
//--------------- Variant-Specific Breakouts ----------------
public _SpecialMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().initialize();
}
/** Override to modify this slime's base attributes by size. */
protected void modifyVariantAttributes( int size ) { }
/** Called in the MobEntity.class constructor to initialize AI goals. */
@Override
protected void registerGoals() {
@ -107,62 +98,39 @@ public class _SpecialMagmaCubeEntity extends MagmaCubeEntity implements ISpecial
/** The parameter for special mob render scale. */
private static final DataParameter<Float> SCALE = EntityDataManager.defineId( _SpecialMagmaCubeEntity.class, DataSerializers.FLOAT );
protected int slimeExperienceValue = 0;
/** Used to reset slimes' attributes to their freshly spawned state so attribute adjustments may be reapplied on size change. */
private static ListNBT magmaCubeAttributeSnapshot;
private static ListNBT getAttributeSnapshot() {
if( magmaCubeAttributeSnapshot == null )
magmaCubeAttributeSnapshot = new AttributeModifierManager( createAttributes().build() ).save();
return magmaCubeAttributeSnapshot;
}
private int slimeExperienceValue = 0;
public _SpecialMagmaCubeEntity( EntityType<? extends _SpecialMagmaCubeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().initialize();
}
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
specialData = new SpecialMobData<>( this, SCALE, 1.0F );
specialData = new SpecialMobData<>( this, SCALE );
}
/** Sets this slime's size, optionally resetting its health to max. */
@Override
protected void setSize( int size, boolean resetHealth ) {
// We must reset all attributes and reapply changes since slimes set attribute base values on size change
getAttributes().load( getAttributeSnapshot() );
super.setSize( size, resetHealth );
getSpecies().config.GENERAL.attributeChanges.apply( this );
modifyVariantAttributes( size );
if( resetHealth ) setHealth( getMaxHealth() );
xpReward = size + slimeExperienceValue;
}
/**
* Alters this magma cube's base attribute by adding an amount to it.
* Do NOT use this for move speed, instead use {@link #multAttribute(Attribute, double)}
*/
protected void addAttribute( Attribute attribute, double amount ) {
if( attribute != Attributes.MAX_HEALTH && attribute != Attributes.ARMOR && attribute != Attributes.ATTACK_DAMAGE && attribute != Attributes.MOVEMENT_SPEED )
throw new IllegalArgumentException( "Magma cube relative attributes are only health, armor, damage, and speed!" );
final ModifiableAttributeInstance attributeInstance = getAttribute( attribute );
if( attributeInstance == null )
throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() );
attributeInstance.setBaseValue( attributeInstance.getBaseValue() + amount );
}
/**
* Alters this magma cube's base attribute by multiplying it by an amount.
* Mainly use this for move speed, for other attributes use {@link #addAttribute(Attribute, double)}
*/
protected void multAttribute( Attribute attribute, double amount ) {
if( attribute != Attributes.MAX_HEALTH && attribute != Attributes.ARMOR && attribute != Attributes.ATTACK_DAMAGE && attribute != Attributes.MOVEMENT_SPEED )
throw new IllegalArgumentException( "Magma cube relative attributes are only health, armor, damage, and speed!" );
final ModifiableAttributeInstance attributeInstance = getAttribute( attribute );
if( attributeInstance == null )
throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() );
attributeInstance.setBaseValue( attributeInstance.getBaseValue() * amount );
}
/** Sets this magma cube's base attribute. */
protected void setAttribute( Attribute attribute, double amount ) {
if( attribute == Attributes.MAX_HEALTH || attribute == Attributes.ARMOR || attribute == Attributes.ATTACK_DAMAGE || attribute == Attributes.MOVEMENT_SPEED )
throw new IllegalArgumentException( "Use magma cube relative attribute!" );
final ModifiableAttributeInstance attributeInstance = getAttribute( attribute );
if( attributeInstance == null )
throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() );
attributeInstance.setBaseValue( amount );
setExperience( getExperience() ); // Update for new size
}
@ -174,6 +142,11 @@ public class _SpecialMagmaCubeEntity extends MagmaCubeEntity implements ISpecial
@Override
public SpecialMobData<_SpecialMagmaCubeEntity> getSpecialData() { return specialData; }
/** @return This entity's mob species. */
@SpecialMob.SpeciesSupplier
@Override
public MobFamily.Species<? extends _SpecialMagmaCubeEntity> getSpecies() { return SPECIES; }
/** @return The experience that should be dropped by this entity. */
@Override
public final int getExperience() { return slimeExperienceValue; } // Slime base xp
@ -185,18 +158,6 @@ public class _SpecialMagmaCubeEntity extends MagmaCubeEntity implements ISpecial
xpReward = getSize() + xp;
}
static ResourceLocation GET_TEXTURE_PATH( String type ) {
return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "magmacube/" + type + ".png" );
}
private static final ResourceLocation[] TEXTURES = {
new ResourceLocation( "textures/entity/slime/magmacube.png" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- SpecialMobData Hooks ----------------

View file

@ -6,12 +6,12 @@ import fathertoast.specialmobs.common.core.register.SMEntities;
import fathertoast.specialmobs.common.core.register.SMItems;
import fathertoast.specialmobs.common.entity.ghast.CorporealShiftGhastEntity;
import fathertoast.specialmobs.common.util.References;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.AbstractFireballEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
@ -31,177 +31,180 @@ import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.fml.network.NetworkHooks;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public class CorporealShiftFireballEntity extends AbstractFireballEntity {
private static final DataParameter<Boolean> CORPOREAL = EntityDataManager.defineId(CorporealShiftFireballEntity.class, DataSerializers.BOOLEAN);
private static final DataParameter<Boolean> CORPOREAL = EntityDataManager.defineId( CorporealShiftFireballEntity.class, DataSerializers.BOOLEAN );
public int explosionPower = 1;
private boolean shouldExplode = false;
@Nullable
private LivingEntity target;
public CorporealShiftFireballEntity(EntityType<? extends AbstractFireballEntity> entityType, World world) {
super(entityType, world);
public CorporealShiftFireballEntity( EntityType<? extends AbstractFireballEntity> entityType, World world ) {
super( entityType, world );
}
public CorporealShiftFireballEntity(World world, CorporealShiftGhastEntity ghast, double x, double y, double z) {
super(SMEntities.CORPOREAL_FIREBALL.get(), ghast, x, y, z, world);
setCorporeal(ghast.isCorporeal());
public CorporealShiftFireballEntity( World world, CorporealShiftGhastEntity ghast, double x, double y, double z ) {
super( SMEntities.CORPOREAL_FIREBALL.get(), ghast, x, y, z, world );
setCorporeal( ghast.isCorporeal() );
target = ghast.getTarget();
setItem(isCorporeal() ? new ItemStack(Items.FIRE_CHARGE) : new ItemStack(SMItems.INCORPOREAL_FIREBALL.get()));
setItem( isCorporeal() ? new ItemStack( Items.FIRE_CHARGE ) : new ItemStack( SMItems.INCORPOREAL_FIREBALL.get() ) );
}
public CorporealShiftFireballEntity(World world, PlayerEntity owner, LivingEntity target, double x, double y, double z) {
super(SMEntities.CORPOREAL_FIREBALL.get(), owner, x, y, z, world);
setCorporeal(false);
public CorporealShiftFireballEntity( World world, PlayerEntity owner, LivingEntity target, double x, double y, double z ) {
super( SMEntities.CORPOREAL_FIREBALL.get(), owner, x, y, z, world );
setCorporeal( false );
this.target = target;
setItem(new ItemStack(SMItems.INCORPOREAL_FIREBALL.get()));
setItem( new ItemStack( SMItems.INCORPOREAL_FIREBALL.get() ) );
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Corporeal Shift Fireball",
"", "", "", "", "", "" );//TODO
}
@Override
protected void defineSynchedData() {
super.defineSynchedData();
entityData.define(CORPOREAL, true);
entityData.define( CORPOREAL, true );
}
public boolean isCorporeal() {
return entityData.get(CORPOREAL);
return entityData.get( CORPOREAL );
}
public void setCorporeal(boolean corporeal) {
entityData.set(CORPOREAL, corporeal);
public void setCorporeal( boolean corporeal ) {
entityData.set( CORPOREAL, corporeal );
}
@Override
public void tick() {
super.tick();
if ( !level.isClientSide && !isCorporeal() ) {
if( !level.isClientSide && !isCorporeal() ) {
// Fizzle out and die when the target is dead or lost,
// or else the fireball goes bonkers.
if ( target == null || !target.isAlive() ) {
playSound(SoundEvents.FIRE_EXTINGUISH, 1.0F, 1.0F);
if( target == null || !target.isAlive() ) {
playSound( SoundEvents.FIRE_EXTINGUISH, 1.0F, 1.0F );
remove();
return;
}
// Follow target
Vector3d vector3d = new Vector3d( target.getX() - this.getX(), (target.getY() + (target.getEyeHeight() / 2)) - this.getY(), target.getZ() - this.getZ() );
setDeltaMovement(vector3d.normalize().scale(0.5));
setDeltaMovement( vector3d.normalize().scale( 0.5 ) );
}
if ( !level.isClientSide && shouldExplode )
if( !level.isClientSide && shouldExplode )
explode();
}
private void explode() {
boolean mobGrief = ForgeEventFactory.getMobGriefingEvent( level, getOwner() );
Explosion.Mode mode = mobGrief ? Explosion.Mode.DESTROY : Explosion.Mode.NONE;
level.explode( null, this.getX(), this.getY(), this.getZ(), (float)explosionPower, mobGrief, mode );
level.explode( null, this.getX(), this.getY(), this.getZ(), (float) explosionPower, mobGrief, mode );
target = null;
remove();
}
@Override
protected boolean shouldBurn() {
// Hee hee hee haw
return isCorporeal();
}
@Override
protected void onHit( RayTraceResult traceResult ) {
super.onHit( traceResult );
// Only go boom if the fireball is corporeal.
// If not, pass through blocks to be a menace.
if ( !level.isClientSide && isCorporeal() ) {
if( !level.isClientSide && isCorporeal() ) {
shouldExplode = true;
}
}
@Override
protected void onHitEntity( EntityRayTraceResult traceResult ) {
super.onHitEntity(traceResult);
if ( !this.level.isClientSide ) {
super.onHitEntity( traceResult );
if( !this.level.isClientSide ) {
Entity target = traceResult.getEntity();
Entity owner = getOwner();
if (!isCorporeal()) {
if( !isCorporeal() ) {
// TODO - Figure out why this is cringe
// TODO part 2. - What the fuck
// TODO part 3. - HELP
SpecialMobs.LOG.info("X={}, XO={}", target.getX(), target.xo);
SpecialMobs.LOG.info("Z={}, ZO={}", target.getZ(), target.zo);
if (target.getX() != target.xo || target.getY() != target.yo || target.getZ() != target.zo) {
SpecialMobs.LOG.info( "X={}, XO={}", target.getX(), target.xo );
SpecialMobs.LOG.info( "Z={}, ZO={}", target.getZ(), target.zo );
if( target.getX() != target.xo || target.getY() != target.yo || target.getZ() != target.zo ) {
explode();
}
else {
playSound(SoundEvents.FIRE_EXTINGUISH, 1.0F, 1.0F);
playSound( SoundEvents.FIRE_EXTINGUISH, 1.0F, 1.0F );
remove();
}
}
else {
target.hurt(DamageSource.fireball(this, owner), 6.0F);
if (owner instanceof LivingEntity) {
doEnchantDamageEffects((LivingEntity) owner, target);
target.hurt( DamageSource.fireball( this, owner ), 6.0F );
if( owner instanceof LivingEntity ) {
doEnchantDamageEffects( (LivingEntity) owner, target );
}
}
}
}
@Override
public boolean hurt(DamageSource damageSource, float damage) {
if (!isCorporeal()) {
if (isInvulnerableTo(damageSource) || damageSource.isFire()) {
public boolean hurt( DamageSource damageSource, float damage ) {
if( !isCorporeal() ) {
if( isInvulnerableTo( damageSource ) || damageSource.isFire() ) {
return false;
}
shouldExplode = true;
return true;
}
else {
return super.hurt(damageSource, damage);
return super.hurt( damageSource, damage );
}
}
@Override
public void addAdditionalSaveData(CompoundNBT compoundNBT) {
super.addAdditionalSaveData(compoundNBT);
compoundNBT.putInt("ExplosionPower", explosionPower);
compoundNBT.putBoolean("Corporeal", isCorporeal());
compoundNBT.putInt("TargetId", target == null ? -1 : target.getId());
public void addAdditionalSaveData( CompoundNBT compoundNBT ) {
super.addAdditionalSaveData( compoundNBT );
compoundNBT.putInt( "ExplosionPower", explosionPower );
compoundNBT.putBoolean( "Corporeal", isCorporeal() );
compoundNBT.putInt( "TargetId", target == null ? -1 : target.getId() );
}
@Override
public void readAdditionalSaveData(CompoundNBT compoundNBT) {
super.readAdditionalSaveData(compoundNBT);
if (compoundNBT.contains("ExplosionPower", Constants.NBT.TAG_ANY_NUMERIC)) {
explosionPower = compoundNBT.getInt("ExplosionPower");
public void readAdditionalSaveData( CompoundNBT compoundNBT ) {
super.readAdditionalSaveData( compoundNBT );
if( compoundNBT.contains( "ExplosionPower", Constants.NBT.TAG_ANY_NUMERIC ) ) {
explosionPower = compoundNBT.getInt( "ExplosionPower" );
}
entityData.set(CORPOREAL, compoundNBT.getBoolean("Corporeal"));
if (compoundNBT.contains("TargetId", Constants.NBT.TAG_ANY_NUMERIC)) {
Entity entity = level.getEntity(compoundNBT.getInt("TargetId"));
if (entity instanceof LivingEntity) {
entityData.set( CORPOREAL, compoundNBT.getBoolean( "Corporeal" ) );
if( compoundNBT.contains( "TargetId", Constants.NBT.TAG_ANY_NUMERIC ) ) {
Entity entity = level.getEntity( compoundNBT.getInt( "TargetId" ) );
if( entity instanceof LivingEntity ) {
target = (LivingEntity) entity;
}
}
}
@Override
public IPacket<?> getAddEntityPacket() {
return NetworkHooks.getEntitySpawningPacket(this);
return NetworkHooks.getEntitySpawningPacket( this );
}
}
}

Some files were not shown because too many files have changed in this diff Show more