A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

349 linhas
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;