tilepaletteunit.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. unit tilepaletteunit;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, Forms, Controls, ComCtrls, ExtCtrls, fpjson,
  6. tilepalettedrawerunit, Graphics, StdCtrls, tilesetgraphicsunit,
  7. BGRABitmapTypes, BGRABitmap, tileselectionunit, palettehelper,
  8. LCLTranslator;
  9. type
  10. { TTilePaletteFrame }
  11. TTilePaletteType = (tptDrawing, tptEditing);
  12. TEditedTileSetting = (etsPassability, etsFourDir, etsSingleBit, etsTerrainTag);
  13. TTilePaletteFrame = class(TFrame)
  14. IdleTimerDrawPalette: TIdleTimer;
  15. UpdatingLabel: TLabel;
  16. TilePalettePaintBox: TPaintBox;
  17. TilePaletteScrollBox: TScrollBox;
  18. TilePaletteTabControl: TTabControl;
  19. procedure IdleTimerDrawPaletteTimer(Sender: TObject);
  20. procedure TilePalettePaintBoxMouseDown(Sender: TObject;
  21. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  22. procedure TilePalettePaintBoxMouseMove(Sender: TObject; Shift: TShiftState;
  23. X, Y: Integer);
  24. procedure TilePalettePaintBoxMouseUp(Sender: TObject; Button: TMouseButton;
  25. Shift: TShiftState; X, Y: Integer);
  26. procedure TilePalettePaintBoxPaint(Sender: TObject);
  27. procedure TilePaletteScrollBoxResize(Sender: TObject);
  28. procedure TilePaletteTabControlChange(Sender: TObject);
  29. private
  30. Drawer: TTilePaletteDrawer;
  31. PaletteBitmap: TBGRABitmap;
  32. ResizedBitmap: TBGRACustomBitmap;
  33. TilesetGraphics: TTilesetGraphics;
  34. PaletteBeingDrawn: Boolean;
  35. NeedsResize: Boolean;
  36. FType: TTilePaletteType;
  37. {Only used when FType = tptEditing}
  38. TilesetData: TJSONArray;
  39. EditedTileSetting: TEditedTileSetting;
  40. EditedBit: Integer;
  41. {Only used when FType = tptDrawing}
  42. SelTab: Integer;
  43. SelRect: TRect;
  44. SelStartX, SelStartY: Integer;
  45. SelStarted: Boolean;
  46. IsSingleLayer: Boolean;
  47. IsWorldType: Boolean;
  48. procedure UpdateSelection(X, Y: Integer);
  49. function IsSelectionValid: Boolean;
  50. function MakeSelection: TTileSelection;
  51. function GetFlags(TileId: Integer): Dword;
  52. public
  53. UpdateCallback: SelectionUpdateCallback;
  54. constructor Create(TheOwner: TComponent); override;
  55. procedure SetType(AType: TTilePaletteType);
  56. procedure UpdatePaletteImage;
  57. procedure UpdatePaletteSize;
  58. procedure DoPaletteUpdate;
  59. procedure UpdateFromTileset(ATilesetGraphics: TTilesetGraphics; Tileset: TJSONObject);
  60. procedure SelectMapLayer(LayerId: Integer);
  61. procedure SetEditedData(ATilesetData: TJSONArray; AEditedTileSetting: TEditedTileSetting; AEditedBit: Integer = 0);
  62. //procedure SetEditedSetting();
  63. destructor Destroy; override;
  64. end;
  65. implementation
  66. uses
  67. globals, math, Dialogs, constants, Types, autotilehelper;
  68. {$R *.lfm}
  69. constructor TTilePaletteFrame.Create(TheOwner: TComponent);
  70. begin
  71. inherited;
  72. Drawer := TTilePaletteDrawer.Create;
  73. end;
  74. procedure TTilePaletteFrame.SetType(AType: TTilePaletteType);
  75. begin
  76. FType := AType;
  77. end;
  78. procedure TTilePaletteFrame.UpdateFromTileset(ATilesetGraphics: TTilesetGraphics; Tileset: TJSONObject);
  79. var
  80. Mode: Integer;
  81. ModeJson: TJSONNumber;
  82. begin
  83. Mode := 0;
  84. if Tileset.Find('mode', ModeJson) then
  85. Mode := ModeJson.AsInteger;
  86. TilesetGraphics := ATilesetGraphics;
  87. SelTab := -1;
  88. IsWorldType := Mode = 0;
  89. UpdatePaletteImage;
  90. TilePalettePaintBox.Visible := False;
  91. end;
  92. procedure TTilePaletteFrame.UpdatePaletteImage;
  93. begin
  94. if PaletteBitmap <> nil then begin
  95. PaletteBitmap.Free;
  96. PaletteBitmap := nil;
  97. end;
  98. if TilesetGraphics <> nil then begin
  99. PaletteBeingDrawn := True;
  100. UpdatingLabel.Visible := True;
  101. TilePalettePaintBox.Visible := False;
  102. IdleTimerDrawPalette.Enabled := True;
  103. end
  104. else begin
  105. if ResizedBitmap <> nil then begin
  106. ResizedBitmap.Free;
  107. ResizedBitmap := nil;
  108. end;
  109. TilePalettePaintBox.Visible := False;
  110. end;
  111. Refresh;
  112. end;
  113. procedure TTilePaletteFrame.UpdatePaletteSize;
  114. var
  115. NewHeight: Integer;
  116. begin
  117. if PaletteBitmap = nil then begin
  118. TilePalettePaintBox.Visible := False;
  119. Exit;
  120. end;
  121. TilePalettePaintBox.Visible := True;
  122. NewHeight := PaletteBitmap.Height * (TilePaletteScrollBox.ClientWidth - 20)
  123. div PaletteBitmap.Width;
  124. //TODO: remove this hack with 20, add some normal way to get scrollbar width
  125. //or some way to resize that without going into an endless loop
  126. TilePalettePaintBox.ClientHeight := NewHeight;
  127. TilePaletteScrollBox.ClientWidth := TilePaletteScrollBox.ClientWidth - 20;
  128. if ResizedBitmap <> nil then
  129. ResizedBitmap.Free;
  130. ResizedBitmap := PaletteBitmap.Resample(
  131. TilePalettePaintBox.ClientWidth,
  132. TilePalettePaintBox.ClientHeight,
  133. rmSimpleStretch
  134. );
  135. end;
  136. function TTilePaletteFrame.GetFlags(TileId: Integer): Dword;
  137. begin
  138. if (TileId >= 0) and (TileId < TilesetData.Count) and (TilesetData[TileId].JSONType = jtNumber) then
  139. GetFlags := DWord(TilesetData.Integers[TileId])
  140. else
  141. GetFlags := GetDefaultTileFlags(TileId);
  142. end;
  143. procedure TTilePaletteFrame.TilePalettePaintBoxPaint(Sender: TObject);
  144. var
  145. OriginalTileWidth, OriginalTileHeight: Integer;
  146. ResizedTileWidth, ResizedTileHeight: Double;
  147. procedure DrawSelection;
  148. begin
  149. TilePalettePaintBox.Canvas.Pen.Width := 3;
  150. TilePalettePaintBox.Canvas.Pen.Color := clWhite;
  151. TilePalettePaintBox.Canvas.Brush.Style := bsClear;
  152. TilePalettePaintBox.Canvas.Rectangle(
  153. Math.floor(SelRect.Left * ResizedTileWidth) + 1,
  154. Math.floor(SelRect.Top * ResizedTileHeight) + 1,
  155. Math.ceil(SelRect.Right * ResizedTileWidth) -2,
  156. Math.ceil(SelRect.Bottom * ResizedTileHeight) -2
  157. );
  158. TilePalettePaintBox.Canvas.Pen.Width := 1;
  159. TilePalettePaintBox.Canvas.Pen.Color := clBlack;
  160. TilePalettePaintBox.Canvas.Rectangle(
  161. Math.floor(SelRect.Left * ResizedTileWidth) + 1,
  162. Math.floor(SelRect.Top * ResizedTileHeight) + 1,
  163. Math.ceil(SelRect.Right * ResizedTileWidth) - 2,
  164. Math.ceil(SelRect.Bottom * ResizedTileHeight) - 2
  165. );
  166. end;
  167. procedure DrawFlags;
  168. var
  169. X, Y: Integer;
  170. procedure TextOnTile(Text: String);
  171. var
  172. Extent: TSize;
  173. TextX, TextY: Integer;
  174. begin
  175. with TilePalettePaintBox.Canvas do begin
  176. Extent := TextExtent(Text);
  177. TextX := Ceil(X * ResizedTileWidth) + (Ceil(ResizedTileWidth) - Extent.cx) div 2;
  178. TextY := Ceil(Y * ResizedTileHeight) + + (Ceil(ResizedTileHeight) - Extent.cy) div 2;
  179. Font.Color := clBlack;
  180. TextOut(TextX -1, TextY -1, Text);
  181. TextOut(TextX -1, TextY + 1, Text);
  182. TextOut(TextX +1, TextY -1, Text);
  183. TextOut(TextX +1, TextY +1, Text);
  184. TextOut(TextX -1, TextY, Text);
  185. TextOut(TextX -1, TextY, Text);
  186. TextOut(TextX, TextY -1, Text);
  187. TextOut(TextX, TextY +1, Text);
  188. Font.Color := clWhite;
  189. TextOut(TextX, TextY, Text);
  190. end;
  191. end;
  192. function GetBit(TileId, BitNumber: Integer): Boolean;
  193. begin
  194. GetBit := ((GetFlags(TileId) shr BitNumber) and 1) > 0
  195. end;
  196. procedure DrawSingleBitFlag;
  197. var
  198. Bit: Boolean;
  199. Item: String;
  200. Tile: TPaletteTile;
  201. begin
  202. Tile := GetPaletteTile(X, Y, TilePaletteTabControl.TabIndex, TilesetGraphics);
  203. Bit := GetBit(Tile.MainTileId, EditedBit);
  204. if Bit then begin
  205. case EditedBit of
  206. 5: Item := 'H';
  207. 6: Item := '≈';
  208. 7: Item := '◆';
  209. 8: Item := 'W';
  210. 9..11: Item := '#';
  211. else
  212. Item := '+';
  213. end;
  214. end
  215. else
  216. Item := '.';
  217. TextOnTile(Item);
  218. end;
  219. procedure DrawTriangle(X1, Y1, X2, Y2, X3, Y3: Integer); inline;
  220. var
  221. Points: array [1..3] of TPoint;
  222. begin
  223. with TilePalettePaintBox.Canvas do begin
  224. Brush.Style := bsSolid;
  225. Pen.Style := psSolid;
  226. Brush.Color := clWhite;
  227. Pen.Color := clBlack;
  228. Points[1] := Point(X1, Y1);
  229. Points[2] := Point(X2, Y2);
  230. Points[3] := Point(X3, Y3);
  231. Polygon(@Points[1], 3, True);
  232. end;
  233. end;
  234. procedure DrawDot(DotX, DotY: Integer); inline;
  235. begin
  236. with TilePalettePaintBox.Canvas do begin
  237. Brush.Style := bsSolid;
  238. Pen.Style := psSolid;
  239. Brush.Color := clWhite;
  240. Pen.Color := clBlack;
  241. Ellipse(DotX -2, DotY -2, DotX +2, DotY + 2);
  242. end;
  243. end;
  244. procedure DrawPassability;
  245. var
  246. Star, FullBlock: Boolean;
  247. Tile: TPaletteTile;
  248. begin
  249. Tile := GetPaletteTile(X, Y, TilePaletteTabControl.TabIndex, TilesetGraphics);
  250. Star := GetBit(Tile.MainTileId, 4);
  251. FullBlock := IsFullBlock(GetFlags(Tile.MainTileId));
  252. if Star then
  253. TextOnTile('★')
  254. else if FullBlock then
  255. TextOnTile('x')
  256. else
  257. TextOnTile('o');
  258. end;
  259. procedure DrawTerrainTag;
  260. var
  261. Tag: Dword;
  262. Tile: TPaletteTile;
  263. begin
  264. Tile := GetPaletteTile(X, Y, TilePaletteTabControl.TabIndex, TilesetGraphics);
  265. Tag := ((GetFlags(Tile.MainTileId) shr 12) and 7);
  266. TextOnTile(IntToStr(Tag));
  267. end;
  268. procedure DrawFourDir;
  269. var
  270. Tile: TPaletteTile;
  271. TopBlock, LeftBlock, RightBlock, BottomBlock: Boolean;
  272. begin
  273. Tile := GetPaletteTile(X, Y, TilePaletteTabControl.TabIndex, TilesetGraphics);
  274. if IsAutotile(Tile.MainTileId) then
  275. Exit;
  276. BottomBlock := GetBit(Tile.MainTileId, 0);
  277. LeftBlock := GetBit(Tile.MainTileId, 1);
  278. RightBlock := GetBit(Tile.MainTileId, 2);
  279. TopBlock := GetBit(Tile.MainTileId, 3);
  280. if TopBlock then
  281. DrawDot(
  282. Round((0.5 + X) * ResizedTileWidth),
  283. Round((0.25 + Y) * ResizedTileHeight)
  284. )
  285. else
  286. DrawTriangle(
  287. Round((0.5 + X) * ResizedTileWidth),
  288. Ceil(Y * ResizedTileHeight),
  289. Floor((0.4 + X) * ResizedTileWidth),
  290. Ceil((0.4 + Y) * ResizedTileHeight),
  291. Ceil((0.6 + X) * ResizedTileWidth),
  292. Ceil((0.4 + Y) * ResizedTileHeight)
  293. );
  294. if LeftBlock then
  295. DrawDot(
  296. Round((0.25 + X) * ResizedTileWidth),
  297. Round((0.5 + Y) * ResizedTileHeight)
  298. )
  299. else
  300. DrawTriangle(
  301. Floor(X * ResizedTileWidth),
  302. Round((0.5 + Y) * ResizedTileHeight),
  303. Ceil((0.4 + X) * ResizedTileWidth),
  304. Ceil((0.6 + Y) * ResizedTileHeight),
  305. Ceil((0.4 + X) * ResizedTileWidth),
  306. Floor((0.4 + Y) * ResizedTileHeight)
  307. );
  308. if RightBlock then
  309. DrawDot(
  310. Round((0.75 + X) * ResizedTileWidth),
  311. Round((0.5 + Y) * ResizedTileHeight)
  312. )
  313. else
  314. DrawTriangle(
  315. Ceil((1 + X) * ResizedTileWidth),
  316. Round((0.5 + Y) * ResizedTileHeight),
  317. Floor((0.6 + X) * ResizedTileWidth),
  318. Ceil((0.6 + Y) * ResizedTileHeight),
  319. Floor((0.6 + X) * ResizedTileWidth),
  320. Floor((0.4 + Y) * ResizedTileHeight)
  321. );
  322. if BottomBlock then
  323. DrawDot(
  324. Round((0.5 + X) * ResizedTileWidth),
  325. Round((0.75 + Y) * ResizedTileHeight)
  326. )
  327. else
  328. DrawTriangle(
  329. Round((0.5 + X) * ResizedTileWidth),
  330. Ceil((Y + 1) * ResizedTileHeight),
  331. Floor((0.4 + X) * ResizedTileWidth),
  332. Ceil((0.6 + Y) * ResizedTileHeight),
  333. Ceil((0.6 + X) * ResizedTileWidth),
  334. Ceil((0.6 + Y) * ResizedTileHeight)
  335. );
  336. end;
  337. procedure DrawFlagsForCell;
  338. begin
  339. case EditedTileSetting of
  340. etsPassability: DrawPassability;
  341. etsFourDir: DrawFourDir;
  342. etsSingleBit: DrawSingleBitFlag;
  343. etsTerrainTag: DrawTerrainTag;
  344. end;
  345. end;
  346. var
  347. XEnd, YEnd: Integer;
  348. begin
  349. TilePalettePaintBox.Canvas.Brush.Style := bsClear;
  350. {TODO: edit it to include only visible parts of the scrollbox}
  351. XEnd := (PaletteBitmap.Width div OriginalTileWidth) - 1;
  352. YEnd := (PaletteBitmap.Height div OriginalTileHeight) -1;
  353. for X := 0 to XEnd do
  354. for Y := 0 to YEnd do
  355. DrawFlagsForCell;
  356. end;
  357. begin
  358. if (ResizedBitmap <> nil) and (PaletteBitmap <> nil) then begin
  359. ResizedBitmap.Draw(TilePalettePaintBox.Canvas, 0, 0, True);
  360. OriginalTileWidth := 48; //TODO: adapt for tilewidth <> 48
  361. OriginalTileHeight := 48; //TODO: adapt for tileheight <> 48
  362. ResizedTileWidth := OriginalTileWidth * ResizedBitmap.Width / PaletteBitmap.Width;
  363. ResizedTileHeight := OriginalTileHeight * ResizedBitmap.Height / PaletteBitmap.Height;
  364. if (SelTab = TilePaletteTabControl.TabIndex) and IsSelectionValid then
  365. DrawSelection;
  366. if FType = tptEditing then
  367. DrawFlags;
  368. end;
  369. end;
  370. procedure TTilePaletteFrame.DoPaletteUpdate;
  371. var
  372. TileType: Integer;
  373. begin
  374. if TilesetGraphics <> nil then begin
  375. TileType := TilePaletteTabControl.TabIndex;
  376. PaletteBitmap := Drawer.MakePaletteBitmap(TileType, TilesetGraphics, True);
  377. UpdatingLabel.Visible := False;
  378. TilePalettePaintBox.Visible := True;
  379. PaletteBeingDrawn := False;
  380. IdleTimerDrawPalette.Enabled := False;
  381. UpdatePaletteSize;
  382. end;
  383. end;
  384. procedure TTilePaletteFrame.IdleTimerDrawPaletteTimer(Sender: TObject);
  385. begin
  386. if PaletteBeingDrawn then
  387. DoPaletteUpdate;
  388. if NeedsResize then begin
  389. UpdatePaletteSize;
  390. NeedsResize := False;
  391. end;
  392. end;
  393. procedure TTilePaletteFrame.TilePalettePaintBoxMouseDown(Sender: TObject;
  394. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  395. begin
  396. if FType = tptDrawing then begin
  397. SelStartX := X;
  398. SelStartY := Y;
  399. SelStarted := True;
  400. end;
  401. end;
  402. procedure TTilePaletteFrame.TilePalettePaintBoxMouseMove(Sender: TObject;
  403. Shift: TShiftState; X, Y: Integer);
  404. begin
  405. if SelStarted then begin
  406. UpdateSelection(X, Y);
  407. Refresh
  408. end;
  409. end;
  410. procedure TTilePaletteFrame.TilePalettePaintBoxMouseUp(Sender: TObject;
  411. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  412. procedure EditSelectedTile;
  413. var
  414. Tile: TPaletteTile;
  415. ResizedTileWidth, ResizedTileHeight: Double;
  416. TileX, TileY: Integer;
  417. Mask: Dword;
  418. procedure ToggleBit(BitNumber: Integer);
  419. var
  420. Bit, NewBit: Boolean;
  421. I: Integer;
  422. begin
  423. Bit := ((DWord(TilesetData.Integers[Tile.MainTileId]) shr BitNumber) and 1) > 0;
  424. NewBit := not Bit;
  425. Mask := 1 shl BitNumber;
  426. { TODO: handle nulls here }
  427. for I := Tile.StartTileId to Tile.EndTileId do
  428. if NewBit then
  429. TilesetData.Integers[I] := DWord(TilesetData.Integers[I]) or Mask
  430. else
  431. TilesetData.Integers[I] := DWord(TilesetData.Integers[I]) and not Mask
  432. end;
  433. procedure ToggleDirection;
  434. var
  435. PosX, PosY: Double;
  436. UnderLRDiagonal, UnderRLDiagonal: Boolean;
  437. {
  438. ^ / ^\
  439. | / | \
  440. | / | \
  441. |/ | \
  442. +-------> +----->
  443. LR diagonal RL diagonal
  444. \ over RL /
  445. \over LR/
  446. \ Top/
  447. \ /
  448. underRL\ / under LR
  449. Left X Right
  450. over LR/ \ over RL
  451. / \
  452. / \
  453. / Bottom\
  454. / underLR \
  455. / under RL \
  456. }
  457. begin
  458. if IsAutotile(Tile.MainTileId) then
  459. Exit;
  460. PosX := X / ResizedTileWidth - TileX;
  461. PosY := Y / ResizedTileHeight - TileY;
  462. UnderLRDiagonal := PosX < PosY;
  463. UnderRLDiagonal := (1.0 - PosX) < PosY;
  464. if UnderLRDiagonal and UnderRLDiagonal then
  465. ToggleBit(0) //bottom
  466. else if not UnderLRDiagonal and UnderRLDiagonal then
  467. ToggleBit(2) //right
  468. else if UnderLRDiagonal and not UnderRLDiagonal then
  469. ToggleBit(1) //left
  470. else
  471. ToggleBit(3) //top
  472. end;
  473. procedure ChangeTerrainTag;
  474. var
  475. Tag: Integer;
  476. RemoveMask, AddMask: Dword;
  477. I: Integer;
  478. begin
  479. RemoveMask := not (7 shl 12);
  480. { TODO: handle nulls here }
  481. Tag := ((DWord(TilesetData.Integers[Tile.MainTileId]) shr 12) and 7);
  482. if Button = mbRight then
  483. Dec(Tag)
  484. else if Button = mbLeft then
  485. Inc(Tag);
  486. if Tag < 0 then
  487. Tag := Tag + 7;
  488. if Tag > 7 then
  489. Tag := Tag - 7;
  490. AddMask := Tag shl 12;
  491. for I := Tile.StartTileId to Tile.EndTileId do
  492. TilesetData.Integers[I] := (DWord(TilesetData.Integers[I]) and RemoveMask)
  493. or AddMask;
  494. end;
  495. procedure TogglePassability;
  496. var
  497. Star, FullBlock: Boolean;
  498. TypeIndex: Integer; //1 is o, 2 is x, 3 is *
  499. RemoveMask, AddMask: DWord;
  500. I: Integer;
  501. begin
  502. Star := ((DWord(TilesetData.Integers[Tile.MainTileId]) shr 4) and 1) > 0;
  503. FullBlock := IsFullBlock(DWord(TilesetData.Integers[Tile.MainTileId]));
  504. if IsAutotile(Tile.MainTileId) then begin
  505. if FullBlock then
  506. AddMask := 0
  507. else
  508. AddMask := 15;
  509. end else begin
  510. if Star then
  511. TypeIndex := 3
  512. else if FullBlock then
  513. TypeIndex := 2
  514. else
  515. TypeIndex := 1;
  516. if Button = mbLeft then
  517. Inc(TypeIndex)
  518. else if Button = mbRight then
  519. Dec(TypeIndex);
  520. if TypeIndex = 0 then
  521. TypeIndex := 3;
  522. if TypeIndex = 4 then
  523. TypeIndex := 1;
  524. if TypeIndex = 1 then begin
  525. AddMask := 0;
  526. end else if TypeIndex = 2 then begin
  527. AddMask := 15;
  528. end else begin
  529. AddMask := 16;
  530. end;
  531. end;
  532. //left: ox*
  533. RemoveMask := not 31;
  534. { TODO: handle nulls here }
  535. for I := Tile.StartTileId to Tile.EndTileId do
  536. TilesetData.Integers[I] := (DWord(TilesetData.Integers[I]) and RemoveMask)
  537. or AddMask;
  538. end;
  539. begin
  540. ResizedTileWidth := 48 * ResizedBitmap.Width / PaletteBitmap.Width; //TODO: adapt for tilewidth <> 48
  541. ResizedTileHeight := 48 * ResizedBitmap.Height / PaletteBitmap.Height; //TODO: adapt for tilewidth <> 48
  542. TileX := Floor(X / ResizedTileWidth);
  543. TileY := Floor(Y / ResizedTileHeight);
  544. Tile := GetPaletteTile(TileX, TileY, TilePaletteTabControl.TabIndex, TilesetGraphics);
  545. while Tile.EndTileId >= TilesetData.Count do
  546. TilesetData.Add(0); { TODO: verify this is the correct default value }
  547. case EditedTileSetting of
  548. etsPassability: TogglePassability;
  549. etsFourDir: ToggleDirection;
  550. etsSingleBit: ToggleBit(EditedBit);
  551. etsTerrainTag: ChangeTerrainTag;
  552. end;
  553. end;
  554. begin
  555. if FType = tptDrawing then begin
  556. UpdateSelection(X, Y);
  557. SelStarted := False;
  558. if UpdateCallback <> nil then
  559. UpdateCallback(MakeSelection);
  560. end else if FType = tptEditing then
  561. EditSelectedTile;
  562. Refresh;
  563. end;
  564. procedure TTilePaletteFrame.UpdateSelection(X, Y: Integer);
  565. var
  566. ResizedTileWidth, ResizedTileHeight: Double;
  567. begin
  568. if (ResizedBitmap = nil) or (PaletteBitmap = nil) then
  569. Exit;
  570. ResizedTileWidth := 48 * ResizedBitmap.Width / PaletteBitmap.Width; //TODO: adapt for tilewidth <> 48
  571. ResizedTileHeight := 48 * ResizedBitmap.Height / PaletteBitmap.Height; //TODO: adapt for tilewidth <> 48
  572. SelTab := TilePaletteTabControl.TabIndex;
  573. if SelStartX < X then begin
  574. SelRect.Left := Floor(SelStartX / ResizedTileWidth);
  575. SelRect.Right := Min(
  576. Ceil(X / ResizedTileWidth),
  577. PaletteBitmap.Width div 48);
  578. end
  579. else begin
  580. SelRect.Left := Max(0, Floor(X / ResizedTileWidth));
  581. SelRect.Right := Ceil(SelStartX / ResizedTileWidth);
  582. end;
  583. if SelStartY < Y then begin
  584. SelRect.Top := Floor(SelStartY / ResizedTileHeight);
  585. SelRect.Bottom := Min(
  586. Ceil(Y / ResizedTileHeight),
  587. PaletteBitmap.Height div 48);
  588. end
  589. else begin
  590. SelRect.Top := Max(0, Floor(Y / ResizedTileHeight));
  591. SelRect.Bottom := Ceil(SelStartY / ResizedTileHeight);
  592. end;
  593. if SelRect.Right = SelRect.Left then
  594. Inc(SelRect.Right);
  595. if SelRect.Bottom = SelRect.Top then
  596. Inc(SelRect.Bottom);
  597. end;
  598. function TTilePaletteFrame.MakeSelection: TTileSelection;
  599. var
  600. Sel: TTileSelection;
  601. X, Y: Integer;
  602. ATileSelectFn: ATileSelectionCallback;
  603. ATileStartY: Integer;
  604. begin
  605. if SelTab < 0 then begin
  606. MakeSelection := nil;
  607. Exit;
  608. end;
  609. Sel := TTileSelection.Create;
  610. Sel.Init(
  611. SelRect.Right - SelRect.Left,
  612. SelRect.Bottom - SelRect.Top,
  613. IsSingleLayer
  614. );
  615. for Y := SelRect.Top to SelRect.Bottom do begin
  616. for X := SelRect.Left to SelRect.Right do begin
  617. if SelTab = 0 then begin
  618. ATileSelectFn := Sel.GetATileSelectFn(Y, TilesetGraphics, ATileStartY);
  619. ATileSelectFn(X - SelRect.Left, Y - SelRect.Top, ATileStartY, Y * 8 + X, IsWorldType)
  620. end
  621. else if SelTab > 0 then
  622. Sel.SelectUpperTile(X-SelRect.Left, Y-SelRect.Top, SelTab, Y * 8 + X);
  623. end;
  624. end;
  625. MakeSelection := Sel;
  626. end;
  627. function TTilePaletteFrame.IsSelectionValid: Boolean;
  628. begin
  629. IsSelectionValid := (SelTab > -1) and (SelRect.Left <> SelRect.Right)
  630. and (SelRect.Top <> SelRect.Bottom);
  631. end;
  632. procedure TTilePaletteFrame.TilePaletteScrollBoxResize(Sender: TObject);
  633. begin
  634. NeedsResize := True;
  635. IdleTimerDrawPalette.Enabled := True;
  636. end;
  637. procedure TTilePaletteFrame.TilePaletteTabControlChange(Sender: TObject);
  638. begin
  639. UpdatePaletteImage
  640. end;
  641. procedure TTilePaletteFrame.SelectMapLayer(LayerId: Integer);
  642. var
  643. NeedsSelectionReset: Boolean;
  644. begin
  645. NeedsSelectionReset := (LayerId <> LAYER_ALL) <> IsSingleLayer;
  646. IsSingleLayer := (LayerId <> LAYER_ALL);
  647. if NeedsSelectionReset then begin
  648. SelTab := -1;
  649. SelRect := Rect(0, 0, 0, 0);
  650. if UpdateCallback <> nil then
  651. UpdateCallback(MakeSelection);
  652. Refresh;
  653. end;
  654. end;
  655. procedure TTilePaletteFrame.SetEditedData(ATilesetData: TJSONArray;
  656. AEditedTileSetting: TEditedTileSetting; AEditedBit: Integer);
  657. begin
  658. TilesetData := ATilesetData;
  659. EditedTileSetting := AEditedTileSetting;
  660. EditedBit := AEditedBit;
  661. TilePalettePaintBox.Refresh;
  662. end;
  663. destructor TTilePaletteFrame.Destroy;
  664. begin
  665. if Drawer <> nil then
  666. Drawer.Free;
  667. if PaletteBitmap <> nil then
  668. PaletteBitmap.Free;
  669. inherited;
  670. end;
  671. end.