A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

285 lines
7.0KB

  1. import GlobalEvents from "/app/js/common/EventCaller.js";
  2. import Utils from "/app/js/common/Utils.js";
  3. import NESPalette from "/app/js/models/NESPalette.js";
  4. const PLI_TEMPLATE = ".palette-list-item-template";
  5. const PLI_TITLE = ".title";
  6. const PLI_SELECTED = ".item-selected";
  7. const PLI_BG_COLOR = "pal-bg-color";
  8. const PLI_FG_BASE = "pal-fg-";
  9. const PLI_BG_BASE = "pal-bg-";
  10. const PLI_COLOR_BASE = "nes-color-bg-";
  11. var Palettes = [];
  12. var CurrentPaletteIndex = 0;
  13. var BlockEmits = false;
  14. function HANDLE_PaletteClick(e){
  15. if (!this.hasAttribute("palname")){return;}
  16. var pname = this.getAttribute("palname");
  17. if (Palettes.length > 0 && Palettes[CurrentPaletteIndex][0] !== pname){
  18. var oel = Palettes[CurrentPaletteIndex][2];
  19. oel.classList.remove(PLI_SELECTED);
  20. for (let i=0; i < Palettes.length; i++){
  21. if (Palettes[i][0] === pname){
  22. Palettes[i][2].classList.add(PLI_SELECTED);
  23. GlobalEvents.emit("set_app_palette", Palettes[i][1]);
  24. break;
  25. }
  26. }
  27. }
  28. }
  29. function SetElPaletteName(el, pname){
  30. el.setAttribute("palname", pname);
  31. var sel = el.querySelectorAll(PLI_TITLE);
  32. if (sel.length === 1){
  33. sel = sel[0];
  34. sel.innerHTML = pname;
  35. }
  36. }
  37. function SetElToColor(el, mode, pi, ci, hex){
  38. var cel = null;
  39. if (ci === 0){
  40. cel = el.querySelectorAll("." + PLI_BG_COLOR);
  41. } else {
  42. cel = el.querySelectorAll("." + ((mode == 0) ? PLI_FG_BASE : PLI_BG_BASE) + pi + "-" + ci);
  43. }
  44. if (cel !== null && cel.length === 1){
  45. cel = cel[0];
  46. var clist = cel.getAttribute("class").split(" ");
  47. for (let i=0; i < clist.length; i++){
  48. if (clist[i].startsWith(PLI_COLOR_BASE)){
  49. cel.classList.remove(clist[i]);
  50. break;
  51. }
  52. }
  53. cel.classList.add(PLI_COLOR_BASE + hex.toUpperCase());
  54. }
  55. }
  56. function ColorElementToPalette(el, palette){
  57. SetElToColor(
  58. el, 0,
  59. 0, 0,
  60. palette.get_palette_syscolor_index(0,0,true)
  61. );
  62. for (let p=0; p < 8; p++){
  63. for (let c=1; c < 4; c++){
  64. SetElToColor(
  65. el, (p >= 4) ? 0 : 1,
  66. p%4, c,
  67. palette.get_palette_syscolor_index(p,c,true)
  68. );
  69. }
  70. }
  71. }
  72. function ConnectElementToPalette(el, palette){
  73. palette.listen("palettes_changed", (e) => {
  74. if (e.type == "ALL"){
  75. if (e.hasOwnProperty("cindex")){
  76. SetElToColor(
  77. el, 0,
  78. 0, 0,
  79. palette.get_palette_syscolor_index(0,0,true)
  80. );
  81. } else {
  82. ColorElementToPalette(el, palette);
  83. }
  84. } else if (e.type == "SPRITE"){
  85. SetElToColor(
  86. el, 0,
  87. e.pindex%4, e.cindex,
  88. palette.get_palette_syscolor_index(e.pindex, e.cindex, true)
  89. );
  90. } else if (e.type == "TILE"){
  91. SetElToColor(
  92. el, 1,
  93. e.pindex, e.cindex,
  94. palette.get_palette_syscolor_index(e.pindex, e.cindex, true)
  95. );
  96. }
  97. });
  98. }
  99. function CreatePaletteDOMEntry(pname, palette){
  100. var oel = document.querySelectorAll(PLI_TEMPLATE);
  101. if (oel.length == 1){
  102. var el = oel[0].cloneNode(true);
  103. el.classList.remove(PLI_TEMPLATE);
  104. el.classList.remove("hidden");
  105. ConnectElementToPalette(el, palette);
  106. ColorElementToPalette(el, palette);
  107. SetElPaletteName(el, pname);
  108. el.addEventListener("click", HANDLE_PaletteClick);
  109. oel[0].parentNode.appendChild(el);
  110. return el;
  111. } else {
  112. console.log("WARNING: Multiple templates found. Ambigous state.");
  113. }
  114. return null;
  115. }
  116. class CTRLPalettesStore{
  117. constructor(){}
  118. get json(){
  119. var d = {
  120. cpi: CurrentPaletteIndex,
  121. pals: []
  122. };
  123. for (let i=0; i < Palettes.length; i++){
  124. d.pals.push([Palettes[i][0], Palettes[i][1].json]);
  125. }
  126. return JSON.stringify(d);
  127. }
  128. set json(j){
  129. try {
  130. var d = JSON.parse(j);
  131. } catch (e) {
  132. throw e;
  133. }
  134. if (d.hasOwnProperty("cpi") && d.hasOwnProperty("pals")){
  135. if (Utils.isInt(d.cpi) && d.pals instanceof Array){
  136. var newPalettes = []
  137. for (let i=0; i < d.pals.length; i++){
  138. if (d.pals[i] instanceof Array){
  139. if (this.getPalette(d.pals[i][0]) === null){
  140. this.createPalette(d.pals[i][0], d.pals[i][1]);
  141. }
  142. }
  143. }
  144. CurrentPaletteIndex = 0
  145. if (Palettes.length > 0){
  146. if (d.cpi >= 0 && d.cpi < Palettes.length){
  147. CurrentPaletteIndex = d.cpi;
  148. }
  149. GlobalEvents.emit("set_app_palette", Palettes[CurrentPaletteIndex][1]);
  150. }
  151. } else {
  152. throw new TypeError("JSON Property Value types invalid.");
  153. }
  154. } else {
  155. throw new TypeError("JSON missing expected properties.");
  156. }
  157. }
  158. initialize(){
  159. if (Palettes.length <= 0)
  160. this.createPalette("Palette");
  161. return this;
  162. }
  163. paletteIndexFromName(name){
  164. for (let i=1; i < Palettes.length; i++){
  165. if (Palettes[i][0] == name){
  166. return i;
  167. }
  168. }
  169. return -1;
  170. }
  171. getPalette(name){
  172. var i = this.paletteIndexFromName(name);
  173. return (i >= 0) ? Palettes[i][1] : null;
  174. }
  175. createPalette(name, pjson){
  176. var palette = this.getPalette(name);
  177. if (palette === null){
  178. palette = new NESPalette();
  179. if (typeof(pjson) === "string"){
  180. try {
  181. palette.json = pjson;
  182. } catch (e) {
  183. console.log("Failed to create palette.", e.toString());
  184. palette = null;
  185. }
  186. } else {
  187. palette.set_palette([
  188. "0F",
  189. "05","06","07",
  190. "09","0A","0B",
  191. "01","02","03",
  192. "0D","00","20",
  193. "15","16","17",
  194. "19","1A","1B",
  195. "11","21","31",
  196. "1D","10","30"
  197. ]);
  198. }
  199. if (palette !== null){
  200. var el = CreatePaletteDOMEntry(name, palette);
  201. Palettes.push([name, palette, el]);
  202. if (Palettes.length <= 1 && !BlockEmits){
  203. GlobalEvents.emit("set_app_palette", palette);
  204. }
  205. }
  206. }
  207. return this;
  208. }
  209. removePalette(name){
  210. for (let i=0; i < Palettes.length; i++){
  211. if (Palettes[i][0] === name){
  212. if (CurrentPaletteIndex === i){
  213. CurrentPaletteIndex = 0;
  214. this.activatePalette(Palettes[0][0]);
  215. }
  216. Palettes[i][2].parentNode.removeChild(Palettes[i][2]);
  217. Palettes.splice(i, 1);
  218. }
  219. }
  220. return this;
  221. }
  222. renamePalette(oldname, newname){
  223. var i = paletteIndexFromName(oldname);
  224. if (i < 0)
  225. throw new ValueError("Failed to find palette named '" + oldname +"'. Cannot rename.");
  226. Palettes[i][0] = newname;
  227. SetElPaletteName(Palettes[i][2], newname);
  228. return this;
  229. }
  230. activatePalette(name){
  231. var i = this.paletteIndexFromName(name);
  232. if (i >= 0 && CurrentPaletteIndex !== i){
  233. CurrentPaletteIndex = i;
  234. Palettes[CurrentPaletteIndex][2].click();
  235. //if (!BlockEmits){
  236. // GlobalEvents.emit("set_app_palette", Palettes[CurrentPaletteIndex][1]);
  237. //}
  238. }
  239. return this;
  240. }
  241. clear(){
  242. for (let i=0; i < Palettes.length; i++){
  243. Palettes[i][2].parentNode.removeChild(Palettes[i][2]);
  244. }
  245. CurrentPaletteIndex = 0;
  246. }
  247. }
  248. const instance = new CTRLPalettesStore();
  249. export default instance;