A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

337 rindas
8.2KB

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