From 078839700c8fc0ec2e3f7a837039bc0569cb9c2a Mon Sep 17 00:00:00 2001 From: Marcos Alonso Date: Mon, 23 Feb 2026 01:11:42 +0100 Subject: [PATCH] Good import of added reference models and minor improvements --- plugins.json | 2 +- plugins/hytale_hitbox_helper/about.md | 8 +- .../hytale_hitbox_helper.js | 199 ++++++++++++++++-- plugins/hytale_hitbox_helper/icon.png | Bin 3117 -> 5526 bytes 4 files changed, 187 insertions(+), 22 deletions(-) diff --git a/plugins.json b/plugins.json index 3d47923a..cc1c2982 100644 --- a/plugins.json +++ b/plugins.json @@ -1397,7 +1397,7 @@ "hytale_hitbox_helper": { "title": "Hytale Hitbox Helper", "author": "Marck.A.A", - "version": "1.0.0", + "version": "1.0.1", "description": "Dedicated tool to create Hytale hitboxes and JSON export.", "icon": "icon.png", "variant": "both", diff --git a/plugins/hytale_hitbox_helper/about.md b/plugins/hytale_hitbox_helper/about.md index 9082fdcc..d95cba6b 100644 --- a/plugins/hytale_hitbox_helper/about.md +++ b/plugins/hytale_hitbox_helper/about.md @@ -3,11 +3,17 @@ ## Features * **Dedicated Format:** Adds a new "Hytale Hitbox" project type. -* **Optimized Workspace:** * Automatic **Wireframe** view mode on activation. * **Quick Add:** One-click button to add a standard 32x32x32 hitbox. +* **Multiple Hitboxes:** It supports adding multiple Hitboxes. * **Smart Export:** * Exports strictly to Hytale's `.json` format. * Only exports elements named "hitbox" (case insensitive). * Automatic coordinate conversion (32 BB units = 1.0 Hytale unit). +* **References:** You can import other reference block mode models; they do not interfere with the hitbox. + +## References Models + +If you go to **File > Import** you can Import a model to use it as reference, it has to be (.blockymodel) +It wont be imported. ## How to Use diff --git a/plugins/hytale_hitbox_helper/hytale_hitbox_helper.js b/plugins/hytale_hitbox_helper/hytale_hitbox_helper.js index 7aac66e9..41027345 100644 --- a/plugins/hytale_hitbox_helper/hytale_hitbox_helper.js +++ b/plugins/hytale_hitbox_helper/hytale_hitbox_helper.js @@ -1,16 +1,46 @@ (function() { let export_action; let add_hitbox_action; + let import_reference_action; + let original_conditions = {}; const HITBOX_FORMAT_ID = 'hytale_hitbox'; + const actions_to_hide = [ + 'import_project', + 'import_bbmodel', + 'import_obj', + 'import_gltf', + 'import_image', + 'extrude_texture' + ]; + + function generateHitboxTexture() { + const canvas = document.createElement('canvas'); + const size = 128; + canvas.width = size; + canvas.height = size; + const ctx = canvas.getContext('2d'); + + ctx.clearRect(0, 0, size, size); + + ctx.strokeStyle = '#ff0000'; + const lineWidth = 2; + ctx.lineWidth = lineWidth; + + const offset = lineWidth / 2; + ctx.strokeRect(offset, offset, size - lineWidth, size - lineWidth); + + return canvas.toDataURL('image/png'); + } + BBPlugin.register('hytale_hitbox_helper', { title: 'Hytale Hitbox Helper', author: 'Marck.A.A', icon: 'icon.png', description: 'Tool to create easy Hytale hitboxes exportable to JSON.', - min_version: '4.8.0', - version: '1.0.0', + min_version: '4.8.0', + version: '1.0.1', variant: 'both', onload() { const format = new ModelFormat(HITBOX_FORMAT_ID, { @@ -21,23 +51,25 @@ target: 'Hytale', block_size: 32, centered_grid: true, + + optional_box_uv: true, box_uv: false, - optional_box_uv: false, single_texture: false, + uv_rotation: true, + per_texture_uv_size: true, + + bone_rig: true, rotate_cubes: false, - + onActivation() { document.body.classList.add('hytale_hitbox_mode'); - const viewMode = BarItems['view_mode']; - if (viewMode) { - viewMode.value = 'wireframe'; - viewMode.onChange(); + let existing_tex = Texture.all.find(t => t.id === 'hitbox_wireframe_tex'); + if (existing_tex) { + existing_tex.fromDataURL(generateHitboxTexture()); } Blockbench.showQuickMessage("Hytale Hitbox Mode Active"); }, onDeactivation() { - BarItems['view_mode'].value = 'textured'; - BarItems['view_mode'].onChange(); document.body.classList.remove('hytale_hitbox_mode'); } }); @@ -49,6 +81,107 @@ } `); + actions_to_hide.forEach(action_id => { + if (BarItems[action_id]) { + original_conditions[action_id] = BarItems[action_id].condition; + BarItems[action_id].condition = () => { + if (Format.id === HITBOX_FORMAT_ID) return false; + return Condition(original_conditions[action_id]); + }; + } + }); + + import_reference_action = new Action('import_hytale_reference', { + name: 'Import Reference (.blockymodel)', + icon: 'fa-file-import', + category: 'file', + condition: () => Format.id === HITBOX_FORMAT_ID, + click: function() { + Blockbench.import({ + extensions: ['blockymodel'], + type: 'Blockymodel Reference', + readtype: 'text', + multiple: true + }, function(files) { + if (!files || files.length === 0) return; + + if (!Codecs.blockymodel) { + Blockbench.showMessageBox({ + title: 'Missing Plugin', + message: 'The official Hytale plugin is required to read .blockymodel files.' + }); + return; + } + + let imported_root_groups = []; + let original_id = Format.id; + + Undo.initEdit({outliner: true}); + + Format.id = 'hytale_prop'; + + files.forEach(file => { + try { + let json = JSON.parse(file.content); + let content = Codecs.blockymodel.parse(json, file.path, { import_to_current_project: true }); + + if (content && content.new_groups) { + let new_groups = content.new_groups; + let imported_tex = content.new_textures && content.new_textures.length > 0 ? content.new_textures[0] : null; + + new_groups.forEach(g => { + if (!new_groups.includes(g.parent)) { + imported_root_groups.push(g); + } + + if (imported_tex) { + g.children.forEach(child => { + if (child instanceof Cube) { + for (const key in child.faces) { + child.faces[key].texture = imported_tex.uuid; + } + } + }); + } + }); + } + } catch (err) { + console.error("Error importing file:", file.name, err); + } + }); + + Format.id = original_id; + + if (imported_root_groups.length > 0) { + Undo.finishEdit('Import Reference (Base)'); + + setTimeout(() => { + Undo.initEdit({outliner: true, elements: [], groups: []}); + unselectAll(); + + let ref_group = Group.all.find(g => g.name.toLowerCase() === 'reference'); + if (!ref_group) { + ref_group = new Group({ + name: 'reference', + isOpen: true + }).init(); + } + + imported_root_groups.forEach(g => { + g.addTo(ref_group); + }); + + unselectAll(); + Undo.finishEdit('Format Reference'); + Canvas.updateAllFaces(); + Blockbench.showQuickMessage('Reference imported, textured and grouped!'); + }, 50); + } else { + Undo.cancelEdit(); + } + }); + } + }); add_hitbox_action = new Action('add_hytale_hitbox', { name: 'Add Hitbox', @@ -56,22 +189,41 @@ category: 'edit', condition: () => Format.id === HITBOX_FORMAT_ID, click: function() { + let hitbox_texture = Texture.all.find(t => t.id === 'hitbox_wireframe_tex'); + + if (!hitbox_texture) { + hitbox_texture = new Texture({ + id: 'hitbox_wireframe_tex', + name: 'Hitbox Wireframe' + }).add(); + + hitbox_texture.fromDataURL(generateHitboxTexture()); + } + const mesh = new Cube({ name: 'hitbox', - color: 2, - from: [-16, 0, -16], + color: 2, + from: [-16, 0, -16], to: [16, 32, 16], - }).init(); - - mesh.mesh.material.transparent = true; - mesh.mesh.material.opacity = 0.5; - - Undo.initEdit({elements: [mesh], outliner: true}); + autouv: 0 + }); + + for (const key in mesh.faces) { + mesh.faces[key].texture = hitbox_texture.uuid; + mesh.faces[key].uv = [0, 0, 16, 16]; + } + + mesh.init(); + + Undo.initEdit({elements: [mesh], textures: [hitbox_texture], outliner: true}); Undo.finishEdit('Add Hytale Hitbox'); + + Canvas.updateAllFaces(); } }); BarItems.add_element.side_menu.addAction(add_hitbox_action); + MenuBar.menus.file.addAction(import_reference_action, 'import'); export_action = new Action('export_hytale_hitbox', { name: 'Export Hytale Hitbox (.json)', @@ -86,13 +238,20 @@ MenuBar.menus.file.addAction(export_action, 'export'); }, onunload() { + actions_to_hide.forEach(action_id => { + if (BarItems[action_id] && original_conditions[action_id] !== undefined) { + BarItems[action_id].condition = original_conditions[action_id]; + } + }); + export_action.delete(); add_hitbox_action.delete(); + import_reference_action.delete(); } }); function exportHytaleHitbox() { - const hitboxes = Cube.all.filter(cube => + const hitboxes = Cube.all.filter(cube => cube.name.toLowerCase() === 'hitbox' && cube.export ); @@ -124,7 +283,7 @@ }); const content = JSON.stringify(json_output, null, 2); - + Blockbench.export({ type: 'JSON Model', extensions: ['json'], diff --git a/plugins/hytale_hitbox_helper/icon.png b/plugins/hytale_hitbox_helper/icon.png index 8533fb328b214ae0f2c9481ec2b473bffff304a2..2b4e6a91309afbada267e4193fe2a24bd8772112 100644 GIT binary patch literal 5526 zcmeHKc~leU77v>si-?ugXw!&@NRr7yfJoSvAOsK)P^vIVCJ-PCNg#lz$Wl;i1yPVi zU$LS{t+)XSDw`7u z%!_%7NA1N)n(Ik%nfnx0-W z(TehVl)T-DoW4`kVHfnL;U@jSgGbLJ5OOIT2L~@#2Zs;gz(M41&!}>%vqjbi`8Z|l zNpf;R5-xf~Z_w&que~?8;G*{CDB$vhE{Q|){vN5+mY8It^XnpPa|Pv+YGSYE;u%Dw zw>V=-Rrb=&*JA`-sgl<#BqNA|4PbG*wvulX!F%lR;iljAP)imA$zU>mHQvYbxGr%N z8T5K|>=m~5!j+aO%Xf#aG$Q^*LqZjx7JqR|#AeFi_CC~FSJg4gv1B`4!yWwA$Rcc=gb$G98Nig0fwEpOqPn$YrmqZpg&y zA6WU8qnaW=y2H8Wpuz(Fu4`uHr< z6b=~8QNjpWI4mX(ih#JVs0g--|IDO|E8Xj(hLnO34p$)4g310&Qp{n0 zBI`5Xq&u>3<~stbf5iPv`a|q8VOWYzr#SMNQBwC@9jR!keF}@u%E zm=ReJhD0DTF&1Pd9%F%rm_#;#foHJH=RvvhL}HM~grra~IFpjo`?d35Ali z1qEczh9Cl?A(qTf7;AQm84B`3Ab5Vv73_y~&fgRR+ni`_MzkbiAOM1kfdRl7G-H4m zkVs&`bznw<%ov}ei}-A@1QbGcAux|HS8##KxI!7vrE1OR@{&+Ung^IN3_!qqqKtsX z%>|2-PK*!HTH*eS4=b6#yb%NI&91?t3!a3ykE3voFX_q*8ul6CogY*{P=uQ(I&>Rc3$={Z>IK4R`(Mt*4EzJ z!_C~MW&d85c;bK(v8=&jchS#TI-$nYYc1F!^|LC431`RXxmQ{`2S?veAMF`SF=PGE z(}VqMI{MuG!89e+lkWP(K1b;Z89N5$o(zXoO}%NUOEtJo{4QN5fUZ`AXgS$dT({Dp zYFG820+bh^uCTE+&9U}ylacqS>ld%)d+6?VL}oVryDl~9e(Q5>H|OdRe%n3!5&6k_ z#+{ZXw%QVfjIFurSIN-~`$BVf(+u4S`Y!8TLoFH#l9Y{FLao{ixo^qC(d)>0%jx$T zTFUWD)r0YY2)z}H^rND-wdOC(w-)*N`9|CTB?sJ%v zIgs8od9F~{a6?2tU;Vu1(OB?>cDy=sJZoFrwiy1*wblKt>`qgIeZtpUZSu%|zqW^o z3l0zUXJe<(a_iTxPje(Zqi^f@;gs*5fhE@8Jbf0Vy?^)8OH}TG$t#m43?&p;3|&fP3qoM&aGM6xMP#ag6|u;m4!wq+dp_#9IPnJ zMx{FBrDffauup86eM8v@3Er^q`L6dc{-=b5lEbLg@@gUbnMvg?efr5Xm!njnVp?lJl&a?)5H!759%L3 z6g04`q>4ZXR>^m?ePb;e+q-1bmM!~^6?I*Cds1CdUfo)+f3!e>u|dVX{_+Tx#y_RI z|7KG63){A+4g=h+g4bAAoUn4o3PZWW^nDvt5+S$%HCDK>a?CvyDd%aHwtL{3>xpN0kINS4RO#(J6^ zskcXGpccnC`upo@XndPyDZKsH*dtH>{&(XV7Dt*nKUM0bdoR)74*-B|$Y2k0T^wuc zc?|8yV9QRNIj{J5(OH$u#)E(9Em8k_!37EO)Al&W}y&Nlke@$2g3;dsVAIPpJV4RE3W delta 3112 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