Jump to content

[1.12.2] Conditionnal recipes


Kinniken

Recommended Posts

Hi,

 

I would like to know if there is a way in 1.12.2 to make (new, mod-provided) recipes conditional per-player? With a custom condition that could be triggered by my mod's code?

 

Ideally I would like both the recipe to be unusable before that trigger, and to make it be "discovered" when the trigger is reached.

 

Thanks!

Link to comment
Share on other sites

 

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

24 minutes ago, Kinniken said:

I saw this, but the last post claims that the solution given isn't advisable and instead refers to a tutorial from 2014 - is that really still valid?

 

Well, you should give it a shot. First of all, diesieben07 was linking to it in 2017 and it is his own tutorial so he probably knows what is relevant or not. Also, even when things change between versions the concepts are usually sound -- like maybe name of a method changes or something but the general idea of getting the player from the container is likely still valid.

 

However if you look at the tutorial, it also starts with a hint about another possible approach. There is the PlayerEvent.ItemCraftedEvent which you can handle. I might suggest starting with that.

 

You really didn't say much about what sort of conditional behavior you want. Like if you want to just deny crafting recipes to players, then it might be very simple. If you want to use entirely different recipes that might get more complicated. For example, the ItemCraftedEvent will pass you the whole crafting matrix, but it would be up to you to try to match it against alternate recipes. But it is all possible.

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

I will try it out.

 

To be a little clearer as to what I want, it's basically the ability to "unlock" recipes. So for a specific recipe, when the player starts a world it's not available (whether in the recipe book or for actual usage); when the player reaches a mod-specific milestone (being taught by an NPC, reaching some kind of goal, whatever), it becomes available to him and he can both see it in the book and use it.

 

I do not need alternate recipes for the same inputs however.

Link to comment
Share on other sites

1 hour ago, Kinniken said:

I will try it out.

 

To be a little clearer as to what I want, it's basically the ability to "unlock" recipes. So for a specific recipe, when the player starts a world it's not available (whether in the recipe book or for actual usage); when the player reaches a mod-specific milestone (being taught by an NPC, reaching some kind of goal, whatever), it becomes available to him and he can both see it in the book and use it.

 

I do not need alternate recipes for the same inputs however.

Normally an event would be perfect for that. You could just cancel it or return a DENY result. However, unfortunately the ItemCraftedEvent doesn't seem to do that. Like you can certainly null out the result, but I think the ingredients would still be consumed and it would take some work to undo that.

 

Is this for your own recipes or for vanilla recipes. For your own recipes I think you can implement your own IRecipe that check the player conditions in the matches() method.

 

I'm thinking maybe another way to approach this is to try to replace the vanilla advancements for unlocking recipes. But that would be a lot of work and would require reflection and a lot of JSONs.

 

I just thought of another possible approach that just might work. The CriteriaTriggers.RECIPE_UNLOCKED field could be modified (might need reflection cause it is final, but it is already public) to use your own trigger instead where extend the vanilla one by override the trigger() and maybe the test() methods.

 

EDIT: There may be even more places to intercept things. Not sure which is the easiest. But for example, the EntityPlayerMP recipe book instance itself could be replaced with a custom version where you override the add() method. That might be the most logical place to intercept actually because that is where the recipe is actually unlocked and also where the advancement trigger is fired.

 

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Okay, yeah, I think my last idea works. It seems pretty easy although maybe needs more testing.

 

Basically the idea is to replace the recipeBook instance field with an extended custom recipe book class that checks a player condition before adding a recipe. The recipeBook field is instantiated for EntityPlayerMP in several situations so I basically just did a player tick event handler that checks to see if the recipe book is already replaced or not.

 

So in a properly registered/annotated event handling class I put:

    public static Field recipeBook = ReflectionHelper.findField(EntityPlayerMP.class, "recipeBook", "field_192036_cb");
    
    @SubscribeEvent(priority = EventPriority.HIGHEST, receiveCanceled = true)
    public static void onEvent(PlayerTickEvent event)
    {
        if (event.player instanceof EntityPlayerMP)
        {
            EntityPlayerMP playerMP = (EntityPlayerMP) event.player;
            RecipeBookServer recipeBookCurrent = playerMP.getRecipeBook();
            if (!(recipeBookCurrent instanceof RecipeBookServerCustom))
            {
                // DEBUG
                System.out.println("Replacing recipe book with custom book");
                
                RecipeBookServerCustom recipeBookNew = new RecipeBookServerCustom();
                recipeBookNew.copyFrom(recipeBookCurrent);
                try
                {
                    recipeBook.set(playerMP, recipeBookNew);
                }
                catch (IllegalArgumentException | IllegalAccessException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

 

And my custom recipe class is pretty simple:

public class RecipeBookServerCustom extends RecipeBookServer
{
    @SuppressWarnings("deprecation")
    @Override
    public void add(List<IRecipe> recipesIn, EntityPlayerMP player)
    {
        List<IRecipe> list = Lists.<IRecipe>newArrayList();

        for (IRecipe irecipe : recipesIn)
        {
            if (!recipes.get(getRecipeId(irecipe)) && !irecipe.isDynamic())
            {
                if (!player.world.isDaytime()) // <--- Put whatever player condition here
                {
                    // DEBUG
                    System.out.println("Unlocking recipe");
                    
                    unlock(irecipe);
                    markNew(irecipe);
                    list.add(irecipe);
                    CriteriaTriggers.RECIPE_UNLOCKED.trigger(player, irecipe);
                    net.minecraftforge.common.ForgeHooks.sendRecipeBook(player.connection, SPacketRecipeBook.State.ADD, recipesIn, Collections.emptyList(), isGuiOpen, isFilteringCraftable);
               }
                else
                {
                    // DEBUG
                    System.out.println("Can't unlock recipe as player condition not met");
                }
            }
            else
            {
                net.minecraftforge.common.ForgeHooks.sendRecipeBook(player.connection, SPacketRecipeBook.State.ADD, recipesIn, Collections.emptyList(), isGuiOpen, isFilteringCraftable);
            }
        }
    }
}

 

For testing, my player condition was simple -- I just checked if the player world was currently in nighttime or daytime and only allowed unlocking recipes during nighttime. You would of course need to change that to any player condition you want -- like they need to be wearing something, or have some capability, or whatever.

 

As far as my quick testing it seemed to work. You can see I added console statements to help testing. So in the daytime if you pick up any new ingredients nothing happens and the console will indicate that recipe wasn't unlocked, but in the daytime if you pick up new ingredients it adds recipe normally.

 

Probably needs a lot more testing, but it seems like it should generally work.

 

Now, one problem is that you didn't really specify what sort of player conditions you were interested in. My solution above is about *unlocking recipes" based on the player condition. But once they are unlocked they are unlocked and even if the condition changes they won't get locked again. What exactly is the behavior you wanted? 

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

7 hours ago, Kinniken said:

However isn't replacing the recipe book like that very invasive? Unless I'm missing something two mods that attempted your solution would clash, right?

 

At some point anything that makes major functionality is invasive. Like if I handle an event and change the health of a player and another mod does too, there isn't any way to prevent that. The best thing is to find specific conflicts if they arrive and work with the other mod (or use understanding of their source if available) to resolve it.

 

Also, the recipe book you're replacing with is a copy of the vanilla and extends it, so any code that calls any recipe book function will still work. So if they are calling add() or remove() or such they will work exactly the same. The custom recipe book extends the RecipeBookServer class so can be passed as a parameter and tested for type just like the vanilla one.

 

By the way, one other thing to think about. The approach I give above allows you to prevent the unlocking. However, I am not changing when the unlocking is called. In other words, if I pick up a stick and don't have the player condition, but then later get the player condition I would have to pick up the stick again to unlock it. That might be what you want, but if you want to create new situations where there is a chance to unlock you can simply call the EntityPlayerMP.unlockRecipes() method at any time. Like if you want all dye recipes to unlock after player does something, you can just call the unlockRecipes() method. You still haven't mentioned what player conditions you want to control the unlocking, so don't know what you need. Basically, I've given you an approach to prevent vanilla unlocking, and the way to force vanilla unlocking already exists.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

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.