From 923fef1c3ddf73ceea5e17d843c5f0302d217f83 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Tue, 1 Oct 2024 17:46:40 -0400 Subject: [PATCH 1/2] Create the FSK Animation Convertor Plugin Source: https://github.com/SnaveSutit/bbanim-to-fsk --- plugins.json | 13 ++++++++++ plugins/fsk_animation_exporter.js | 41 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 plugins/fsk_animation_exporter.js diff --git a/plugins.json b/plugins.json index 27acdb42..d7130670 100644 --- a/plugins.json +++ b/plugins.json @@ -586,6 +586,19 @@ "version": "1.0.0", "variant": "both" }, + "fsk_animation_exporter": { + "title": "FSK Animation Exporter", + "author": "SnaveSutit & Marctron", + "icon": "video_file", + "description": "Converts Blockbench animations to FSK files.", + "about": "Go to Animation -> Export Endimations...", + "tags": ["Minecraft: Java Edition", ".fsk", "Fisk's Superheroes"], + "version": "1.0.0", + "min_version": "4.2.0", + "variant": "desktop", + "repository": "https://github.com/SnaveSutit/bbanim-to-fsk", + "bug_tracker": "https://github.com/SnaveSutit/bbanim-to-fsk/issues" + }, "endimations_exporter": { "title": "Endimator Animations Exporter", "author": "SmellyModder", diff --git a/plugins/fsk_animation_exporter.js b/plugins/fsk_animation_exporter.js new file mode 100644 index 00000000..b3758e2b --- /dev/null +++ b/plugins/fsk_animation_exporter.js @@ -0,0 +1,41 @@ + +// ╭────────────────────────────────────────────────────────────────────────────────╮ +// │ [ FSK Animation Exporter ] │ +// │ Converts Blockbench animations to FSK files. │ +// │ Created by SnaveSutit │ +// │ (snavesutit@gmail.com) [https://discordapp.com/users/213746232923783168] │ +// │ │ +// │ [ SOURCE ] │ +// │ https://github.com/SnaveSutit/bbanim-to-fsk │ +// │ │ +// │ [ LICENSE ] │ +// │ MIT License │ +// │ │ +// │ Copyright (c) 2022 SnaveSutit │ +// │ │ +// │ Permission is hereby granted, free of charge, to any person obtaining a copy │ +// │ of this software and associated documentation files (the "Software"), to deal │ +// │ in the Software without restriction, including without limitation the rights │ +// │ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell │ +// │ copies of the Software, and to permit persons to whom the Software is │ +// │ furnished to do so, subject to the following conditions: │ +// │ │ +// │ The above copyright notice and this permission notice shall be included in all │ +// │ copies or substantial portions of the Software. │ +// │ │ +// │ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +// │ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +// │ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +// │ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +// │ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, │ +// │ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE │ +// │ SOFTWARE. │ +// │ │ +// ╰────────────────────────────────────────────────────────────────────────────────╯ + +"use strict";(()=>{var Ne=Object.defineProperty;var nt=(e,t,n)=>t in e?Ne(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var it=(e,t)=>{for(var n in t)Ne(e,n,{get:t[n],enumerable:!0})};var Te=(e,t,n)=>(nt(e,typeof t!="symbol"?t+"":t,n),n);var C="fsk_animation_exporter",Pe="FSK Animation Exporter",De="1.0.0",je=["Minecraft: Java Edition",".fsk","Fisk's Superheroes"],Ke="4.2.0";var Be="Converts Blockbench animations to FSK files.";var B={name:C,title:Pe,version:De,tags:je,min_blockbench_version:Ke,author:{name:"SnaveSutit",email:"snavesutit@gmail.com",url:"https://discordapp.com/users/213746232923783168"},repository:{type:"github",url:"https://github.com/SnaveSutit/bbanim-to-fsk"},description:Be,main:"./dist/index.js",types:"./dist/src/index.d.ts",private:!0,scripts:{"build:scripts":"esbuild --bundle --platform=node --outfile=dist/build.cjs --packages=external ./tools/esbuild.ts","build:dev":"yarn build:scripts && node ./dist/build.cjs --mode=dev","build:prod":"yarn build:scripts && node ./dist/build.cjs",format:"prettier --write ."},devDependencies:{"@types/eslint":"^8.21.1","@types/node":"^17.0.21","@typescript-eslint/eslint-plugin":"^5.54.0","@typescript-eslint/parser":"^5.54.0","blockbench-types":"https://github.com/SnaveSutit/blockbench-types.git","esbuild-plugin-svelte":"^0.1.1",esbuild:"^0.17.10",eslint:"^8.35.0",prettier:"^2.5.1","svelte-preprocess-esbuild":"^3.0.1","svelte-preprocess":"^5.0.1",svelte:"^3.55.1","ts-loader":"^9.2.6",typescript:"^4.5.5",vue:"^3.2.33"},dependencies:{},license:"MIT"};function z(e,t){return Math.round(e*10**t)/10**t}var H=10;function be(e,t){return t==0?0:e/t}function ee(e){let t={},n=`{${e.name}_time}`,i=e.enable_curve?"curve ":"",s=[`${n}={${e.variable_name}}`],u=[];for(let r of Blockbench.Group.all){let a=[""],d=!1,o=e.animators[r.uuid];if(!o)continue;let p=o.keyframes.sort((h,m)=>Number(h.time)-Number(m.time)),x=p.filter(h=>h.channel=="position"),$=p.filter(h=>h.channel=="rotation"),F=h=>{let m,M,A,w;for(let b of h){if(b.has_expressions)throw Blockbench.showMessageBox({title:"Math-based Keyframe Expressions are Not Supported",message:`Math-based keyframe expressions are not supported in the FSK export. Please remove expressions from keyframe at ${b.time}s in '${e.name}' for '${r.name}'.`,buttons:["OK"]}),new Error("Math-based keyframe expressions are not supported in the FSK export.");let _=b.channel;if(_=="position")_="pos";else if(_=="rotation")_="rot";else if(_=="scale")continue;let y=z(Number(b.data_points[0].x),H),S=z(_=="pos"?-Number(b.data_points[0].y):Number(b.data_points[0].y),H),N=z(Number(b.data_points[0].z),H);if(y==0&&S==0&&N==0)continue;d=!0;let G=be(Number(b.time),e.length);m=h[h.indexOf(b)-1],m?A=be(Number(m.time),e.length):A=0,M=h[h.indexOf(b)+1],M?w=be(Number(M.time),e.length):w=e.length*10;let T=z(Math.abs(A-G),H),D=z(Math.abs(w-G),H),X=A,l=z(w-A,H),K=`${i}animate2(${n},${l},${X},${T},${D})`,j=`{${e.name}_${Object.keys(t).length}}`;t[K]?j=t[K]:(t[K]=j,s.push(`${j}=${K}`));let E=`{${r.name}_${_}%AXIS%}`,k=_=="rot"?"'":"";if(y!=0){let Q=E.replace("%AXIS%","X");a.push(`${Q}+=${y}${k}${j}`)}if(S!=0){let Q=E.replace("%AXIS%","Y");a.push(`${Q}+=${S}${k}${j}`)}if(N!=0){let Q=E.replace("%AXIS%","Z");a.push(`${Q}+=${N}${k}${j}`)}m=b}};F($),F(x),d&&u.push(...a)}return[...s,...u].join(` +`)}function Ie(e){return Object.fromEntries(Object.entries(e))}function ye(e){Object.assign(console,e)}function ot(e,t){for(let[n,i]of Object.entries(e))typeof i=="function"&&(e[n]=t(t,i));return e}var Re=Ie(console);function Ge(e){return function(n,i,s=!1){return(...u)=>{let r=Ie(console),a=s;a?e(n):ot(console,(d,o)=>(...p)=>(a||(a=!0,d!==o&&d(...p),e(n)),ye(r),o(...p)));try{let d=i(...u);return a&&Re.groupEnd(),ye(r),d}catch(d){throw a&&Re.groupEnd(),ye(r),d}}}}var ge=Ge(console.group),R=Ge(console.groupCollapsed);var ue={};it(ue,{EXTRACT_MODS:()=>ne,INJECT_MODS:()=>ae,INSTALL:()=>ce,LOAD:()=>re,PluginEvent:()=>P,UNINSTALL:()=>le,UNLOAD:()=>se});var te=class{subscribers=new Set;dispatching=!1;subscribe(t,n=!1){if(n){let i=s=>{t(s),this.subscribers.delete(i)};this.subscribers.add(i)}else this.subscribers.add(t);return()=>this.subscribers.delete(t)}dispatch(t){this.dispatching||(this.dispatching=!0,this.subscribers.forEach(n=>n(t)),this.dispatching=!1)}};var ve=class extends te{constructor(n){super();this.name=n;ve.events[n]=this}},P=ve;Te(P,"events",{});var re=new P("load"),se=new P("unload"),ce=new P("install"),le=new P("uninstall"),ae=new P("injectMods"),ne=new P("extractMods"),Le=R(`Injecting BlockbenchMods added by ${C}`,()=>ae.dispatch()),Ve=R(`Extracting BlockbenchMods added by ${C}`,()=>ne.dispatch());re.subscribe(Le);se.subscribe(Ve);ce.subscribe(Le);le.subscribe(Ve);var xe=class extends Error{constructor(t,n){super(`Mod '${t}' failed to install: ${n.message}`+(n.stack?` +`+n.stack:""))}},$e=class extends Error{constructor(t,n){super(`Mod '${t}' failed to uninstall: ${n.message}`+(n.stack?` +`+n.stack:""))}};function fe(e,t,n,i){let s=!1,u;ae.subscribe(ge(`Injecting BBMod '${e}'`,()=>{try{s&&new Error("Mod is already installed!"),u=n(t),s=!0}catch(r){throw new xe(e,r)}console.log("Sucess!")}),!0),ne.subscribe(ge(`Extracting BBMod '${e}'`,()=>{try{s||new Error("Mod is not installed!"),i(u),s=!1}catch(r){throw new $e(e,r)}console.log("Sucess!")}),!0)}function we(e,t){let n=new Action(e,t);return ne.subscribe(()=>{n.delete()},!0),n}function v(){}function Se(e){return e()}function qe(){return Object.create(null)}function I(e){e.forEach(Se)}function oe(e){return typeof e=="function"}function _e(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}function He(e){return Object.keys(e).length===0}function V(e,...t){if(e==null)return v;let n=e.subscribe(...t);return n.unsubscribe?()=>n.unsubscribe():n}var Ue=!1;function rt(){Ue=!0}function st(){Ue=!1}function f(e,t){e.appendChild(t)}function Je(e,t,n){e.insertBefore(t,n||null)}function ke(e){e.parentNode&&e.parentNode.removeChild(e)}function g(e){return document.createElement(e)}function ct(e){return document.createTextNode(e)}function O(){return ct(" ")}function W(e,t,n,i){return e.addEventListener(t,n,i),()=>e.removeEventListener(t,n,i)}function c(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function lt(e){return Array.from(e.childNodes)}function q(e,t){e.value=t??""}var Ce;function ie(e){Ce=e}var J=[];var Xe=[],de=[],ze=[],at=Promise.resolve(),Fe=!1;function ut(){Fe||(Fe=!0,at.then(We))}function Ae(e){de.push(e)}var Ee=new Set,U=0;function We(){if(U!==0)return;let e=Ce;do{try{for(;U{let r=e.$$.on_mount.map(Se).filter(oe);e.$$.on_destroy?e.$$.on_destroy.push(...r):I(r),e.$$.on_mount=[]}),u.forEach(Ae)}function Ye(e,t){let n=e.$$;n.fragment!==null&&(I(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function ht(e,t){e.$$.dirty[0]===-1&&(J.push(e),ut(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{let h=F.length?F[0]:$;return o.ctx&&s(o.ctx[x],o.ctx[x]=h)&&(!o.skip_bound&&o.bound[x]&&o.bound[x](h),p&&ht(e,x)),$}):[],o.update(),p=!0,I(o.before_update),o.fragment=i?i(o.ctx):!1,t.target){if(t.hydrate){rt();let x=lt(t.target);o.fragment&&o.fragment.l(x),x.forEach(ke)}else o.fragment&&o.fragment.c();t.intro&&pt(e.$$.fragment),_t(e,t.target,t.anchor,t.customElement),st(),We()}ie(d)}var mt;typeof HTMLElement=="function"&&(mt=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){let{on_mount:e}=this.$$;this.$$.on_disconnect=e.map(Se).filter(oe);for(let t in this.$$.slotted)this.appendChild(this.$$.slotted[t])}attributeChangedCallback(e,t,n){this[e]=n}disconnectedCallback(){I(this.$$.on_disconnect)}$destroy(){Ye(this,1),this.$destroy=v}$on(e,t){if(!oe(t))return v;let n=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return n.push(t),()=>{let i=n.indexOf(t);i!==-1&&n.splice(i,1)}}$set(e){this.$$set&&!He(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}});var pe=class{$destroy(){Ye(this,1),this.$destroy=v}$on(t,n){if(!oe(n))return v;let i=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return i.push(n),()=>{let s=i.indexOf(n);s!==-1&&i.splice(s,1)}}$set(t){this.$$set&&!He(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}};function bt(e){let t,n,i,s,u,r,a,d,o,p,x,$,F,h,m,M,A,w,b,_,y,S,N,G,T,D,X,l,K,j;return{c(){t=g("div"),n=g("div"),i=g("label"),i.textContent="Name:",s=O(),u=g("input"),r=O(),a=g("div"),d=g("label"),d.textContent="Export:",o=O(),p=g("input"),x=O(),$=g("div"),F=g("label"),F.textContent="Enable Curve:",h=O(),m=g("input"),M=O(),A=g("div"),w=g("label"),w.textContent="Snapping:",b=O(),_=g("div"),y=g("input"),S=O(),N=g("div"),N.innerHTML='code',G=O(),T=g("div"),D=g("label"),D.textContent="Data Var:",X=O(),l=g("input"),c(i,"class","name_space_left"),c(i,"for","name"),c(u,"type","text"),c(u,"class","dark_bordered half focusable_input"),c(u,"id","name"),c(n,"class","dialog_bar form_bar"),c(d,"class","name_space_left"),c(d,"for","export"),c(p,"type","checkbox"),c(p,"class","focusable_input"),c(p,"id","export"),c(a,"class","dialog_bar form_bar"),c(F,"class","name_space_left"),c(F,"for","export"),c(m,"type","checkbox"),c(m,"class","focusable_input"),c(m,"id","export"),c($,"class","dialog_bar form_bar"),c(w,"class","name_space_left"),c(w,"for","name"),c(y,"id","snapping"),c(y,"class","dark_bordered focusable_input"),c(y,"inputmode","decimal"),c(N,"class","tool numaric_input_slider"),c(_,"class","numeric_input"),c(A,"class","dialog_bar form_bar"),c(D,"class","name_space_left"),c(D,"for","variable_name"),c(l,"type","text"),c(l,"class","dark_bordered half focusable_input"),c(l,"id","variable_name"),c(T,"class","dialog_bar form_bar")},m(E,k){Je(E,t,k),f(t,n),f(n,i),f(n,s),f(n,u),q(u,e[5]),f(t,r),f(t,a),f(a,d),f(a,o),f(a,p),p.checked=e[6],f(t,x),f(t,$),f($,F),f($,h),f($,m),m.checked=e[7],f(t,M),f(t,A),f(A,w),f(A,b),f(A,_),f(_,y),q(y,e[8]),f(_,S),f(_,N),f(t,G),f(t,T),f(T,D),f(T,X),f(T,l),q(l,e[9]),K||(j=[W(u,"input",e[10]),W(p,"change",e[11]),W(m,"change",e[12]),W(y,"input",e[13]),W(l,"input",e[14])],K=!0)},p(E,[k]){k&32&&u.value!==E[5]&&q(u,E[5]),k&64&&(p.checked=E[6]),k&128&&(m.checked=E[7]),k&256&&y.value!==E[8]&&q(y,E[8]),k&512&&l.value!==E[9]&&q(l,E[9])},i:v,o:v,d(E){E&&ke(t),K=!1,I(j)}}}function yt(e,t,n){let i,s=v,u=()=>(s(),s=V(w,l=>n(5,i=l)),w),r,a=v,d=()=>(a(),a=V(b,l=>n(6,r=l)),b),o,p=v,x=()=>(p(),p=V(_,l=>n(7,o=l)),_),$,F=v,h=()=>(F(),F=V(y,l=>n(8,$=l)),y),m,M=v,A=()=>(M(),M=V(S,l=>n(9,m=l)),S);e.$$.on_destroy.push(()=>s()),e.$$.on_destroy.push(()=>a()),e.$$.on_destroy.push(()=>p()),e.$$.on_destroy.push(()=>F()),e.$$.on_destroy.push(()=>M());let{name:w}=t;u();let{doExport:b}=t;d();let{enableCurve:_}=t;x();let{snapping:y}=t;h();let{variableName:S}=t;A();function N(){i=this.value,w.set(i)}function G(){r=this.checked,b.set(r)}function T(){o=this.checked,_.set(o)}function D(){$=this.value,y.set($)}function X(){m=this.value,S.set(m)}return e.$$set=l=>{"name"in l&&u(n(0,w=l.name)),"doExport"in l&&d(n(1,b=l.doExport)),"enableCurve"in l&&x(n(2,_=l.enableCurve)),"snapping"in l&&h(n(3,y=l.snapping)),"variableName"in l&&A(n(4,S=l.variableName))},[w,b,_,y,S,i,r,o,$,m,N,G,T,D,X]}var Oe=class extends pe{constructor(t){super(),Ze(this,t,yt,bt,_e,{name:0,doExport:1,enableCurve:2,snapping:3,variableName:4})}},Qe=Oe;var Me=[],he=class extends Dialog{instance;constructor(t){let n=document.createComment(`${C}-svelte-dialog-`+guid());super(t.id,{...t,lines:[n]}),this.onOpen=()=>{let i=n.parentElement;this.instance||!i||(i.style.overflow="visible",this.instance=new t.svelteComponent({target:i,props:t.svelteComponentProps}),super.onOpen&&super.onOpen(),t.stackable||(Me.forEach(s=>s.cancel()),Me.empty()),Me.push(this))},this.onButton=(...i)=>{this.instance&&(this.instance.$destroy(),this.instance=void 0,super.onButton&&super.onButton(...i),t.onClose&&t.onClose())},this.onCancel=(...i)=>{this.instance&&(this.instance.$destroy(),this.instance=void 0,super.onCancel&&super.onCancel(...i),t.onClose&&t.onClose())}}};var Y=[];function Z(e,t=v){let n,i=new Set;function s(a){if(_e(e,a)&&(e=a,n)){let d=!Y.length;for(let o of i)o[1](),Y.push(o,e);if(d){for(let o=0;o{i.delete(o),i.size===0&&(n(),n=null)}}return{set:s,update:u,subscribe:r}}function gt(e){let t=Z(e.name),n=Z(e.export),i=Z(e.enable_curve),s=Z(e.snapping),u=Z(e.variable_name);t.subscribe(r=>{e.name=r}),n.subscribe(r=>{e.export=r}),i.subscribe(r=>{e.enable_curve=r}),s.subscribe(r=>{e.snapping=r}),u.subscribe(r=>{e.variable_name=r}),new he({id:`${B.name}:animation_properties`,title:"Animation Properties",width:400,svelteComponent:Qe,svelteComponentProps:{name:t,doExport:n,enableCurve:i,snapping:s,variableName:u}}).show()}fe(`${B.name}:animation_properties_dialog`,{original:Blockbench.Animation.prototype.propertiesDialog},e=>(Blockbench.Animation.prototype.propertiesDialog=function(){gt(this)},e),e=>{Blockbench.Animation.prototype.propertiesDialog=e.original});fe(`${B.name}:animation`,{extend:Blockbench.Animation.prototype.extend,propertyExport:void 0,propertyEnableCurve:void 0,propertyVariableName:void 0},e=>(e.propertyExport=new Property(Blockbench.Animation,"boolean","export",{default:!0,condition:()=>!0}),e.propertyEnableCurve=new Property(Blockbench.Animation,"boolean","enable_curve",{default:!0,condition:()=>!0}),e.propertyVariableName=new Property(Blockbench.Animation,"string","variable_name",{default:"data",condition:()=>!0}),Blockbench.Animation.prototype.extend=function(t){return e.extend.call(this,t),this.snapping=100,this},e),e=>{e.propertyExport?.delete(),e.propertyVariableName?.delete(),Blockbench.Animation.prototype.extend=e.extend});var et=()=>!!Project?.format.animation_mode,vt=we(`${B.name}:export`,{name:"Export FSK",icon:"video_file",condition:et,description:"Export the current animation to FSK",click:()=>{if(!Project?.lastExportPath){tt.click();return}let e=ee(Animator.selected);Blockbench.writeFile(Project?.lastExportPath||"",{content:e,savetype:"text"})}});MenuBar.addAction(vt,"file.export.0");var tt=we(`${B.name}:exportAsAction`,{name:"Export FSK As",icon:"video_file",condition:et,description:"Export all animations to FSK",click:()=>{let e=[];for(let t of Project.animations){if(!t.export)continue;let n=ee(t);e.push(n)}electron.dialog.showSaveDialog({title:"Export to FSK",defaultPath:Project?.lastExportPath,properties:[]}).then(t=>{t.canceled||(Blockbench.writeFile(t.filePath,{content:e.join(` + +`),savetype:"text"}),Project.lastExportPath=t.filePath)})}});MenuBar.addAction(tt,"file.export.1");globalThis.BBAnimToFSK={events:ue,renderAnimationAsFSK:ee};BBPlugin.register(C,{title:Pe,author:"SnaveSutit & Marctron",description:Be,icon:"video_file",variant:"desktop",version:De,min_version:Ke,tags:je,onload:R(`${C}:onload`,()=>{re.dispatch()}),onunload:R(`${C}:onunload`,()=>{se.dispatch()}),oninstall:R(`${C}:oninstall`,()=>{ce.dispatch()}),onuninstall:R(`${C}:onuninstall`,()=>{le.dispatch()})});})(); From bcceff0b7e921a2b40d7ab6f00a920a54f58ce42 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Tue, 1 Oct 2024 17:48:18 -0400 Subject: [PATCH 2/2] Fix incorrect about message --- plugins.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins.json b/plugins.json index d7130670..c72900d3 100644 --- a/plugins.json +++ b/plugins.json @@ -591,7 +591,7 @@ "author": "SnaveSutit & Marctron", "icon": "video_file", "description": "Converts Blockbench animations to FSK files.", - "about": "Go to Animation -> Export Endimations...", + "about": "To export an animation, go to File -> Export -> Export FSK.", "tags": ["Minecraft: Java Edition", ".fsk", "Fisk's Superheroes"], "version": "1.0.0", "min_version": "4.2.0",