A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

293 行
7.2KB

  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. GlobalEvents.listen("palstore-add", (function(e){
  119. if (e.hasOwnProperty("palname")){
  120. this.createPalette(e.palname);
  121. this.activatePalette(e.palname);
  122. }
  123. }).bind(this));
  124. }
  125. get json(){
  126. var d = {
  127. cpi: CurrentPaletteIndex,
  128. pals: []
  129. };
  130. for (let i=0; i < Palettes.length; i++){
  131. d.pals.push([Palettes[i][0], Palettes[i][1].json]);
  132. }
  133. return JSON.stringify(d);
  134. }
  135. set json(j){
  136. try {
  137. var d = JSON.parse(j);
  138. } catch (e) {
  139. throw e;
  140. }
  141. if (d.hasOwnProperty("cpi") && d.hasOwnProperty("pals")){
  142. if (Utils.isInt(d.cpi) && d.pals instanceof Array){
  143. var newPalettes = []
  144. for (let i=0; i < d.pals.length; i++){
  145. if (d.pals[i] instanceof Array){
  146. if (this.getPalette(d.pals[i][0]) === null){
  147. this.createPalette(d.pals[i][0], d.pals[i][1]);
  148. }
  149. }
  150. }
  151. CurrentPaletteIndex = 0
  152. if (Palettes.length > 0){
  153. if (d.cpi >= 0 && d.cpi < Palettes.length){
  154. CurrentPaletteIndex = d.cpi;
  155. }
  156. GlobalEvents.emit("set_app_palette", Palettes[CurrentPaletteIndex][1]);
  157. }
  158. } else {
  159. throw new TypeError("JSON Property Value types invalid.");
  160. }
  161. } else {
  162. throw new TypeError("JSON missing expected properties.");
  163. }
  164. }
  165. initialize(){
  166. if (Palettes.length <= 0)
  167. this.createPalette("Palette");
  168. return this;
  169. }
  170. paletteIndexFromName(name){
  171. for (let i=1; i < Palettes.length; i++){
  172. if (Palettes[i][0] == name){
  173. return i;
  174. }
  175. }
  176. return -1;
  177. }
  178. getPalette(name){
  179. var i = this.paletteIndexFromName(name);
  180. return (i >= 0) ? Palettes[i][1] : null;
  181. }
  182. createPalette(name, pjson){
  183. var palette = this.getPalette(name);
  184. if (palette === null){
  185. palette = new NESPalette();
  186. if (typeof(pjson) === "string"){
  187. try {
  188. palette.json = pjson;
  189. } catch (e) {
  190. console.log("Failed to create palette.", e.toString());
  191. palette = null;
  192. }
  193. } else {
  194. palette.set_palette([
  195. "0F",
  196. "05","06","07",
  197. "09","0A","0B",
  198. "01","02","03",
  199. "0D","00","20",
  200. "15","16","17",
  201. "19","1A","1B",
  202. "11","21","31",
  203. "1D","10","30"
  204. ]);
  205. }
  206. if (palette !== null){
  207. var el = CreatePaletteDOMEntry(name, palette);
  208. Palettes.push([name, palette, el]);
  209. if (Palettes.length <= 1 && !BlockEmits){
  210. GlobalEvents.emit("set_app_palette", palette);
  211. }
  212. }
  213. }
  214. return this;
  215. }
  216. removePalette(name){
  217. for (let i=0; i < Palettes.length; i++){
  218. if (Palettes[i][0] === name){
  219. if (CurrentPaletteIndex === i){
  220. CurrentPaletteIndex = 0;
  221. this.activatePalette(Palettes[0][0]);
  222. }
  223. Palettes[i][2].parentNode.removeChild(Palettes[i][2]);
  224. Palettes.splice(i, 1);
  225. }
  226. }
  227. return this;
  228. }
  229. renamePalette(oldname, newname){
  230. var i = paletteIndexFromName(oldname);
  231. if (i < 0)
  232. throw new ValueError("Failed to find palette named '" + oldname +"'. Cannot rename.");
  233. Palettes[i][0] = newname;
  234. SetElPaletteName(Palettes[i][2], newname);
  235. return this;
  236. }
  237. activatePalette(name){
  238. var i = this.paletteIndexFromName(name);
  239. if (i >= 0 && CurrentPaletteIndex !== i){
  240. CurrentPaletteIndex = i;
  241. Palettes[CurrentPaletteIndex][2].click();
  242. //if (!BlockEmits){
  243. // GlobalEvents.emit("set_app_palette", Palettes[CurrentPaletteIndex][1]);
  244. //}
  245. }
  246. return this;
  247. }
  248. clear(){
  249. for (let i=0; i < Palettes.length; i++){
  250. Palettes[i][2].parentNode.removeChild(Palettes[i][2]);
  251. }
  252. CurrentPaletteIndex = 0;
  253. }
  254. }
  255. const instance = new CTRLPalettesStore();
  256. export default instance;