123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943 |
- unit database_troops;
- {$mode objfpc}{$H+}
- {$modeswitch nestedprocvars}
- interface
- uses
- Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
- Buttons, database_base, itemselectorframeunit, battleeventframeunit, fpjson,
- BGRABitmap, BGRABitmapTypes, fgl;
- type
- TSelectedTroopMemberData = record
- IsActive: Boolean;
- MemberId: Integer;
- ShiftX: Integer;
- ShiftY: Integer;
- end;
- TBitBtnCallback = procedure (Btn: TBitBtn) is nested;
- TMemberList = specialize TFPGList<TJSONObject>;
- { TDatabaseTroopsFrame }
- TDatabaseTroopsFrame = class(TDatabaseBaseFrame)
- AddEnemyBitBtn: TBitBtn;
- TroopBattleEventFrame: TBattleEventFrame;
- EnemyHiddenCheckBox: TCheckBox;
- EnemiesListBoxPanel: TPanel;
- EnemiesListBox: TListBox;
- BattleEventGroupBox: TGroupBox;
- RemoveEnemyBitBtn: TBitBtn;
- AutoNameButton: TButton;
- EnemiesFlowPanel: TFlowPanel;
- EnemiesPaintBox: TPaintBox;
- EnemiesPaintBoxPanel: TPanel;
- EnemyButtons: TPanel;
- ClearEnemiesBitBtn: TBitBtn;
- AlignEnemiesBitBtn: TBitBtn;
- TroopImageList: TImageList;
- SetBackgroundButton: TButton;
- TestBattleButton: TButton;
- SetBackgroundPanel: TPanel;
- TestBattlePanel: TPanel;
- ContentScrollBox: TScrollBox;
- NameEdit: TEdit;
- NameLabel: TLabel;
- MainGenralSettingsFlowPanel: TFlowPanel;
- GeneralSettingsGroupBox: TGroupBox;
- NamePanel: TPanel;
- AutoNamePanel: TPanel;
- SelectorContentSplitter: TSplitter;
- TroopSelectorFrame: TItemSelectorFrame;
- procedure AddEnemyBitBtnClick(Sender: TObject);
- procedure AlignEnemiesBitBtnClick(Sender: TObject);
- procedure AutoNameButtonClick(Sender: TObject);
- procedure ClearEnemiesBitBtnClick(Sender: TObject);
- procedure EnemiesListBoxDblClick(Sender: TObject);
- procedure EnemiesPaintBoxMouseDown(Sender: TObject; Button: TMouseButton;
- Shift: TShiftState; X, Y: Integer);
- procedure EnemiesPaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
- Y: Integer);
- procedure EnemiesPaintBoxMouseUp(Sender: TObject; Button: TMouseButton;
- Shift: TShiftState; X, Y: Integer);
- procedure EnemiesPaintBoxPaint(Sender: TObject);
- procedure EnemyHiddenCheckBoxChange(Sender: TObject);
- procedure Init; override;
- procedure NameEditChange(Sender: TObject);
- procedure PagesTabControlChange(Sender: TObject);
- procedure RemoveEnemyBitBtnClick(Sender: TObject);
- procedure SetBackgroundButtonClick(Sender: TObject);
- function GetSortedMembers(Troop: TJSONObject): TMemberList;
- private
- IsLoading: Boolean;
- IsUpdatingHiddenCheckbox: Boolean;
- SelectedId: Integer;
- SelectedMember: TSelectedTroopMemberData;
- EnemyIsMoved: Boolean;
- EnemyImages: array of TBGRACustomBitmap;
- Background: TBGRACustomBitmap;
- EnemyImageBuffer: TBGRABitmap;
- procedure CacheEnemyImage(EnemyId: Integer);
- procedure CacheEnemyImages(Troop: TJSONObject);
- procedure UpdateBackgroud;
- function GetSelectedTroop: TJSONObject;
- function GetSelectedMember: TJSONObject;
- procedure LoadGeneralSettings(Troop: TJSONObject);
- procedure LoadBattleEvent(Troop: TJSONObject);
- function GetEnemyIdByCoordinates(X, Y: Integer): TSelectedTroopMemberData;
- procedure MoveSelectedEnemyByMouse(X, Y: Integer);
- procedure FillEnemiesListBox;
- procedure UpdateBtnsEnabled;
- procedure UpdateHiddenCheckbox;
- function IsAlignedByDefaultAlgorithm(Troop: TJSONObject): Boolean;
- procedure Align(Troop: TJSONObject);
- function GetTotalMembersWidth(Members: TJSONArray): Integer;
- function GetMemberWidth(Member: TJSONObject): Integer;
- procedure SetSameWidthForAllButtons;
- function GetScaleFactor: Double; inline;
- public
- procedure LoadTroopData(Id: Integer);
- destructor Destroy; override;
- end;
- const
- DEFAULT_Y_POSITION = 436;
- resourcestring
- TroopsHeader = 'Troops';
- implementation
- uses
- globals, gameproject, constants, doublebackgroundselection;
- {$R *.lfm}
- { TDatabaseTroopsFrame }
- procedure TDatabaseTroopsFrame.Init;
- begin
- IsLoading := True;
- TroopSelectorFrame.SetData(Db.Troops, @Db.ResizeTroops, @LoadTroopData);
- TroopSelectorFrame.Title := TroopsHeader;
- TroopSelectorFrame.JsonDataType := 'application/rpgmv-troops';
- TroopSelectorFrame.CreateElementCallback := @Db.CreateEmptyTroop;
- SetSameWidthForAllButtons;
- UpdateBackgroud;
- EnemyImageBuffer := TBGRABitmap.Create(EnemiesPaintBox.Width, EnemiesPaintBox.Height);
- EnemyIsMoved := False;
- SelectedMember.IsActive := False;
- TroopBattleEventFrame.SetDatabase(Db);
- SetLength(EnemyImages, Db.Enemies.Count);
- FillEnemiesListBox;
- if EnemiesListBox.Count > 1 then
- EnemiesListBox.ItemIndex := 0;
- LoadTroopData(1);
- end;
- procedure TDatabaseTroopsFrame.NameEditChange(Sender: TObject);
- var
- Troop: TJSONObject = nil;
- begin
- if not IsLoading then
- Troop := GetSelectedTroop;
- if Troop <> nil then
- Troop.Strings['name'] := NameEdit.Text;
- TroopSelectorFrame.RefreshSelectedName;
- end;
- procedure TDatabaseTroopsFrame.PagesTabControlChange(Sender: TObject);
- begin
- end;
- procedure TDatabaseTroopsFrame.RemoveEnemyBitBtnClick(Sender: TObject);
- var
- Troop: TJSONObject;
- MemberId: Integer;
- begin
- Troop := GetSelectedTroop;
- if SelectedMember.IsActive then begin
- MemberId := SelectedMember.MemberId;
- SelectedMember.IsActive := False;
- SelectedMember.MemberId := -1;
- Troop.Arrays['members'].Delete(MemberId);
- UpdateBtnsEnabled;
- UpdateHiddenCheckbox;
- EnemiesPaintBox.Refresh;
- end;
- end;
- procedure TDatabaseTroopsFrame.SetBackgroundButtonClick(Sender: TObject);
- var
- Bg1: String = '';
- Bg2: String = '';
- Bg1Json, Bg2Json: TJSONString;
- begin
- if (Db.System <> nil) and Db.System.Find('battleback1Name', Bg1Json) then
- Bg1 := Bg1Json.AsString;
- if (Db.System <> nil) and Db.System.Find('battleback2Name', Bg2Json) then
- Bg2 := Bg2Json.AsString;
- with DoubleBackgroundSelectionForm do
- if ShowBattleBgSelection(Bg1, Bg2) then begin
- Db.System.Strings['battleback1Name'] := SelectedBgs[1];
- Db.System.Strings['battleback2Name'] := SelectedBgs[2];
- UpdateBackgroud;
- EnemiesPaintBox.Refresh;
- end;
- end;
- function CompareMembersByY(const A, B: TJSONObject): Integer;
- var
- AX: Integer = 0;
- AY: Integer = 0;
- AXJson, AYJson: TJSONNumber;
- BX: Integer = 0;
- BY: Integer = 0;
- BXJson, BYJson: TJSONNumber;
- begin
- if A.Find('y', AYJson) then
- AY := AYJson.AsInteger;
- if B.Find('y', BYJson) then
- BY := BYJson.AsInteger;
- if AY = BY then begin
- if A.Find('x', AXJson) then
- AX := AXJson.AsInteger;
- if B.Find('x', BXJson) then
- BX := BXJson.AsInteger;
- if AX = BX then
- CompareMembersByY := 0
- else if AX < BX then
- CompareMembersByY := -1
- else
- CompareMembersByY := 1
- end else if AY < BY then
- CompareMembersByY := -1
- else
- CompareMembersByY := 1;
- end;
- function TDatabaseTroopsFrame.GetSortedMembers(Troop: TJSONObject): TMemberList;
- var
- Members: TJSONArray;
- List: TMemberList;
- I: Integer;
- begin
- List := TMemberList.Create;
- if Troop.Find('members', Members) then begin
- List := TMemberList.Create;
- List.Capacity := Members.Count;
- for I := 0 to Members.Count -1 do begin
- List.Add(Members.Objects[I]);
- end;
- List.Sort(@CompareMembersByY);
- end;
- GetSortedMembers := List;
- end;
- procedure TDatabaseTroopsFrame.EnemiesPaintBoxPaint(Sender: TObject);
- var
- Troop: TJSONObject;
- Members: TJSONArray;
- Member: TJSONObject;
- EnemyId, CenterX, BottomY, LeftX, TopY: Integer;
- EnemyIdJson, CenterXJson, BottomYJson: TJSONNumber;
- IsHidden: Boolean;
- IsHiddenJson: TJSONBoolean;
- EnemyImage: TBGRACustomBitmap;
- MaskedImage, MaskImage: TBGRABitmap;
- SortedMembers: TMemberList;
- I: Integer;
- procedure FillMemberData;
- begin
- EnemyId := 0;
- if (Member <> nil) and Member.Find('enemyId', EnemyIdJson) then
- EnemyId := EnemyIdJson.AsInteger;
- CenterX := 0;
- if (Member <> nil) and Member.Find('x', CenterXJson) then
- CenterX := CenterXJson.AsInteger;
- BottomY := 0;
- if (Member <> nil) and Member.Find('y', BottomYJson) then
- BottomY := BottomYJson.AsInteger;
- IsHidden := False;
- if (Member <> nil) and Member.Find('hidden', IsHiddenJson) then
- IsHidden := IsHiddenJson.AsBoolean;
- EnemyImage := nil;
- if (EnemyId > 0) and (EnemyId <= High(EnemyImages)) then
- EnemyImage := EnemyImages[EnemyId];
- if EnemyImage <> nil then begin
- LeftX := Round(CenterX * GetScaleFactor) - EnemyImage.Width div 2;
- TopY := Round(BottomY * GetScaleFactor) - EnemyImage.Height;
- end;
- end;
- begin
- if Background <> nil then
- { The shift accounts for position reserved for screen shake (the image width
- is larger than screen so that shaking wouldn't add black borders). }
- EnemyImageBuffer.CanvasBGRA.Draw(-30, 0, Background)
- else
- with EnemyImageBuffer.CanvasBGRA do begin
- Brush.Color := ColorToBGRA(clWhite);
- FillRect(0, 0, EnemiesPaintBox.Width, EnemiesPaintBox.Height);
- end;
- Troop := GetSelectedTroop;
- if (Troop <> nil) and Troop.Find('members', Members) then begin
- SortedMembers := GetSortedMembers(Troop);
- try
- for I := 0 to SortedMembers.Count -1 do begin
- Member := SortedMembers[I];
- FillMemberData;
- if EnemyImage <> nil then begin
- if IsHidden then begin
- MaskedImage := TBGRABitmap.Create(EnemyImage);
- MaskImage := TBGRABitmap.Create(EnemyImage.Width, EnemyImage.Height, BGRA(160, 160, 160));
- MaskedImage.ApplyMask(MaskImage);
- EnemyImageBuffer.CanvasBGRA.Draw(LeftX, TopY, MaskedImage);
- MaskImage.Free;
- MaskedImage.Free;
- end else
- EnemyImageBuffer.CanvasBGRA.Draw(LeftX, TopY, EnemyImage);
- end;
- end;
- finally
- SortedMembers.Free;
- end;
- for I := 0 to Members.Count -1 do begin
- Member := Members.Objects[I];
- FillMemberData;
- if SelectedMember.IsActive and (SelectedMember.MemberId = I) then
- with EnemyImageBuffer.CanvasBGRA do begin
- Pen.Color := clWhite;
- Brush.Style := bsClear;
- Rectangle(LeftX, TopY, LeftX + EnemyImage.Width, TopY + EnemyImage.Height);
- Pen.Color := clBlack;
- Rectangle(LeftX - 1, TopY - 1, LeftX + EnemyImage.Width + 1, TopY + EnemyImage.Height + 1);
- Pen.Color := clWhite;
- Rectangle(LeftX - 2, TopY - 2, LeftX + EnemyImage.Width + 2, TopY + EnemyImage.Height + 2);
- end;
- end;
- end;
- EnemyImageBuffer.Draw(EnemiesPaintBox.Canvas, 0, 0);
- end;
- procedure TDatabaseTroopsFrame.EnemyHiddenCheckBoxChange(Sender: TObject);
- var
- Member: TJSONObject;
- begin
- if IsUpdatingHiddenCheckbox then
- Exit;
- Member := GetSelectedMember;
- if Member <> nil then
- Member.Booleans['hidden'] := EnemyHiddenCheckBox.Checked;
- EnemiesPaintBox.Refresh
- end;
- procedure TDatabaseTroopsFrame.EnemiesPaintBoxMouseUp(Sender: TObject;
- Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- begin
- if SelectedMember.IsActive and EnemyIsMoved then begin
- MoveSelectedEnemyByMouse(X, Y);
- EnemiesPaintBox.Refresh;
- end;
- EnemyIsMoved := False;
- end;
- procedure TDatabaseTroopsFrame.EnemiesPaintBoxMouseDown(Sender: TObject;
- Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- begin
- SelectedMember := GetEnemyIdByCoordinates(X, Y);
- if SelectedMember.IsActive then
- EnemyIsMoved := True
- else
- EnemyIsMoved := False;
- UpdateBtnsEnabled;
- UpdateHiddenCheckbox;
- EnemiesPaintBoxPanel.SetFocus;
- EnemiesPaintBox.Refresh;
- end;
- procedure TDatabaseTroopsFrame.ClearEnemiesBitBtnClick(Sender: TObject);
- var
- Troop: TJSONObject;
- begin
- Troop := GetSelectedTroop;
- SelectedMember.IsActive := False;
- SelectedMember.MemberId := -1;
- Troop.Delete('members');
- Troop.Arrays['members'] := TJSONArray.Create;
- UpdateBtnsEnabled;
- UpdateHiddenCheckbox;
- EnemiesPaintBox.Refresh;
- end;
- procedure TDatabaseTroopsFrame.EnemiesListBoxDblClick(Sender: TObject);
- begin
- if AddEnemyBitBtn.Enabled then
- AddEnemyBitBtn.Click;
- end;
- procedure TDatabaseTroopsFrame.AddEnemyBitBtnClick(Sender: TObject);
- var
- Troop: TJSONObject;
- NewEnemyId: Integer;
- NewX, NewY: Integer;
- Member: TJSONObject;
- Img: TBGRACustomBitmap;
- DefaultAlignment: Boolean;
- begin
- Troop := GetSelectedTroop;
- if (Troop <> nil) then begin
- DefaultAlignment := IsAlignedByDefaultAlgorithm(Troop);
- NewX := SCREEN_WIDTH div 2;
- NewY := DEFAULT_Y_POSITION;
- NewEnemyId := EnemiesListBox.ItemIndex + 1;
- CacheEnemyImage(NewEnemyId);
- Img := EnemyImages[NewEnemyId];
- if Img <> nil then
- Dec(NewX, Round(Img.Width / GetScaleFactor / 2));
- Member := TJSONObject.Create([
- 'enemyId', NewEnemyId,
- 'x', NewX,
- 'y', NewY,
- 'hidden', False
- ]);
- Troop.Arrays['members'].Add(Member);
- if DefaultAlignment then
- Align(Troop);
- UpdateBtnsEnabled;
- EnemiesPaintBox.Refresh;
- end;
- end;
- procedure TDatabaseTroopsFrame.AlignEnemiesBitBtnClick(Sender: TObject);
- begin
- Align(GetSelectedTroop);
- EnemiesPaintBox.Refresh;
- end;
- procedure TDatabaseTroopsFrame.AutoNameButtonClick(Sender: TObject);
- var
- Troop: TJSONObject = nil;
- Members: TJSONArray = nil;
- Enemy: TJSONObject = nil;
- NameJson: TJSONString = nil;
- Name1: String = '';
- Name2: String = '';
- Count1: Integer = 0;
- Count2: Integer = 0;
- HasMore: Boolean = False;
- I: Integer;
- function NameWithCount(N: String; C: Integer): String;
- begin
- if C = 1 then
- NameWithCount := N
- else
- NameWithCount := '%s*%d'.Format([N, C]);
- end;
- var
- Res: String;
- begin
- Troop := GetSelectedTroop;
- if (Troop <> nil) and Troop.Find('members', Members) then begin
- for I := 0 to Members.Count -1 do begin
- Enemy := Db.Enemies.Objects[Members.Objects[I].Integers['enemyId']];
- if Enemy.Find('name', NameJson) then begin
- if (Name1 = NameJson.AsString) and (Count1 > 0) then
- Inc(Count1)
- else if (Name2 = NameJson.AsString) and (Count2 > 0) then
- Inc(Count2)
- else if Name1 = '' then begin
- Name1 := NameJson.AsString;
- Count1 := 1;
- end else if Name2 = '' then begin
- Name2 := NameJson.AsString;
- Count2 := 1;
- end else
- HasMore := True;
- end;
- end;
- end;
- Res := NameWithCount(Name1, Count1);
- if Count2 > 0 then
- Res := Res + ', ' + NameWithCount(Name2, Count2);
- if HasMore then
- Res := Res + '...';
- NameEdit.Text := Res;
- end;
- procedure TDatabaseTroopsFrame.EnemiesPaintBoxMouseMove(Sender: TObject;
- Shift: TShiftState; X, Y: Integer);
- begin
- if SelectedMember.IsActive and EnemyIsMoved then begin
- MoveSelectedEnemyByMouse(X, Y);
- EnemiesPaintBox.Refresh
- end;
- end;
- procedure TDatabaseTroopsFrame.CacheEnemyImage(EnemyId: Integer);
- var
- Enemy: TJSONObject;
- EnemyFilename: String = '';
- EnemyFilenameJson: TJSONString;
- Hue: Integer = 0;
- HueJson: TJSONNumber;
- BattleStyle: TBattleStyle;
- begin
- if (EnemyId >= 1) and (EnemyId < Db.Enemies.Count) then begin
- Enemy := Db.Enemies.Objects[EnemyId];
- if Enemy.Find('battlerName', EnemyFilenameJson) then
- EnemyFilename := EnemyFilenameJson.AsString;
- if Enemy.Find('battlerHue', HueJson) then
- Hue := HueJson.AsInteger;
- BattleStyle := bsFrontView;
- if Db.IsSvBattle then
- BattleStyle := bsSideView;
- EnemyImages[EnemyId] := Game.GetEnemyGraphicsBGRA(EnemyFilename, Hue,
- EnemiesPaintBox.Width / SCREEN_WIDTH, BattleStyle);
- end;
- end;
- procedure TDatabaseTroopsFrame.CacheEnemyImages(Troop: TJSONObject);
- var
- Members: TJSONArray;
- Member: TJSONObject;
- EnemyIdJson: TJSONNumber;
- I: Integer;
- begin
- if Troop.Find('members', Members) then
- for I := 0 to Members.Count - 1 do begin
- Member := Members.Objects[I];
- if Member.Find('enemyId', EnemyIdJson) then
- CacheEnemyImage(EnemyIdJson.AsInteger);
- end;
- end;
- procedure TDatabaseTroopsFrame.UpdateBackgroud;
- var
- Bg1: String = '';
- Bg2: String = '';
- Bg1Json, Bg2Json: TJSONString;
- begin
- if (Db.System <> nil) and Db.System.Find('battleback1Name', Bg1Json) then
- Bg1 := Bg1Json.AsString;
- if (Db.System <> nil) and Db.System.Find('battleback2Name', Bg2Json) then
- Bg2 := Bg2Json.AsString;
- Background := Game.GetBackgroundBGRA(btBattle, Bg1, Bg2, EnemiesPaintBox.Width / SCREEN_WIDTH);
- end;
- function TDatabaseTroopsFrame.GetSelectedTroop: TJSONObject;
- begin
- GetSelectedTroop := nil;
- if Db.Troops <> nil then
- if (SelectedId >= 1) and (SelectedId <= Db.Troops.Count -1) then
- GetSelectedTroop := Db.Troops.Objects[SelectedId];
- end;
- function TDatabaseTroopsFrame.GetSelectedMember: TJSONObject;
- var
- Troop: TJSONObject;
- Members: TJSONArray;
- begin
- GetSelectedMember := nil;
- if not SelectedMember.IsActive then
- Exit;
- if SelectedMember.MemberId < 0 then
- Exit;
- Troop := GetSelectedTroop;
- if (Troop <> nil) and Troop.Find('members', Members) then begin
- if SelectedMember.MemberId < Members.Count then
- GetSelectedMember := Members.Objects[SelectedMember.MemberId];
- end;
- end;
- procedure TDatabaseTroopsFrame.LoadGeneralSettings(Troop: TJSONObject);
- var
- TroopName: String;
- NameJson: TJSONString;
- begin
- if (Troop <> nil) and Troop.Find('name', NameJson) then
- TroopName := NameJson.AsString;
- NameEdit.Text := TroopName;
- end;
- procedure TDatabaseTroopsFrame.LoadBattleEvent(Troop: TJSONObject);
- var
- Pages: TJSONArray = nil;
- begin
- if (Troop <> nil) and Troop.Find('pages', Pages) then
- TroopBattleEventFrame.SetEditedPages(Pages, Troop);
- end;
- function TDatabaseTroopsFrame.GetEnemyIdByCoordinates(X, Y: Integer
- ): TSelectedTroopMemberData;
- var
- Troop: TJSONObject = nil;
- Members: TJSONArray = nil;
- Member: TJSONObject = nil;
- CenterX, BottomY, LeftX, TopY, EnemyId: Integer;
- EnemyImage: TBGRACustomBitmap;
- CenterXJson, BottomYJson, EnemyIdJson: TJSONNumber;
- I: Integer;
- XMatches, YMatches: Boolean;
- begin
- Troop := GetSelectedTroop;
- if (Troop <> nil) and Troop.Find('members', Members) then begin
- for I := 0 to Members.Count -1 do begin
- Member := Members.Objects[I];
- EnemyId := 0;
- if (Member <> nil) and Member.Find('enemyId', EnemyIdJson) then
- EnemyId := EnemyIdJson.AsInteger;
- CenterX := 0;
- if (Member <> nil) and Member.Find('x', CenterXJson) then
- CenterX := CenterXJson.AsInteger;
- BottomY := 0;
- if (Member <> nil) and Member.Find('y', BottomYJson) then
- BottomY := BottomYJson.AsInteger;
- EnemyImage := nil;
- if (EnemyId > 0) and (EnemyId <= High(EnemyImages)) then
- EnemyImage := EnemyImages[EnemyId];
- if EnemyImage <> nil then begin
- LeftX := Round(CenterX * GetScaleFactor) - EnemyImage.Width div 2;
- TopY := Round(BottomY * GetScaleFactor) - EnemyImage.Height;
- XMatches := (X >= LeftX) and (X <= LeftX + EnemyImage.Width);
- YMatches := (Y >= TopY) and (Y <= TopY + EnemyImage.Height);
- if XMatches and YMatches then begin
- GetEnemyIdByCoordinates.MemberId := I;
- GetEnemyIdByCoordinates.ShiftX := X - Round(CenterX * GetScaleFactor);
- GetEnemyIdByCoordinates.ShiftY := Y - Round(BottomY * GetScaleFactor);
- GetEnemyIdByCoordinates.IsActive := True;
- Exit
- end
- end
- end
- end;
- GetEnemyIdByCoordinates.MemberId := -1;
- GetEnemyIdByCoordinates.IsActive := False;
- end;
- procedure TDatabaseTroopsFrame.MoveSelectedEnemyByMouse(X, Y: Integer);
- var
- NewX, NewY: Integer;
- Troop, Member: TJSONObject;
- begin
- //TODO
- NewX := Round((X - SelectedMember.ShiftX) / GetScaleFactor);
- NewY := Round((Y - SelectedMember.ShiftY) / GetScaleFactor);
- Troop := GetSelectedTroop;
- Member := Troop.Arrays['members'].Objects[SelectedMember.MemberId];
- Member.Integers['x'] := NewX;
- Member.Integers['y'] := NewY;
- end;
- procedure TDatabaseTroopsFrame.FillEnemiesListBox;
- var
- I: Integer;
- begin
- with EnemiesListBox.Items do begin
- BeginUpdate;
- Clear;
- for I := 1 to Db.Enemies.Count -1 do begin
- Add(Db.FormatObjName(Db.Enemies.Objects[I], True));
- end;
- EndUpdate;
- end;
- end;
- procedure TDatabaseTroopsFrame.UpdateBtnsEnabled;
- var
- Troop: TJSONObject;
- Members: TJSONArray;
- CanAdd, CanDelete, CanClear, CanAlign: Boolean;
- begin
- CanAdd := False;
- CanDelete := SelectedMember.IsActive and (SelectedMember.MemberId >= 0);
- CanClear := False;
- CanAlign := False;
- Troop := GetSelectedTroop;
- if (Troop <> nil) and Troop.Find('members', Members) then begin
- CanAdd := Members.Count < 8;
- CanClear := Members.Count > 0;
- CanAlign := Members.Count > 0;
- end;
- AddEnemyBitBtn.Enabled := CanAdd;
- RemoveEnemyBitBtn.Enabled := CanDelete;
- ClearEnemiesBitBtn.Enabled := CanClear;
- AlignEnemiesBitBtn.Enabled := CanAlign;
- end;
- procedure TDatabaseTroopsFrame.UpdateHiddenCheckbox;
- var
- Member: TJSONObject;
- IsHidden: Boolean;
- IsHiddenJson: TJSONBoolean;
- begin
- IsUpdatingHiddenCheckbox := True;
- Member := GetSelectedMember;
- if (Member <> nil) and Member.Find('hidden', IsHiddenJson) then
- IsHidden := IsHiddenJson.AsBoolean;
- EnemyHiddenCheckBox.Checked := IsHidden;
- EnemyHiddenCheckBox.Enabled := Member <> nil;
- IsUpdatingHiddenCheckbox := False;
- end;
- function TDatabaseTroopsFrame.IsAlignedByDefaultAlgorithm(Troop: TJSONObject
- ): Boolean;
- var
- Members: TJSONArray;
- TotalWidth: Integer;
- I: Integer;
- CurrectXOffset: Integer = 0;
- EveryItemOffset: Integer = 0;
- XIsAligned, YIsAligned: Boolean;
- function RelativelyEquals(A, B: Integer): Boolean;
- var
- AllowedDifference: Integer;
- begin
- { The exact difference can be different because we use downscaled images
- for comparisons, and scale factor is different depending on the system
- DPI. So, we allow things to be a few pixels off.
- Also, RPG Maker MV uses a slightly different algorithm, but this lazy
- comparison at least recognises RMMV's default placement when all the
- enemies are of the same type. }
- AllowedDifference := Round(5 / GetScaleFactor);
- RelativelyEquals := Abs(A - B) <= AllowedDifference;
- end;
- begin
- Members := Troop.Arrays['members'];
- if Members.Count < 1 then begin
- IsAlignedByDefaultAlgorithm := True;
- Exit;
- end;
- YIsAligned := True;
- for I := 0 to Members.Count -1 do
- if Members.Objects[I].Integers['y'] <> DEFAULT_Y_POSITION then
- YIsAligned := False;
- TotalWidth := GetTotalMembersWidth(Members);
- if TotalWidth <= SCREEN_WIDTH then
- CurrectXOffset := (SCREEN_WIDTH - TotalWidth) div 2 + GetMemberWidth(Members.Objects[0]) div 2
- else begin
- EveryItemOffset := (TotalWidth - SCREEN_WIDTH + GetMemberWidth(Members.Objects[0]) div 2) div Members.Count;
- CurrectXOffset := GetMemberWidth(Members.Objects[0]) div 2;
- end;
- XIsAligned := True;
- for I := 0 to Members.Count -1 do begin
- if not RelativelyEquals(Members.Objects[I].Integers['x'], CurrectXOffset) then
- XIsAligned := False;
- Inc(CurrectXOffset, GetMemberWidth(Members.Objects[I]) - EveryItemOffset);
- end;
- IsAlignedByDefaultAlgorithm := XIsAligned and YIsAligned;
- end;
- function TDatabaseTroopsFrame.GetTotalMembersWidth(Members: TJSONArray): Integer;
- var
- I: Integer;
- ImgForWidth: TBGRACustomBitmap;
- Sum: Integer;
- begin
- Sum := 0;
- for I := 0 to Members.Count -1 do begin
- ImgForWidth := EnemyImages[Members.Objects[I].Integers['enemyId']];
- if ImgForWidth <> nil then
- Inc(Sum, ImgForWidth.Width);
- end;
- GetTotalMembersWidth := Round(Sum / GetScaleFactor);
- end;
- function TDatabaseTroopsFrame.GetMemberWidth(Member: TJSONObject): Integer;
- var
- Img: TBGRACustomBitmap;
- begin
- Img := EnemyImages[Member.Integers['enemyId']];
- if Img <> nil then
- GetMemberWidth := Round(Img.Width / GetScaleFactor)
- else
- GetMemberWidth := 0;
- end;
- procedure TDatabaseTroopsFrame.SetSameWidthForAllButtons;
- procedure DoForAllButtons(Callback: TBitBtnCallback);
- begin
- Callback(AddEnemyBitBtn);
- Callback(RemoveEnemyBitBtn);
- Callback(ClearEnemiesBitBtn);
- Callback(AlignEnemiesBitBtn);
- end;
- var
- MaxWidth: Integer = 0;
- procedure FindMaxWidth(Btn: TBitBtn);
- begin
- if Btn.Width > MaxWidth then
- MaxWidth := Btn.Width;
- end;
- procedure SetMaxWidth(Btn: TBitBtn);
- begin
- Btn.AutoSize := False;
- Btn.Width := MaxWidth;
- end;
- begin
- DoForAllButtons(@FindMaxWidth);
- DoForAllButtons(@SetMaxWidth);
- end;
- function TDatabaseTroopsFrame.GetScaleFactor: Double; inline;
- begin
- GetScaleFactor := EnemiesPaintBox.Width / SCREEN_WIDTH;
- end;
- procedure TDatabaseTroopsFrame.Align(Troop: TJSONObject);
- var
- Members: TJSONArray;
- TotalWidth: Integer;
- I: Integer;
- CurrectXOffset: Integer = 0;
- EveryItemOffset: Integer = 0;
- begin
- { TODO: fix alignment so that it works well with troops of different enemy
- types. (Maybe add a second row, too?) }
- Members := Troop.Arrays['members'];
- for I := 0 to Members.Count -1 do
- Members.Objects[I].Integers['y'] := DEFAULT_Y_POSITION;
- TotalWidth := GetTotalMembersWidth(Members);
- if TotalWidth <= SCREEN_WIDTH then
- CurrectXOffset := (SCREEN_WIDTH - TotalWidth) div 2 + GetMemberWidth(Members.Objects[0]) div 2
- else begin
- EveryItemOffset := (TotalWidth - SCREEN_WIDTH + GetMemberWidth(Members.Objects[0]) div 2) div Members.Count;
- CurrectXOffset := GetMemberWidth(Members.Objects[0]) div 2;
- end;
- for I := 0 to Members.Count -1 do begin
- Members.Objects[I].Integers['x'] := CurrectXOffset;
- Inc(CurrectXOffset, GetMemberWidth(Members.Objects[I]) - EveryItemOffset);
- end;
- end;
- procedure TDatabaseTroopsFrame.LoadTroopData(Id: Integer);
- var
- Troop: TJSONObject = nil;
- begin
- IsLoading := True;
- SelectedId := Id;
- Troop := GetSelectedTroop;
- CacheEnemyImages(Troop);
- LoadGeneralSettings(Troop);
- LoadBattleEvent(Troop);
- SelectedMember.IsActive := False;
- SelectedMember.MemberId := -1;
- EnemyIsMoved := False;
- UpdateBtnsEnabled;
- UpdateHiddenCheckbox;
- EnemiesPaintBox.Refresh;
- IsLoading := False;
- end;
- destructor TDatabaseTroopsFrame.Destroy;
- var
- I: Integer;
- begin
- for I := Low(EnemyImages) to High(EnemyImages) - 1 do
- if EnemyImages[I] <> nil then begin
- EnemyImages[I].Free;
- EnemyImages[I] := nil;
- end;
- if Background <> nil then
- Background.Free;
- if EnemyImageBuffer <> nil then
- EnemyImageBuffer.Free;
- inherited Destroy;
- end;
- end.
|