Jump to content

Recommended Posts

Posted

This used to be so simple, but now I have no idea what I'm doing. I haven't done mobs since 1.7.10 and the only 1.16 tutorial on spawning is for a pre-1.16.5 version which apparently uses code that no longer exists, so that's no help. Everything below is the result of looking through other people's posts, but I still can't get it.

I'm just trying to make a hostile mob spawn in beaches and rivers on the surface at all times of day/in all brightness levels.

At first it wouldn't spawn unless it made it a Monster, but then for some reason my mob would ONLY spawn in water in the ocean and would just sink down. In the rare occurrence it spawned on an island in the ocean it would just take a step with super speed then continue to sink back into the ocean.

Then I tried changing it back from Monster to Creature, giving it the tasks from zombies and changing what other people have said would make it spawn in daylight, and now it sometimes "spawns" on land (though not even in the biomes I specified) but when I teleport to the location it supposedly spawned at, it's just not there at all.

Can someone tell me what I'm doing wrong?

Mob class & parent class:

Spoiler
public class EntityBokoblin extends FMob
{
	public EntityBokoblin(EntityType<? extends EntityBokoblin> type, World world)
	{
		super(type, world);
	}

	public static AttributeModifierMap.MutableAttribute createAttributes()
	{
		MutableAttribute attributes = MonsterEntity.createMonsterAttributes();
		attributes.add(Attributes.MAX_HEALTH, 15);
		attributes.add(Attributes.ATTACK_DAMAGE, 4);
		attributes.add(Attributes.MOVEMENT_SPEED, (double) 2F);
		attributes.add(Attributes.FOLLOW_RANGE, 24);
		attributes.add(Attributes.ARMOR, 1);
		return attributes;
	}

	@Override
	public void registerGoals()
	{
		this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
		this.goalSelector.addGoal(6, new MoveThroughVillageGoal(this, 1.0D, true, 4, () -> false));
		this.goalSelector.addGoal(7, new WaterAvoidingRandomWalkingGoal(this, 1.0D));
		this.goalSelector.addGoal(8, new LookAtGoal(this, PlayerEntity.class, 8.0F));
		this.goalSelector.addGoal(8, new LookRandomlyGoal(this));
		this.targetSelector.addGoal(1, (new HurtByTargetGoal(this)).setAlertOthers(EntityBokoblin.class));
		this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, PlayerEntity.class, true));
		this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillagerEntity.class, false));
		this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AnimalEntity.class, true));
	}

	@Override
	protected void addDrops()
	{
		droplist.add(new MobDrop(MaterialRegistry.rupeegreen.get(), 1, 100));
		droplist.add(new MobDrop(MaterialRegistry.rupeered.get(), 1, 30));
		droplist.add(new MobDrop(Items.ARROW, 3, 100, true));
		droplist.add(new MobDrop(EquipmentRegistry.dekunut.get(), 1, 20, true));
	}

	@Override
	protected Element getMobElement()
	{
		return Elements.None;
	}

	@Override
	protected int getExp()
	{
		return 5;
	}

	@Override
	protected boolean canSpawnInLight()
	{
		return true;
	}

	@Override
	protected boolean disruptsPlayerSleep()
	{
		return true;
	}

	@Override
	protected int spawnType()
	{
		return 0;
	}
}

public abstract class FMob extends CreatureEntity implements IHasElement
{
	protected List<MobDrop> droplist;

	public FMob(EntityType<? extends CreatureEntity> entitytype, World world)
	{
		super(entitytype, world);
		droplist = new ArrayList<MobDrop>();
		addDrops();
	}

	@Override
	public IPacket<?> getAddEntityPacket()
	{
		return NetworkHooks.getEntitySpawningPacket(this);
	}

	protected abstract void addDrops();

	protected abstract int getExp();

	protected abstract boolean canSpawnInLight();

	protected abstract boolean disruptsPlayerSleep();

	/*
	 * 0 = land, 1 = water
	 */
	protected abstract int spawnType();

	protected abstract Element getMobElement();

	@Override
	public Element getElement()
	{
		return getMobElement();
	}

	@Override
	public ITextComponent getDisplayName()
	{
		return new StringTextComponent(EntityRegistry.names.get(getType()));
	}

	@Override
	public float getWalkTargetValue(BlockPos pos, IWorldReader world)
	{
		BlockState block = world.getBlockState(pos.below());
		boolean canspawn = spawnType() == 0 ? (!block.getMaterial().isLiquid() && block.getMaterial().isSolid()) : block.getMaterial().isLiquid();
		return canspawn ? 1 : 0;
	}

	public boolean isPreventingPlayerRest(PlayerEntity player)
	{
		return true;
	}

	@Override
	protected int getExperienceReward(PlayerEntity player)
	{
		return getExp();
	}

	@Override
	protected void dropCustomDeathLoot(DamageSource source, int lootinglevel, boolean hurtbyplayer)
	{
		super.dropCustomDeathLoot(source, lootinglevel, hurtbyplayer);
		List<ItemStack> drops = new ArrayList<ItemStack>();
		droplist.forEach((drop) ->
		{
			ItemStack stack = drop.getDrop(lootinglevel);
			if (stack != null && !stack.isEmpty())
			{
				drops.add(stack);
			}
		});
		drops.forEach(this::spawnAtLocation);
	}

	public void aiStep()
	{
		this.updateSwingTime();
		super.aiStep();
	}
}

 

Entity registry / spawn setup:

Spoiler
	public static final HashMap<Biome, List<Spawners>> spawnlist = new HashMap<Biome, List<Spawners>>();
	public static final HashMap<EntityType<?>, String> names = new HashMap<EntityType<?>, String>();

	public static final DeferredRegister<EntityType<?>> registry = DeferredRegister.create(ForgeRegistries.ENTITIES, Main.modid);

	public static final RegistryObject<EntityType<EntityBokoblin>> bokoblin = register("bokoblin", "Bokoblin",
			EntityType.Builder.<EntityBokoblin>of(EntityBokoblin::new, EntityClassification.CREATURE).sized(0.8f, 1.33f).clientTrackingRange(8));

	private static <T extends Entity> RegistryObject<EntityType<T>> register(String registryname, String displayname, EntityType.Builder<T> builder)
	{
		EntityType<T> entity = builder.build(new ResourceLocation(Main.modid, registryname).toString());
		names.put(entity, displayname);
		return registry.register(registryname, () -> entity);
	}

	public static void post()
	{
		createSpawn(Category.BEACH, from(bokoblin, 100, 1, 4));
		createSpawn(Category.RIVER, from(bokoblin, 70, 1, 2));

		Main.dev("---");
		Main.dev("Creating mob spawns...");
		for (Biome biome : ForgeRegistries.BIOMES)
		{
			if (spawnlist.containsKey(biome))
			{
				Main.dev("	->	Biome: " + biome.getRegistryName().toString().toUpperCase());
				spawnlist.get(biome).forEach((spawn) -> Main.dev("	->	->	" + names.get(spawn.type) + "	->	WEIGHT: " + spawn.weight + ", AMOUNT: " + spawn.minCount + "-" + spawn.maxCount));
			}
		}
		Main.dev("---");
	}

	private static void createSpawn(Category biome, Spawners... spawners)
	{
		for (Biome b : ForgeRegistries.BIOMES)
		{
			if (b.getBiomeCategory() == biome)
			{
				createSpawn(b, spawners);
			}
		}
	}

	private static void createSpawn(Biome biome, Spawners... spawners)
	{
		List<Spawners> list = spawnlist.containsKey(biome) ? spawnlist.get(biome) : new ArrayList<Spawners>();
		for (Spawners spawner : spawners)
		{
			list.add(spawner);
		}
		spawnlist.put(biome, list);
	}

	private static <T extends LivingEntity> Spawners from(RegistryObject<EntityType<T>> entity, int weight, int min, int max)
	{
		return new Spawners(entity.get(), weight, min, max);
	}

	public static void registerRenderers()
	{
		register(bokoblin.get(), RenderBokoblin::new, EntityBokoblin.createAttributes().build());
	}

	@SuppressWarnings("deprecation")
	private static <T extends LivingEntity> void register(EntityType<T> entityClass, IRenderFactory<? super T> renderFactory, AttributeModifierMap map)
	{
		RenderingRegistry.registerEntityRenderingHandler(entityClass, renderFactory);
		DeferredWorkQueue.runLater(() ->
		{
			GlobalEntityTypeAttributes.put(entityClass, map);
		});
	}

 

Spawn event:

Spoiler
	@SubscribeEvent(priority = EventPriority.HIGH)
	public void spawn(BiomeLoadingEvent event)
	{
		if (EntityRegistry.spawnlist != null)
		{
			MobSpawnInfoBuilder spawninfo = event.getSpawns();
			for (Biome biome : ForgeRegistries.BIOMES)
			{
				List<Spawners> spawnlist = EntityRegistry.spawnlist.get(biome);
				if (spawnlist != null)
				{
					for (Spawners spawner : spawnlist)
					{
						spawninfo.addSpawn(spawner.type.getCategory(), spawner);
					}
				}
			}
		}
	}

 

 

Posted

let's do it like this - start a new mod - a test mod. copy only entity class from the mod above. nothing else.

then follow this:

it is a summary of how i created entities. it is not a proper tutorial and it is by no means official manual.

Posted
1 hour ago, MFMods said:

let's do it like this - start a new mod - a test mod. copy only entity class from the mod above. nothing else.

then follow this:

it is a summary of how i created entities. it is not a proper tutorial and it is by no means official manual.

Your step #19 (though shouldn't it be 9?) was exactly what I needed without having to start anything new, so many thanks!

Posted

great, but you should have used this  to clean up the mess you made while trying to figure things out.

one example - BiomeLoadingEvent - don't iterate through all biomes; you have an exact biome in the event object.

Posted
17 hours ago, MFMods said:

great, but you should have used this  to clean up the mess you made while trying to figure things out.

one example - BiomeLoadingEvent - don't iterate through all biomes; you have an exact biome in the event object.

I definitely didn't intend to do that, thanks for pointing that out.

Posted
17 hours ago, MFMods said:

great, but you should have used this  to clean up the mess you made while trying to figure things out.

one example - BiomeLoadingEvent - don't iterate through all biomes; you have an exact biome in the event object.

1 minute ago, SapphireSky said:

I definitely didn't intend to do that, thanks for pointing that out.

Scratch that, yes I did. Because BiomeLoadingEvent doesn't give you the biome, only the biome category. I want it to be biome-specific, not category-specific. Unless there's a better way of getting the actual biomes of the event's category?

Posted

category is what we want 90% of the time.

if event.Category==SWAMP then event.spawner.add(new thingy)

result is - mob spawns in all swamps, even those added by other people.

if you want the opposite, to spawn a bird in single exact type of a forest, BiomeLoadingEvent has a name field.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.