diff --git a/examplemods/diamondBlockCustomCraft.js b/examplemods/diamondBlockCustomCraft.js index d6a9520..28e3b8d 100644 --- a/examplemods/diamondBlockCustomCraft.js +++ b/examplemods/diamondBlockCustomCraft.js @@ -34,7 +34,7 @@ var recipeInternal = []; Object.keys(recipeLegend).forEach((key) => { recipeInternal.push(ToChar(key)); - var ingredient = ModAPI.blocks[recipeLegend[key].id].getRef(); + var ingredient = (recipeLegend[key].type === "block" ? ModAPI.blocks : ModAPI.items)[recipeLegend[key].id].getRef(); recipeInternal.push(ingredient); }); diff --git a/examplemods/guns_craftable.js b/examplemods/guns_craftable.js new file mode 100644 index 0000000..3795cb5 --- /dev/null +++ b/examplemods/guns_craftable.js @@ -0,0 +1,185 @@ +(()=>{ + const itemTexture = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADySURBVFhH7ZQxDoMwDEVNr4G6VmJC6mk4Itdh7VpxjsBPDaKVCLZxypInIccg5zs/CVQoKAj8uHLjeETouo6HvlQcU3yJ932PIKkTceRAFH8NA6dE3IzbVogdQBOPtuVXfk5IJ8jWhKY4SxPaQvcmLEWuTUj/A1sqiEEcTQBENDWjvh3mvZtxccLiwMqvE0DrhNUBCCy1KSdAUsPiwFYcpM5ENrYWh3tdI4cT4dk0MSKPXzMSRSC+PMi14mcO4e7esv2iJqyHEGDVPCR6jyMC5luERXOfuoY7QFi8MKsDe6tXk8OBv7Ge/E96DZeKFwoOEE1wUX7TFh5zsgAAAABJRU5ErkJggg=="; + ModAPI.meta.title("guns"); + ModAPI.meta.version("v1.0"); + ModAPI.meta.icon(itemTexture); + ModAPI.meta.description("Requires AsyncSink."); + + function PistolItem() { + var recoilSpeed = 0; //recoil controller + var DamageSourceClass = ModAPI.reflect.getClassByName("DamageSource"); + var creativeMiscTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabMisc; + var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); + var itemSuper = ModAPI.reflect.getSuper(itemClass, (x) => x.length === 1); + var nmi_ItemPistol = function nmi_ItemPistol() { + itemSuper(this); //Use super function to get block properties on this class. + this.$setCreativeTab(creativeMiscTab); + } + + ModAPI.addEventListener("update", ()=>{ //recoil update loop (client) + ModAPI.player.rotationPitch -= recoilSpeed; + recoilSpeed *= 0.7; + }); + + function entityRayCast(player, world, range) { + const HEADSHOT_MAX_DISTANCE_FROM_HEAD = 0.72; + var eyePosition = player.getPositionEyes(0.0); + var targetPosition = player.rayTrace(range, 0).hitVec; + var entities = world.getEntitiesWithinAABBExcludingEntity( + player.getRef(), + player.getEntityBoundingBox().expand(range, range, range).getRef() + ).getCorrective().array; + var closestEntity = null; + var isHeadshot = false; + var closestDistance = range; + + // Iterate through all entities to find the one the player is looking at + for (var i = 0; i < entities.length; i++) { + if (!entities[i]) { + continue; + } + var entity = entities[i]; + + // Check if the entity's bounding box intersects with the player's ray + var entityBB = entity.getEntityBoundingBox().expand(0.3, 0.3, 0.3); + var intercept = entityBB.calculateIntercept(eyePosition.getRef(), targetPosition.getRef()); + + if (intercept != null) { + var distance = eyePosition.distanceTo(intercept.hitVec.getRef()); + if (distance < closestDistance) { + closestDistance = distance; + closestEntity = entity; + isHeadshot = entity.getPositionEyes(0.0).distanceTo(intercept.hitVec.getRef()) < HEADSHOT_MAX_DISTANCE_FROM_HEAD; + } + } + } + + var rayTraceResult = closestEntity; + if (rayTraceResult != null){ + return {entity: rayTraceResult, headshot: isHeadshot}; + } else{ + return null; + } + } + ModAPI.reflect.prototypeStack(itemClass, nmi_ItemPistol); + nmi_ItemPistol.prototype.$onItemRightClick = function ($itemstack, $world, $player) { + DamageSourceClass.staticMethods.$callClinit.method(); + //Noticed that the gun only worked after an entity in the world takes damage XD + //TeaVM is very optimised. Using $callClinit tells it to hurry up pretty much lol + var cactus = DamageSourceClass.staticVariables.cactus; + var world = ModAPI.util.wrap($world); + var entityplayer = ModAPI.util.wrap($player); + var shotentitydata = entityRayCast(entityplayer, world, 16.0); + if (shotentitydata != null){ + if (world.isRemote) { + recoilSpeed += 4; + } else { + shotentitydata.entity.attackEntityFrom(cactus, 10 + (16 * shotentitydata.headshot)); + if (shotentitydata.headshot) { + console.log("H E A D S H O T"); + } + world.playSoundAtEntity(entityplayer.getRef(), ModAPI.util.str("tile.piston.out"), 1.0, 1.8); + } + } else if (!world.isRemote) { + world.playSoundAtEntity(entityplayer.getRef(), ModAPI.util.str("random.click"), 1.0, 1.8); + } + return $itemstack; + } + + async function addGunRecipe(gunItem) { + var ObjectClass = ModAPI.reflect.getClassById("java.lang.Object").class; + function ToChar(char) { + return ModAPI.reflect.getClassById("java.lang.Character").staticMethods.valueOf.method(char[0].charCodeAt(0)); + } + + // Define the recipe legend to map characters to items + var recipeLegend = { + "I": { + type: "item", + id: "iron_ingot" // Using dirt blocks + }, + "C": { + type: "block", + id: "iron_block" // Using dirt blocks + }, + "Q": { + type: "item", + id: "gunpowder" // Using dirt blocks + }, + }; + + // Define the crafting grid pattern for the recipe + var recipePattern = [ + "IIC", + " QI" + ]; + + // Convert the recipe pattern and legend into the required format + var recipeInternal = []; + Object.keys(recipeLegend).forEach((key) => { + recipeInternal.push(ToChar(key)); + var ingredient = (recipeLegend[key].type === "block" ? ModAPI.blocks : ModAPI.items)[recipeLegend[key].id].getRef(); + recipeInternal.push(ingredient); + }); + + var recipeContents = recipePattern.flatMap(row => ModAPI.util.str(row)); + var recipe = ModAPI.util.makeArray(ObjectClass, recipeContents.concat(recipeInternal)); + + var resultItem = ModAPI.reflect.getClassById("net.minecraft.item.ItemStack").constructors[4](gunItem, 1); + + + // Register the recipe with CraftingManager + var craftingManager = ModAPI.reflect.getClassById("net.minecraft.item.crafting.CraftingManager").staticMethods.getInstance.method(); + ModAPI.hooks.methods.nmic_CraftingManager_addRecipe(craftingManager, resultItem, recipe); + } + + function internal_reg() { + var pistol_item = (new nmi_ItemPistol()).$setUnlocalizedName( + ModAPI.util.str("pistol") + ).$setMaxStackSize(1); + itemClass.staticMethods.registerItem.method(ModAPI.keygen.item("pistol"), ModAPI.util.str("pistol"), pistol_item); + ModAPI.items["pistol"] = pistol_item; + addGunRecipe(pistol_item); + return pistol_item; + } + + if (ModAPI.items) { + return internal_reg(); + } else { + ModAPI.addEventListener("bootstrap", internal_reg); + } + } + + ModAPI.dedicatedServer.appendCode(PistolItem); + var pistol_item = PistolItem(); + + ModAPI.addEventListener("lib:asyncsink", async () => { + ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{ + ModAPI.mc.renderItem.registerItem(pistol_item, ModAPI.util.str("pistol")); + }); + AsyncSink.L10N.set("item.pistol.name", "Pistol"); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/pistol.json", JSON.stringify( + { + "parent": "builtin/generated", + "textures": { + "layer0": "items/pistol" + }, + "display": { + "thirdperson": { + "rotation": [ 5, 80, -45 ], + "translation": [ 0, 1, -3 ], + "scale": [ 1.0, 1.0, 1.0 ] + }, + "firstperson": { + "rotation": [ 0, -135, 25 ], + "translation": [ 0, 4, 2 ], + "scale": [ 1.8, 1.8, 1.8 ] + } + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/items/pistol.png", await (await fetch( + itemTexture + )).arrayBuffer()); + }); +})(); \ No newline at end of file