1"use strict"; 2const inp_width = getInput("inp-width"); 3const inp_height = getInput("inp-height"); 4const inp_tilesize = getInput("inp-tilesize"); 5const btn_up = getButton("btn-up"); 6const btn_right = getButton("btn-right"); 7const btn_down = getButton("btn-down"); 8const btn_left = getButton("btn-left"); 9const btn_save = getButton("btn-save"); 10const inp_load = getInput("inp-load"); 11const btn_new = getButton("btn-new"); 12const inp_cameraSpeed = getInput("inp_cameraSpeed"); 13const inp_mode_pen = getInput("inp-mode-pen"); 14const inp_mode_fill = getInput("inp-mode-fill"); 15const inp_mode_view = getInput("inp-mode-view"); 16const inp_mode_entity = getInput("inp-mode-entity"); 17const inp_mode_decor = getInput("inp-mode-decor"); 18const inp_highlight_tiles = getInput("inp-highlight-tiles"); 19const inp_highlight_decor = getInput("inp-highlight-decor"); 20const inp_highlight_stacked = getInput("inp-highlight-stacked"); 21const inp_auto_edge = getInput("inp-auto-edge"); 22// const world_map = getTable("world-map") 23const div_viewport = getDiv("viewport"); 24const div_palette = getDiv("palette"); 25const div_fast_palette = getDiv("fast-palette"); 26const div_palette_group = getDiv("palette-group"); 27const div_palette_group_imgs = getDiv("palette-group-imgs"); 28const canvas = getCanvas("canvas"); 29const ctx = getCanvasContext(canvas); 30const localStorageKey = "WorldEditor-world-data"; 31const ViewWidth = 20; 32const ViewHeight = 9; 33let TileSize = 16 * 2; 34// let TileSize = 16 * 4; // Temp 35const entity = []; 36for (const key in EntityDict) 37 entity.push(EntityDict[key]); 38const decor = []; 39for (const key in DecorDict) 40 decor.push(DecorDict[key]); 41const tileImages = {}; 42const startViews_JSON = localStorage.getItem("WorldEditor-startViews"); 43const startViews = startViews_JSON ? JSON.parse(startViews_JSON) : [DefaultNewView]; 44let startView = 0; 45let icon_move = null; 46let icon_plus = null; 47const icon_center_rect = () => { const size = TileSize * 2; return { x: (ViewWidth * TileSize - size) / 2, y: (ViewHeight * TileSize - size) / 2, w: size, h: size }; }; 48let icon_trash = null; 49const icon_trash_rect = () => { const size = TileSize * 0.9; return { x: (ViewWidth - 0.5) * TileSize - size, y: TileSize / 2, w: size, h: size * 1.5 }; }; 50let icon_save = null; 51const icon_save_rect = () => { const size = TileSize * 0.9; return { x: 0.5 * TileSize, y: ((ViewHeight - 0.5) * TileSize - size), w: size, h: size }; }; 52inp_tilesize.valueAsNumber = TileSize; 53let camera_x = 0; 54let camera_y = 0; 55const camera_speed = () => { 56 return Math.round(TileSize * inp_cameraSpeed.valueAsNumber / 2); 57}; 58let penEntity = null; 59let penDecor = null; 60let penDecorData = null; 61let selectedEntity = null; 62let selectedDecor = null; 63let worldFileName = localStorage.getItem("WorldEditor-world-filename") || "worldData.json"; 64let mousePos = { x: 0, y: 0 }; 65let mousePosCanvas = { x: 0, y: 0 }; 66let selectedEntities = []; 67let selectedDecors = []; 68let copied = { entities: [], decors: [] }; 69class World { 70 map = []; 71 width; 72 height; 73 constructor(width, height, world) { 74 this.width = width; 75 this.height = height; 76 inp_width.valueAsNumber = width; 77 inp_height.valueAsNumber = height; 78 this.map = []; 79 if (world) { 80 if (world.width > width) { 81 for (let y = 0; y < world.height; y++) { 82 for (let x = width; x < world.width; x++) { 83 if (world.map[y][x]) { 84 const popup = new Popup(); 85 popup.content.appendChild(Div([], [], "УменÑÑение миÑа нелÑÐ·Ñ Ð¿ÑовеÑÑи без поÑеÑÑ!")); 86 popup.open(); 87 this.width = world.width; 88 this.height = world.height; 89 this.map = world.map; 90 return; 91 } 92 } 93 } 94 } 95 if (world.height > height) { 96 for (let y = height; y < world.height; y++) { 97 for (let x = 0; x < world.width; x++) {
98 if (world.map[y][x]) { 99 const popup = new Popup(); 100 popup.content.appendChild(Div([], [], "УменÑÑение миÑа нелÑÐ·Ñ Ð¿ÑовеÑÑи без поÑеÑÑ!")); 101 popup.open(); 102 this.width = world.width; 103 this.height = world.height; 104 this.map = world.map; 105 return; 106 } 107 } 108 } 109 } 110 for (let y = 0; y < height; y++) { 111 const line = []; 112 for (let x = 0; x < width; x++) { 113 if (y < world.height && x < world.width) { 114 line.push(world.map[y][x]); 115 } 116 else { 117 line.push(undefined); 118 } 119 } 120 this.map.push(line); 121 } 122 } 123 else { 124 for (let y = 0; y < height; y++) { 125 const line = []; 126 for (let x = 0; x < width; x++) { 127 line.push(undefined); 128 } 129 this.map.push(line); 130 } 131 } 132 // this.createTable(); 133 } 134 up() { 135 if (this.height == 0) 136 return; 137 const line = this.map.splice(0, 1); 138 this.map.push(line[0]); 139 this.createTable(); 140 } 141 right() { 142 if (this.width == 0) 143 return; 144 for (let y = 0; y < this.height; y++) { 145 this.map[y].unshift(this.map[y].pop()); 146 } 147 this.createTable(); 148 } 149 down() { 150 if (this.height == 0) 151 return; 152 const line = this.map.splice(this.height - 1, 1); 153 this.map.unshift(line[0]); 154 this.createTable(); 155 } 156 left() { 157 if (this.width == 0) 158 return; 159 for (let y = 0; y < this.height; y++) { 160 this.map[y].push(this.map[y].shift()); 161 } 162 this.createTable(); 163 } 164 createTable() { 165 // world_map.innerHTML = ""; 166 for (let y = 0; y < this.height; y++) { 167 const row = document.createElement("tr"); 168 // world_map.appendChild(row); 169 for (let x = 0; x < this.width; x++) { 170 const cell = document.createElement("td"); 171 row.appendChild(cell); 172 const btn = document.createElement("button"); 173 if (this.map[y][x]) 174 btn.classList.add("active"); 175 cell.appendChild(btn); 176 btn.addEventListener("click", async () => { 177 if (this.map[y][x]) { 178 const popup = new Popup(); 179 popup.focusOn = "cancel"; 180 popup.content.appendChild(Div([], [], "ÐÑ ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ÑдалиÑÑ ÑкÑан?")); 181 const r = await popup.openAsync(); 182 if (r) { 183 const popup = new Popup(); 184 popup.focusOn = "cancel"; 185 popup.content.appendChild(Div([], [], "ÐÑ ÑоÑно ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ÑдалиÑÑ ÑкÑан?")); 186 popup.reverse = true; 187 const r = await popup.openAsync(); 188 if (!r) 189 return; 190 } 191 if (!r) 192 return; 193 this.map[y][x] = undefined; 194 btn.classList.remove("active"); 195 } 196 else { 197 this.map[y][x] = new View(); 198 btn.classList.add("active"); 199 } 200 }); 201 } 202 } 203 } 204 draw() { 205 const viewWidth = ViewWidth * TileSize; 206 const viewHeight = ViewHeight * TileSize; 207 for (let y = 0; y < this.height; y++) { 208 for (let x = 0; x < this.width; x++) { 209 if (x * viewWidth + camera_x > canvas.width || 210 y * viewHeight + camera_y > canvas.height || 211 (x + 1) * viewWidth + camera_x < 0 || 212 (y + 1) * viewHeight + camera_y < 0) { 213 continue; 214 } 215 const view = this.map[y][x]; 216 ctx.save(); 217 ctx.fillStyle = "lightblue"; 218 ctx.translate(x * viewWidth, y * viewHeight); 219 ctx.fillRect(0, 0, viewWidth, viewHeight); 220 if (view) { 221 ctx.save(); 222 if (view_moving && view_moving.vx == x && view_moving.vy == y) 223 ctx.translate(view_moving.dx, view_moving.dy); 224 view.draw(x, y);
225 ctx.restore(); 226 } 227 else { 228 if (inp_mode_view.checked && icon_plus) { 229 const rect = icon_center_rect(); 230 ctx.drawImage(icon_plus, rect.x, rect.y, rect.w, rect.h); 231 } 232 } 233 ctx.restore(); 234 } 235 } 236 if (view_moving) { 237 ctx.save(); 238 ctx.translate(view_moving.vx * viewWidth, view_moving.vy * viewHeight); 239 ctx.translate(view_moving.dx, view_moving.dy); 240 const view = this.map[view_moving.vy][view_moving.vx]; 241 if (view) 242 view.draw(); 243 ctx.restore(); 244 } 245 else if (inp_highlight_tiles.checked && !inp_mode_view.checked) { 246 ctx.save(); 247 const { view, X, Y, vx, vy } = this.getView(mousePosCanvas.x - camera_x, mousePosCanvas.y - camera_y); 248 if (vx >= this.width || vy >= this.height) 249 return; 250 const tilex = Math.floor(X / TileSize); 251 const tiley = Math.floor(Y / TileSize); 252 ctx.strokeStyle = "black"; 253 ctx.lineWidth = 2; 254 ctx.strokeRect((vx * ViewWidth + tilex) * TileSize, (vy * ViewHeight + tiley) * TileSize, TileSize, TileSize); 255 ctx.strokeStyle = "orange"; 256 ctx.strokeRect((vx * ViewWidth + tilex) * TileSize + 1, (vy * ViewHeight + tiley) * TileSize + 1, TileSize - 2, TileSize - 2); 257 ctx.restore(); 258 } 259 } 260 getView(x, y) { 261 const width = TileSize * ViewWidth; 262 const height = TileSize * ViewHeight; 263 const X = Math.floor(x / width); 264 const Y = Math.floor(y / height); 265 if (X >= this.width || Y >= this.height) 266 return { view: null, X, Y, vx: X, vy: Y }; 267 return { view: this.map[Y][X] || null, X: x - X * width, Y: y - Y * height, vx: X, vy: Y }; 268 } 269 findView(obj) { 270 for (let y = 0; y < this.height; y++) { 271 for (let x = 0; x < this.width; x++) { 272 const view = this.map[y][x]; 273 if (!view) 274 continue; 275 let i = -1; 276 if (obj instanceof Decor) 277 i = view.decor.indexOf(obj); 278 else 279 i = view.entity.indexOf(obj); 280 if (i > -1) 281 return { view, i }; 282 } 283 } 284 return { view: null, i: -1 }; 285 } 286 fill(x, y) { 287 const { view, X, Y } = this.getView(x, y); 288 if (view) 289 view.fill(X, Y); 290 } 291 pen(x, y) { 292 const { view, X, Y } = this.getView(x, y); 293 if (view) 294 view.pen(X, Y); 295 } 296 pick(x, y) { 297 const { view, X, Y } = this.getView(x, y); 298 if (view) 299 view.pick(X, Y); 300 } 301 setCursor(x, y) { 302 const { view, X, Y } = this.getView(x, y); 303 if (view) 304 view.setCursor(X, Y); 305 else 306 setCursor("pointer"); 307 } 308 mousedown(x, y) { 309 const { view, X, Y, vx, vy } = this.getView(x - camera_x, y - camera_y); 310 if (!view) 311 return; 312 view.mousedown(X, Y, x, y, vx, vy); 313 } 314 mouseup(x, y) { 315 const { view, X, Y, vx, vy } = this.getView(x - camera_x, y - camera_y); 316 if (view_moving) { 317 if (view) 318 return; 319 if (vx >= this.width || vy >= this.height || vx < 0 || vy < 0) 320 return; 321 this.map[vy][vx] = this.map[view_moving.vy][view_moving.vx]; 322 this.map[view_moving.vy][view_moving.vx] = undefined; 323 return; 324 } 325 if (view) 326 view.mouseup(X, Y, vx, vy); 327 else { 328 const data = JSON.parse(startViews[startView]); 329 this.map[vy][vx] = View.fromData(data); 330 } 331 } 332 getEntity(x, y) { 333 const { view, X, Y, vx, vy } = this.getView(x, y); 334 return { entity: view && view.getEntity(X, Y) || null, vx, vy }; 335 } 336 getDecor(x, y) { 337 const { view, X, Y, vx, vy } = this.getView(x, y); 338 return { decor: view && view.getDecor(X, Y) || null, vx, vy }; 339 } 340 entity(x, y) { 341 const { view, X, Y } = this.getView(x, y); 342 if (view) 343 view.setEntity(X, Y); 344 } 345 decor(x, y) { 346 const { view, X, Y } = this.getView(x, y); 347 if (view) 348 view.setDecor(X, Y); 349 } 350 saveToLocalStarage() { 351 const data = this.getData(); 352 const json = JSON.stringify(data); 353 localStorage.setItem(localStorageKey, json); 354 } 355 getData() { 356 const data = { 357 width: this.width, 358 height: this.height, 359 map: [], 360 }; 361 for (let y = 0; y < this.height; y++) {
362 const row = []; 363 for (let x = 0; x < this.width; x++) { 364 const view = this.map[y][x]; 365 if (view) 366 row.push(view.getData()); 367 else 368 row.push(undefined); 369 } 370 data.map.push(row); 371 } 372 return data; 373 } 374 static fromData(data) { 375 const world = new World(data.width, data.height); 376 for (let y = 0; y < data.height; y++) { 377 for (let x = 0; x < data.width; x++) { 378 const vData = data.map[y][x]; 379 if (vData) 380 world.map[y][x] = View.fromData(vData); 381 } 382 } 383 return world; 384 } 385 static loadFromLocalStorage() { 386 const json = localStorage.getItem(localStorageKey); 387 if (!json) 388 return; 389 World.loadData(json); 390 } 391 static loadData(json) { 392 try { 393 const data = JSON.parse(json); 394 const newWorld = World.fromData(data); 395 world = newWorld; 396 } 397 catch (error) { 398 console.error(error); 399 const popup = new Popup(); 400 popup.cancelBtn = false; 401 popup.content.appendChild(Div([], [], "ÐÑоизоÑла оÑибка пÑи загÑÑзке даннÑÑ ")); 402 popup.open(); 403 } 404 } 405 calcSelected() { 406 if (!selection) 407 return; 408 selectedDecors = []; 409 selectedEntities = []; 410 const selectionWorld = { 411 x: (selection.x - camera_x) / TileSize, 412 y: (selection.y - camera_y) / TileSize, 413 w: (mousePosCanvas.x - selection.x) / TileSize, 414 h: (mousePosCanvas.y - selection.y) / TileSize, 415 }; 416 normalizeRect(selectionWorld); 417 for (let y = 0; y < this.height; y++) { 418 for (let x = 0; x < this.width; x++) { 419 const view = this.map[y][x]; 420 if (!view) 421 continue; 422 if (rectIntersect(selectionWorld, { x: x * ViewWidth, y: y * ViewHeight, w: ViewWidth, h: ViewHeight })) { 423 if (inp_mode_entity.checked) { 424 for (const entity of view.entity) { 425 if (rectIntersect(selectionWorld, { x: x * ViewWidth + entity.x, y: y * ViewHeight + entity.y, w: entity.getWidth(), h: entity.getHeight() })) { 426 selectedEntities.push(entity); 427 } 428 } 429 } 430 else if (inp_mode_decor.checked) { 431 for (const decor of view.decor) { 432 if (rectIntersect(selectionWorld, { x: x * ViewWidth + decor.x, y: y * ViewHeight + decor.y, w: decor.getWidth(), h: decor.getHeight() })) { 433 selectedDecors.push(decor); 434 } 435 } 436 } 437 } 438 } 439 } 440 } 441} 442class View { 443 tiles = []; 444 entity = []; 445 decor = []; 446 constructor() { 447 for (let y = 0; y < ViewHeight; y++) { 448 const line = []; 449 for (let x = 0; x < ViewWidth; x++) { 450 line.push(new Tile("water_deep")); 451 // if (x == 0 || x == ViewWidth - 1 || y == 0 || y == ViewHeight - 1) 452 // { 453 // line.push(new Tile("water_deep")) 454 // } 455 // else 456 // { 457 // const random = Math.floor(Math.random() * 3); 458 // if (random == 0) line.push(new Tile("sand1")); 459 // else if (random == 1) line.push(new Tile("sand2")); 460 // else line.push(new Tile("sand3")); 461 // if (x == 1 || x == ViewWidth - 2 || y == 1 || y == ViewHeight - 2) 462 // { 463 // const d = new DecorDict["tileEdge_water_deep"](x, y); 464 // this.decor.push(d); 465 // if (x == 1 && y == 1) d.objData[0].value = [true, false, false, true]; 466 // else if (x == 1 && y == ViewHeight - 2) d.objData[0].value = [false, false, true, true]; 467 // else if (x == ViewWidth - 2 && y == 1) d.objData[0].value = [true, true, false, false]; 468 // else if (x == ViewWidth - 2 && y == ViewHeight - 2) d.objData[0].value = [false, true, true, false]; 469 // else if (x == 1) d.objData[0].value = [false, false, false, true]; 470 // else if (x == ViewWidth - 2) d.objData[0].value = [false, true, false, false]; 471 // else if (y == 1) d.objData[0].value = [true, false, false, false]; 472 // else if (y == ViewHeight - 2) d.objData[0].value = [false, false, true, false]; 473 // d.afterDataSet(); 474 // } 475 // } 476 } 477 this.tiles.push(line); 478 } 479 } 480 draw(x, y) { 481 for (let y = 0; y < ViewHeight; y++) { 482 for (let x = 0; x < ViewWidth; x++) { 483 let tile = this.tiles[y][x]; 484 ctx.save(); 485 ctx.translate(x * TileSize, y * TileSize); 486 tile.draw(ctx);
487 ctx.restore(); 488 } 489 } 490 ctx.save(); 491 if (inp_highlight_decor.checked && !inp_mode_decor.checked) 492 ctx.globalAlpha = 0.2; 493 const decorPlaces = {}; 494 this.decor.forEach(d => { 495 d.draw(ctx); 496 if (inp_highlight_stacked.checked && inp_mode_decor.checked) { 497 const key = `${Math.floor(d.x)}:${Math.floor(d.y)}`; 498 if (!decorPlaces[key]) 499 decorPlaces[key] = 1; 500 else 501 decorPlaces[key] += 1; 502 } 503 }); 504 ctx.restore(); 505 ctx.save(); 506 if (!inp_mode_entity.checked) 507 ctx.globalAlpha = 0.5; 508 const entityPlaces = {}; 509 this.entity.forEach(e => { 510 e.draw(ctx); 511 if (inp_highlight_stacked.checked && inp_mode_entity.checked) { 512 const key = `${Math.floor(e.x)}:${Math.floor(e.y)}`; 513 if (!entityPlaces[key]) 514 entityPlaces[key] = 1; 515 else 516 entityPlaces[key] += 1; 517 } 518 }); 519 ctx.restore(); 520 const drawStacked = (pointS, count) => { 521 if (count < 2) 522 return; 523 const splited = pointS.split(":"); 524 ctx.save(); 525 ctx.translate(parseInt(splited[0]) * TileSize, parseInt(splited[1]) * TileSize); 526 ctx.fillStyle = "red"; 527 // ctx.fillRect(0.5 * TileSize, 0.1 * TileSize, 0.4 * TileSize, 0.1 * TileSize); 528 // ctx.fillRect(0.5 * TileSize, 0.3 * TileSize, 0.4 * TileSize, 0.16 * TileSize); 529 ctx.font = `${TileSize * 0.6}px Arial`; 530 ctx.fillText(`${count}`, 0.5 * TileSize, 0.5 * TileSize); 531 ctx.restore(); 532 }; 533 for (const key in decorPlaces) 534 drawStacked(key, decorPlaces[key]); 535 for (const key in entityPlaces) 536 drawStacked(key, entityPlaces[key]); 537 ctx.strokeStyle = "black"; 538 ctx.lineWidth = 1; 539 ctx.strokeRect(0, 0, ViewWidth * TileSize, ViewHeight * TileSize); 540 if (inp_mode_view.checked) { 541 ctx.save(); 542 ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; 543 ctx.fillRect(0, 0, ViewWidth * TileSize, ViewHeight * TileSize); 544 if (icon_move) { 545 const rect = icon_center_rect(); 546 ctx.drawImage(icon_move, rect.x, rect.y, rect.w, rect.h); 547 } 548 if (icon_trash) { 549 const rect = icon_trash_rect(); 550 ctx.drawImage(icon_trash, rect.x, rect.y, rect.w, rect.h); 551 } 552 if (icon_save) { 553 const rect = icon_save_rect(); 554 ctx.drawImage(icon_save, rect.x, rect.y, rect.w, rect.h); 555 } 556 if (x != undefined && y != undefined) { 557 ctx.save(); 558 ctx.font = `${TileSize}px Arial`; 559 ctx.fillStyle = "lime"; 560 ctx.fillText(`${x}:${y}`, TileSize * 0.3, TileSize); 561 ctx.restore(); 562 } 563 ctx.restore(); 564 } 565 } 566 drawInd(ctx) { 567 for (let y = 0; y < ViewHeight; y++) { 568 for (let x = 0; x < ViewWidth; x++) { 569 let tile = this.tiles[y][x]; 570 ctx.save(); 571 ctx.translate(x * TileSize, y * TileSize); 572 tile.draw(ctx); 573 ctx.restore(); 574 } 575 } 576 this.decor.forEach(d => d.draw(ctx)); 577 this.entity.forEach(e => e.draw(ctx)); 578 } 579 fill(x, y) { 580 const X = Math.floor(x / TileSize); 581 const Y = Math.floor(y / TileSize); 582 const tile = this.tiles[Y][X].id; 583 const tiles = []; 584 const group = PenTiles.getGroup(tile); 585 if (group && group.random) { 586 for (const k in tileGroups[group.key].tiles) 587 tiles.push(k); 588 } 589 else { 590 tiles.push(tile); 591 } 592 this.fillR(X, Y, tiles, []); 593 } 594 fillR(x, y, tiles, path) { 595 if (x < 0 || y < 0 || x >= ViewWidth || y >= ViewHeight) 596 return; 597 if (!tiles.includes(this.tiles[y][x].id)) 598 return; 599 this.tiles[y][x].id = pen.getTile();
600 const key = (x, y) => y * ViewWidth + x; 601 path.push(key(x, y)); 602 if (path.indexOf(key(x + 1, y)) == -1) 603 this.fillR(x + 1, y, tiles, path); 604 if (path.indexOf(key(x - 1, y)) == -1) 605 this.fillR(x - 1, y, tiles, path); 606 if (path.indexOf(key(x, y + 1)) == -1) 607 this.fillR(x, y + 1, tiles, path); 608 if (path.indexOf(key(x, y - 1)) == -1) 609 this.fillR(x, y - 1, tiles, path); 610 } 611 pen(x, y) { 612 const X = Math.floor(x / TileSize); 613 const Y = Math.floor(y / TileSize); 614 this.tiles[Y][X].id = pen.getTile(); 615 } 616 pick(x, y) { 617 const X = Math.floor(x / TileSize); 618 const Y = Math.floor(y / TileSize); 619 const tile = this.tiles[Y][X].id; 620 pen.setPen(tile); 621 pen.setGroup(PenTiles.getGroup(tile)); 622 } 623 setCursor(x, y) { 624 if (rectPointIntersect(icon_trash_rect(), { x, y })) { 625 setCursor("pointer"); 626 } 627 else if (rectPointIntersect(icon_save_rect(), { x, y })) { 628 setCursor("pointer"); 629 } 630 else { 631 setCursor("move"); 632 } 633 } 634 mousedown(x, y, rx, ry, vx, vy) { 635 if (rectPointIntersect(icon_trash_rect(), { x, y })) { } 636 else if (rectPointIntersect(icon_save_rect(), { x, y })) { } 637 else { 638 view_moving = { x: rx, y: ry, dx: 0, dy: 0, vx, vy }; 639 } 640 } 641 async mouseup(x, y, vx, vy) { 642 if (rectPointIntersect(icon_trash_rect(), { x, y })) { 643 const popup = new Popup(); 644 popup.focusOn = "cancel"; 645 popup.content.appendChild(Div([], [], "ÐÑ ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ÑдалиÑÑ ÑкÑан?")); 646 const r = await popup.openAsync(); 647 if (r) { 648 const popup = new Popup(); 649 popup.focusOn = "cancel"; 650 popup.content.appendChild(Div([], [], "ÐÑ ÑоÑно ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ÑдалиÑÑ ÑкÑан?")); 651 popup.reverse = true; 652 const r = await popup.openAsync(); 653 if (!r) 654 return; 655 } 656 if (!r) 657 return; 658 world.map[vy][vx] = undefined; 659 } 660 else if (rectPointIntersect(icon_save_rect(), { x, y })) { 661 const popup = new Popup(); 662 popup.focusOn = "cancel"; 663 popup.content.appendChild(Div([], [], "Ð¡Ð¾Ñ ÑаниÑÑ ÑкÑан в палиÑÑе?")); 664 const r = await popup.openAsync(); 665 if (!r) 666 return; 667 const data = JSON.stringify(this.getData()); 668 div_palette.children[startView].classList.remove("palette-view-selected"); 669 startView = startViews.push(data) - 1; 670 localStorage.setItem("WorldEditor-startViews", JSON.stringify(startViews)); 671 setPalete(); 672 div_palette.children[startView].classList.add("palette-view-selected"); 673 } 674 } 675 getEntity(x, y) { 676 for (let i = 0; i < this.entity.length; i++) { 677 const e = this.entity[i]; 678 if (e.intersect(x, y)) 679 return e; 680 } 681 return null; 682 } 683 getDecor(x, y) { 684 for (let i = 0; i < this.decor.length; i++) { 685 const e = this.decor[i]; 686 if (e.intersect(x, y)) 687 return e; 688 } 689 return null; 690 } 691 setEntity(x, y) { 692 if (penEntity && !this.getEntity(x, y)) { 693 const e = new penEntity(x / TileSize, y / TileSize); 694 e.x -= e.getWidth() / 2; 695 e.y -= e.getHeight() / 2; 696 if (shiftKey) 697 e.snapToPixels(); 698 else 699 e.center(); 700 this.entity.push(e); 701 selectedEntity = e; 702 } 703 } 704 setDecor(x, y) { 705 if (penDecor && !this.getDecor(x, y)) { 706 const d = new penDecor(x / TileSize, y / TileSize); 707 if (penDecorData) 708 d.apllyData(penDecorData); 709 if (shiftKey) 710 d.snapToPixels(); 711 else 712 d.center(); 713 this.decor.push(d); 714 selectedDecor = d; 715 if (d instanceof Decor_TileEdge && inp_auto_edge.checked) { 716 const x = Math.floor(d.x); 717 const y = Math.floor(d.y); 718 const type = d.constructor.className; 719 const checkTile = (x, y, id) => { 720 if (x < 0 || x >= ViewWidth || y < 0 || y >= ViewHeight) 721 return false;
722 if (typeof id == "string") 723 return this.tiles[y][x].id == id; 724 return id(this.tiles[y][x].id); 725 }; 726 const setSides = (tileId, startsWith = false) => { 727 const cond = startsWith ? ((id) => id.startsWith(tileId)) : ((id) => id == tileId); 728 d.sides[1] = checkTile(x + 1, y, cond); 729 d.sides[3] = checkTile(x - 1, y, cond); 730 d.sides[2] = checkTile(x, y + 1, cond); 731 d.sides[0] = checkTile(x, y - 1, cond); 732 if (!(d.sides[1] || d.sides[3] || d.sides[2] || d.sides[0])) { 733 if (penDecorData) 734 d.apllyData(penDecorData); 735 else 736 d.sides[0] = true; 737 } 738 }; 739 const setSidesAndCorners = (tileId, startsWith = false) => { 740 const cond = startsWith ? ((id) => id.startsWith(tileId)) : ((id) => id == tileId); 741 d.sides[1] = checkTile(x + 1, y, cond); 742 d.sides[3] = checkTile(x - 1, y, cond); 743 d.sides[2] = checkTile(x, y + 1, cond); 744 d.sides[0] = checkTile(x, y - 1, cond); 745 d.corners[1] = checkTile(x + 1, y + 1, cond); 746 d.corners[0] = checkTile(x + 1, y - 1, cond); 747 d.corners[2] = checkTile(x - 1, y + 1, cond); 748 d.corners[3] = checkTile(x - 1, y - 1, cond); 749 if (!(d.sides[1] || d.sides[3] || d.sides[2] || d.sides[0] || d.corners[1] || d.corners[3] || d.corners[2] || d.corners[0])) { 750 if (penDecorData) 751 d.apllyData(penDecorData); 752 else 753 d.sides[0] = true; 754 } 755 }; 756 if (type == "tileEdge_water") 757 setSides("water_low"); 758 else if (type == "tileEdge_water_deep") 759 setSides("water_deep"); 760 else if (type == "tileEdge_lava") 761 setSidesAndCorners("lava2"); 762 else if (type == "tileEdge_sand") 763 setSidesAndCorners("sand", true); 764 else if (type == "tileEdge_grass") 765 setSidesAndCorners("grass", true); 766 d.afterDataSet(); 767 } 768 penDecorData = d.objData; 769 } 770 } 771 getData() { 772 const data = { 773 tiles: [], 774 entity: [], 775 decor: [], 776 }; 777 for (let y = 0; y < ViewHeight; y++) { 778 const row = []; 779 for (let x = 0; x < ViewWidth; x++) { 780 row.push(this.tiles[y][x].id); 781 } 782 data.tiles.push(row); 783 } 784 this.entity.forEach(e => data.entity.push(e.getData())); 785 this.decor.forEach(d => data.decor.push(d.getData())); 786 return data; 787 } 788 static fromData(data) { 789 const view = new View(); 790 view.decor = []; 791 for (let y = 0; y < ViewHeight && y < data.tiles.length; y++) { 792 for (let x = 0; x < ViewWidth && x < data.tiles[y].length; x++) { 793 view.tiles[y][x] = new Tile(data.tiles[y][x]); 794 } 795 } 796 data.entity.forEach(eData => { 797 const entity = Entity.fromData(eData); 798 if (entity) 799 view.entity.push(entity); 800 }); 801 if (!data.decor) 802 return view; 803 data.decor.forEach(dData => { 804 const decor = Decor.fromData(dData); 805 if (decor) 806 view.decor.push(decor); 807 }); 808 return view; 809 } 810} 811class Tile { 812 id = "sand1"; 813 constructor(id) { 814 if (id) 815 this.id = id; 816 } 817 draw(ctx) { 818 const img = tileImages[this.id]; 819 if (img) { 820 ctx.drawImage(img, 0, 0, TileSize, TileSize); 821 } 822 } 823} 824class MiniMap { 825 canvas = getCanvas("minimap"); 826 ctx = getCanvasContext(this.canvas); 827 size = 20; 828 constructor() { 829 this.canvas.addEventListener("contextmenu", e => e.preventDefault()); 830 this.canvas.addEventListener("mousedown", e => { 831 const x = Math.min(Math.floor(e.offsetX / this.size), world.width); 832 const y = Math.min(Math.floor(e.offsetY / this.size), world.height); 833 const X = x * TileSize * ViewWidth; 834 const Y = y * TileSize * ViewHeight; 835 if (e.button == 0) { 836 camera_x = -(X - Math.round((canvas.width - ViewWidth * TileSize) / 2)); 837 camera_y = -(Y - Math.round((canvas.height - ViewHeight * TileSize) / 2)); 838 normalizeCamera(); 839 } 840 else { 841 centerView(X, Y); 842 } 843 }); 844 } 845 draw() { 846 this.canvas.width = (this.size + 1) * world.width + 2; 847 this.canvas.height = (this.size + 1) * world.height + 2; 848 for (let y = 0; y < world.height; y++) { 849 for (let x = 0; x < world.width; x++) {
850 if (world.map[y] && world.map[y][x]) 851 this.ctx.fillStyle = "rgb(243, 200, 81)"; 852 else 853 this.ctx.fillStyle = "rgb(177, 225, 255)"; 854 this.ctx.fillRect(x * (this.size + 1) + 1, y * (this.size + 1) + 1, this.size, this.size); 855 } 856 } 857 const coefX = (this.canvas.width - 2) / (world.width * ViewWidth * TileSize); 858 const coefY = (this.canvas.height - 2) / (world.height * ViewHeight * TileSize); 859 this.ctx.strokeStyle = "blue"; 860 this.ctx.strokeRect(-camera_x * coefX + 1, -camera_y * coefY + 1, canvas.width * coefX, canvas.height * coefY); 861 } 862} 863class FastPalette { 864 opened = false; 865 closedByClick = false; 866 imgs = []; 867 tiles = [pen.getTile()]; 868 entity = [penEntity]; 869 decor = [penDecor]; 870 hovered = null; 871 addtileI = 1; 872 addentityI = 1; 873 adddecorI = 1; 874 constructor() { 875 const imgs = div_fast_palette.querySelectorAll(".fast-palette-part"); 876 for (let i = 0; i < imgs.length; i++) { 877 const el = imgs[i]; 878 if (el instanceof HTMLDivElement) { 879 this.imgs.push(el); 880 el.addEventListener("mouseover", () => this.hovered = i); 881 el.addEventListener("click", () => { 882 this.hovered = i; 883 this.close(true); 884 }); 885 } 886 } 887 } 888 open() { 889 if (this.opened) 890 return; 891 this.hovered = null; 892 this.opened = true; 893 this.closedByClick = false; 894 if (inp_mode_entity.checked) 895 this.setPaletteEntity(); 896 else if (inp_mode_decor.checked) 897 this.setPaletteDecor(); 898 else 899 this.setPaletteTiles(); 900 div_fast_palette.style.left = `${mousePos.x}px`; 901 div_fast_palette.style.top = `${mousePos.y}px`; 902 div_fast_palette.classList.add("fast-palette-visible"); 903 } 904 setPaletteTiles() { 905 for (let i = 0; i < this.imgs.length; i++) { 906 const imgDiv = this.imgs[i]; 907 imgDiv.innerHTML = ""; 908 const imgTile = this.tiles.length > i ? tileImages[this.tiles[i]] : tileImages[this.tiles[0]]; 909 if (!imgTile) 910 continue; 911 const img = document.createElement("img"); 912 img.src = imgTile?.src; 913 imgDiv.appendChild(img); 914 } 915 } 916 setPaletteEntity() { 917 for (let i = 0; i < this.imgs.length; i++) { 918 const imgDiv = this.imgs[i]; 919 imgDiv.innerHTML = ""; 920 const e = this.entity.length > i ? this.entity[i] : this.entity[0]; 921 if (!e) { 922 const img = document.createElement("img"); 923 img.src = "imgs/none.png"; 924 imgDiv.appendChild(img); 925 continue; 926 } 927 const img = document.createElement("canvas"); 928 e.draw(img, 65); 929 imgDiv.appendChild(img); 930 } 931 } 932 setPaletteDecor() { 933 for (let i = 0; i < this.imgs.length; i++) { 934 const imgDiv = this.imgs[i]; 935 imgDiv.innerHTML = ""; 936 const d = this.decor.length > i ? this.decor[i] : this.decor[0]; 937 if (!d) { 938 const img = document.createElement("img"); 939 img.src = "imgs/none.png"; 940 imgDiv.appendChild(img); 941 continue; 942 } 943 const img = document.createElement("canvas"); 944 d.draw(img, 65); 945 imgDiv.appendChild(img); 946 } 947 } 948 close(closedByClick = false) { 949 this.opened = false || this.closedByClick; 950 div_fast_palette.classList.remove("fast-palette-visible"); 951 if (this.closedByClick || this.hovered == null) 952 return; 953 this.closedByClick = closedByClick; 954 if (inp_mode_entity.checked) 955 penEntity = this.entity.length > this.hovered ? this.entity[this.hovered] : this.entity[0]; 956 else if (inp_mode_decor.checked) { 957 penDecor = this.decor.length > this.hovered ? this.decor[this.hovered] : this.decor[0]; 958 penDecorData = null; 959 } 960 else { 961 const tile = this.tiles.length > this.hovered ? this.tiles[this.hovered] : this.tiles[0]; 962 pen.setPen(tile); 963 pen.setGroup(PenTiles.getGroup(tile)); 964 } 965 } 966 addTile(tileid) {
967 if (this.tiles.indexOf(tileid) != -1) 968 return; 969 this.tiles[this.addtileI] = tileid; 970 this.addtileI = (this.addtileI + 1) % this.imgs.length; 971 } 972 addEntity(entity) { 973 if (this.entity.indexOf(entity) != -1) 974 return; 975 this.entity[this.addentityI] = entity; 976 this.addentityI = (this.addentityI + 1) % this.imgs.length; 977 } 978 addDecor(decor) { 979 if (this.decor.indexOf(decor) != -1) 980 return; 981 this.decor[this.adddecorI] = decor; 982 this.adddecorI = (this.adddecorI + 1) % this.imgs.length; 983 } 984} 985class PenTiles { 986 pen = "sand1"; 987 group = null; 988 groupTiles = ["sand1"]; 989 constructor() { 990 div_palette_group.addEventListener("click", () => { 991 div_palette_group.classList.remove("fast-palette-visible"); 992 }); 993 } 994 setPen(tile, resetGroup = true) { 995 if (resetGroup) 996 this.group = null; 997 this.pen = tile; 998 fastPalette.addTile(this.pen); 999 } 1000 getTile() { 1001 if (!this.group?.random) 1002 return this.pen; 1003 if (this.groupTiles.length == 0) 1004 return this.group.key; 1005 return this.groupTiles[Math.floor(Math.random() * this.groupTiles.length)]; 1006 } 1007 openGroup(group) { 1008 if (group == undefined) { 1009 if (this.group && !this.group.random) { 1010 div_palette_group.classList.toggle("fast-palette-visible"); 1011 } 1012 return; 1013 } 1014 this.setGroup(group); 1015 if (group.random) 1016 return; 1017 div_palette_group.classList.add("fast-palette-visible"); 1018 } 1019 setGroup(group) { 1020 if (!group) 1021 return; 1022 if (!group.group) 1023 return; 1024 this.group = group; 1025 this.groupTiles = []; 1026 if (group.random) { 1027 for (const k in tileGroups[group.key].tiles) 1028 this.groupTiles.push(k); 1029 } 1030 div_palette_group_imgs.innerHTML = ""; 1031 for (const k in tileGroups[group.key].tiles) { 1032 const key = k; 1033 function addImg() { 1034 if (inp_mode_entity.checked) 1035 return; 1036 const img = tileImages[key]; 1037 if (img) { 1038 const imgNew = document.createElement("img"); 1039 imgNew.src = img.src; 1040 imgNew.title = key; 1041 div_palette_group_imgs.appendChild(imgNew); 1042 imgNew.addEventListener("click", () => { 1043 pen.setPen(key, false); 1044 div_palette_group.classList.remove("fast-palette-visible"); 1045 }); 1046 } 1047 else 1048 setTimeout(addImg, 100); 1049 } 1050 addImg(); 1051 } 1052 } 1053 static getGroup(tile) { 1054 for (const group of tileList) { 1055 if (group.key == tile) 1056 return group; 1057 if (group.group) { 1058 const tiles = tileGroups[group.key].tiles; 1059 for (const key in tiles) { 1060 if (key == tile) 1061 return group; 1062 } 1063 } 1064 } 1065 return undefined; 1066 } 1067} 1068const pen = new PenTiles(); 1069let world = new World(0, 0); 1070const minimap = new MiniMap(); 1071const fastPalette = new FastPalette(); 1072inp_width.addEventListener("change", () => { 1073 world = new World(inp_width.valueAsNumber, inp_height.valueAsNumber, world); 1074 inp_width.valueAsNumber = world.width; 1075 inp_height.valueAsNumber = world.height; 1076}); 1077inp_height.addEventListener("change", () => { 1078 world = new World(inp_width.valueAsNumber, inp_height.valueAsNumber, world); 1079 inp_width.valueAsNumber = world.width; 1080 inp_height.valueAsNumber = world.height; 1081}); 1082btn_up.addEventListener("click", () => world.up()); 1083btn_right.addEventListener("click", () => world.right()); 1084btn_down.addEventListener("click", () => world.down()); 1085btn_left.addEventListener("click", () => world.left()); 1086btn_save.addEventListener("click", () => { 1087 const text = JSON.stringify(world.getData()); 1088 var el = document.createElement('a'); 1089 el.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); 1090 el.setAttribute('download', worldFileName); 1091 el.style.display = 'none'; 1092 document.body.appendChild(el); 1093 el.click(); 1094 document.body.removeChild(el); 1095}); 1096inp_load.addEventListener("input", async () => { 1097 const curFiles = inp_load.files; 1098 if (!curFiles) 1099 return; 1100 if (curFiles.length == 0) 1101 return; 1102 if (world.height != 0 && world.width != 0) { 1103 if (!await askForSure("замениÑÑ ÑекÑÑий миÑ")) 1104 return; 1105 } 1106 const file = curFiles[0]; 1107 worldFileName = file.name; 1108 localStorage.setItem("WorldEditor-world-filename", worldFileName); 1109 const data = await file.text(); 1110 World.loadData(data); 1111}); 1112btn_new.addEventListener("click", async () => { 1113 if (world.height != 0 && world.width != 0) { 1114 if (!await askForSure("ÑоздаÑÑ Ð¿ÑÑÑой миÑ")) 1115 return; 1116 } 1117 world = new World(inp_width.valueAsNumber, inp_height.valueAsNumber); 1118 worldFileName = "worldData.json"; 1119 localStorage.setItem("WorldEditor-world-filename", worldFileName); 1120}); 1121inp_tilesize.addEventListener("change", () => TileSize = inp_tilesize.valueAsNumber); 1122inp_mode_entity.addEventListener("change", () => { 1123 if (inp_mode_entity.checked) 1124 inp_mode_decor.checked = false;
1125 else 1126 selectedEntity = null; 1127 selectedDecor = null; 1128 setPalete(); 1129}); 1130inp_mode_decor.addEventListener("change", () => { 1131 if (inp_mode_decor.checked) 1132 inp_mode_entity.checked = false; 1133 else 1134 selectedDecor = null; 1135 selectedEntity = null; 1136 setPalete(); 1137}); 1138inp_mode_view.addEventListener("change", () => { 1139 setPalete(); 1140}); 1141canvas.addEventListener("wheel", e => { 1142 const moveSpeed = camera_speed(); 1143 if (e.ctrlKey) { 1144 e.preventDefault(); 1145 const x = (mousePosCanvas.x - camera_x) / TileSize; 1146 const y = (mousePosCanvas.y - camera_y) / TileSize; 1147 const v = 1.4; 1148 if (e.deltaY > 0) 1149 TileSize /= v; 1150 else 1151 TileSize *= v; 1152 TileSize = Math.max(Math.round(TileSize), 2); 1153 camera_x = -x * TileSize + mousePosCanvas.x; 1154 camera_y = -y * TileSize + mousePosCanvas.y; 1155 inp_tilesize.valueAsNumber = TileSize; 1156 normalizeCamera(); 1157 } 1158 else if (e.shiftKey) { 1159 if (e.deltaY > 0) 1160 camera_x -= moveSpeed; 1161 else 1162 camera_x += moveSpeed; 1163 normalizeCamera(); 1164 } 1165 else { 1166 if (e.deltaY > 0) 1167 camera_y -= moveSpeed; 1168 else 1169 camera_y += moveSpeed; 1170 normalizeCamera(); 1171 } 1172}); 1173let camera_moving = null; 1174let view_moving = null; 1175let entity_moving = null; 1176let decor_moving = null; 1177let selection = null; 1178let drawing = null; 1179let ctrl = false; 1180let shiftKey = false; 1181canvas.addEventListener("mousedown", e => { 1182 e.preventDefault(); 1183 if (e.button == 0) { 1184 if (camera_moving) { 1185 centerView(e.offsetX - camera_x, e.offsetY - camera_y); 1186 camera_moving = null; 1187 return; 1188 } 1189 if (inp_mode_view.checked) { 1190 world.mousedown(e.offsetX, e.offsetY); 1191 return; 1192 } 1193 if (e.ctrlKey) { 1194 if (selection) { 1195 selection.cx = e.offsetX; 1196 selection.cy = e.offsetY; 1197 selection.changed = false; 1198 } 1199 else { 1200 selection = { x: e.offsetX, y: e.offsetY, cx: e.offsetX, cy: e.offsetY, changed: false }; 1201 } 1202 if (inp_mode_entity.checked) { 1203 const { entity } = world.getEntity(e.offsetX - camera_x, e.offsetY - camera_y); 1204 if (entity) { 1205 const i = selectedEntities.indexOf(entity); 1206 if (i >= 0) 1207 selectedEntities.splice(i, 1); 1208 else 1209 selectedEntities.push(entity); 1210 selection.changed = true; 1211 } 1212 } 1213 else if (inp_mode_decor.checked) { 1214 const { decor } = world.getDecor(e.offsetX - camera_x, e.offsetY - camera_y); 1215 if (decor) { 1216 const i = selectedDecors.indexOf(decor); 1217 if (i >= 0) 1218 selectedDecors.splice(i, 1); 1219 else 1220 selectedDecors.push(decor); 1221 selection.changed = true; 1222 } 1223 } 1224 } 1225 else if (inp_mode_entity.checked) { 1226 const { entity } = world.getEntity(e.offsetX - camera_x, e.offsetY - camera_y); 1227 if (entity) { 1228 if (shiftKey) 1229 entity.snapToPixels(); 1230 else 1231 entity.center(); 1232 entity_moving = { x: e.offsetX, y: e.offsetY, dx: 0, dy: 0, entity }; 1233 selectedEntity = entity; 1234 } 1235 else { 1236 drawing = "entity"; 1237 selectedEntity = null; 1238 } 1239 } 1240 else if (inp_mode_decor.checked) { 1241 const { decor } = world.getDecor(e.offsetX - camera_x, e.offsetY - camera_y); 1242 if (decor) { 1243 if (shiftKey) 1244 decor.snapToPixels(); 1245 else 1246 decor.center(); 1247 decor_moving = { x: e.offsetX, y: e.offsetY, dx: 0, dy: 0, decor }; 1248 selectedDecor = decor; 1249 } 1250 else { 1251 drawing = "decor"; 1252 selectedDecor = null; 1253 } 1254 } 1255 else { 1256 if (inp_mode_pen.checked) { 1257 drawing = "pen"; 1258 } 1259 else { 1260 drawing = "fill"; 1261 } 1262 } 1263 } 1264 if (e.button == 2) { 1265 if (drawing) { 1266 centerView(e.offsetX - camera_x, e.offsetY - camera_y); 1267 drawing = null; 1268 return; 1269 } 1270 camera_moving = { x: e.offsetX, y: e.offsetY, cx: camera_x, cy: camera_y }; 1271 setCursor("move"); 1272 } 1273 if (e.button == 1) { 1274 let action = false;
1275 if (inp_mode_entity.checked) { 1276 const { entity } = world.getEntity(e.offsetX - camera_x, e.offsetY - camera_y); 1277 if (entity) { 1278 action = true; 1279 penEntity = entity.constructor; 1280 fastPalette.addEntity(penEntity); 1281 } 1282 } 1283 else if (inp_mode_decor.checked) { 1284 const { decor } = world.getDecor(e.offsetX - camera_x, e.offsetY - camera_y); 1285 if (decor) { 1286 action = true; 1287 penDecor = decor.constructor; 1288 penDecorData = decor.objData; 1289 fastPalette.addDecor(penDecor); 1290 } 1291 } 1292 else { 1293 world.pick(e.offsetX - camera_x, e.offsetY - camera_y); 1294 } 1295 if (!action) { 1296 if (selection) { 1297 selection.cx = e.offsetX; 1298 selection.cy = e.offsetY; 1299 selection.changed = false; 1300 } 1301 else { 1302 selection = { x: e.offsetX, y: e.offsetY, cx: e.offsetX, cy: e.offsetY, changed: false }; 1303 } 1304 } 1305 } 1306}); 1307canvas.addEventListener("mousemove", e => { 1308 mousePosCanvas.x = e.offsetX; 1309 mousePosCanvas.y = e.offsetY; 1310 setCursor("none"); 1311 if (selection) { 1312 if (Math.abs(e.offsetX - selection.cx) > 5 || Math.abs(e.offsetY - selection.cy) > 5) { 1313 world.calcSelected(); 1314 selection.changed = true; 1315 } 1316 } 1317 if (inp_mode_view.checked) { 1318 world.setCursor(e.offsetX - camera_x, e.offsetY - camera_y); 1319 } 1320 if (drawing) { 1321 if (drawing == "pen") 1322 world.pen(e.offsetX - camera_x, e.offsetY - camera_y); 1323 else if (drawing == "decor") { 1324 if (penDecor && penDecor.width >= 1 && penDecor.height >= 1) { 1325 world.decor(e.offsetX - camera_x, e.offsetY - camera_y); 1326 } 1327 } 1328 } 1329 else if (entity_moving) { 1330 // entity_moving.dx = e.offsetX - entity_moving.x; 1331 // entity_moving.dy = e.offsetY - entity_moving.y; 1332 if (shiftKey) { 1333 entity_moving.dx = Math.floor((e.offsetX - entity_moving.x + TileSize / 16 / 2) / (TileSize / 16)) * TileSize / 16; 1334 entity_moving.dy = Math.floor((e.offsetY - entity_moving.y + TileSize / 16 / 2) / (TileSize / 16)) * TileSize / 16; 1335 } 1336 else { 1337 entity_moving.dx = Math.floor((e.offsetX - entity_moving.x + TileSize / 2) / TileSize) * TileSize; 1338 entity_moving.dy = Math.floor((e.offsetY - entity_moving.y + TileSize / 2) / TileSize) * TileSize; 1339 } 1340 setCursor("move"); 1341 } 1342 else if (decor_moving) { 1343 // decor_moving.dx = e.offsetX - decor_moving.x; 1344 // decor_moving.dy = e.offsetY - decor_moving.y; 1345 if (shiftKey) { 1346 decor_moving.dx = Math.floor((e.offsetX - decor_moving.x + TileSize / 16 / 2) / (TileSize / 16)) * TileSize / 16; 1347 decor_moving.dy = Math.floor((e.offsetY - decor_moving.y + TileSize / 16 / 2) / (TileSize / 16)) * TileSize / 16; 1348 } 1349 else { 1350 decor_moving.dx = Math.floor((e.offsetX - decor_moving.x + TileSize / 2) / TileSize) * TileSize; 1351 decor_moving.dy = Math.floor((e.offsetY - decor_moving.y + TileSize / 2) / TileSize) * TileSize; 1352 } 1353 setCursor("move"); 1354 } 1355 else if (camera_moving) { 1356 camera_x = camera_moving.cx + e.offsetX - camera_moving.x; 1357 camera_y = camera_moving.cy + e.offsetY - camera_moving.y; 1358 setCursor("move"); 1359 normalizeCamera(); 1360 } 1361 else if (view_moving) { 1362 view_moving.dx = e.offsetX - view_moving.x; 1363 view_moving.dy = e.offsetY - view_moving.y; 1364 setCursor("move"); 1365 normalizeCamera(); 1366 } 1367}); 1368canvas.addEventListener("mouseup", e => { 1369 setCursor("none"); 1370 if (inp_mode_view.checked) { 1371 if (e.button == 0) 1372 world.mouseup(e.offsetX, e.offsetY); 1373 world.setCursor(e.offsetX - camera_x, e.offsetY - camera_y); 1374 } 1375 if (drawing == "pen") 1376 world.pen(e.offsetX - camera_x, e.offsetY - camera_y); 1377 if (drawing == "fill") 1378 world.fill(e.offsetX - camera_x, e.offsetY - camera_y); 1379 if (drawing == "entity") 1380 world.entity(e.offsetX - camera_x, e.offsetY - camera_y); 1381 if (drawing == "decor") 1382 world.decor(e.offsetX - camera_x, e.offsetY - camera_y); 1383 if (entity_moving) 1384 endEntityMove(); 1385 if (decor_moving) 1386 endDecorMove(); 1387 if (selection && !selection.changed) { 1388 selectedDecors = []; 1389 selectedEntities = []; 1390 } 1391 drawing = null; 1392 camera_moving = null; 1393 view_moving = null; 1394 entity_moving = null; 1395 decor_moving = null; 1396 selection = null; 1397}); 1398canvas.addEventListener("mouseleave", () => { 1399 drawing = null; 1400 camera_moving = null; 1401 view_moving = null; 1402 entity_moving = null; 1403 setCursor("none"); 1404}); 1405canvas.addEventListener("contextmenu", e => e.preventDefault()); 1406canvas.addEventListener("dblclick", e => { 1407 if (inp_mode_entity.checked && e.button == 0) { 1408 const { entity, vx, vy } = world.getEntity(e.offsetX - camera_x, e.offsetY - camera_y); 1409 if (entity) { 1410 selectedEntity = entity; 1411 entity.openMenu(vx, vy); 1412 } 1413 } 1414 if (inp_mode_decor.checked && e.button == 0) { 1415 const { decor, vx, vy } = world.getDecor(e.offsetX - camera_x, e.offsetY - camera_y); 1416 if (decor) { 1417 selectedDecor = decor; 1418 decor.openMenu(vx, vy); 1419 } 1420 } 1421}); 1422window.addEventListener("keypress", e => { 1423 if (Popup.Opened) 1424 return; 1425 switch (e.code) { 1426 case "KeyF": 1427 inp_mode_fill.checked = true; 1428 break;
1429 case "KeyR": 1430 inp_mode_pen.checked = true; 1431 break; 1432 case "KeyS": 1433 inp_mode_view.checked = !inp_mode_view.checked; 1434 setPalete(); 1435 break; 1436 case "KeyH": 1437 inp_highlight_tiles.checked = !inp_highlight_tiles.checked; 1438 break; 1439 case "KeyJ": 1440 inp_highlight_decor.checked = !inp_highlight_decor.checked; 1441 break; 1442 case "KeyE": { 1443 inp_mode_entity.checked = !inp_mode_entity.checked; 1444 if (inp_mode_entity.checked) 1445 inp_mode_decor.checked = false; 1446 else 1447 selectedEntity = null; 1448 selectedDecor = null; 1449 setPalete(); 1450 break; 1451 } 1452 case "KeyD": { 1453 inp_mode_decor.checked = !inp_mode_decor.checked; 1454 if (inp_mode_decor.checked) 1455 inp_mode_entity.checked = false; 1456 else 1457 selectedDecor = null; 1458 selectedEntity = null; 1459 setPalete(); 1460 break; 1461 } 1462 case "KeyW": 1463 pen.openGroup(); 1464 break; 1465 // case "KeyC": endEntityMove()?.center(); break; 1466 } 1467}); 1468window.addEventListener("keydown", e => { 1469 if (Popup.Opened) 1470 return; 1471 switch (e.code) { 1472 case "ArrowUp": 1473 world.up(); 1474 break; 1475 case "ArrowRight": 1476 world.right(); 1477 break; 1478 case "ArrowDown": 1479 world.down(); 1480 break; 1481 case "ArrowLeft": 1482 world.left(); 1483 break; 1484 case "KeyQ": 1485 fastPalette.open(); 1486 break; 1487 case "ControlLeft": 1488 case "ControlRight": 1489 ctrl = true; 1490 break; 1491 case "ShiftLeft": 1492 case "ShiftRight": 1493 shiftKey = true; 1494 break; 1495 } 1496}); 1497window.addEventListener("keyup", async (e) => { 1498 if (Popup.Opened) 1499 return; 1500 switch (e.code) { 1501 case "KeyQ": 1502 fastPalette.close(); 1503 break; 1504 case "Delete": 1505 deleteSelected(); 1506 break; 1507 case "ControlLeft": 1508 case "ControlRight": 1509 ctrl = false; 1510 break; 1511 case "ShiftLeft": 1512 case "ShiftRight": 1513 shiftKey = false; 1514 break; 1515 case "KeyC": 1516 if (e.ctrlKey) 1517 copySelected(); 1518 break; 1519 case "KeyX": 1520 if (e.ctrlKey) 1521 cutSelected(); 1522 break; 1523 case "KeyV": 1524 if (e.ctrlKey) 1525 pasteSelected(); 1526 break; 1527 } 1528}); 1529window.addEventListener("mousemove", e => { 1530 mousePos.x = e.pageX; 1531 mousePos.y = e.pageY; 1532}); 1533function loadImages() { 1534 for (const k in tileIds) { 1535 const key = k; 1536 const imgName = tileIds[key]; 1537 tileImages[key] = undefined; 1538 loadImage(imgName, img => tileImages[key] = img, "tiles"); 1539 } 1540 loadImage("/icon-move.png", img => icon_move = img); 1541 loadImage("/icon-trash.png", img => icon_trash = img); 1542 loadImage("/icon-plus.png", img => icon_plus = img); 1543 loadImage("/icon-save.png", img => icon_save = img); 1544 entity.forEach(e => { 1545 loadImage(e.imgUrl, img => e.img = img, "entities"); 1546 }); 1547 decor.forEach(d => { 1548 loadImage(d.imgUrl, img => d.img = img, "decor"); 1549 }); 1550} 1551function centerView(x, y) { 1552 let width = TileSize * ViewWidth; 1553 let height = TileSize * ViewHeight; 1554 const X = Math.floor(x / width); 1555 const Y = Math.floor(y / height); 1556 if (X >= world.width || Y >= world.height) 1557 return; 1558 TileSize = Math.min(canvas.width / ViewWidth, canvas.height / ViewHeight); 1559 TileSize = Math.floor(TileSize); 1560 inp_tilesize.valueAsNumber = TileSize; 1561 width = TileSize * ViewWidth; 1562 height = TileSize * ViewHeight; 1563 camera_x = -X * width + Math.floor((canvas.width - width) / 2); 1564 camera_y = -Y * height + Math.floor((canvas.height - height) / 2); 1565 normalizeCamera(); 1566} 1567function normalizeCamera() { 1568 const width = world.width * ViewWidth * TileSize; 1569 const height = world.height * ViewHeight * TileSize; 1570 const gapW = canvas.width / 2; 1571 const gapH = canvas.height / 2; 1572 if (width > canvas.width) { 1573 camera_x = Math.max(Math.min(camera_x, gapW), -width + canvas.width - gapW); 1574 } 1575 else 1576 camera_x = Math.min(camera_x, gapW); 1577 if (height > canvas.height) { 1578 camera_y = Math.max(Math.min(camera_y, gapH), -height + canvas.height - gapH); 1579 } 1580 else 1581 camera_y = Math.min(camera_y, gapH); 1582} 1583function setCursor(cursor) { 1584 switch (cursor) { 1585 case "none": 1586 canvas.classList.remove("cursor-move"); 1587 canvas.classList.remove("cursor-pointer"); 1588 break; 1589 case "pointer": 1590 canvas.classList.remove("cursor-move"); 1591 canvas.classList.add("cursor-pointer"); 1592 break; 1593 case "move": 1594 canvas.classList.add("cursor-move"); 1595 canvas.classList.remove("cursor-pointer"); 1596 break; 1597 } 1598} 1599function setPalete() { 1600 div_palette.innerHTML = ""; 1601 if (inp_mode_view.checked) { 1602 const _TileSize = TileSize; 1603 TileSize = 15; 1604 startViews.forEach((e, i) => { 1605 const img = document.createElement("canvas"); 1606 img.width = TileSize * ViewWidth; 1607 img.height = TileSize * ViewHeight; 1608 const ctx = getCanvasContext(img); 1609 const data = JSON.parse(startViews[i]); 1610 const view = View.fromData(data); 1611 view.drawInd(ctx); 1612 img.addEventListener("click", () => { 1613 div_palette.children[startView].classList.remove("palette-view-selected"); 1614 div_palette.children[i].classList.add("palette-view-selected"); 1615 startView = i; 1616 }); 1617 const delbtn = document.createElement("button"); 1618 delbtn.innerText = "Ã"; 1619 delbtn.addEventListener("click", async () => { 1620 if (startViews.length <= 1) { 1621 let popup = new Popup(); 1622 popup.cancelBtn = false;
1623 popup.content.appendChild(Div([], [], `ÐелÑÐ·Ñ ÑдалиÑÑ Ð¿Ð¾Ñледний ÑкÑан`)); 1624 popup.open(); 1625 return; 1626 } 1627 let popup = new Popup(); 1628 popup.focusOn = "cancel"; 1629 popup.content.appendChild(Div([], [], `ÐÑ ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ÑдалиÑÑ ÑкÑан из палиÑÑÑ?`)); 1630 let r = await popup.openAsync(); 1631 if (!r) 1632 return; 1633 popup = new Popup(); 1634 popup.focusOn = "cancel"; 1635 popup.content.appendChild(Div([], [], `ÐÑ ÑоÑно ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ÑдалиÑÑ ÑкÑан из палиÑÑÑ?`)); 1636 popup.reverse = true; 1637 r = await popup.openAsync(); 1638 if (!r) 1639 return; 1640 startViews.splice(i, 1); 1641 startView = Math.min(startView, startViews.length - 1); 1642 localStorage.setItem("WorldEditor-startViews", JSON.stringify(startViews)); 1643 setPalete(); 1644 }); 1645 div_palette.appendChild(Div(["palette-view"], [ 1646 img, 1647 delbtn 1648 ])); 1649 }); 1650 div_palette.children[startView].classList.add("palette-view-selected"); 1651 TileSize = _TileSize; 1652 } 1653 else if (inp_mode_entity.checked) { 1654 const img = document.createElement("img"); 1655 img.title = "ÐÑклÑÑиÑÑ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ðµ"; 1656 img.src = "./imgs/none.png"; 1657 img.addEventListener("click", () => penEntity = null); 1658 div_palette.appendChild(img); 1659 entity.forEach(e => { 1660 const img = document.createElement("canvas"); 1661 img.title = e.className; 1662 div_palette.appendChild(img); 1663 function addImg() { 1664 if (!inp_mode_entity.checked) 1665 return; 1666 if (e.img) { 1667 e.draw(img, 48); 1668 img.addEventListener("click", () => { 1669 penEntity = e; 1670 fastPalette.addEntity(penEntity); 1671 }); 1672 } 1673 else 1674 setTimeout(addImg, 100); 1675 } 1676 addImg(); 1677 }); 1678 } 1679 else if (inp_mode_decor.checked) { 1680 selectedDecor = null; 1681 penDecorData = null; 1682 decor.forEach(d => { 1683 const img = document.createElement("canvas"); 1684 img.title = d.className; 1685 div_palette.appendChild(img); 1686 function addImg() { 1687 if (!inp_mode_decor.checked) 1688 return; 1689 if (d.img) { 1690 d.draw(img, 48); 1691 img.addEventListener("click", () => { 1692 penDecor = d; 1693 penDecorData = null; 1694 fastPalette.addDecor(penDecor); 1695 }); 1696 } 1697 else 1698 setTimeout(addImg, 100); 1699 } 1700 addImg(); 1701 }); 1702 } 1703 else { 1704 selectedEntity = null; 1705 for (const k of tileList) { 1706 const key = k.key; 1707 function addImg() { 1708 if (inp_mode_entity.checked) 1709 return; 1710 const img = tileImages[key]; 1711 if (img) { 1712 img.title = key; 1713 div_palette.appendChild(img); 1714 img.addEventListener("click", () => { 1715 if (k.group) { 1716 pen.openGroup(k); 1717 } 1718 else { 1719 pen.setPen(key); 1720 } 1721 }); 1722 } 1723 else 1724 setTimeout(addImg, 100); 1725 } 1726 addImg(); 1727 } 1728 } 1729} 1730function endEntityMove() { 1731 function move(entity) { 1732 if (!entity_moving) 1733 return; 1734 entity.x += entity_moving.dx / TileSize; 1735 entity.y += entity_moving.dy / TileSize; 1736 entity.x = Math.min(Math.max(entity.x, 0), ViewWidth - entity.getWidth()); 1737 entity.y = Math.min(Math.max(entity.y, 0), ViewHeight - entity.getHeight()); 1738 if (shiftKey) 1739 entity.snapToPixels(); 1740 else 1741 entity.center(); 1742 } 1743 if (entity_moving) { 1744 move(entity_moving.entity); 1745 for (const entity of selectedEntities) { 1746 if (entity == entity_moving.entity) 1747 continue; 1748 move(entity); 1749 } 1750 entity_moving = null; 1751 } 1752} 1753function endDecorMove() { 1754 function move(decor) { 1755 if (!decor_moving) 1756 return; 1757 decor.x += decor_moving.dx / TileSize; 1758 decor.y += decor_moving.dy / TileSize; 1759 decor.x = Math.min(Math.max(decor.x, 0), ViewWidth - decor.getWidth()); 1760 decor.y = Math.min(Math.max(decor.y, 0), ViewHeight - decor.getHeight()); 1761 if (shiftKey) 1762 decor.snapToPixels(); 1763 else 1764 decor.center(); 1765 } 1766 if (decor_moving) { 1767 move(decor_moving.decor); 1768 for (const decor of selectedDecors) { 1769 if (decor == decor_moving.decor) 1770 continue; 1771 move(decor); 1772 } 1773 entity_moving = null; 1774 } 1775} 1776async function askForSure(action) { 1777 let popup = new Popup(); 1778 popup.focusOn = "cancel"; 1779 popup.content.appendChild(Div([], [], `ÐÑ ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ${action}?`)); 1780 let r = await popup.openAsync(); 1781 if (!r) 1782 return false;
1783 popup = new Popup(); 1784 popup.focusOn = "cancel"; 1785 popup.content.appendChild(Div([], [], `ÐÑ ÑоÑно ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ${action}?`)); 1786 popup.reverse = true; 1787 r = await popup.openAsync(); 1788 if (!r) 1789 return false; 1790 popup = new Popup(); 1791 popup.focusOn = "cancel"; 1792 popup.content.appendChild(Div([], [], `ÐÑ ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ${action}? ÐÑе неÑÐ¾Ñ ÑанÑннÑе даннÑе бÑдÑÑ Ð¿Ð¾ÑеÑÑнÑ!`)); 1793 r = await popup.openAsync(); 1794 if (!r) 1795 return false; 1796 return true; 1797} 1798async function deleteSelected(askForSure = true) { 1799 if (selectedDecor || selectedDecors.length > 0) { 1800 function del(decor) { 1801 const { view, i } = world.findView(decor); 1802 if (!view || i < 0) 1803 return; 1804 view.decor.splice(i, 1); 1805 } 1806 if (selectedDecor && selectedDecors.length == 0) 1807 del(selectedDecor); 1808 selectedDecor = null; 1809 for (const decor of selectedDecors) { 1810 del(decor); 1811 } 1812 selectedDecors = []; 1813 } 1814 else if (selectedEntity || selectedEntities.length > 0) { 1815 function del(entity) { 1816 const { view, i } = world.findView(entity); 1817 if (!view || i < 0) 1818 return; 1819 view.entity.splice(i, 1); 1820 } 1821 if (askForSure) { 1822 if (selectedEntities.length > 0) { 1823 let popup = new Popup(); 1824 popup.focusOn = "cancel"; 1825 const count = selectedEntities.length; 1826 popup.content.appendChild(Div([], [], `ÐÑ ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ÑдалиÑÑ ÑÑÐ¸Ñ ÑÑÑноÑÑей (${count}ÑÑ)?`)); 1827 let r = await popup.openAsync(); 1828 if (!r) 1829 return; 1830 } 1831 else { 1832 let popup = new Popup(); 1833 popup.focusOn = "cancel"; 1834 popup.content.appendChild(Div([], [], "ÐÑ ÑвеÑенÑ, ÑÑо Ñ Ð¾ÑиÑе ÑдалиÑÑ ÑÑÑноÑÑÑ?")); 1835 let r = await popup.openAsync(); 1836 if (!r) 1837 return; 1838 } 1839 } 1840 if (selectedEntity && selectedEntities.length == 0) 1841 del(selectedEntity); 1842 selectedEntity = null; 1843 for (const entity of selectedEntities) { 1844 del(entity); 1845 } 1846 selectedEntities = []; 1847 } 1848} 1849function copySelected() { 1850 if (inp_mode_decor.checked) { 1851 copied.decors = []; 1852 if (selectedDecor) 1853 copied.decors.push(selectedDecor); 1854 for (const decor of selectedDecors) { 1855 copied.decors.push(decor); 1856 } 1857 } 1858 if (inp_mode_entity.checked) { 1859 copied.entities = []; 1860 if (selectedEntity) 1861 copied.entities.push(selectedEntity); 1862 for (const entity of selectedEntities) { 1863 copied.entities.push(entity); 1864 } 1865 } 1866} 1867function cutSelected() { 1868 copySelected(); 1869 deleteSelected(); 1870} 1871function pasteSelected() { 1872 const { view } = world.getView(mousePosCanvas.x - camera_x, mousePosCanvas.y - camera_y); 1873 if (!view) 1874 return; 1875 if (inp_mode_decor.checked) { 1876 selectedDecor = null; 1877 selectedDecors = []; 1878 for (const decor of copied.decors) { 1879 const copy = Decor.fromData(decor.getData()); 1880 if (copy) 1881 selectedDecors.push(copy); 1882 } 1883 view.decor = selectedDecors.concat(view.decor); 1884 } 1885 if (inp_mode_entity.checked) { 1886 selectedEntity = null; 1887 selectedEntities = []; 1888 for (const entity of copied.entities) { 1889 const copy = Entity.fromData(entity.getData()); 1890 if (copy) { 1891 // view.entity.push(copy); 1892 selectedEntities.push(copy); 1893 } 1894 } 1895 view.entity = selectedEntities.concat(view.entity); 1896 } 1897} 1898loadImages(); 1899setPalete(); 1900World.loadFromLocalStorage(); 1901loop(); 1902setInterval(() => world.saveToLocalStarage(), 1000); 1903// btn_new.click() // Temp 1904// world.map[0][0] = new View(); // Temp 1905// penEntity = Entity_Crab; // Temp 1906// world.map[0][0]?.setEntity(100, 100); // Temp 1907// inp_mode_entity.checked = !inp_mode_entity.checked; // Temp 1908// setPalete(); // Temp 1909function loop() { 1910 const rect = div_viewport.getBoundingClientRect(); 1911 canvas.width = rect.width; 1912 canvas.height = rect.height; 1913 // ctx.clearRect(0, 0, canvas.width, canvas.height); 1914 ctx.fillStyle = "hsl(195deg 53% 70%)"; 1915 ctx.fillRect(0, 0, canvas.width, canvas.height); 1916 ctx.imageSmoothingEnabled = false;
1917 ctx.save(); 1918 ctx.translate(camera_x, camera_y); 1919 world.draw(); 1920 ctx.restore(); 1921 if (selection) { 1922 ctx.save(); 1923 ctx.strokeStyle = "orange"; 1924 ctx.lineWidth = 2; 1925 ctx.strokeRect(selection.x, selection.y, mousePosCanvas.x - selection.x, mousePosCanvas.y - selection.y); 1926 ctx.restore(); 1927 } 1928 ctx.save(); 1929 ctx.strokeStyle = "blue"; 1930 ctx.lineWidth = 2; 1931 const partX = -camera_x / (world.width * ViewWidth * TileSize - canvas.width); 1932 const partY = -camera_y / (world.height * ViewHeight * TileSize - canvas.height); 1933 ctx.beginPath(); 1934 ctx.moveTo(0, canvas.height - 2); 1935 ctx.lineTo(canvas.width * partX, canvas.height - 2); 1936 ctx.moveTo(canvas.width - 2, 0); 1937 ctx.lineTo(canvas.width - 2, canvas.height * partY); 1938 ctx.stroke(); 1939 ctx.restore(); 1940 minimap.draw(); 1941 requestAnimationFrame(loop); 1942} 1943console.log("saveScreenImg(x = 0, y = 0, w?: number)"); 1944console.log('saveWorldImg(w?: number, back: string | null = "lightblue")'); 1945function saveScreenImg(x = 0, y = 0, w) { 1946 let tileSize = TileSize; 1947 if (w == undefined) 1948 w = 1920; 1949 tileSize = Math.floor(w / ViewWidth); 1950 w = tileSize * ViewWidth; 1951 let h = tileSize * ViewHeight; 1952 const canvas = document.createElement("canvas"); 1953 document.body.appendChild(canvas); 1954 canvas.width = w; 1955 canvas.height = h; 1956 const ctx = getCanvasContext(canvas); 1957 ctx.imageSmoothingEnabled = false; 1958 const view = world.map[y][x]; 1959 if (view == undefined) { 1960 console.log("view == undefined"); 1961 return; 1962 } 1963 for (let y = 0; y < ViewHeight; y++) { 1964 for (let x = 0; x < ViewWidth; x++) { 1965 let tile = view.tiles[y][x]; 1966 const img = tileImages[tile.id]; 1967 if (!img) 1968 continue; 1969 ctx.drawImage(img, x * tileSize, y * tileSize, tileSize, tileSize); 1970 } 1971 } 1972 const hide = inp_highlight_decor.checked; 1973 const decor = inp_mode_decor.checked; 1974 const _TileSize = TileSize; 1975 TileSize = tileSize; 1976 inp_highlight_decor.checked = false; 1977 for (const decor of view.decor) 1978 decor.draw(ctx); 1979 TileSize = _TileSize; 1980 inp_highlight_decor.checked = hide; 1981 inp_mode_decor.checked = decor; 1982 for (const entity of view.entity) { 1983 const obj = entity.constructor; 1984 if (obj.img == undefined) 1985 continue; 1986 ctx.drawImage(obj.img, 0, 0, obj.width, obj.height, (entity.x + obj.xImg) * tileSize, (entity.y + obj.yImg) * tileSize, obj.widthImg * tileSize, obj.heightImg * tileSize); 1987 } 1988 canvas.addEventListener("click", () => { 1989 document.body.removeChild(canvas); 1990 }); 1991 // const img = canvas.toDataURL("image/png"); 1992 // var image = new Image(); 1993 // image.src = img; 1994 // const W = window.open(""); 1995 // W?.document.write(image.outerHTML); 1996} 1997function saveWorldImg(w, back = "lightblue") { 1998 let tileSize = TileSize; 1999 let h = 0; 2000 if (w != undefined) { 2001 tileSize = Math.floor(w / ViewWidth); 2002 } 2003 w = tileSize * ViewWidth * world.width; 2004 h = tileSize * ViewHeight * world.height; 2005 const canvas = document.createElement("canvas"); 2006 document.body.appendChild(canvas); 2007 canvas.width = w; 2008 canvas.height = h; 2009 const ctx = getCanvasContext(canvas); 2010 ctx.imageSmoothingEnabled = false; 2011 if (back != null) { 2012 ctx.fillStyle = back; 2013 ctx.fillRect(0, 0, w, h); 2014 } 2015 for (let Y = 0; Y < world.height; Y++) { 2016 for (let X = 0; X < world.width; X++) { 2017 const view = world.map[Y][X]; 2018 if (view == undefined) 2019 continue; 2020 ctx.translate(X * ViewWidth * tileSize, Y * ViewHeight * tileSize); 2021 for (let y = 0; y < ViewHeight; y++) { 2022 for (let x = 0; x < ViewWidth; x++) { 2023 let tile = view.tiles[y][x]; 2024 const img = tileImages[tile.id]; 2025 if (!img) 2026 continue; 2027 ctx.drawImage(img, x * tileSize, y * tileSize, tileSize, tileSize); 2028 } 2029 } 2030 for (const entity of view.entity) { 2031 const obj = entity.constructor; 2032 if (obj.img == undefined) 2033 continue; 2034 ctx.drawImage(obj.img, 0, 0, obj.width, obj.height, (entity.x + obj.xImg) * tileSize,
2034(entity.y + obj.yImg) * tileSize, obj.widthImg * tileSize, obj.heightImg * tileSize); 2035 } 2036 const hide = inp_highlight_decor.checked; 2037 const decor = inp_mode_decor.checked; 2038 const _TileSize = TileSize; 2039 TileSize = tileSize; 2040 inp_highlight_decor.checked = false; 2041 for (const decor of view.decor) 2042 decor.draw(ctx); 2043 TileSize = _TileSize; 2044 inp_highlight_decor.checked = hide; 2045 inp_mode_decor.checked = decor; 2046 ctx.translate(-X * ViewWidth * tileSize, -Y * ViewHeight * tileSize); 2047 } 2048 } 2049 canvas.addEventListener("click", () => { 2050 document.body.removeChild(canvas); 2051 }); 2052 // const img = canvas.toDataURL("image/png"); 2053 // var image = new Image(); 2054 // image.src = img; 2055 // const W = window.open(""); 2056 // W?.document.write(image.outerHTML); 2057}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.