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文字以内のものにしてください。

349 行
8.6KB

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