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个字符

315 行
7.8KB

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