Draco18s / HarderStuff

Tweaking vanilla and making it more interesting
8 stars 2 forks source link

HOWTO: Add harder ores to an autominer #9

Closed Baughn closed 9 years ago

Baughn commented 9 years ago

Hi,

I'm trying to add support for Harder Ores to the Electrical Age auto-miner. I hope you can help me figure out how...

I'm probably meant to use IHardStones::isUnstableBlock to check if a stone is part of HO. Right? But what do I do with it afterwards, to actually break it?

As a stopgap, I guess I'll grab the block itself instead of breaking it.

Draco18s commented 9 years ago

I am not familiar with Electrical Age and its auto-miner, unfortunately.

IHardStones::isUnstableBlock is for the stone replacement blocks from HarderUnderground. These are effectively "stone" "granite" etc.

Breaking blocks should be done the same way as any other block is broken...ah, that would depend on how it's currently working. One moment...

Hmm. Yeah, there's no good way to do this, it looks like. dropBlockAsItemWithChance will cause the chunks to spawn in the world (you don't want that).

Your best bet might be to construct the item stacks manually with getItemDropped, damageDropped. That would give you the item stack (minus size). Getting size you would loop from current-meta down to (and including 0) calling quantityDropped. That would give you a stack size. Then you could set the block to air. Note: diamond and uranium should have meta reduced by 3 each "break." Redstone's replacement is as normal, nothing special.

If you have any kind of fuel/time parameters you could set the block's meta each step until air, or you could just deduct/delay based on the metadata value ((meta + 1) * whatever).

Baughn commented 9 years ago

Ok. Obvious problem is...

How do I know if the block I'm breaking is one of yours?

It'd be very helpful if you could add an API for this, especially given what you said about diamond/uranium. If I hardcode those, I'm likely to get out of sync when you, e.g, add Osmium or whatever.

Draco18s commented 9 years ago

Do an instanceof check for BlockHardOreBase.

The other bit (diamond, etc) is trickier. As that's not even currently encoded as a block-level value.

Alternatively, IHardRecipes::getOreList() will return a hashmap where the Keys are all HardOre blocks (+redstone) so you could do hash.containsKey() on the block in question. Then if the dropped item is Items.redstone_dust to ignore the metadata. This method would work for any other blocks that have been registered with my mod as well.

But the metadeduction would require some refactoring on my part.

Baughn commented 9 years ago

There are, presently, a whole lot of autominers that don't work with your ores.

I have my own reasons to want to make this work, but I think you should probably do that refactoring—for the sake of mods which aren't EA, at least.

Draco18s commented 9 years ago

Oh I plan on adding the methods you seek. I was just looking for something that'll work for you without that.

Baughn commented 9 years ago

Ok, cool. I just added the code to do so, but it'll probably never end up getting merged, at the rate they're handling pull requests right now. I have about 5,000 lines' worth for the main devs to look at first. :P

Draco18s commented 9 years ago

Hehe.

Baughn commented 9 years ago

Though I didn't quite use your code. It works, see https://brage.info/e9/94.html, but...

Is this about right?

    boolean isHarderOre(Block block) {
        final IHardRecipes recipeManager = HardLibAPI.recipeManager;
        if (recipeManager == null) return false;
        for (BlockWrapper ore : recipeManager.getOreList().keySet()) {
            // Might be redstone, see https://github.com/Draco18s/HarderStuff/issues/9
            if (ore.block.equals(block) && !(ore.block instanceof BlockRedstoneOre)) {
                return true;
            }
        }
        return false;
    }
                    Block block = jobCoord.world().getBlock(jobCoord.x, jobCoord.y, jobCoord.z);
                    int meta = jobCoord.world().getBlockMetadata(jobCoord.x, jobCoord.y, jobCoord.z);
                    ArrayList<ItemStack> drop = block.getDrops(jobCoord.world(), jobCoord.x, jobCoord.y, jobCoord.z, meta, 0);

                    for (ItemStack stack : drop) {
                        drop(stack);
                    }

                    if (isHarderOre(block) && meta > 0) {
                        // TODO: Should do -3 for diamond, uranium. Probably wait for API, but block PR on this.
                        jobCoord.world().setBlock(jobCoord.x, jobCoord.y, jobCoord.z, block, meta - 1, 3);
                    } else {
                        jobCoord.world().setBlockToAir(jobCoord.x, jobCoord.y, jobCoord.z);
                    }
Baughn commented 9 years ago

Also, since there isn't a license that I can see, do I have your permission to include 1.7.10-HarderOres-9.16.1a-deobf.jar (which I made myself) in the repository?

Draco18s commented 9 years ago

The redstone check there won't work. I have a replacement block to do the ore flower thingy.

I'll get you an API call here as soon as I can.

re license: what for? I will note that I'm generally open-source about stuff, I just haven't figured out where I sit with Reasonable Realism.

Baughn commented 9 years ago

Without a license, it's illegal for me to include the API in my repository. I'm anti-closed-source in general, because closed-source mods are near-impossible for me to fix bugs in when they pop up on my server, but the particular case here is...

In theory, it's legal for me to call your API without having a copy of it. Via reflection. Which is annoying and bug-prone. In practice, I'm unlikely to go to the bother. Also, while it doesn't affect me here in Europe, there are some suggestions that the TPP would make it illegal to use APIs like that over in America, which would be a problem for the main Eln devs.

Draco18s commented 9 years ago

Oh oh. I gotcha. The API is totally open source. Yeah. Not a problem.

Just as an FYI, you don't need to distribute it when you compile though. Forge has Optional annotations (cpw.mods.fml.common.Optional), as well as the fact that you can hide a call to an API inside another class, and as long as that class doesn't get called (due to the call being wrapped in an if(Loader.isModLoaded) check) things stay happy.

But yeah, the API is freely available.

Baughn commented 9 years ago

Right. You should add a license file, so other people will know.

Draco18s commented 9 years ago

re license: good point.

Looking at this closely I might be able to make this work without needing any API fiddling. I have to abuse the getDrops method to do it, but I think it will work.

Draco18s commented 9 years ago

Ok, yeah, that'll work. Here's what I'm going to do: I've modified getDrops(...) to return ALL of the chunks that would drop if fully mined right now as separate stacks for each standard "break." E.g. allDrops.get(0) would be current meta, allDrops.get(1) would be "current meta -1" and so on (the diamond/uranium -3 alteration has been generalized, this method accounts for that as well).

Can you think of a scenario where this will fail spectacularly? Is there anything else you would need?

Baughn commented 9 years ago

I'm spectacularly undereducated on anything not to do with Electrical Age, so I'm probably not the best person to ask.

It seems to me that it'd work, for the EA autominer at least. What would happen for the Tinker's hammer or other conveniences? I have no idea. Well, at least version 10 of my test server will include Harder Ores, so if it breaks I'll probably send you a whole heap of bug reports. :)

(And if it were open-source, I'd send you PRs instead. Just saying.)

Draco18s commented 9 years ago

I suspect that most things use getDrops or dropBlockAsItemWithChance. The former here will drop the full load of items and the block can be removed, the latter will drop the current lump and reset itself.

Anyway, we'll see.

Draco18s commented 9 years ago

Here's a updated copy to play with. https://dl.dropboxusercontent.com/u/7950499/1.7.10-HarderOres-9.16.2a.jar

Baughn commented 9 years ago

EA, of course, uses getDrops...

I'll try the new version once I have a computer capable of running it again. But there's one problem; doing it this way would make the miner exceedingly fast.

Draco18s commented 9 years ago

Unfortunately, yes.

Baughn commented 9 years ago

So maybe you should go with an isHarder/mineOre combo in an API of your own?

It'd be simple enough; the mineOre function can take a block parameter that it'll use once the ore is entirely mined out. In that case you probably shouldn't modify getDrops, though; there'd be perverse incentives, in that any mod which does not implement your API will be perceived as better.

Hmm, or have the getDrops version give only about half of the drops...

Draco18s commented 9 years ago

I can certainly add it. You would need a isHardOre() method as well as a mineOnce() yeah? The mine function would do everything required (reduce meta or set to air) and return the ItemStack drops back to the caller. Is that sufficient?

Baughn commented 9 years ago

Yes, that's exactly what I had in mind.

I'd use that. It's only really going to work if you don't also modify getDrops, however.

Baughn commented 9 years ago

Though mineOnce should take a block parameter, since I very specifically don't want to set it to air. :)

Draco18s commented 9 years ago

MineOnce would take world, x,y,z and handle that itself.

Baughn commented 9 years ago

Well, what I mean is that I specifically would find it problematic if the block is air afterwards, since that could cause massive underground spawning et c.

But now that I think about it, I can just check if it's air post-call and set it to cobble in that case. So carry on.

Draco18s commented 9 years ago

You would have to do that yourself, yes. I can't see a way to make it universal. Some miners might wish the block to become air, others might want smooth stone, etc.

Baughn commented 9 years ago

Which is why I suggested adding a block parameter. But it's probably cleaner to leave this to the caller. Maybe return true once it's done with the block?

Draco18s commented 9 years ago

That would require either an array ref parameter or a custom class return object. Or the block parameter of what to set to when hitting minimum meta.

None feel very clean.

Draco18s commented 9 years ago

Alright, here's what you're getting for the mineOnce:

    /**
     * Mines the hard ore block at (x,y,z) and returns the resulting ArrayList<ItemStack> drops.<br/>
     * The replacement block and meta are placed into the world when the ore being mined is fully depleted.
     * @param world
     * @param x
     * @param y
     * @param z
     * @param fortune - fortune enchantment level for miner
     * @param replacement (optional) - The block to place if completely mined.  Default: air
     * @param replacementMeta (optional) - the metadata of the block to replace.
     * @return drops
     */
    public ArrayList<ItemStack> mineHardOreOnce(World world, int x, int y, int z, int fortune, Block replacement, int replacementMeta);

The block and meta are optional (there's an overloaded method lacking those parameters).

Baughn commented 9 years ago

That works~

Draco18s commented 9 years ago

10.17 and up will have those methods. I'll update the git repo today sometime.