redmap/MapEditor.pas

1152 lines
39 KiB
Plaintext

unit MapEditor;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, Menus, XPMan, IniFiles, StdCtrls, Spin, ImgList, ComCtrls,
pngimage, Map, Tileset, About, Misc, BlockEditor;
type
TPointer = packed record
First: Byte;
Second: Byte;
end;
TPokemon = packed record
Level: Byte;
Number: Byte;
end;
TLandWildPkmn = packed record
Rate: Byte;
Pkmn: array [0..9] of TPokemon;
end;
TWaterWildPkmn = packed record
Rate: Byte;
Pkmn: array [0..9] of TPokemon;
end;
TMapEditorForm = class(TForm)
XPManifest1: TXPManifest;
MainMenu1: TMainMenu;
File1: TMenuItem;
LoadRom1: TMenuItem;
SaveRom1: TMenuItem;
SaveRomAs1: TMenuItem;
N1: TMenuItem;
LoadMap1: TMenuItem;
SaveMap1: TMenuItem;
N2: TMenuItem;
Exit1: TMenuItem;
Settings1: TMenuItem;
Grid1: TMenuItem;
N4: TMenuItem;
Palette1: TMenuItem;
Morning1: TMenuItem;
Day1: TMenuItem;
Night1: TMenuItem;
Darkness1: TMenuItem;
Indoor1: TMenuItem;
Tools: TMenuItem;
BlockEditor1: TMenuItem;
ReconstructMap1: TMenuItem;
ReBuildEvents1: TMenuItem;
N3: TMenuItem;
SaveMapPic1: TMenuItem;
SaveTilesetPic1: TMenuItem;
TryMap1: TMenuItem;
Help1: TMenuItem;
About1: TMenuItem;
SaveMapDialog: TSaveDialog;
OpenRomDialog: TOpenDialog;
SaveRomDialog: TSaveDialog;
OpenMapDialog: TOpenDialog;
PageControl1: TPageControl;
TabSheet1: TTabSheet;
MapScrollBox: TScrollBox;
MapBox: TPaintBox;
BlockPaletteScrollBox: TScrollBox;
BlockPalette: TPaintBox;
TabSheet2: TTabSheet;
NoEventsLabel: TLabel;
EventScrollBox: TScrollBox;
SignpostImage: TImage;
PeopleImage: TImage;
WarpImage: TImage;
EventBox: TPaintBox;
TriggerImage: TImage;
TabSheet3: TTabSheet;
NoWildPkmnLabel: TLabel;
LandGroup1: TGroupBox;
LandPkmnLevel: TSpinEdit;
LandPkmnBox: TListBox;
LandPkmnCombo: TComboBox;
StatusBar1: TStatusBar;
ImageList1: TImageList;
MapSelectPanel: TPanel;
MapSelectCombo: TComboBox;
Label5: TLabel;
shpHighlight: TShape;
TabSheet4: TTabSheet;
Edit1: TMenuItem;
Undo1: TMenuItem;
LandLevelBox: TListBox;
LandPkmnGroup: TGroupBox;
LandLevelGroup: TGroupBox;
LandReplaceBtn: TButton;
WaterGroup1: TGroupBox;
WaterPkmnBox: TListBox;
WaterLevelBox: TListBox;
WaterPkmnGroup: TGroupBox;
WaterPkmnCombo: TComboBox;
WaterLevelGroup: TGroupBox;
WaterPkmnLevel: TSpinEdit;
WaterReplaceBtn: TButton;
LandRateTrack: TTrackBar;
LandRateGroup: TGroupBox;
Less1: TLabel;
More1: TLabel;
LandPercent: TLabel;
WaterRateGroup: TGroupBox;
Less2: TLabel;
More2: TLabel;
WaterPercent: TLabel;
WaterRateTrack: TTrackBar;
OffsetLabel1: TLabel;
LandOffset1: TLabel;
OffsetLabel2: TLabel;
WaterOffset1: TLabel;
procedure BlockEditor1Click(Sender: TObject);
procedure Grid1Click(Sender: TObject);
procedure About1Click(Sender: TObject);
procedure BlockPaletteMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure BlockPaletteMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure MapBoxMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure MapBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure MapBoxMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure MapSelectComboChange(Sender: TObject);
procedure MapBoxPaint(Sender: TObject);
procedure BlockPalettePaint(Sender: TObject);
procedure TilesetComboDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure SpinEdit1Change(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure LoadRom1Click(Sender: TObject);
//saves currently open map, event and pokemon data to rom (in memory) and writes to file
procedure SaveRom(FileName: string);
//loads specified map, tileset for map, map's events and pokemon
procedure LoadMap(MapIndex: byte);
//loads specified tileset
procedure LoadTileset(TilesetNumber: Byte);
//loads wild pokemon data for specified map
procedure LoadPokemon(MapIndex: Byte);
//saves currently loaded map, map's events and pokemon
procedure SaveMap;
//draws tileset blocks from MapData into Canvas
procedure DrawMap(Canvas: TCanvas);
//draws the tileset blocks on Canvas
procedure DrawBlocks(Canvas: TCanvas);
//refreshes the wild pokemon data controls in Wild Pokemon tab
procedure UpdatePokemon;
procedure SavePokemon;
function IsOutsideOfMapBounds(X, Y: Integer): Boolean;
procedure Exit1Click(Sender: TObject);
procedure SaveRom1Click(Sender: TObject);
procedure LandReplaceBtnClick(Sender: TObject);
procedure WaterReplaceBtnClick(Sender: TObject);
procedure LandPkmnBoxClick(Sender: TObject);
procedure WaterPkmnBoxClick(Sender: TObject);
procedure WaterRateTrackExit(Sender: TObject);
procedure LandRateTrackExit(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MapEditorForm: TMapEditorForm;
LoadedTileset: Byte = 0;
LoadedMap: string = '';
LoadedMapBank: Byte;
LoadedMapNum: Byte;
SelBlock, SelPic, MapStart, TilesetStart, PokemonStart, Blocks: Integer;
LoadedMapHeader: TMapHeader;
Rom: TMemoryStream = nil;
MapData: array of Byte;
BlockPics: array [0..255] of TBitmap;
Painting,DraggingEvent: Boolean;
Tiles: TBitmap;
MapModified: boolean;
RomModified: boolean;
DragOldX,DragOldY: Integer;
TilesetCache: array [0..23] of TBitmap;
ConnectionData: array [$00..$03] of TConnectionData;
ConnectedMapHeaders: array [$00..$03] of TMapHeader;
TopMapData: array of Byte;
BottomMapData: array of Byte;
LeftMapData: array of Byte;
RightMapData: array of Byte;
Pointer: TPointer;
LandWildPkmn: TLandWildPkmn;
WaterWildPkmn: TWaterWildPkmn;
Location: string;
implementation
{$R *.dfm}
procedure TMapEditorForm.FormCreate(Sender: TObject);
begin
ControlStyle := ControlStyle + [csOpaque];
MapScrollBox.ControlStyle := MapScrollBox.ControlStyle + [csOpaque];
MapBox.ControlStyle := MapBox.ControlStyle + [csOpaque];
// Create the tileset graphic
Tiles := TBitmap.Create;
Tiles.Width := 16*16;
Tiles.Height := 16*6;
Tiles.PixelFormat := pf24bit;
// Import for drawing grid.
MapBox.Canvas.Brush.Style := bsClear;
EventBox.Canvas.Brush.Style := bsClear;
BlockPalette.Canvas.Brush.Style := bsClear;
end;
procedure TMapEditorForm.Grid1Click(Sender: TObject);
begin
//if grid is checked then it will be hidden
if Grid1.Checked then
Grid1.Checked := False
else
//if grid is unchecked then it will be shown
Grid1.Checked := True;
//PokemonIni.WriteBool('Settings','Grid',Grid1.Checked);
//show the grid if needed
if MapStart > 0 then
begin
DrawMap(MapBox.Canvas);
//DrawEvents(MainForm.EventBox.Canvas);
DrawBlocks(BlockPalette.Canvas);
end;
end;
procedure TMapEditorForm.LoadRom1Click(Sender: TObject);
var
I: Integer;
begin
if OpenRomDialog.Execute then
begin
// Check if memory stream is null.
if Rom = nil then
Rom := TMemoryStream.Create;
// Load TMemoryStream from file.
Rom.LoadFromFile(OpenRomDialog.FileName);
ReadMapPallets;
// Load up tileset combos.
for I := 0 to 23 do
begin
TilesetCache[I] := TBitmap.Create;
TilesetCache[I].Width := 128;
TilesetCache[I].Height := 48;
TilesetCache[I].PixelFormat := pf24bit;
ReadTilesetGraphics(GetTilesetHeader(I), TilesetCache[I]);
end;
MapModified := False;
RomModified := False;
BlockEditor1.Enabled := False;
ReBuildEvents1.Enabled := False;
ReconstructMap1.Enabled := False;
SaveRom1.Enabled := True;
SaveRomAs1.Enabled := True;
//ReadAreaNames;
//ScanMapHeaders;
ReadMapHeaderLocations;
MapSelectPanel.Show;
StatusBar1.Panels[1].Text := 'ROM Loaded...';
//LoadMap(0);
MapSelectCombo.ItemIndex := 0;
MapSelectComboChange(Sender);
end;
end;
procedure TMapEditorForm.SpinEdit1Change(Sender: TObject);
begin
//ReadTilesetGraphics(GetTilesetHeader(SpinEdit1.Value), Tiles);
//TilePalette.Picture.Graphic := Tiles;
end;
procedure TMapEditorForm.TilesetComboDrawItem(Control: TWinControl;
Index: Integer; Rect: TRect; State: TOwnerDrawState);
begin
with TComboBox(Control).Canvas do
begin
FillRect(Rect);
TextOut(Rect.Left,Rect.Top,'Tileset ' + IntToStr(Index) + ':');
Draw((Rect.Right - Rect.Left) div 2 - 64, Rect.Top + 13, TilesetCache[Index]);
end;
end;
procedure TMapEditorForm.LoadTileset(TilesetNumber: Byte);
label LoadingDone;
var
X, Y, Block, TileX, TileY: Integer;
TileNumber: Byte;
Temp: TBitmap;
TilesetHdr: TTilesetHeader;
begin
//if the tileset needed is already loaded, just exit
//if LoadedTileset = TilesetNumber then Exit;
TilesetHdr := GetTilesetHeader(TilesetNumber);
TilesetStart := GBPtrToFilePos(TilesetHdr.BankNumber,TilesetHdr.TilesetArrangementPointer);
if (TilesetStart = 0) or (GBPtrToFilePos(TilesetHdr.BankNumber,TilesetHdr.TilesetGraphicsPointer) = 0) then
begin
MessageDlg('Tileset information in rom seem to be corrupted. Loading will be cancelled.',mtError,[mbOK],0);
Abort;
end;
Temp := TBitmap.Create;
Temp.Width := 16*16;
Temp.Height := 16*6;
Temp.PixelFormat := pf24bit;
//Temp.LoadFromFile(ExtractFilePath(Application.ExeName) + 'Tileset' + IntToStr(TilesetNumber) + '.dib');
ReadTilesetGraphics(GetTilesetHeader(TilesetNumber),Temp);
Tiles.Canvas.StretchDraw(Tiles.Canvas.ClipRect,Temp);
//start loading
StatusBar1.Panels[0].Text := 'Loading Tileset ' + IntToStr(TilesetNumber) + ' please wait...';
//start reading the block data from rom & rendering the tiles
Rom.Position := TilesetStart;
Blocks := 255;
for Block := 0 to 255 do
begin
for Y := 0 to 3 do
for X := 0 to 3 do
begin
Rom.Read(TileNumber,1);
//create bitmap for block if not already existing
if BlockPics[Block] = nil then
begin
BlockPics[Block] := TBitmap.Create;
BlockPics[Block].Width := 32;
BlockPics[Block].Height := 32;
BlockPics[Block].PixelFormat := pf24bit;
end;
//calculate x and y coordinate where to copy tile from, the tileset picture has 16 tiles per line
TileY := TileNumber div 16;
TileX := TileNumber mod 16;
//draw tile to block picture
BlockPics[Block].Canvas.CopyRect(Rect(X*8,Y*8,X*8+8,Y*8+8),Tiles.Canvas,Rect(TileX*8,TileY*8,TileX*8+8,TileY*8+8));
end;
end;
BlockPalette.Height := 32 * Blocks;
BlockPaletteScrollBox.Show;
SelBlock := 1;
LoadedTileset := TilesetNumber;
//updates blocks because tileset is changed
DrawBlocks(BlockPalette.Canvas);
//enable editing blocks
BlockEditor1.Enabled := True;
StatusBar1.Panels[0].Text := 'Tileset ' + inttostr(TilesetNumber) + ' loaded.';
end;
procedure TMapEditorForm.About1Click(Sender: TObject);
begin
with TAboutDialog.Create(nil) do
begin
ShowModal;
Free;
end;
end;
procedure TMapEditorForm.BlockEditor1Click(Sender: TObject);
begin
with TBlockEditorForm.Create(nil) do
begin
ShowModal;
Free;
end;
end;
procedure TMapEditorForm.BlockPaletteMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
SelBlock := (Y div 32);
end;
procedure TMapEditorForm.BlockPaletteMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
StatusBar1.Panels[0].Text := 'Block: $' + IntToHex(Y div 32 , 2) + ' Offset: $' + IntToHex(TilesetStart + ((Y div 32) * 16),0);
end;
procedure TMapEditorForm.BlockPalettePaint(Sender: TObject);
begin
DrawBlocks(BlockPalette.Canvas);
end;
procedure TMapEditorForm.DrawBlocks(Canvas: TCanvas);
var
I: Integer;
begin
Canvas.Brush.Style := bsClear;
//go through the block pictures and draw then on BlockPalette
for I := 0 to Blocks -1 do
begin
Canvas.Draw(0, I * 32, BlockPics[I]);
if Grid1.Checked then
Canvas.Rectangle(0, I * 32, 32 + 32, I * 32 + 32);
end;
//allow saving tileset picture
SaveTilesetPic1.Enabled := True;
end;
procedure TMapEditorForm.DrawMap(Canvas: TCanvas);
var
X, Y: Integer;
MaxX, MaxY: Integer;
StartTileConnect, StartWriteConnect: Integer;
StartHeightWidthDifference: Integer;
StartX, StartY, EndX, EndY: Integer;
begin
Canvas.Brush.Style := bsClear;
MaxX := Canvas.ClipRect.Right div 32;
MaxY := Canvas.ClipRect.Bottom div 32;
if MaxX >= LoadedMapHeader.Width then MaxX := LoadedMapHeader.Width-1;
if MaxY >= LoadedMapHeader.Height then MaxY := LoadedMapHeader.Height-1;
for X := Canvas.ClipRect.Left div 32 to MaxX + 6 do
for Y := Canvas.ClipRect.Top div 32 to MaxY + 6 do
begin
if (X >= 3) and (Y >= 3) and (X <= MaxX + 3) and (Y <= MaxY + 3) then
begin
if (MapData[(Y-3) * LoadedMapHeader.Width + (X-3)] <= Blocks) then
Canvas.Draw(X * 32, Y * 32, BlockPics[MapData[(Y-3) * LoadedMapHeader.Width + (X-3)]])
else
begin
// Invalid blocks detected
Canvas.Brush.Style := bsSolid;
Canvas.Rectangle(X * 32, Y * 32, X * 32 + 32, Y * 32 + 32);
Canvas.Brush.Style := bsClear;
end;
end
else
begin
Canvas.Draw(X * 32, Y * 32, BlockPics[LoadedMapHeader.BorderBlock]);
// Top
if (LoadedMapHeader.ConnectionDataControl and $08) = $08 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[0].MapDataPointer - $4000) - (ConnectedMapHeaders[0].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[0].MapLocationRamPointer - $C6E8;
StartX := StartWriteConnect;
StartY := 0;
EndX := (LoadedMapHeader.ConnectionData[0].WidthHeightVisiblePart - 1) + StartWriteConnect;
EndY := StartY + 3;
if (X >= StartX) and (Y >= StartY) and (X <= EndX) and (Y < EndY) then
begin
StartHeightWidthDifference := ConnectedMapHeaders[0].Width - LoadedMapHeader.ConnectionData[0].WidthHeightVisiblePart;
Canvas.Draw(X * 32, Y * 32, BlockPics[TopMapData[StartTileConnect + (Y * StartHeightWidthDifference) + (Y * LoadedMapHeader.ConnectionData[0].WidthHeightVisiblePart + (X - StartWriteConnect))]]);
end;
end;
// Bottom
if (LoadedMapHeader.ConnectionDataControl and $04) = $04 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[1].MapDataPointer - $4000) - (ConnectedMapHeaders[1].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[1].MapLocationRamPointer - $C6E8;
StartX := StartWriteConnect - ((LoadedMapHeader.Width + 6) * (LoadedMapHeader.Height + 3));
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := StartX + (LoadedMapHeader.ConnectionData[1].WidthHeightVisiblePart - 1);
EndY := StartY + 3;
if (X >= StartX) and (Y >= StartY) and (X <= EndX) and (Y < EndY) then
begin
StartHeightWidthDifference := ConnectedMapHeaders[1].Width - LoadedMapHeader.ConnectionData[1].WidthHeightVisiblePart;
Canvas.Draw(X * 32, Y * 32, BlockPics[BottomMapData[StartTileConnect + ((Y - StartY) * StartHeightWidthDifference) + ((Y - StartY) * LoadedMapHeader.ConnectionData[1].WidthHeightVisiblePart + (X - StartX))]]);
end;
end;
// Left
if (LoadedMapHeader.ConnectionDataControl and $02) = $02 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[2].MapDataPointer - $4000) - (ConnectedMapHeaders[2].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[2].MapLocationRamPointer - $C6E8;
StartX := 0;
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := (StartX + 3) - 1;
EndY := StartY + LoadedMapHeader.ConnectionData[2].WidthHeightVisiblePart;
if (X >= StartX) and (Y >= StartY) and (X <= EndX) and (Y < EndY) then
begin
StartHeightWidthDifference := ConnectedMapHeaders[2].Width - LoadedMapHeader.ConnectionData[2].WidthHeightVisiblePart;
Canvas.Draw(X * 32, Y * 32, BlockPics[LeftMapData[StartTileConnect + ((Y - StartY) * StartHeightWidthDifference) + ((Y - StartY) * LoadedMapHeader.ConnectionData[2].WidthHeightVisiblePart + (X - StartX))]]);
end;
end;
// Right
if (LoadedMapHeader.ConnectionDataControl and $01) = $01 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[3].MapDataPointer - $4000) - (ConnectedMapHeaders[3].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[3].MapLocationRamPointer - $C6E8;
StartX := LoadedMapHeader.Width+3;
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := (StartX + 3) - 1;
EndY := StartY + LoadedMapHeader.ConnectionData[3].WidthHeightVisiblePart;
if (X >= StartX) and (Y >= StartY) and (X <= EndX) and (Y < EndY) then
begin
StartHeightWidthDifference := ConnectedMapHeaders[3].Width - LoadedMapHeader.ConnectionData[3].WidthHeightVisiblePart;
Canvas.Draw(X * 32, Y * 32, BlockPics[RightMapData[StartTileConnect + ((Y - StartY) * StartHeightWidthDifference) + ((Y - StartY) * LoadedMapHeader.ConnectionData[3].WidthHeightVisiblePart + (X - StartX))]]);
end;
end;
end;
if Grid1.Checked then
Canvas.Rectangle(X * 32, Y * 32, X * 32 + 32, Y * 32 + 32);
end;
end;
procedure TMapEditorForm.LoadMap(MapIndex: byte);
var
TempMapHeader: TMapHeader;
TempMapStart: Integer;
TempMapIndex: Byte;
begin
//first check if we need to save previously loaded map
if MapModified then
case MessageDlg('Save changes?',mtConfirmation,mbYesNoCancel,0) of
mrYes: SaveMap;
mrCancel: Abort;
end;
LoadedMapHeader := GetMapHeader(MapIndex);
if (LoadedMapHeader.Width * LoadedMapHeader.Height = 0) then
begin
MessageDlg('There seems be error in map header. Map will not be loaded.',mtWarning,[mbOK],0);
Abort;
end;
DebugString('LoadMap', IntToStr(Map.BankLocations[MapIndex]));
// load palettes
if (MapIndex >= 0) and (MapIndex <= 10) then
begin
Palette[0] := WordToColor(RawMapPalettes[MapIndex+1][0]);
Palette[1] := WordToColor(RawMapPalettes[MapIndex+1][1]);
Palette[2] := WordToColor(RawMapPalettes[MapIndex+1][2]);
Palette[3] := WordToColor(RawMapPalettes[MapIndex+1][3]);
end
else
begin
// default
Palette[0] := WordToColor(RawMapPalettes[0][0]);
Palette[1] := WordToColor(RawMapPalettes[0][1]);
Palette[2] := WordToColor(RawMapPalettes[0][2]);
Palette[3] := WordToColor(RawMapPalettes[0][3]);
end;
// Load the tileset
LoadTileset(LoadedMapHeader.TilesetNumber);
// Set the pointer to the map data
MapStart := GBPtrToFilePos(Map.BankLocations[MapIndex], LoadedMapHeader.DataPointer);
// Make sure ROM is big enough
if Rom.Size - Rom.Position < LoadedMapHeader.Width * LoadedMapHeader.Height then
begin
MessageDlg('Map is too big to be read from file.', mtWarning, [mbOK], 0);
Abort;
end;
// Resize the map, and read the map data.
SetLength(MapData, LoadedMapHeader.Width * LoadedMapHeader.Height);
//read map data to array
Rom.Position := MapStart;
Rom.Read(MapData[0], LoadedMapHeader.Width * LoadedMapHeader.Height);
// Load Top connection data
if (LoadedMapHeader.ConnectionDataControl and $08) = $08 then
begin
TempMapIndex := LoadedMapHeader.ConnectionData[0].MapNumber;
TempMapHeader := GetMapHeader(TempMapIndex);
TempMapStart := GBPtrToFilePos(Map.BankLocations[TempMapIndex],
TempMapHeader.DataPointer);
ConnectedMapHeaders[0] := TempMapHeader;
SetLength(TopMapData, TempMapHeader.Width * TempMapHeader.Height);
Rom.Position := TempMapStart;
Rom.Read(TopMapData[0], TempMapHeader.Width * TempMapHeader.Height);
end;
// Load Bottom connection data
if (LoadedMapHeader.ConnectionDataControl and $04) = $04 then
begin
TempMapIndex := LoadedMapHeader.ConnectionData[1].MapNumber;
TempMapHeader := GetMapHeader(TempMapIndex);
TempMapStart := GBPtrToFilePos(Map.BankLocations[TempMapIndex],
TempMapHeader.DataPointer);
ConnectedMapHeaders[1] := TempMapHeader;
SetLength(BottomMapData, TempMapHeader.Width * TempMapHeader.Height);
Rom.Position := TempMapStart;
Rom.Read(BottomMapData[0], TempMapHeader.Width * TempMapHeader.Height);
end;
// Load Left connection data
if (LoadedMapHeader.ConnectionDataControl and $02) = $02 then
begin
TempMapIndex := LoadedMapHeader.ConnectionData[2].MapNumber;
TempMapHeader := GetMapHeader(TempMapIndex);
TempMapStart := GBPtrToFilePos(Map.BankLocations[TempMapIndex],
TempMapHeader.DataPointer);
ConnectedMapHeaders[2] := TempMapHeader;
SetLength(LeftMapData, TempMapHeader.Width * TempMapHeader.Height);
Rom.Position := TempMapStart;
Rom.Read(LeftMapData[0], TempMapHeader.Width * TempMapHeader.Height);
end;
// Load Right connection data
if (LoadedMapHeader.ConnectionDataControl and $01) = $01 then
begin
TempMapIndex := LoadedMapHeader.ConnectionData[3].MapNumber;
TempMapHeader := GetMapHeader(TempMapIndex);
TempMapStart := GBPtrToFilePos(Map.BankLocations[TempMapIndex],
TempMapHeader.DataPointer);
ConnectedMapHeaders[3] := TempMapHeader;
SetLength(RightMapData, TempMapHeader.Width * TempMapHeader.Height);
Rom.Position := TempMapStart;
Rom.Read(RightMapData[0], TempMapHeader.Width * TempMapHeader.Height);
end;
//give MapBox right size
MapBox.Height := LoadedMapHeader.Height * 32 + (6 * 32); //Pad with 6 Blocks
MapBox.Width := LoadedMapHeader.Width * 32 + (6 * 32); //Pad with 6 Blocks
//Show map
MapScrollBox.Hide;
MapScrollBox.Show;
MapScrollBox.ScrollInView(MapBox);
MapBox.Refresh;
//TODO: Load events and pokemon
LoadedMapBank := Map.BankLocations[MapIndex];
LoadedMapNum := MapIndex;
MapModified := False;
SaveMapPic1.Enabled := True;
LoadPokemon(MapIndex);
//hide event properties
//WarpForm.Hide;
//SignpostForm.Hide;
//PeopleForm.Hide;
end;
procedure TMapEditorForm.LoadPokemon(MapIndex: Byte);
var
First: string;
Second: string;
begin
PokemonStart := 0;
LandWildPkmn.Rate := $00;
WaterWildPkmn.Rate := $00;
LandPkmnBox.Clear;
WaterPkmnBox.Clear;
Rom.Position := $CEEB + (MapIndex * 2);
Rom.Read(Pointer,SizeOf(Pointer));
First := IntToStr(Pointer.Second + $80);
Second := IntToStr(Pointer.First);
Location := IntToHex(StrToInt(First),2) + IntToHex(StrToInt(Second),2);
Rom.Position := StrToInt('$' + Location);
Rom.Read(LandWildPkmn,SizeOf(TLandWildPkmn));
LandOffset1.Caption := '$' + Location;
if (LandWildPkmn.Rate <> $00) then
begin
PokemonStart := StrToInt('$' + Location);
Rom.Position := StrToInt('$' + Location) + SizeOf(TLandWildPkmn);
Rom.Read(WaterWildPkmn,SizeOf(TWaterWildPkmn));
WaterOffset1.Caption := '$' + IntToHex(StrToInt('$' + Location) + SizeOf(TLandWildPkmn),4);
end
else
begin
WaterOffset1.Caption := '$' + IntToHex(StrToInt('$' + Location) + $01,4);
Rom.Position := StrToInt('$' + Location) + $01;
Rom.Read(WaterWildPkmn,SizeOf(TWaterWildPkmn));
if WaterWildPkmn.Rate <> $00 then
begin
PokemonStart := StrToInt('$' + Location) + $01;
end;
end;
if (LandWildPkmn.Rate = $00) and (WaterWildPkmn.Rate = $00) then
begin
NoWildPkmnLabel.Show;
Exit;
end;
if LandWildPkmn.Rate <> $00 then
begin
LandPkmnBox.Enabled := True;
LandPkmnCombo.Enabled := True;
LandPkmnLevel.Enabled := True;
LandRateTrack.Enabled := True;
LandReplaceBtn.Enabled := True;
end
else
begin
LandPkmnBox.Enabled := False;
LandPkmnCombo.Enabled := False;
LandPkmnLevel.Enabled := False;
LandRateTrack.Enabled := False;
LandReplaceBtn.Enabled := False;
end;
if WaterWildPkmn.Rate <> $00 then
begin
WaterPkmnBox.Enabled := True;
WaterPkmnCombo.Enabled := True;
WaterPkmnLevel.Enabled := True;
WaterRateTrack.Enabled := True;
WaterReplaceBtn.Enabled := True;
end
else
begin
WaterPkmnBox.Enabled := False;
WaterPkmnCombo.Enabled := False;
WaterPkmnLevel.Enabled := False;
WaterRateTrack.Enabled := False;
WaterReplaceBtn.Enabled := False;
end;
NoWildPkmnLabel.Hide;
LandRateTrack.Position := LandWildPkmn.Rate;
LandPercent.Caption := IntToStr(LandWildPkmn.Rate) + '%';
WaterRateTrack.Position := WaterWildPkmn.Rate;
WaterPercent.Caption := IntToStr(WaterWildPkmn.Rate) + '%';
UpdatePokemon;
end;
procedure TMapEditorForm.UpdatePokemon;
var
I: Integer;
begin
LandPkmnBox.Clear;
WaterPkmnBox.Clear;
LandLevelBox.Clear;
WaterLevelBox.Clear;
if LandWildPkmn.Rate <> $00 then
begin
for I := 0 to 9 do
begin
LandPkmnBox.AddItem(LandPkmnCombo.Items[LandWildPkmn.Pkmn[I].Number],LandPkmnBox);
LandLevelBox.AddItem(IntToStr(LandWildPkmn.Pkmn[I].Level),LandLevelBox);
end;
end;
if WaterWildPkmn.Rate <> $00 then
begin
for I := 0 to 9 do
begin
WaterPkmnBox.AddItem(WaterPkmnCombo.Items[WaterWildPkmn.Pkmn[I].Number],WaterPkmnBox);
WaterLevelBox.AddItem(IntToStr(WaterWildPkmn.Pkmn[I].Level),WaterLevelBox);
end;
end;
end;
procedure TMapEditorForm.MapBoxMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
StartTileConnect, StartWriteConnect: Integer;
StartX, StartY, EndX, EndY: Integer;
BlockX, BlockY: Integer;
begin
BlockX := X div 32;
BlockY := Y div 32;
if IsOutsideOfMapBounds(X, Y) = false then
begin
//change block if mouse button is left
if Button = mbLeft then
begin
MapModified := True;
MapData[SelPic] := SelBlock;
MapBox.Canvas.Draw((X div 32) * 32, (Y div 32) * 32, BlockPics[SelBlock]);
//if gid is sellected, add it
if Grid1.Checked then
MapBox.Canvas.Rectangle(X div 32 * 32, Y div 32 * 32,
X div 32 * 32 + 32, Y div 32 * 32 + 32);
//enable painting for OnMouseMove
Painting := True;
end
else
begin
//other buttons will select the block at cursor
if (MapData[SelPic] <= Blocks) then
begin
BlockPaletteScrollBox.VertScrollBar.Position := 32 * MapData[SelPic];
SelBlock := MapData[SelPic];
end;
end;
end
else
begin
// check if we are over a top connection
if (LoadedMapHeader.ConnectionDataControl and $08) = $08 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[0].MapDataPointer - $4000) - (ConnectedMapHeaders[0].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[0].MapLocationRamPointer - $C6E8;
StartX := StartWriteConnect;
StartY := 0;
EndX := (LoadedMapHeader.ConnectionData[0].WidthHeightVisiblePart - 1) + StartWriteConnect;
EndY := StartY + 3;
if (BlockX >= StartX) and (BlockY >= StartY) and (BlockX <= EndX) and (BlockY < EndY) then
begin
MapSelectCombo.ItemIndex := LoadedMapHeader.ConnectionData[0].MapNumber;
MapSelectComboChange(Sender);
end;
end;
// check if we are over a bottom connection
if (LoadedMapHeader.ConnectionDataControl and $04) = $04 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[1].MapDataPointer - $4000) - (ConnectedMapHeaders[1].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[1].MapLocationRamPointer - $C6E8;
StartX := StartWriteConnect - ((LoadedMapHeader.Width + 6) * (LoadedMapHeader.Height + 3));
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := StartX + (LoadedMapHeader.ConnectionData[1].WidthHeightVisiblePart - 1);
EndY := StartY + 3;
if (BlockX >= StartX) and (BlockY >= StartY) and (BlockX <= EndX) and (BlockY < EndY) then
begin
MapSelectCombo.ItemIndex := LoadedMapHeader.ConnectionData[1].MapNumber;
MapSelectComboChange(Sender);
end;
end;
// Left
if (LoadedMapHeader.ConnectionDataControl and $02) = $02 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[2].MapDataPointer - $4000) - (ConnectedMapHeaders[2].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[2].MapLocationRamPointer - $C6E8;
StartX := 0;
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := (StartX + 3) - 1;
EndY := StartY + LoadedMapHeader.ConnectionData[2].WidthHeightVisiblePart;
if (BlockX >= StartX) and (BlockY >= StartY) and (BlockX <= EndX) and (BlockY < EndY) then
begin
MapSelectCombo.ItemIndex := LoadedMapHeader.ConnectionData[2].MapNumber;
MapSelectComboChange(Sender);
end;
end;
// Right
if (LoadedMapHeader.ConnectionDataControl and $01) = $01 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[3].MapDataPointer - $4000) - (ConnectedMapHeaders[3].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[3].MapLocationRamPointer - $C6E8;
StartX := LoadedMapHeader.Width+3;
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := (StartX + 3) - 1;
EndY := StartY + LoadedMapHeader.ConnectionData[3].WidthHeightVisiblePart;
if (BlockX >= StartX) and (BlockY >= StartY) and (BlockX <= EndX) and (BlockY < EndY) then
begin
MapSelectCombo.ItemIndex := LoadedMapHeader.ConnectionData[3].MapNumber;
MapSelectComboChange(Sender);
end;
end;
end;
end;
procedure TMapEditorForm.MapBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
StartTileConnect, StartWriteConnect: Integer;
StartX, StartY, EndX, EndY: Integer;
BlockX, BlockY: Integer;
begin
BlockX := X div 32;
BlockY := Y div 32;
//Get block number where cursor is
SelPic := ((Y div 32)-3) * LoadedMapHeader.Width + ((X div 32)-3);
//don't do anything with wrong coordinates
if (X < 0) or (Y < 0) or (SelPic >= (LoadedMapHeader.Width+3) * (LoadedMapHeader.Height+3)) then Exit;
shpHighlight.Width := 32+1;
shpHighlight.Height := 32+1;
shpHighlight.Top := MapBox.Top + ((Y div 32) * 32);
shpHighlight.Left := MapBox.Left + (X div 32) * 32;
if IsOutsideOfMapBounds(X, Y) = True then
begin
shpHighlight.Brush.Color := $80FF;
StatusBar1.Panels[0].Text := 'Border Tile (Read Only)';
MapBox.Cursor := crDrag;
// check if we are over a top connection
if (LoadedMapHeader.ConnectionDataControl and $08) = $08 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[0].MapDataPointer - $4000) - (ConnectedMapHeaders[0].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[0].MapLocationRamPointer - $C6E8;
StartX := StartWriteConnect;
StartY := 0;
EndX := (LoadedMapHeader.ConnectionData[0].WidthHeightVisiblePart - 1) + StartWriteConnect;
EndY := StartY + 3;
if (BlockX >= StartX) and (BlockY >= StartY) and (BlockX <= EndX) and (BlockY < EndY) then
begin
shpHighlight.Left := (StartX*32)-MapBox.Canvas.ClipRect.Left;
shpHighlight.Top := (StartY*32)-MapBox.Canvas.ClipRect.Top;
shpHighlight.Width := (LoadedMapHeader.ConnectionData[0].WidthHeightVisiblePart*32)+1;
shpHighlight.Height := (3*32)+1;
StatusBar1.Panels[0].Text := 'Connected Map #: ' + IntToStr(LoadedMapHeader.ConnectionData[0].MapNumber);
MapBox.Cursor := crHandPoint;
end;
end;
// check if we are over a bottom connection
if (LoadedMapHeader.ConnectionDataControl and $04) = $04 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[1].MapDataPointer - $4000) - (ConnectedMapHeaders[1].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[1].MapLocationRamPointer - $C6E8;
StartX := StartWriteConnect - ((LoadedMapHeader.Width + 6) * (LoadedMapHeader.Height + 3));
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := StartX + (LoadedMapHeader.ConnectionData[1].WidthHeightVisiblePart - 1);
EndY := StartY + 3;
if (BlockX >= StartX) and (BlockY >= StartY) and (BlockX <= EndX) and (BlockY < EndY) then
begin
shpHighlight.Left := (StartX * 32) - MapBox.Canvas.ClipRect.Left;
shpHighlight.Top := (StartY * 32) - MapBox.Canvas.ClipRect.Top;
shpHighlight.Width := (LoadedMapHeader.ConnectionData[1].WidthHeightVisiblePart * 32)+1;
shpHighlight.Height := (3*32)+1;
StatusBar1.Panels[0].Text := 'Connected Map #: ' + IntToStr(LoadedMapHeader.ConnectionData[1].MapNumber);
MapBox.Cursor := crHandPoint;
end;
end;
// Left
if (LoadedMapHeader.ConnectionDataControl and $02) = $02 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[2].MapDataPointer - $4000) - (ConnectedMapHeaders[2].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[2].MapLocationRamPointer - $C6E8;
StartX := 0;
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := (StartX + 3) - 1;
EndY := StartY + LoadedMapHeader.ConnectionData[2].WidthHeightVisiblePart;
if (BlockX >= StartX) and (BlockY >= StartY) and (BlockX <= EndX) and (BlockY < EndY) then
begin
shpHighlight.Left := (StartX * 32) - MapBox.Canvas.ClipRect.Left;
shpHighlight.Top := (StartY * 32) - MapBox.Canvas.ClipRect.Top;
shpHighlight.Width := (3*32)+1;
shpHighlight.Height := (LoadedMapHeader.ConnectionData[2].WidthHeightVisiblePart * 32)+1;
StatusBar1.Panels[0].Text := 'Connected Map #: ' + IntToStr(LoadedMapHeader.ConnectionData[2].MapNumber);
MapBox.Cursor := crHandPoint;
end;
end;
// Right
if (LoadedMapHeader.ConnectionDataControl and $01) = $01 then
begin
StartTileConnect := (LoadedMapHeader.ConnectionData[3].MapDataPointer - $4000) - (ConnectedMapHeaders[3].DataPointer - $4000);
StartWriteConnect := LoadedMapHeader.ConnectionData[3].MapLocationRamPointer - $C6E8;
StartX := LoadedMapHeader.Width+3;
StartY := StartWriteConnect div (LoadedMapHeader.Width + 6);
EndX := (StartX + 3) - 1;
EndY := StartY + LoadedMapHeader.ConnectionData[3].WidthHeightVisiblePart;
if (BlockX >= StartX) and (BlockY >= StartY) and (BlockX <= EndX) and (BlockY < EndY) then
begin
shpHighlight.Left := (StartX * 32) - MapBox.Canvas.ClipRect.Left;
shpHighlight.Top := (StartY * 32) - MapBox.Canvas.ClipRect.Top;
shpHighlight.Width := (3*32)+1;
shpHighlight.Height := (LoadedMapHeader.ConnectionData[3].WidthHeightVisiblePart * 32)+1;
StatusBar1.Panels[0].Text := 'Connected Map #: ' + IntToStr(LoadedMapHeader.ConnectionData[3].MapNumber);
MapBox.Cursor := crHandPoint;
end;
end;
end
else
begin
StatusBar1.Panels[0].Text := 'X: ' + IntToStr((X div 32)-3) +
' Y: ' + IntToStr((Y div 32)-3) +
' Block: $' + IntToHex(MapData[SelPic], 2) +
' Offset: $' + IntToHex(MapStart + SelPic, 2);
shpHighlight.Brush.Color := clLime;
MapBox.Cursor := crDrag;
end;
//If mouse left button is pressed block is changed
if Painting then
begin
MapData[SelPic] := SelBlock;
MapBox.Canvas.Draw(X div 32 * 32,Y div 32 * 32,BlockPics[SelBlock]);
//if grid is selected, add it
if Grid1.Checked then
MapBox.Canvas.Rectangle(X div 32 * 32, Y div 32 * 32,
X div 32 * 32 + 32, Y div 32 * 32 + 32);
end;
end;
procedure TMapEditorForm.MapBoxMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Painting := False;
end;
procedure TMapEditorForm.MapBoxPaint(Sender: TObject);
begin
DrawMap(MapBox.Canvas);
end;
procedure TMapEditorForm.MapSelectComboChange(Sender: TObject);
begin
LoadMap(MapSelectCombo.ItemIndex);
//Tab := PageControl1.ActivePageIndex;
//PageControl1.ActivePageIndex := Tab;
//MapBoxPaint(nil);
//MapBox.Invalidate;
//MapScrollBox.Refresh;
end;
function TMapEditorForm.IsOutsideOfMapBounds(X, Y: Integer): Boolean;
begin
if ((Y div 32) - 3 < 0) or ((X div 32) - 3 < 0)
or ((X div 32) - 3 >= LoadedMapHeader.Width) or ((Y div 32) - 3 >= LoadedMapHeader.Height) then
Result := True
else
Result := False;
end;
procedure TMapEditorForm.Exit1Click(Sender: TObject);
begin
Close;
end;
procedure TMapEditorForm.SaveRom1Click(Sender: TObject);
begin
SaveRom(OpenRomDialog.FileName);
end;
procedure TMapEditorForm.SaveRom(FileName: string);
begin
if MapStart > 0 then
SaveMap;
//if (EventsAddr>0) then
// SaveEvents;
if PokemonStart > 0 then
SavePokemon;
Rom.SaveToFile(FileName);
RomModified := False;
end;
procedure TMapEditorForm.SaveMap;
begin
//write map data
Rom.Position := MapStart;
Rom.Write(MapData[0],LoadedMapHeader.Width * LoadedMapHeader.Height);
//save other stuff
//SaveEvents;
//SavePokemon;
MapModified := False;
RomModified := True;
end;
procedure TMapEditorForm.SavePokemon;
begin
if PokemonStart = 0 then
Exit;
if LandWildPkmn.Rate <> $00 then
begin
Rom.Position := PokemonStart;
Rom.Write(LandWildPkmn,SizeOf(TLandWildPkmn));
end;
if WaterWildPkmn.Rate <> $00 then
begin
if LandWildPkmn.Rate = $00 then
begin
Rom.Position := PokemonStart;
Rom.Write(WaterWildPkmn,SizeOf(TWaterWildPkmn));
end
else
begin
Rom.Position := PokemonStart + SizeOf(TLandWildPkmn);
Rom.Write(WaterWildPkmn,SizeOf(TWaterWildPkmn));
end;
end;
end;
procedure TMapEditorForm.LandReplaceBtnClick(Sender: TObject);
begin
LandWildPkmn.Pkmn[LandPkmnBox.ItemIndex].Number := LandPkmnCombo.ItemIndex;
LandWildPkmn.Pkmn[LandPkmnBox.ItemIndex].Level := LandPkmnLevel.Value;
UpdatePokemon;
MapModified := True;
end;
procedure TMapEditorForm.WaterReplaceBtnClick(Sender: TObject);
begin
WaterWildPkmn.Pkmn[WaterPkmnBox.ItemIndex].Number := WaterPkmnCombo.ItemIndex;
WaterWildPkmn.Pkmn[WaterPkmnBox.ItemIndex].Level := WaterPkmnLevel.Value;
UpdatePokemon;
MapModified := True;
end;
procedure TMapEditorForm.LandPkmnBoxClick(Sender: TObject);
begin
LandPkmnCombo.ItemIndex := LandWildPkmn.Pkmn[LandPkmnBox.ItemIndex].Number;
LandPkmnLevel.Value := LandWildPkmn.Pkmn[LandPkmnBox.ItemIndex].Level;
end;
procedure TMapEditorForm.WaterPkmnBoxClick(Sender: TObject);
begin
WaterPkmnCombo.ItemIndex := WaterWildPkmn.Pkmn[WaterPkmnBox.ItemIndex].Number;
WaterPkmnLevel.Value := WaterWildPkmn.Pkmn[WaterPkmnBox.ItemIndex].Level;
end;
procedure TMapEditorForm.LandRateTrackExit(Sender: TObject);
begin
LandWildPkmn.Rate := LandRateTrack.Position;
LandPercent.Caption := IntToStr(LandRateTrack.Position) + '%';
MapModified := True;
end;
procedure TMapEditorForm.WaterRateTrackExit(Sender: TObject);
begin
WaterWildPkmn.Rate := WaterRateTrack.Position;
WaterPercent.Caption := IntToStr(WaterRateTrack.Position) + '%';
MapModified := True;
end;
end.