├── TODO.txt ├── l2102.zip ├── refs ├── L2102.TXT ├── L2102.xlsx ├── CNW-1362.TXT ├── CNW-1370.TXT ├── CNW-1371.TXT ├── L2+CNW.xlsx └── CNW-1370.xlsx ├── docs ├── RTSCRIPT.TXT ├── 3RDPARTY.DOC └── REFHELP.DOC ├── bin ├── i386-linux │ └── LORD2 ├── i386-go32v2 │ └── LORD2.exe ├── i386-win32 │ └── LORD2.exe └── x86_64-win64 │ └── LORD2.exe ├── fpc-win32.cfg ├── .gitignore ├── source ├── helpers.pas ├── rtreflabel.pas ├── rtreffile.pas ├── rtrefsection.pas ├── LORD2.lpr ├── rtglobal.pas ├── LORD2.lpi ├── struct.pas ├── game.pas └── rtreader.pas ├── .vscode └── tasks.json └── README.md /TODO.txt: -------------------------------------------------------------------------------- 1 | BUYMANAGER 2 | Gold has comma -------------------------------------------------------------------------------- /l2102.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/l2102.zip -------------------------------------------------------------------------------- /refs/L2102.TXT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/refs/L2102.TXT -------------------------------------------------------------------------------- /refs/L2102.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/refs/L2102.xlsx -------------------------------------------------------------------------------- /docs/RTSCRIPT.TXT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/docs/RTSCRIPT.TXT -------------------------------------------------------------------------------- /refs/CNW-1362.TXT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/refs/CNW-1362.TXT -------------------------------------------------------------------------------- /refs/CNW-1370.TXT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/refs/CNW-1370.TXT -------------------------------------------------------------------------------- /refs/CNW-1371.TXT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/refs/CNW-1371.TXT -------------------------------------------------------------------------------- /refs/L2+CNW.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/refs/L2+CNW.xlsx -------------------------------------------------------------------------------- /bin/i386-linux/LORD2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/bin/i386-linux/LORD2 -------------------------------------------------------------------------------- /refs/CNW-1370.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/refs/CNW-1370.xlsx -------------------------------------------------------------------------------- /bin/i386-go32v2/LORD2.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/bin/i386-go32v2/LORD2.exe -------------------------------------------------------------------------------- /bin/i386-win32/LORD2.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/bin/i386-win32/LORD2.exe -------------------------------------------------------------------------------- /bin/x86_64-win64/LORD2.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickparrish/LORD2/HEAD/bin/x86_64-win64/LORD2.exe -------------------------------------------------------------------------------- /fpc-win32.cfg: -------------------------------------------------------------------------------- 1 | -B 2 | -FEbin\i386-win32 3 | -Fu..\RMDoor 4 | -FUobj\i386-win32 5 | -g 6 | -gl 7 | -l 8 | -MObjFPC 9 | -O1 10 | -Scghi 11 | -vewnhiq 12 | -vm5024 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.lps 2 | backup/ 3 | bin/i386-go32v2/* 4 | !bin/i386-go32v2/LORD2.exe 5 | bin/i386-linux/* 6 | !bin/i386-linux/LORD2 7 | bin/i386-win32/* 8 | !bin/i386-win32/LORD2.exe 9 | bin/x86_64-win64/* 10 | !bin/x86_64-win64/LORD2.exe 11 | obj/ -------------------------------------------------------------------------------- /source/helpers.pas: -------------------------------------------------------------------------------- 1 | unit Helpers; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | function GetAbsolutePath(AFileName: String): String; 8 | 9 | implementation 10 | 11 | uses 12 | Door, 13 | SysUtils; 14 | 15 | function GetAbsolutePath(AFileName: String): String; 16 | begin 17 | AFileName := StringReplace(AFileName, '`*', IntToStr(DoorDropInfo.Node), [rfReplaceAll, rfIgnoreCase]); 18 | Result := ExpandFileName(AFileName); 19 | end; 20 | 21 | end. 22 | 23 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "windows": { 4 | "command": "Z:\\FPC\\3.0.2\\bin\\i386-win32\\fpc.exe", 5 | "args": ["@fpc-win32.cfg"] 6 | }, 7 | "linux": { 8 | "command": "FPC_BIN_PATH" 9 | }, 10 | "isShellCommand": true, 11 | "showOutput": "always", 12 | "args": ["source\\LORD2.lpr"], 13 | "problemMatcher": { 14 | "owner": "external", 15 | "pattern": { 16 | "regexp": "^([\\w]+\\.(p|pp|pas))\\((\\d+)\\,(\\d+)\\)\\s(Fatal|Error|Warning|Note):(.*)", 17 | "file": 1, 18 | "line": 3, 19 | "column": 4, 20 | "message": 6 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /source/rtreflabel.pas: -------------------------------------------------------------------------------- 1 | unit RTRefLabel; 2 | 3 | {$mode objfpc}{$h+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, fgl; 9 | 10 | type 11 | TRTRefLabel = class 12 | public 13 | LineNumber: Integer; 14 | Name: String; 15 | 16 | constructor Create(ALabelName: String; ALineNumber: Integer); 17 | destructor Destroy; override; 18 | end; 19 | 20 | TRTRefLabelMap = specialize TFPGMap; 21 | 22 | implementation 23 | 24 | constructor TRTRefLabel.Create(ALabelName: String; ALineNumber: Integer); 25 | begin 26 | inherited Create; 27 | LineNumber := ALineNumber; 28 | Name := ALabelName; 29 | end; 30 | 31 | destructor TRTRefLabel.Destroy; 32 | begin 33 | inherited Destroy; 34 | end; 35 | 36 | end. 37 | 38 | -------------------------------------------------------------------------------- /source/rtreffile.pas: -------------------------------------------------------------------------------- 1 | unit RTRefFile; 2 | 3 | {$mode objfpc}{$h+} 4 | 5 | interface 6 | 7 | uses 8 | RTRefSection, 9 | fgl; 10 | 11 | type 12 | TRTRefFile = class 13 | public 14 | Name: String; 15 | Sections: TRTRefSectionMap; 16 | 17 | constructor Create(AFileName: String); 18 | destructor Destroy; override; 19 | end; 20 | 21 | TRTRefFileMap = specialize TFPGMap; 22 | 23 | implementation 24 | 25 | constructor TRTRefFile.Create(AFileName: String); 26 | begin 27 | inherited Create; 28 | Name := AFileName; 29 | Sections := TRTRefSectionMap.Create; 30 | end; 31 | 32 | destructor TRTRefFile.Destroy; 33 | begin 34 | inherited Destroy; 35 | 36 | Sections.Clear; 37 | Sections.Free; 38 | end; 39 | 40 | end. 41 | 42 | -------------------------------------------------------------------------------- /source/rtrefsection.pas: -------------------------------------------------------------------------------- 1 | unit RTRefSection; 2 | 3 | {$mode objfpc}{$h+} 4 | 5 | interface 6 | 7 | uses 8 | RTRefLabel, 9 | Classes, fgl; 10 | 11 | type 12 | TRTRefSection = class 13 | public 14 | Labels: TRTRefLabelMap; 15 | Name: String; 16 | Script: TStringList; 17 | 18 | constructor Create(ASectionName: String); 19 | destructor Destroy; override; 20 | end; 21 | 22 | TRTRefSectionMap = specialize TFPGMap; 23 | 24 | implementation 25 | 26 | constructor TRTRefSection.Create(ASectionName: String); 27 | begin 28 | inherited Create; 29 | Labels := TRTRefLabelMap.Create; 30 | Name := ASectionName; 31 | Script := TStringList.Create; 32 | end; 33 | 34 | destructor TRTRefSection.Destroy; 35 | begin 36 | inherited Destroy; 37 | 38 | Labels.Clear; 39 | Labels.Free; 40 | Script.Free; 41 | end; 42 | 43 | end. 44 | 45 | -------------------------------------------------------------------------------- /source/LORD2.lpr: -------------------------------------------------------------------------------- 1 | // TODO Legend: 2 | // TODOX: Complete before considering playable 3 | // TODOY: Multiplayer enhancements 4 | // TODOZ: Shouldn't be needed for the game 5 | program LORD2; 6 | 7 | {$mode objfpc}{$h+} 8 | 9 | uses 10 | Game, Helpers, 11 | Door, FileUtils, 12 | Crt, SysUtils; 13 | 14 | var 15 | CrashLogFile: Text; 16 | Maint: Boolean; 17 | 18 | begin 19 | try 20 | try 21 | ClrScr; 22 | DoorSession.SethWrite := true; 23 | 24 | // Check if maintenance was requested 25 | Maint := (ParamCount > 0) AND (LowerCase(ParamStr(1)) = '/maint'); 26 | if (Maint) then 27 | begin 28 | // Maintenance was requested 29 | Game.Init; 30 | Game.Maint; 31 | end else 32 | begin 33 | // Not asking for maintenance, so start the game 34 | DoorStartUp; 35 | DoorClrScr; 36 | Game.Init; 37 | Game.Start; 38 | end; 39 | except 40 | on E: Exception do 41 | begin 42 | if (OpenFileForAppend(CrashLogFile, Helpers.GetAbsolutePath('CRASHLOG.TXT'), 100)) then 43 | begin 44 | WriteLn(CrashLogFile, DateTimeToStr(Now)); 45 | DumpExceptionCallStack(CrashLogFile, E); 46 | WriteLn(CrashLogFile, ''); 47 | Close(CrashLogFile); 48 | end; 49 | 50 | DoorWriteLn; 51 | DoorWriteLn('`4`b**`% ERROR : `2' + E.Message + ' `4`b**`2'); 52 | DoorWrite('Hit a key to quit'); 53 | DoorReadKey; 54 | end; 55 | end; 56 | finally 57 | if NOT(Maint) then 58 | begin 59 | DoorShutDown; 60 | end; 61 | end; 62 | end. 63 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Legend Of the Red Dragon II 2 | =========================== 3 | 4 | An open source version of Legend Of the Red Dragon II. Binaries are in the bin/[platform] directories, just drop one in the same directory as your LORD2 installation and enjoy! 5 | 6 | NOTE: This is not 100% complete yet -- there are still some RTReader commands that have not been implemented yet (unhandled commands are displayed at the bottom of the screen when 7 | they are encountered), and no doubt there are a lot of bugs to squish in the commands that have been implemented, so if you want to test this best to run it as a second instance 8 | of LORD2, and not to replace your existing instance. 9 | 10 | I've personally compiled and tested the following configurations. The build scripts are all based off my paths, so you'll probably 11 | need to tweak them before you get it compiling for yourself. And the doorkit code has recently been moved out of the LORD2 repository, so you'll need 12 | RMDoor as well to get things compiling. 13 | 14 | - Windows Server 2012 (64bit)
15 | - Lazarus 1.0.14 (32bit) (FreePascal 2.6.2)
16 | - Just open LORD2.lpi and build
17 | - Tested with GameSrv
18 | 19 | - Windows 7 (32bit) 20 | - FreePascal 2.6.4 (GO32V2) 21 | - build-go32v2.cmd 22 | - Tested with GameSrv 23 | 24 | - Ubuntu Server 13.10 (32bit): 25 | - FreePascal 2.6.2 (32bit) (also needs fp-units-fcl) 26 | - build-linux.sh 27 | - Tested with Synchronet 28 | 29 | - FreeBSD 9.1 (32bit): 30 | - FreePascal 2.6.0 (32bit) 31 | - cd /usr/ports/lang/fpc && make install && make clean 32 | - fpc -B -Furmdoor -FEbin/i386-freebsd LORD2.lpr 33 | - Tested with Synchronet 34 | - NOTE: These instructions are now out of date. You should probably just create a new script based off the build-linux.sh script and use that to compile. 35 | 36 | Legend Of the Red Dragon II is owned by Metropolis GamePort, which I have no affiliation with. 37 | 38 | The code in this repository has been written from scratch -- no LORD2 source code (neither original Pascal source, nor decompiled Assembly source) has been referenced in its implementation. 39 | (Except for the data file structures, which are included in the LORD II release archive.) 40 | -------------------------------------------------------------------------------- /source/rtglobal.pas: -------------------------------------------------------------------------------- 1 | unit RTGlobal; 2 | 3 | {$mode objfpc}{$h+} 4 | 5 | interface 6 | 7 | uses 8 | Helpers, RTRefLabel, RTRefFile, RTRefSection, 9 | Classes, SysUtils; 10 | 11 | var 12 | RefFiles: TRTRefFileMap; 13 | 14 | implementation 15 | 16 | procedure LoadRefFile(AFileName: String); forward; 17 | 18 | procedure LoadRefFile(AFileName: String); 19 | var 20 | I: Integer; 21 | LineTrimmed: String; 22 | NewFile: TRTRefFile; 23 | NewSection: TRTRefSection; 24 | NewSectionName: String; 25 | SL: TStringList; 26 | begin 27 | // A place to store all the sections found in this file 28 | NewFile := TRTRefFile.Create(ChangeFileExt(ExtractFileName(AFileName), '')); 29 | 30 | // Where to store the info for the section we're currently working on 31 | NewSectionName := '_HEADER'; 32 | NewSection := TRTRefSection.Create(NewSectionName); 33 | 34 | // Loop through the file 35 | SL := TStringList.Create; 36 | SL.LoadFromFile(AFileName); 37 | for I := 0 to SL.Count - 1 do 38 | begin 39 | LineTrimmed := UpperCase(Trim(Sl[I])); 40 | 41 | // Check for new section 42 | if (Pos('@#', LineTrimmed) = 1) then 43 | begin 44 | // Store last section we were working on in dictionary 45 | if (NewFile.Sections.IndexOf(NewSectionName) <> -1) then 46 | begin 47 | // Section already exists, so we can't add it 48 | // CASTLE4 has multiple DONE sections 49 | // STONEB has multiple NOTHING sections 50 | // Both appear harmless, but keep that in mind if either ever seems buggy 51 | end else 52 | begin 53 | NewFile.Sections.Add(NewSectionName, NewSection); 54 | end; 55 | 56 | // Get new section name (presumes only one word headers allowed, trims @# off start) and reset script block 57 | NewSectionName := Copy(LineTrimmed, 3, Length(LineTrimmed) - 2); 58 | if (Pos(' ', NewSectionName) > 0) then 59 | begin 60 | NewSectionName := Copy(NewSectionName, 1, Pos(' ', NewSectionName) - 1); 61 | end; 62 | NewSection := TRTRefSection.Create(NewSectionName); 63 | end else 64 | if (Pos('@LABEL ', LineTrimmed) = 1) then 65 | begin 66 | NewSection.Script.Add(SL[I]); 67 | 68 | LineTrimmed := Copy(LineTrimmed, 8, Length(LineTrimmed) - 7); 69 | if (Pos(' ', LineTrimmed) > 0) then 70 | begin 71 | LineTrimmed := Copy(LineTrimmed, 1, Pos(' ', LineTrimmed) - 1); 72 | end; 73 | NewSection.Labels.Add(LineTrimmed, TRTRefLabel.Create(LineTrimmed, NewSection.Script.Count - 1)); 74 | end else 75 | begin 76 | NewSection.Script.Add(SL[I]); 77 | end; 78 | end; 79 | SL.Free; 80 | 81 | // Store last section we were working on in dictionary 82 | if (NewFile.Sections.IndexOf(NewSectionName) <> -1) then 83 | begin 84 | // Section already exists, so we can't add it 85 | // CASTLE4 has multiple DONE sections 86 | // STONEB has multiple NOTHING sections 87 | // Both appear harmless, but keep that in mind if either ever seems buggy 88 | end else 89 | begin 90 | NewFile.Sections.Add(NewSectionName, NewSection); 91 | end; 92 | 93 | RefFiles.Add(NewFile.Name, NewFile); 94 | end; 95 | 96 | var 97 | SR: TSearchRec; 98 | begin 99 | RefFiles := TRTRefFileMap.Create(); 100 | 101 | if (FindFirst(Helpers.GetAbsolutePath('*.REF'), faAnyFile, SR) = 0) then 102 | begin 103 | repeat 104 | LoadRefFile(Helpers.GetAbsolutePath(SR.Name)); 105 | until (FindNext(SR) <> 0); 106 | end; 107 | FindClose(SR); 108 | end. 109 | 110 | -------------------------------------------------------------------------------- /source/LORD2.lpi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /source/struct.pas: -------------------------------------------------------------------------------- 1 | unit Struct; 2 | 3 | {$mode objfpc}{$h+} 4 | 5 | interface 6 | 7 | type 8 | FIGHT_WEAPON = packed record 9 | Text: String; 10 | Strength: Integer; 11 | end; 12 | 13 | type 14 | FightRecord = packed record 15 | MonsterName: String; 16 | SightingText: String; 17 | PowerKillText: String; 18 | HimHerIt: String; 19 | Weapons: Array of FIGHT_WEAPON; 20 | Defense: Integer; 21 | Experience: Integer; 22 | Gold: Integer; 23 | HitPoints: Integer; 24 | HitPointsMax: Integer; 25 | RefFileVictory: String; 26 | RefSectionVictory: String; 27 | RefFileDefeat: String; 28 | RefSectionDefeat: String; 29 | RefFileRun: String; 30 | RefSectionRun: String; 31 | end; 32 | 33 | type 34 | // The format for the new *.IDF files. 35 | IdfRecord = packed record {format of the @data commands when saved} 36 | LastUsed: longint; {used for the @datanewday feature, this is how it tells 37 | if it should reset the data because it is a new day 38 | or not} 39 | Data: array[1..200] of longint; {the real data is kept here} 40 | Extra: array[1..200] of char; {reserved for gracefull upgrades.. 41 | hey, you never know} 42 | end; 43 | 44 | type 45 | ItemsDatRecord = packed record {used by item.ref} 46 | Name: string[30]; {name of item} 47 | HitAction: string[40]; {string for hitting someone with it} 48 | UseOnce, 49 | Armour, 50 | Weapon, 51 | Sell, 52 | Used: boolean; 53 | Value: longint; {gold value} 54 | Breakage: SmallInt; {break percentage per use} 55 | MaxBuy: SmallInt; {unused for now} 56 | Defence, 57 | Strength: SmallInt; {strength/defence added if equipped} 58 | Eat: SmallInt; {unused for now} 59 | RefSection: string[12]; {label of .ref procedure in ITEMS.REF} 60 | UseAction: string[30]; {text for using it with the .ref} 61 | Description: string[30]; {description of item that shows to the right} 62 | QuestItem: boolean; {if true, item cannot be dropped, it is a quest item} 63 | Extra: array[1..37] of char; {reserved} 64 | end; 65 | 66 | type 67 | ItemsDatCollection = packed record {the entire ITEMS.DAT file is ONE of this 68 | record format} 69 | Item: array[1..99] of ItemsDatRecord; 70 | end; 71 | 72 | type 73 | MAP_INFO = packed record {used by plan_rec, which is for each screen} 74 | ForeColour: shortint; {foreground color of square} 75 | BackColour: shortint; {background color of square} 76 | Ch: char; {actual char} 77 | T: SmallInt; {can't remember} 78 | Terrain: shortint; {what type so it knows if you can walk through it or 79 | not.. here is the list: 80 | if map^.w[x,y].s = 0 then ter := 'Unpassable'; 81 | if map^.w[x,y].s = 1 then ter := 'Grass'; 82 | if map^.w[x,y].s = 2 then ter := 'Rocky'; 83 | if map^.w[x,y].s = 3 then ter := 'Water'; 84 | if map^.w[x,y].s = 4 then ter := 'Ocean'; 85 | if map^.w[x,y].s = 5 then ter := 'Forest'; } 86 | 87 | end; 88 | 89 | type 90 | HOT_SPOT = packed record {also used by plan_rec, the ten 91 | hotspots available all use this format} 92 | WarpToMap: SmallInt; {map to move to, 0 if not a warp} 93 | HotSpotX, 94 | HotSpotY: shortint; {xy cords of hotspot, 0 if hotspot not used} 95 | WarpToX, 96 | WarpToY: shortint; {xy cords of warp destination, 0 if not a warp} 97 | RefSection: string[12]; {label of ref procedure to run, if not a warp} 98 | RefFile: string[12]; {filename of .ref to run if not a warp} 99 | Extra: array[1..100] of char; {reserved by me} 100 | end; 101 | 102 | 103 | type 104 | MapDatRecord = packed record {record for the MAP.DAT file, which is screen info} 105 | Name: string[30]; {name of screen} 106 | MapInfo: array[1..80] of array[1..20] of MAP_INFO; {each block on screen} 107 | HotSpots: array[1..10] of HOT_SPOT; {the 10 hotspots} 108 | BattleOdds: longint; {odds of running the 'screen random ref'} 109 | RefFile: string[12]; {ref file name} 110 | RefSection: string[12]; {label of ref procedure} 111 | NoFighting: boolean; {true if players cannot fight on this screen} 112 | Extra: array[1..469] of char; {reserved by me} 113 | end; 114 | 115 | type 116 | TraderDatRecord = packed record 117 | Name : string[25]; {handle they choose for LORD2} 118 | RealName: string[40]; {name from BBS} 119 | Money, 120 | Bank, 121 | Experience: longint; {exp isn't used but reserved} 122 | LastDayOn, 123 | Love: SmallInt; {love isn't used but reserved} 124 | WeaponNumber, 125 | ArmourNumber: shortint; {item # of wep/arm they have equipped} 126 | Race: string[30]; {reserved} 127 | SexMale: SmallInt; {1 if male.. yes there is a reason I didn't use 128 | a byte!! } 129 | OnNow, 130 | Battle: byte; {these will be OFF when a player is offline} 131 | Dead, 132 | Busy, 133 | Deleted, 134 | Nice, 135 | Map, 136 | E6: SmallInt; {If dead, dead is 1, if deleted, deleted is 1. Map is map block #.} 137 | X, 138 | Y: SmallInt; {current cordinates of player} 139 | I: array[1..99] of SmallInt; {items. used by `i} 140 | P: array[1..99] of longint; {longints. used by `i} 141 | T: array[1..99] of byte; {bytes. Used by `t} 142 | LastSaved: longint; {last day saved} 143 | LastDayPlayed: longint; {duh} 144 | LastMap: SmallInt; {last map player was on that was 'visable'} 145 | Extra: array[1..354] of char; {reserved for me} 146 | end; 147 | 148 | {The update.tmp file is made up of this record, one for each player 149 | in order. Just in case you wanted to write an ap that needed to know 150 | what was going on realtime.} 151 | type 152 | UpdateTmpRecord = packed record 153 | X, 154 | Y: shortint; 155 | Map: SmallInt; 156 | OnNow: byte; 157 | Busy: byte; {these are all 0 or 1 if true} 158 | Battle: byte; 159 | end; 160 | 161 | type 162 | WorldDatRecord = packed record {For the world.dat file - this is kind of 163 | the index for the MAP.REF file. It tells LORD2 how each screen hooks 164 | to each other. loc is each screen - starting at x1,y1 is 1, then this 165 | goes right until 80 is reached, then starts at x1,y2 for 81 etc.} 166 | Name: string[60]; 167 | MapDatIndex: array[1..1600] of SmallInt; {holds the physical map # of the 168 | record for this screen from the MAP.DAT file.. If # is 0, there is 169 | no screen here. } 170 | V: array[1..40] of longint; {used by `v} 171 | S: array[1..10] of string[80]; {used by `s} 172 | Time: longint; {year+month+day?.. not sure can't remember} 173 | HideOnMap: array[1..1600] of byte; {show up on the players auto 'map'?} 174 | Extra: array[1..396] of char; {extra for me} 175 | end; 176 | 177 | implementation 178 | 179 | end. 180 | 181 | -------------------------------------------------------------------------------- /docs/3RDPARTY.DOC: -------------------------------------------------------------------------------- 1 | 2 | THIRD PARTY DOCUMENTATION FOR THE LORD2 ENGINE 3 | 4 | 5 | FOR INFO ON CREATING AN IGM FOR LORD2, READ IGM.DOC!!!!!!!!!!!!!! This 6 | stuff is generally usefull info too, though. 7 | 8 | This is some extra info and help on creating addons/new areas/stand alone 9 | .refs/new worlds for the LORD2 engine. 10 | 11 | My goal is to make it easy and free (besides our cheap $15 registration 12 | fee to use more than the unregistered portion of the world screens) for 13 | any non programmer to make his own door or world. 14 | 15 | ** The pascal structures of most data files are listed at the end of this ** 16 | ** file. ** 17 | 18 | Q: How do I start adding screens from a clear map? 19 | 20 | A: Delete the files WORLD.DAT and MAP.DAT. 21 | 22 | Q: If I make my own world, what do I need to distribute it? 23 | 24 | A: MAP.DAT, WORLD.DAT and all your .REF files. Probably a good idea to 25 | also throw in a file_id.diz and a .doc file of some sort. 26 | 27 | To install it, a person should setup a NEW game of LORD2 somewhere, then 28 | copy your files over it. Otherwise it would destroy the real LORD2 game 29 | they were running. Be sure to make this clear in your documentation. 30 | 31 | Do *NOT* distribute the GAME.DAT file. This is where non game 32 | information is held like their registration info (they probably don't 33 | want this overwritten!) and ticks per second, etc. 34 | 35 | Q: You made LORD2 and my game needs it to run - does this mean I cannot 36 | charge people for it? 37 | 38 | A: Charge away, I don't want any royalities, you might thank me in your 39 | docs though. ;> Since there is no way to compile or protect your work, 40 | I doubt many will charge for addons which is fine by me. 41 | 42 | A situation that would make charging feasible is say, if you have one 43 | full world addon, and for $5 you would send them two more. This worked 44 | with new LORD ansi packs for a few people, and so much more than new 45 | ANSI can be done now. 46 | 47 | Q: I'm editting some stuff in LORD2 - How do I know what vars you used for 48 | what? 49 | 50 | A: Check the VARLIST.DAT file - at the top I list every var I am using and 51 | tell you what it is for. Any addon you make should contain a list like 52 | this in the file. It also helps the Sysop's to know what stuff is, as 53 | L2CFG.EXE reads this file to put descriptions by vars. 54 | 55 | Q: I made my own game using your system - but it says LORDII: New World at 56 | the bottom of the screen!! How tacky! 57 | 58 | A: You can change this. As well as the default prompt. Check 59 | REFHELP.DOC to learn about the @PROGNAME and @MOREMAP commands. 60 | 61 | Q: Do I have to use your realtime movement/messaging system? 62 | 63 | A: No. Download my addon BRADYREF.ZIP from my website and check it out - it 64 | demonstrates how you can run a single .REF file for the game instead of 65 | using the full on system. For .REF's like these, it is possible to run 66 | them FROM the LORD2 dir, and not disrupt LORD2's real data files, in 67 | case the sysop also has a LORD2 game running there. 68 | 69 | It looks like this: LORD2 crap+crap.ref . This is 70 | good for testing specific .ref files also. "crap" is the label to 71 | run, and "crap.ref" is the filename. 72 | 73 | When LORD2 is run this way, it behaves a little differently - for one, it 74 | quits the game as soon as the .ref is done. Also, you cannot use the 75 | built in load/save player data. 76 | 77 | If you NEEDED this, but didn't want to use the mapping system at all, you 78 | still can. You would edit the @#STARTGAME function to start your main 79 | .REF game, then do a @halt to stop the game before it moves on to the 80 | mapping system. 81 | 82 | -= Summary =- 83 | 84 | In any case - *IF* you are using the mapping engine in your game, be sure 85 | to tell the user in the docs if a registered LORD2 is required so they 86 | don't think your .ref is buggy if things go wrong. 87 | 88 | They need a registered LORD2 if: 89 | 90 | * You use maps that are not in the unregistered area. This kinda tricky - 91 | but basically all the maps you can access in the very first part of the 92 | game up to until you get to Porttown are the unregistered area. 93 | 94 | * You use more than 57 items in the items system. If they pickup/buy an 95 | item pass 57, it will show up as blank, and will not be useable. 96 | 97 | Please send your finishes addons/games over to me to check out, I'll put the 98 | best ones/ones that work on my web page for download! 99 | 100 | Just email attach them mime or uue to e_maus@covingtononline.com. 101 | 102 | If you have questions and need help with .REF's please feel free to email 103 | me for help and suggestions, but even in LORD2's beta form I am getting 104 | .REF's sent to me every day to diagnose problems and such and I can see 105 | I won't be able to do this for everyone... 106 | 107 | But I'll do what I can! 108 | 109 | Also: I'll probably have LORD2 ref/world download area on my website soon, 110 | as well as a developers area. So hit www.rtsoft.com once in a while to 111 | see what is new there! 112 | 113 | -Michael Preslar e_maus@covingtononline.com 114 | 115 | -=-=-= Record formats for the map.dat, world.dat and trader.dat files =-=- 116 | 117 | I got a few people wanting to make their own player/screen editors for 118 | L2, this is fine with me. As promised here is the record formats in 119 | Pascal: 120 | 121 | type igm_data = record {format of the @data commands when saved} 122 | last_used: longint; {used for the @datanewday feature, this is how it tells 123 | if it should reset the data because it is a new day 124 | or not} 125 | data: array[1..200] of longint; {the real data is kept here} 126 | extra: array[1..200] of char; {reserved for gracefull upgrades.. 127 | hey, you never know} 128 | end; 129 | 130 | The above is the format for the new *.IDF files. 131 | 132 | type world_info = record {For the world.dat file - this is kind of 133 | the index for the MAP.REF file. It tells LORD2 how each screen hooks 134 | to each other. loc is each screen - starting at x1,y1 is 1, then this 135 | goes right until 80 is reached, then starts at x1,y2 for 81 etc.} 136 | name: string[60]; 137 | loc: array[1..1600] of integer; {holds the physical map # of the 138 | record for this screen from the MAP.DAT file.. If # is 0, there is 139 | no screen here. } 140 | v: array[1..40] of longint; {used by `v} 141 | s: array[1..10] of string[80]; {used by `s} 142 | time: longint; {year+month+day?.. not sure can't remember} 143 | show: array[1..1600] of byte; {show up on the players auto 'map'?} 144 | extra: array[1..396] of char; {extra for me} 145 | end; 146 | 147 | 148 | type all_players = record 149 | p: array[1..200] of all_p; 150 | end; 151 | 152 | 153 | type user_rec = record 154 | name : string[25]; {handle they choose for LORD2} 155 | real_names: string[40]; {name from BBS} 156 | gold,bank,exp: longint; {exp isn't used but reserved} 157 | last_day_on,love: integer; {love isn't used but reserved} 158 | wep,arm: shortint; {item # of wep/arm they have equipped} 159 | race: string[30]; {reserved} 160 | sex_male: integer; {1 if male.. yes there is a reason I didn't use 161 | a byte!! } 162 | on_now,battle: byte; {these will be OFF when a player is offline} 163 | dead,busy,deleted,nice,map,e6: integer; 164 | {If dead, dead is 1, if deleted, deleted is 1. Map is map block #.} 165 | x,y: integer; {current cordinates of player} 166 | item: array[1..99] of integer; {items. used by `i} 167 | p: array[1..99] of longint; {longints. used by `i} 168 | b: array[1..99] of byte; {bytes. Used by `t} 169 | last_saved: longint; {last day saved} 170 | last_day_played: longint; {duh} 171 | lmap: integer; {last map player was on that was 'visable'} 172 | extra: array[1..354] of char; {reserved for me} 173 | end; 174 | 175 | 176 | {The update.tmp file is made up of this record, one for each player 177 | in order. Just in case you wanted to write an ap that needed to know 178 | what was going on realtime.} 179 | 180 | type q_update = record 181 | x,y: shortint; 182 | map: integer; 183 | on_now: byte; 184 | busy: byte; {these are all 0 or 1 if true} 185 | battle: byte; 186 | end; 187 | 188 | 189 | 190 | 191 | 192 | type item_struct = record {used by item.ref} 193 | name: string[30]; {name of item} 194 | action: string[40]; {string for hitting someone with it} 195 | use_once,armour,weapon,sell,used: boolean; 196 | value: longint; {gold value} 197 | breakage: integer; {break percentage per use} 198 | max_buy: integer; {unused for now} 199 | def,strength: integer; {strength/defence added if equipped} 200 | eat: integer; {unused for now} 201 | ref: string[12]; {label of .ref procedure in ITEMS.REF} 202 | use_action: string[30]; {text for using it with the .ref} 203 | descrip: string[30]; {description of item that shows to the right} 204 | drop: boolean; {if true, item cannot be dropped, it is a quest item} 205 | extra: array[1..37] of char; {reserved} 206 | end; 207 | 208 | type item_rec = record {the entire ITEMS.DAT file is ONE of this 209 | record format} 210 | i: array[1..99] of item_struct; 211 | end; 212 | 213 | 214 | type map_info = record {used by plan_rec, which is for each 215 | screen} 216 | fc: shortint; {foreground color of square} 217 | bc: shortint; {background color of square} 218 | c: char; {actual char} 219 | t: integer; {can't remember} 220 | s: shortint; {what type so it knows if you can walk through it or 221 | not.. here is the list: 222 | 223 | if map^.w[x,y].s = 0 then ter := 'Unpassable'; 224 | if map^.w[x,y].s = 1 then ter := 'Grass'; 225 | if map^.w[x,y].s = 2 then ter := 'Rocky'; 226 | if map^.w[x,y].s = 3 then ter := 'Water'; 227 | if map^.w[x,y].s = 4 then ter := 'Ocean'; 228 | if map^.w[x,y].s = 5 then ter := 'Forest'; } 229 | 230 | end; 231 | 232 | 233 | type special_struct = record {also used by plan_rec, the ten 234 | hotspots available all use this format} 235 | move_place: integer; {map to move to, 0 if not a warp} 236 | dx,dy: shortint; {xy cords of hotspot, 0 if hotspot not used} 237 | x,y: shortint; {xy cords of warp destination, 0 if not a warp} 238 | refname: string[12]; {label of ref procedure to run, if not a warp} 239 | reffile: string[12]; {filename of .ref to run if not a warp} 240 | extra: array[1..100] of char; {reserved by me} 241 | end; 242 | 243 | 244 | type plan_rec = record {record for the MAP.DAT file, which is 245 | screen info} 246 | name: string[30]; {name of screen} 247 | w: array[1..80] of array[1..20] of map_info; {each block on screen} 248 | special: array[1..10] of special_struct; {the 10 hotspots} 249 | battle_odds: longint; {odds of running the 'screen random ref'} 250 | batfile: string[12]; {ref file name} 251 | batname: string[12]; {label of ref procedure} 252 | safe: boolean; {true if players cannot fight on this screen} 253 | extra: array[1..469] of char; {reserved by me} 254 | end; 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /docs/REFHELP.DOC: -------------------------------------------------------------------------------- 1 | 2 | ******* HOW TO MAKE YOUR OWN REF FILES FOR RTREADER ******* 3 | 4 | For Version .002 5 | 6 | Since RTREADER is freeware, this means anybody can use it - For mags, 7 | newsletters, Bulletin board logon news, whatever. 8 | 9 | I only request you don't change this ZIP. (for instance, packaging it 10 | without docs and RTNEWS02.REF) If you want to create a special ZIP 11 | including this for your users or something, please run the ZIP by me 12 | first. 13 | 14 | The heart of this system is the .REF format. It is a script language that 15 | can be used to do complex gaming things, like math and getting random 16 | numbers, adding and deleting other varibles in the game - nearly 17 | everything a simple basic could do. 18 | 19 | REF is short for - (Not sure at this point). 20 | 21 | Although tricky at first, making a ref file is really pretty simple. If 22 | something doesn't work, just go look at your .REF file, (a simple text 23 | file) and fix it! 24 | 25 | To see a demonstration of most commands, check out RTNEWS02.REF, included 26 | with this archive. 27 | 28 | * If a command isn't understood, it will tell you upon execution. 29 | * .REF size is limited only by your total ram 30 | * REF files are completely loaded, and interpreted BEFORE execution for 31 | speed. 32 | * Allows BEGIN and END statements, thousands of levels deep. 33 | * Ansi and SethAnsi (Sorry, don't have another name yet, ) are 34 | automatically displayed correctly locally and remotely. 35 | 36 | IN THIS DOC FILE: 37 | 38 | 1. Varibles used by the .REFs 39 | 40 | 2. Compact Command list 41 | 42 | 3. About headers 43 | 44 | 4. Explanation of each command in detail 45 | 46 | 5. String and file commands 47 | 48 | 6. @SAY and other special commands 49 | 50 | 7. The Shell command 51 | 52 | 8. Known problems & comments 53 | 54 | ** Varibles the REF's use: ** 55 | 56 | 57 | LOCAL : Will equal 5 if the ref is being viewed locally, otherwise 0. 58 | RESPONCE : What @CHOICE returned. 59 | 60 | NOTE: If the following are found in a string being displayed they 61 | will be replaces with their values. 62 | 63 | `V01 through `V40 These are the 40 global varibles. (for numbers) 64 | `P01 through `P20 These are the 20 more varibles. (for numbers) 65 | `S01 through `S10 These are 10 string varibles. (for text) 66 | `N this is the users name. 67 | `G Current Graphics Level 68 | `X Adds a space. 69 | `D Sends a #8 (delete). 70 | `0 through `9 and `! through `^ change color. 71 | `W One tenth a second wait 72 | `L About a half second wait 73 | 74 | (NOTE, these waits are the SAME on all computers, goes by the clock) 75 | 76 | (NOTE: No varibles are saved after RTREADER is exitted, however, you can 77 | save what you wish with the @WRITEFILE and @READFILE commands) 78 | 79 | 80 | ***************** COMMAND LIST, A GOOD THING TO PRINT! ******************* 81 | 82 | @DO GOTO
83 | @NOCHECK : This makes it not scan for labels/headers before running it. 84 | @ROUTINE
IN 85 | @RUN
IN 86 | @CHOICE 87 | @VERSION 88 | @SHOW : Shows following text/ansi. Stops when a @ is hit on 89 | @SHOWLOCAL : Same thing, but shows LOCALLY only. 90 | @SHOW SCROLL : Same thing, but puts all the text in a nifty scroll window 91 | @DO WRITE 92 | @DO BEEP : Makes a weird beep noise, locally only 93 | @DISPLAY
IN : This displays a sectioned 94 | defined by headers. Options are: NOPAUSE and NOSKIP. 95 | @DISPLAYFILE : Display entire file. Same options as above 96 | @IF (Math can be MORE, LESS, NOT, IS) 97 | @GRAPHICS IS : 3 or more enable remote ANSI. If you never wanted 98 | @CLOSESCRIPT : This ends the script. 99 | @CLEAR SCREEN: Clears entire screen. 100 | @KEY : Does a [MORE] prompt, centered on current line. 101 | @KEY NODISPLAY : Waits for keypress without saying anything. 102 | @DO 103 | Ways to change it:(-,+,/,*,IS) 104 | @DO RANDOM 105 | @DO MOVE : This moves the curser. (like GOTOXY in TP) Enter 0 for 106 | @DO GETKEY : This command is usefull, *IF* a key 107 | @DO READNUM (Optional: 108 | : The number is put into `V40. 109 | @DO READSTRING : Get a string. 110 | @DO `S04 ADD 111 | @DO COPY TO NAME Put whatever is in `S10 into `N. (name) 112 | @IF INSIDE THEN (ect) : Allows you to search 113 | a string for something inside of it. Not case sensitive. 114 | @DO STRIP : This strips beginning and end spaces of a string. 115 | @DO UPCASE : Makes a string all capitals. 116 | @DO STRIPBAD : Strips all ` codes out, EXCEPT color codes. 117 | @DO NUMRETURN <`p or `v var> : This returns the 118 | @DO REPLACE : Replaces something in string 119 | @DO REPLACEALL : Replaces all in string 120 | @DO IS LENGTH : Gets length, smart way. 121 | @DO IS REALLENGTH : Gets length dumb way. (includes 122 | @DO PAD : Adds spaces to end until string is as long 123 | as . 124 | @IF EXISTS TRUE THEN (..ect..) 125 | @IF EXISTS FALSE THEN (..ect..) 126 | @WRITEFILE 127 | 128 | 129 | 130 | @READFILE 131 | 132 | 133 | 134 | @DELETE 135 | @DO DELETE : Both delete a file. 136 | @DO TRIM : Trims text file 137 | @HALT : Quits and returns correct error level. 138 | @SHELL <.EXE, BAT or COM> (note, use `* for node num) 139 | @DECLARE