Jump to content

[1.16] What is a good way to fill an itemstack capability with default values?


Recommended Posts

Posted (edited)

Hello!

 

I want to fill a capability from itemstack with default values only once. I had the same problem with livingEntities, but here I simply modified the spawning event. It seems like I won't be able to do the same for itemstacks, since there are so many possible ways for them to be created (picking up itemEntities, crafting, brewing, smelting. With other mods even more...)

 

One solution i had in mind was to add another boolean to the capability, called isFirstSpawn. It default values is true and once I copy the default values into the capability, I will also set this bool to false. This way, I could trigger the change of the cability in LivingEquipmentChangeEvent (for example) once and then never again. This method seems to be rather complicated however..

 

Do you have a better idea?

Edited by Tavi007
Posted (edited)

That's a good idea, however the data doesn't get saved. Here is my repo. Right now I moved my code from ElementifyLivingSpawn and ElementifyProjectileSpawn to private functions in AttachCapabilityEvent. Once this is working I can change elementifyItemstack in the same fashion.

package Tavi007.ElementalCombat.events;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import Tavi007.ElementalCombat.ElementalCombat;
import Tavi007.ElementalCombat.ElementalCombatAPI;
import Tavi007.ElementalCombat.capabilities.CapabilityProviderEntityAndItem;
import Tavi007.ElementalCombat.capabilities.CapabilityProviderProjectile;
import Tavi007.ElementalCombat.capabilities.ElementalAttack;
import Tavi007.ElementalCombat.capabilities.ElementalDefense;
import Tavi007.ElementalCombat.loading.AttackFormat;
import Tavi007.ElementalCombat.loading.EntityData;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.projectile.DamagingProjectileEntity;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.biome.Biome.TempCategory;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;

@Mod.EventBusSubscriber(modid = ElementalCombat.MOD_ID, bus = Bus.FORGE)
public class AttachCapabilityEvent 
{
	private static final ResourceLocation ENTITY = new ResourceLocation(ElementalCombat.MOD_ID, "entity_cap");
	private static final ResourceLocation PROJECTILE = new ResourceLocation(ElementalCombat.MOD_ID, "projectile_cap");
	private static final ResourceLocation ITEM = new ResourceLocation(ElementalCombat.MOD_ID, "item_cap");

	@SubscribeEvent
	public static void onAttachEntity(AttachCapabilitiesEvent<Entity> e)
	{
		Entity entity = e.getObject();
		if(entity instanceof LivingEntity) {
			e.addCapability(ENTITY, new CapabilityProviderEntityAndItem());
			elementifyLivingEntity((LivingEntity) entity);
		}
		if(entity instanceof DamagingProjectileEntity) {
			e.addCapability(PROJECTILE, new CapabilityProviderProjectile()); 
			elementifyProjectile((DamagingProjectileEntity) entity);
		}
	}

	@SubscribeEvent
	public static void onAttachItemStack(AttachCapabilitiesEvent<ItemStack> i)
	{
		i.addCapability(ITEM, new CapabilityProviderEntityAndItem());
		elementifyItemstack(i.getObject());
	}

	private static void elementifyLivingEntity(LivingEntity entity) {
		// load default data from dataManager
		ResourceLocation rl = new ResourceLocation(entity.getType().getRegistryName().getNamespace(), "elementalproperties/entities/" + entity.getType().getRegistryName().getPath());
		EntityData entityData = ElementalCombat.DATAMANAGER.getEntityDataFromLocation(rl);
		Set<String> weaknessSet = entityData.getWeaknessSet();
		Set<String> resistanceSet = entityData.getResistanceSet();
		Set<String> immunitySet = entityData.getImmunitySet();
		Set<String> absorbSet = entityData.getAbsorbSet();
		
		// rewrite set to mapping
		Set<AttackFormat> attackFormatSet = entityData.getAttackSet();
		Map<String, Integer> attackMap = new HashMap<String, Integer>();
		attackFormatSet.forEach((attack) ->
		{
			Integer value = attack.getValue();
			if (value <= 0){
				ElementalCombat.LOGGER.info("Elemental damage value of " + attack.getName() + " for " + entity.getName().toString() + " is <= 0. Using 1 instead.");
				value = 1;
			}
			attackMap.put(attack.getName(), value);
		});

		// player spawn should be biome independent
		if(entityData.getBiomeDependency()) 
		{
			String biomeProperties = null;
			BlockPos blockPos = new BlockPos(entity.getPositionVec());

			TempCategory category = entity.getEntityWorld().getBiome(blockPos).getTempCategory();
			if(category == TempCategory.COLD){
				biomeProperties = "ice";
			}
			else if(category == TempCategory.WARM){
				biomeProperties = "fire";
			}
			else if(category == TempCategory.OCEAN){
				biomeProperties = "water";
			}
			if(biomeProperties != null){
				resistanceSet.add(biomeProperties);
			}
		}

		// set capability
		ElementalAttack elemAtckCap = ElementalCombatAPI.getElementalAttackData(entity);
		ElementalDefense elemDefCap = ElementalCombatAPI.getElementalDefenseData(entity);
		elemAtckCap.setAttackMap(attackMap);
		elemDefCap.setWeaknessSet(weaknessSet);
		elemDefCap.setResistanceSet(resistanceSet);
		elemDefCap.setImmunitySet(immunitySet);
		elemDefCap.setAbsorbSet(absorbSet);
	}

	public static void elementifyProjectile(DamagingProjectileEntity entity) {
		if(entity.ticksExisted == 0){
			System.out.println("Newly spawned projectile found");

			DamagingProjectileEntity projectile = (DamagingProjectileEntity) entity;
			Entity  source = projectile.shootingEntity;
			if(source != null && source instanceof LivingEntity){
				LivingEntity sourceEntity = (LivingEntity) source;

				System.out.println("Shooter: " +sourceEntity.getDisplayName().getString());

				// If sourceEntity holds an item, use item data.
				// If not, use data from sourceEntity as default.
				ElementalAttack sourceData;
				if(sourceEntity.hasItemInSlot(EquipmentSlotType.MAINHAND)){
					ItemStack item = sourceEntity.getActiveItemStack();
					sourceData = ElementalCombatAPI.getElementalAttackData(item);
				}
				else{
					sourceData = ElementalCombatAPI.getElementalAttackData(sourceEntity);
				}

				//copy elemental attack capability
				ElementalAttack projectileData = ElementalCombatAPI.getElementalAttackData(projectile);
				projectileData.setAttackMap(sourceData.getAttackMap());
			}
		}
	}


	public static void elementifyItemstack(ItemStack item) {
		
	}

}

 

In these functions everything is happening as it should be, like accessing the data from DATAMANGER. However in ElementifyLivingHurtEvent I only get the (default) empty capability.

 

I know that my capability currently doesn't save its contents when leaving the world and restarting it. This is mostly likely due to an error in my saveNBT or readNBT function. Are these problems connected?

 

 

Edited by Tavi007
Posted
44 minutes ago, Tavi007 said:

I know that my capability currently doesn't save its contents when leaving the world and restarting it. This is mostly likely due to an error in my saveNBT or readNBT function. Are these problems connected?

Not to mention your code is very everywhere and doesn't even store a LazyOptional (why do you call creating a lazy optional EVERY SINGLE TIME), you don't even store the capability you mention.

 

First thing, why in the world are you storing a capability for an ItemStack on an Entity? Second thing, why is there any reference to storing information between these two capabilities. They might have dependent data, but their read and write functions are solely independent. Go look at Choonster's test mod if you want an example. 

Posted (edited)
46 minutes ago, ChampionAsh5357 said:

Not to mention your code is very everywhere and doesn't even store a LazyOptional (why do you call creating a lazy optional EVERY SINGLE TIME), you don't even store the capability you mention.

I used minecraftByExample as reference. I did not know, that this is bad practise...

 

Quote

First thing, why in the world are you storing a capability for an ItemStack on an Entity? Second thing, why is there any reference to storing information between these two capabilities. They might have dependent data, but their read and write functions are solely independent.

they have the same capability, but are completly independent of each other. Otherwise I would create 2 classes with identical code (but different names).

 

I will take a look at the test mod to see, how I could improve my code. But please answere me this: Why isn't the capability storing the data? What is going wrong?

Edited by Tavi007
Posted
57 minutes ago, Tavi007 said:

they have the same capability, but are completly independent of each other. Otherwise I would create 2 classes with identical code (but different names).

Ah ok. I misread the code then.

57 minutes ago, Tavi007 said:

I will take a look at the test mod to see, how I could improve my code. But please answere me this: Why isn't the capability storing the data? What is going wrong?

I don't really see anything particularly an issue, but I also couldn't pinpoint the information with the code. I should note that you have two listeners pointing towards AttachCapbilityEvent However, there seems to be a lot of information that is just not right. Most of the information is logical server only so you most likely wouldn't detect any "rendering" changes on the logical client. There is no synchronization if I do say which would be fine if you're not retrieve information on the logical client. How did you test that your capability was returning empty? It could just be you were trying to retrieve information that is not stored on a specific side. Of course, with the mess of code, I could just be completely wrong with my assumptions.

Posted

I completly rewrote the whole capability to be more like the testmod example. I think everything is working now, but i need time for a more in depth testing. I just wanted to let you know, that your link helped me a lot :)

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • I know that this may be a basic question, but I am very new to modding. I am trying to have it so that I can create modified Vanilla loot tables that use a custom enchantment as a condition (i.e. enchantment present = item). However, I am having trouble trying to implement this; the LootItemRandomChanceWithEnchantedBonusCondition constructor needs a Holder<Enchantment> and I am unable to use the getOrThrow() method on the custom enchantment declared in my mod's enchantments class. Here is what I have so far in the GLM:   protected void start(HolderLookup.Provider registries) { HolderLookup.RegistryLookup<Enchantment> registrylookup = registries.lookupOrThrow(Registries.ENCHANTMENT); LootItemRandomChanceWithEnchantedBonusCondition lootItemRandomChanceWithEnchantedBonusCondition = new LootItemRandomChanceWithEnchantedBonusCondition(0.0f, LevelBasedValue.perLevel(0.07f), registrylookup.getOrThrow(*enchantment here*)); this.add("nebu_from_deepslate", new AddItemModifier(new LootItemCondition[]{ LootItemBlockStatePropertyCondition.hasBlockStateProperties(Blocks.DEEPSLATE).build(), LootItemRandomChanceCondition.randomChance(0.25f).build(), lootItemRandomChanceWithEnchantedBonusCondition }, OrichalcumItems.NEBU.get())); }   Inserting Enchantments.[vanilla enchantment here] actually works but trying to declare an enchantment from my custom enchantments class as [mod enchantment class].[custom enchantment] does not work even though they are both a ResourceKey and are registered in Registries.ENCHANTMENT. Basically, how would I go about making it so that a custom enchantment declared as a ResourceKey<Enchantment> of value ResourceKey.create(Registries.ENCHANTMENT, ResourceLocation.fromNamespaceAndPath([modid], [name])), declared in a seperate enchantments class, can be used in the LootItemRandomChanceWithEnchantedBonusCondition constructor as a Holder? I can't use getOrThrow() because there is no level or block entity/entity in the start() method and it is running as datagen. It's driving me nuts.
    • Hi here is an update. I was able to fix the code so my mod does not crash Minecraft. Please understand that I am new to modding but I honestly am having a hard time understanding how anyone can get this to work without having extensive programming and debugging experience as well as searching across the Internet, multiple gen AI bots (claude, grok, openai), and examining source code hidden in the gradle directory and in various github repositories. I guess I am wrong because clearly there are thousands of mods so maybe I am just a newbie. Ok, rant over, here is a step by step summary so others can save the 3 days it took me to figure this out.   1. First, I am using forge 54.1.0 and Minecraft 1.21.4 2. I am creating a mod to add a shotgun to Minecraft 3. After creating the mod and compiling it, I installed the .jar file to the proper directory in Minecraft and used 1.21.4-forge-54.1.0 4. The mod immediately crashed with the error: Caused by: java.lang.NullPointerException: Item id not set 5. Using the stack trace, I determined that the Exception was being thrown from the net.minecraft.world.item.Item.Properties class 6. It seems that there are no javadocs for this class, so I used IntelliJ which was able to provide a decompiled version of the class, which I then examined to see the source of the error. Side question: Are there javadocs? 7. This method, specifically, was the culprit: protected String effectiveDescriptionId() {      return this.descriptionId.get(Objects.requireNonNull(this.id, "Item id not set"));  } 8. Now my quest was to determine how to set this.id. Looking at the same source file, I determined there was another method:  public Item.Properties setId(ResourceKey<Item> pId) {             this.id = pId;             return this;   } 9. So now, I need to figure out how to call setId(). This required working backwards a bit. Starting from the constructor, I stubbed out the variable p which is of type Item.Properties public static final RegistryObject<Item> SHOTGUN = ITEMS.register("shotgun", () -> new ShotgunItem(p)); Rather than putting this all on one line, I split it up for readability like this: private static final Item.Properties p = new Item.Properties().useItemDescriptionPrefix().setId(rk); Here is was the missing function, setId(), which takes a type of ResourceKey<Item>. My next problem is that due to the apparent lack of documentation (I tried searching the docs on this site) I could not determine the full import path to ResourceKey. I did some random searching on the Internet and stumbled across a Github repository which gave two clues: import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; Then I created the rk variable like this: private static ResourceKey<Item> rk = ResourceKey.create(Registries.ITEM, ResourceLocation.parse("modid:shotgunmod")); And now putting it all together in order: private static ResourceKey<Item> rk = ResourceKey.create(Registries.ITEM, ResourceLocation.parse("modid:shotgunmod")); private static final Item.Properties p = new Item.Properties().useItemDescriptionPrefix().setId(rk); public static final RegistryObject<Item> SHOTGUN = ITEMS.register("shotgun", () -> new ShotgunItem(p)); This compiled and the mod no longer crashes. I still have more to do on it, but hopefully this will save someone hours. I welcome any feedback and if I missed some obvious modding resource or tutorial that has this information. If not, I might suggest we add it somewhere for people trying to write a mod that doesn't crash. Thank you !!!  
    • I will keep adding to this thread with more information in case anyone can help, or at the very least I can keep my troubleshooting organized. I decided to downgrade to 54.1.0 in the hopes that this would fix the issue but it didn't. At least now I am on a "recommended" version. The crash report did confirm my earlier post that the Exception is coming from effectiveDescriptionId(). I'll continue to see if I can find a way to set the ID manually.   Caused by: java.lang.NullPointerException: Item id not set         at java.base/java.util.Objects.requireNonNull(Objects.java:259) ~[?:?]         at TRANSFORMER/[email protected]/net.minecraft.world.item.Item$Properties.effectiveDescriptionId(Item.java:465) ~[forge-1.21.4-54.1.0-client.jar!/:?]         at TRANSFORMER/[email protected]/net.minecraft.world.item.Item.<init>(Item.java:111) ~[forge-1.21.4-54.1.0-client.jar!/:?]         at TRANSFORMER/[email protected]/com.example.shotgunmod.ShotgunItem.<init>(ShotgunItem.java:19) ~[shotgunmod-1.0.0.jar!/:1.0.0]         at TRANSFORMER/[email protected]/com.example.shotgunmod.ModItems.lambda$static$0(ModItems.java:15) ~[shotgunmod-1.0.0.jar!/:1.0.0]         at TRANSFORMER/[email protected]/net.minecraftforge.registries.DeferredRegister$EventDispatcher.lambda$handleEvent      
    • It just randomly stop working after a rebooted my dedicated server PLEASE HELP!   com.google.gson   Failed to start the minecraft server com.google.gson.JsonSyntaxException: Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $  
  • Topics

×
×
  • Create New...

Important Information

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