mirror of
https://bitbucket.org/grumdrig/pq.git
synced 2026-09-29 08:44:00 -06:00
ProgressQuest v6.3 ------------------ This directory represents an ex post facto rendering of pq6 in its various versions in svn, so that changes will be trackable thataway. The version as of this moment in svn is 6.3 (I didn't add this file in PQ 6.0, but after that you can use this README to trace the checkin history using svn.)
1706 lines
48 KiB
ObjectPascal
1706 lines
48 KiB
ObjectPascal
unit Main;
|
|
{ copyright (c)2002 Eric Fredricksen all rights reserved }
|
|
|
|
{$DEFINE CHEATS}
|
|
{$UNDEF LOGGING}
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
|
Dialogs, ComCtrls, StdCtrls, ExtCtrls, Buttons, ImgList, Menus, ShellAPI;
|
|
|
|
const
|
|
// revs:
|
|
// 5: pq 6.3
|
|
// 4: pq 6.2
|
|
// 3: pq 6.1
|
|
// 2: pq 6.0
|
|
// 1: pq 6.0, some early release I guess; don't remember
|
|
RevString = '&rev=5';
|
|
wmIconTray = WM_USER + Ord('t');
|
|
kFileExt = '.pq3';
|
|
|
|
type
|
|
TMainForm = class(TForm)
|
|
Panel1: TPanel;
|
|
Label1: TLabel;
|
|
Traits: TListView;
|
|
Equips: TListView;
|
|
Panel3: TPanel;
|
|
Label3: TLabel;
|
|
QuestBar: TProgressBar;
|
|
Stats: TListView;
|
|
Label2: TLabel;
|
|
PlotBar: TProgressBar;
|
|
Plots: TListView;
|
|
Quests: TListView;
|
|
Panel2: TPanel;
|
|
Label4: TLabel;
|
|
Spells: TListView;
|
|
InventoryLabelAlsoGameStyle: TLabel;
|
|
Inventory: TListView;
|
|
Panel4: TPanel;
|
|
Kill: TStatusBar;
|
|
Label6: TLabel;
|
|
ExpBar: TProgressBar;
|
|
TaskBar: TProgressBar;
|
|
Timer1: TTimer;
|
|
EncumBar: TProgressBar;
|
|
Label7: TLabel;
|
|
ImageList1: TImageList;
|
|
Label8: TLabel;
|
|
Cheats: TPanel;
|
|
CashIn: TButton;
|
|
Button1: TButton;
|
|
FinishQuest: TButton;
|
|
Button3: TButton;
|
|
CheatPlot: TButton;
|
|
vars: TPanel;
|
|
fTask: TLabel;
|
|
fQuest: TLabel;
|
|
fQueue: TListBox;
|
|
procedure GoButtonClick(Sender: TObject);
|
|
procedure Timer1Timer(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure SpeedButton1Click(Sender: TObject);
|
|
procedure FormShow(Sender: TObject);
|
|
procedure Button1Click(Sender: TObject);
|
|
procedure CashInClick(Sender: TObject);
|
|
procedure FinishQuestClick(Sender: TObject);
|
|
procedure CheatPlotClick(Sender: TObject);
|
|
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
|
procedure FormKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
private
|
|
procedure Task(caption: String; msec: Integer);
|
|
procedure Dequeue;
|
|
procedure Q(s: string);
|
|
function TaskDone: Boolean;
|
|
procedure CompleteQuest;
|
|
procedure CompleteAct;
|
|
procedure WinEquip;
|
|
procedure WinSpell;
|
|
procedure WinStat;
|
|
procedure WinItem;
|
|
function SpecialItem: String;
|
|
procedure LevelUp;
|
|
function BoringItem: String;
|
|
function InterestingItem: String;
|
|
function MonsterTask(var level: Integer): String;
|
|
function EquipPrice: Integer;
|
|
procedure Brag(trigger: String);
|
|
procedure TriggerAutosizes;
|
|
function GameSaveName: String;
|
|
procedure OnTrayMessage(var Msg: TMessage); message wmIconTray;
|
|
procedure OnSysCommand(var Msg : TWMSysCommand); message WM_SYSCOMMAND;
|
|
procedure Guildify;
|
|
procedure ClearAllSelections;
|
|
procedure OnQueryEndSession(var Msg : TMessage); message WM_QUERYENDSESSION;
|
|
procedure OnEndSession(var Msg : TMessage); message WM_ENDSESSION;
|
|
procedure RestoreIt;
|
|
function AuthenticateUrl(url: String): String;
|
|
{$IFDEF LOGGING}
|
|
procedure Log(line: String);
|
|
{$ENDIF}
|
|
procedure ExportCharSheet;
|
|
function CharSheet: String;
|
|
procedure InterplotCinematic;
|
|
function NamedMonster(level: Integer): String;
|
|
function ImpressiveGuy: String;
|
|
public
|
|
FTrayIcon: TNotifyIconData;
|
|
FReportSave: Boolean;
|
|
FLogEvents: Boolean;
|
|
FMakeBackups: Boolean;
|
|
FMinToTray: Boolean;
|
|
FExportSheets: Boolean;
|
|
FSaveFileName: String;
|
|
procedure MinimizeIt;
|
|
procedure LoadGame(name: String);
|
|
function SaveGame: Boolean;
|
|
procedure Put(list: TListView; key: String; value: String); overload;
|
|
procedure Put(list: TListView; pos: Integer; value: String); overload;
|
|
procedure Put(list: TListView; key: String; value: Integer); overload;
|
|
procedure Add(list: TListView; key: String; value: Integer); overload;
|
|
procedure AddR(list: TListView; key: String; value: Integer); overload;
|
|
function Get(list: TListView; key: String): String; overload;
|
|
function Get(list: TListView; index: Integer): String; overload;
|
|
function GetI(list: TListView; key: String): Integer; overload;
|
|
function GetI(list: TListView; index: Integer): Integer; overload;
|
|
function Sum(list: TListView): Integer;
|
|
function RollCharacter: Boolean;
|
|
function GetMotto: String;
|
|
function GetPasskey: Integer;
|
|
procedure SetMotto(v: String);
|
|
procedure SetPasskey(v: String);
|
|
function GetHostAddr: String;
|
|
function GetHostName: String;
|
|
procedure SetHostAddr(v: String);
|
|
procedure SetHostName(v: String);
|
|
function GetLogin: String;
|
|
function GetPassword: String;
|
|
procedure SetLogin(v: String);
|
|
procedure SetPassword(v: String);
|
|
function GetGuild: String;
|
|
procedure SetGuild(v: String);
|
|
end;
|
|
|
|
var
|
|
MainForm: TMainForm;
|
|
|
|
function Split(s: String; field: Integer): String; overload;
|
|
function Split(s: String; field: Integer; separator: String): String; overload;
|
|
|
|
procedure Navigate(url: String);
|
|
|
|
implementation
|
|
|
|
uses Web, StrUtils, NewGuy, Math, Config, Front, zlibex, SelServ, Login,
|
|
mmsystem, Registry, ShlObj;
|
|
|
|
{$R *.dfm}
|
|
|
|
// Returns '' if not there, which is lame, but okay for my purposes
|
|
function RegRead(root: HKEY; path, name: String): String;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Reg := TRegistry.Create;
|
|
try
|
|
Reg.RootKey := root;
|
|
if Reg.OpenKey(path, false) then
|
|
Result := Reg.ReadString(name);
|
|
Reg.CloseKey;
|
|
finally
|
|
Reg.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure RegWrite(root: HKEY; path, name, value: String);
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Reg := TRegistry.Create;
|
|
try
|
|
Reg.RootKey := root;
|
|
Reg.OpenKey(path, true);
|
|
Reg.WriteString(name, value);
|
|
Reg.CloseKey;
|
|
finally
|
|
Reg.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure MakeFileAssociations;
|
|
const
|
|
kPQFileType = 'ProgressQuest.GameSave';
|
|
var
|
|
kOpenCommand: String;
|
|
begin
|
|
kOpenCommand := '"' + Application.ExeName + '" "%1"';
|
|
RegWrite(HKEY_CLASSES_ROOT, kFileExt,'', kPQFileType);
|
|
RegWrite(HKEY_CLASSES_ROOT, kPQFileType, '', 'Progresss Quest saved game');
|
|
RegWrite(HKEY_CLASSES_ROOT, kPQFileType + '\DefaultIcon', '', Application.ExeName + ',0');
|
|
RegWrite(HKEY_CLASSES_ROOT, kPQFileType + '\Shell\Open', '', '&Open');
|
|
if RegRead(HKEY_CLASSES_ROOT, kPQFileType + '\Shell\Open\Command', '') <> kOpenCommand then begin
|
|
RegWrite(HKEY_CLASSES_ROOT, kPQFileType + '\Shell\Open\Command', '', kOpenCommand);
|
|
// Notify Windows Explorer to realize we added this. In case this is slow
|
|
// I don't do this unless I'm sure this one has changed.
|
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil);
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.MinimizeIt;
|
|
begin
|
|
if not FMinToTray then Exit;
|
|
with FTrayIcon do
|
|
begin
|
|
cbSize := SizeOf(FTrayIcon);
|
|
Wnd := Handle;
|
|
uID := 0;
|
|
uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
|
|
uCallbackMessage := wmIconTray;
|
|
hIcon := Application.Icon.Handle;
|
|
StrPLCopy(szTip, Caption, 63);
|
|
end;
|
|
Application.Minimize;
|
|
ShowWindow(Application.Handle, SW_HIDE);
|
|
Shell_NotifyIcon(NIM_ADD,@FTrayIcon);
|
|
end;
|
|
|
|
procedure TMainForm.OnSysCommand(var Msg: TWMSysCommand);
|
|
begin
|
|
if (Msg.CmdType = SC_MINIMIZE) and FMinToTray then
|
|
MinimizeIt();
|
|
inherited;
|
|
end;
|
|
|
|
procedure TMainForm.RestoreIt;
|
|
begin
|
|
ShowWindow(Application.Handle, SW_SHOW);
|
|
Application.Restore;
|
|
Shell_NotifyIcon(NIM_DELETE, @MainForm.FTrayIcon);
|
|
end;
|
|
|
|
procedure TMainForm.OnTrayMessage(var Msg: TMessage);
|
|
begin
|
|
case Msg.lParam of
|
|
WM_LBUTTONDOWN, WM_RBUTTONDOWN:
|
|
RestoreIt;
|
|
|
|
WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK:
|
|
RestoreIt;
|
|
end;
|
|
end;
|
|
|
|
procedure StartTimer;
|
|
begin
|
|
if not MainForm.Timer1.Enabled then begin
|
|
MainForm.Timer1.Tag := timeGetTime;
|
|
//Shell_NotifyIcon(NIM_ADD, @MainForm.FTrayIcon);
|
|
end;
|
|
MainForm.Timer1.Enabled := True;
|
|
// BS location for this, but...
|
|
MainForm.Caption := 'ProgressQuest - ' + ChangeFileExt(ExtractFileName(MainForm.GameSaveName), '');
|
|
end;
|
|
|
|
function TMainForm.GetPasskey: Integer; begin Result := Traits.Tag; end;
|
|
procedure TMainForm.SetPasskey(v: String);
|
|
begin
|
|
Traits.Hint := v;
|
|
Traits.Tag := StrToIntDef(Traits.Hint,0);
|
|
end;
|
|
|
|
function TMainForm.GetMotto: String; begin Result := Stats.Hint; end;
|
|
procedure TMainForm.SetMotto(v: String); begin Stats.Hint := v; end;
|
|
|
|
function TMainForm.GetHostName: String; begin Result := Spells.Hint; end;
|
|
procedure TMainForm.SetHostName(v: String); begin Spells.Hint := v; end;
|
|
|
|
function TMainForm.GetHostAddr: String;
|
|
begin
|
|
Result := Equips.Hint;
|
|
if (Result = '') and (GetPasskey <> 0) then
|
|
Result := 'http://www.progressquest.com/knoram.php?';
|
|
end;
|
|
procedure TMainForm.SetHostAddr(v: String); begin Equips.Hint := v; end;
|
|
|
|
function TMainForm.GetLogin: String; begin Result := Inventory.Hint; end;
|
|
procedure TMainForm.SetLogin(v: String); begin Inventory.Hint := v; end;
|
|
|
|
function TMainForm.GetPassword: String; begin Result := Plots.Hint; end;
|
|
procedure TMainForm.SetPassword(v: String); begin Plots.Hint := v; end;
|
|
|
|
function TMainForm.GetGuild: String; begin Result := Label1.Hint; end;
|
|
procedure TMainForm.SetGuild(v: String); begin Label1.Hint := v; end;
|
|
|
|
procedure TMainForm.Q(s: string);
|
|
begin
|
|
fQueue.Items.Add(s);
|
|
Dequeue;
|
|
end;
|
|
|
|
function TMainForm.TaskDone: Boolean;
|
|
begin
|
|
with TaskBar do
|
|
Result := Position >= Max;
|
|
end;
|
|
|
|
function Odds(chance, outof: Integer): Boolean;
|
|
begin
|
|
Result := Random(outof) < chance;
|
|
end;
|
|
|
|
function RandSign(): Integer;
|
|
begin
|
|
Result := Random(2) * 2 - 1;
|
|
end;
|
|
|
|
function Pick(s: TStrings): String;
|
|
begin
|
|
Result := s[Random(s.Count)];
|
|
end;
|
|
|
|
function RandomLow(below: Integer): Integer;
|
|
begin
|
|
Result := Min(Random(below),Random(below));
|
|
end;
|
|
|
|
function PickLow(s: TStrings): String;
|
|
begin
|
|
Result := s[RandomLow(s.Count)];
|
|
end;
|
|
|
|
function Ends(s,e: String): Boolean;
|
|
begin
|
|
Result := Copy(s,1+Length(s)-Length(e),Length(e)) = e;
|
|
end;
|
|
|
|
function Plural(s: String): String;
|
|
begin
|
|
if Ends(s,'y')
|
|
then Result := Copy(s,1,Length(s)-1) + 'ies'
|
|
else if Ends(s,'us')
|
|
then Result := Copy(s,1,Length(s)-2) + 'i'
|
|
else if Ends(s,'ch') or Ends(s,'x') or Ends(s,'s')
|
|
then Result := s + 'es'
|
|
else if Ends(s,'f')
|
|
then Result := Copy(s,1,Length(s)-1) + 'ves'
|
|
else if Ends(s,'man') or Ends(s,'Man')
|
|
then Result := Copy(s,1,Length(s)-2) + 'en'
|
|
else Result := s + 's';
|
|
end;
|
|
|
|
function Split(s: String; field: Integer; separator: String): String;
|
|
var
|
|
p: Integer;
|
|
begin
|
|
while field > 0 do begin
|
|
p := Pos(separator,s);
|
|
s := Copy(s,p+1,10000);
|
|
Dec(field);
|
|
end;
|
|
if Pos(separator,s) > 0
|
|
then Result := Copy(s,1,Pos(separator,s)-1)
|
|
else Result := s;
|
|
end;
|
|
|
|
function Split(s: String; field: Integer): String;
|
|
begin
|
|
result := Split(s, field, '|');
|
|
end;
|
|
|
|
function Indefinite(s: String; qty: Integer): String;
|
|
begin
|
|
if qty = 1 then begin
|
|
if Pos(s[1], 'AEIOUÜaeiouü') > 0
|
|
then Result := 'an ' + s
|
|
else Result := 'a ' + s;
|
|
end else begin
|
|
Result := IntToStr(qty) + ' ' + Plural(s);
|
|
end;
|
|
end;
|
|
|
|
function Definite(s: String; qty: Integer): String;
|
|
begin
|
|
if qty > 1 then
|
|
s := {IntToStr(qty) + ' ' +} Plural(s);
|
|
Result := 'the ' + s;
|
|
end;
|
|
|
|
function Sick(m: Integer; s: String): String;
|
|
begin
|
|
Result := IntToStr(m) + s; // in case I screw up
|
|
case m of
|
|
-5,5: Result := 'dead ' + s;
|
|
-4,4: Result := 'comatose ' + s;
|
|
-3,3: Result := 'crippled ' + s;
|
|
-2,2: Result := 'sick ' + s;
|
|
-1,1: Result := 'undernourished ' + s;
|
|
end;
|
|
end;
|
|
|
|
function Young(m: Integer; s: String): String;
|
|
begin
|
|
Result := IntToStr(m) + s; // in case I screw up
|
|
case -m of
|
|
-5,5: Result := 'foetal ' + s;
|
|
-4,4: Result := 'baby ' + s;
|
|
-3,3: Result := 'preadolescent ' + s;
|
|
-2,2: Result := 'teenage ' + s;
|
|
-1,1: Result := 'underage ' + s;
|
|
end;
|
|
end;
|
|
|
|
function Big(m: Integer; s: String): String;
|
|
begin
|
|
Result := s; // in case I screw up
|
|
case m of
|
|
1,-1: Result := 'greater ' + s;
|
|
2,-2: Result := 'massive ' + s;
|
|
3,-3: Result := 'enormous ' + s;
|
|
4,-4: Result := 'giant ' + s;
|
|
5,-5: Result := 'titanic ' + s;
|
|
end;
|
|
end;
|
|
|
|
function Special(m: Integer; s: String): String;
|
|
begin
|
|
Result := s; // in case I screw up
|
|
case -m of
|
|
1,-1:
|
|
if Pos(' ',Result) > 0
|
|
then Result := 'veteran ' + s
|
|
else Result := 'Battle-' + s;
|
|
2,-2: Result := 'cursed ' + s;
|
|
3,-3:
|
|
if Pos(' ',Result) > 0
|
|
then Result := 'warrior ' + s
|
|
else Result := 'Were-' + s;
|
|
4,-4: Result := 'undead ' + s;
|
|
5,-5: Result := 'demon ' + s;
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.InterplotCinematic;
|
|
var
|
|
nemesis: String;
|
|
i, s: Integer;
|
|
begin
|
|
case Random(3) of
|
|
0: begin
|
|
Q('task|1|Exhausted, you arrive at a friendly oasis in a hostile land');
|
|
Q('task|2|You greet old friends and meet new allies');
|
|
Q('task|2|You are privy to a council of powerful do-gooders');
|
|
Q('task|1|There is much to be done. You are chosen!');
|
|
end;
|
|
1: begin
|
|
Q('task|1|Your quarry is in sight, but a mighty enemy bars your path!');
|
|
nemesis := NamedMonster(GetI(Traits,'Level')+3);
|
|
Q('task|4|A desperate struggle commences with ' + nemesis);
|
|
s := Random(3);
|
|
for i := 1 to Random(1 + Plots.Items.Count) do begin
|
|
Inc(s, 1 + Random(2));
|
|
case s mod 3 of
|
|
0: Q('task|2|Locked in grim combat with ' + nemesis);
|
|
1: Q('task|2|' + nemesis + ' seems to have the upper hand');
|
|
2: Q('task|2|You seem to gain the advantage over ' + nemesis);
|
|
end;
|
|
end;
|
|
Q('task|3|Victory! ' + nemesis + ' is slain! Exhausted, you lose conciousness');
|
|
Q('task|2|You awake in a friendly place, but the road awaits');
|
|
end;
|
|
2: begin
|
|
nemesis := ImpressiveGuy;
|
|
Q('task|2|Oh sweet relief! You''ve reached the protection of the good ' + nemesis);
|
|
Q('task|3|There is rejoicing, and an unnerving encouter with ' + nemesis + ' in private');
|
|
Q('task|2|You forget your ' + BoringItem + ' and go back to get it');
|
|
Q('task|2|What''s this!? You overhear something shocking!');
|
|
Q('task|2|Could ' + nemesis + ' be a dirty double-dealer?');
|
|
Q('task|3|Who can possibly be trusted with this news!? ... Oh yes, of course');
|
|
end;
|
|
end;
|
|
Q('plot|1|Loading');
|
|
end;
|
|
|
|
|
|
function TMainForm.NamedMonster(level: Integer): String;
|
|
var
|
|
lev, i: Integer;
|
|
m: String;
|
|
begin
|
|
lev := 0; // shut up, compiler hint
|
|
for i := 1 to 5 do begin
|
|
m := Pick(K.Monsters.Lines);
|
|
if (Result = '') or (abs(level-StrToInt(Split(m,1))) < abs(level-lev)) then begin
|
|
Result := Split(m,0);
|
|
lev := StrToInt(Split(m,1));
|
|
end;
|
|
end;
|
|
Result := GenerateName + ' the ' + Result;
|
|
end;
|
|
|
|
function TMainForm.ImpressiveGuy: String;
|
|
begin
|
|
Result := Pick(K.ImpressiveTitles.Lines);
|
|
case Random(2) of
|
|
0: Result := Result + ' of the ' + Pick(K.Races.Lines);
|
|
1: Result := Result + ' of ' + GenerateName;
|
|
end;
|
|
end;
|
|
|
|
function TMainForm.MonsterTask(var level: Integer): String;
|
|
var
|
|
qty, lev, i: Integer;
|
|
monster, m1: string;
|
|
definite: Boolean;
|
|
begin
|
|
definite := false;
|
|
for i := level downto 1 do begin
|
|
if Odds(2,5) then
|
|
Inc(level, RandSign());
|
|
end;
|
|
if level < 1 then level := 1;
|
|
// level = level of puissance of opponent(s) we'll return
|
|
|
|
if Odds(1,25) then begin
|
|
// use an NPC every once in a while
|
|
monster := ' ' + Pick(NewGuyForm.Race.Items);
|
|
if Odds(1,2)
|
|
then monster := 'passing' + monster + ' ' + Pick(NewGuyForm.Klass.Items)
|
|
else begin
|
|
monster := PickLow(K.Titles.Lines) + ' ' + GenerateName + ' the' + monster;
|
|
definite := true;
|
|
end;
|
|
lev := level;
|
|
monster := monster + '|' + IntToStr(level) + '|*';
|
|
end else if (fQuest.Caption <> '') and Odds(1,4) then begin
|
|
// use the quest monster
|
|
monster := k.Monsters.Lines[fQuest.Tag];
|
|
lev := StrToInt(Split(monster,1));
|
|
end else begin
|
|
// pick the monster out of so many random ones closest to the level we want
|
|
monster := Pick(K.Monsters.Lines);
|
|
lev := StrToInt(Split(monster,1));
|
|
i := 5;
|
|
while (i > 0) do begin // or (lev - level > 4) do begin
|
|
m1 := Pick(K.Monsters.Lines);
|
|
if abs(level-StrToInt(Split(m1,1))) < abs(level-lev) then begin
|
|
monster := m1;
|
|
lev := StrToInt(Split(monster,1));
|
|
end;
|
|
if i > 0 then Dec(i);
|
|
end;
|
|
end;
|
|
|
|
fTask.Caption := monster;
|
|
Result := Split(monster,0);
|
|
fTask.Caption := 'kill|' + fTask.Caption;
|
|
|
|
qty := 1;
|
|
if (level-lev) > 10 then begin
|
|
// lev is too low. multiply...
|
|
qty := (level + Random(lev)) div max(lev,1);
|
|
if qty < 1 then qty := 1;
|
|
level := level div qty;
|
|
end;
|
|
|
|
if (level - lev) <= -10 then begin
|
|
Result := 'imaginary ' + Result;
|
|
end else if (level-lev) < -5 then begin
|
|
i := 10+(level-lev);
|
|
i := 5-Random(i+1);
|
|
Result := Sick(i,Young((lev-level)-i,Result));
|
|
end else if ((level-lev) < 0) and (Random(2) = 1) then begin
|
|
Result := Sick(level-lev,Result);
|
|
end else if ((level-lev) < 0) then begin
|
|
Result := Young(level-lev,Result);
|
|
end else if (level-lev) >= 10 then begin
|
|
Result := 'messianic ' + Result;
|
|
end else if (level-lev) > 5 then begin
|
|
i := 10-(level-lev);
|
|
i := 5-Random(i+1);
|
|
Result := Big(i,Special((level-lev)-i,Result));
|
|
end else if ((level-lev) > 0) and (Random(2) = 1) then begin
|
|
Result := Big(level-lev,Result);
|
|
end else if ((level-lev) > 0) then begin
|
|
Result := Special(level-lev,Result);
|
|
end;
|
|
|
|
lev := level;
|
|
level := lev * qty;
|
|
|
|
if not definite then Result := Indefinite(Result, qty);
|
|
end;
|
|
|
|
function ProperCase(s:String):String;
|
|
begin
|
|
Result := UpperCase(Copy(s,1,1)) + Copy(s,2,10000);
|
|
end;
|
|
|
|
function TMainForm.EquipPrice: Integer;
|
|
begin
|
|
Result := 5 * GetI(Traits,'Level') * GetI(Traits,'Level')
|
|
+ 10 * GetI(Traits,'Level')
|
|
+ 20;
|
|
end;
|
|
|
|
procedure TMainForm.Dequeue;
|
|
var
|
|
s, a, old: String;
|
|
n, l: Integer;
|
|
begin
|
|
while TaskDone do begin
|
|
if Split(fTask.Caption,0) = 'kill' then begin
|
|
if Split(fTask.Caption,3) = '*' then begin
|
|
WinItem;
|
|
end else if Split(fTask.Caption,3) <> '' then begin
|
|
Add(Inventory,LowerCase(Split(fTask.Caption,1) + ' ' + ProperCase(Split(fTask.Caption,3))),1);
|
|
end;
|
|
end else if fTask.Caption = 'buying' then begin
|
|
// buy some equipment
|
|
Add(Inventory,'Gold',-EquipPrice);
|
|
WinEquip;
|
|
end else if (fTask.Caption = 'market') or (fTask.Caption = 'sell') then with Inventory do begin
|
|
if fTask.Caption = 'sell' then begin
|
|
Tag := GetI(Inventory,1);
|
|
Tag := Tag * GetI(Traits,'Level');
|
|
if Pos(' of ',Items[1].Caption) > 0 then
|
|
Tag := Tag * (1+RandomLow(10)) * (1+RandomLow(GetI(Traits,'Level')));
|
|
Items[0].MakeVisible(false);
|
|
Items.Delete(1);
|
|
Add(Inventory,'Gold',Tag);
|
|
end;
|
|
if Items.Count > 1 then begin
|
|
Task('Selling ' + Indefinite(Inventory.Items[1].Caption, GetI(Inventory,1)), 1 * 1000);
|
|
fTask.Caption := 'sell';
|
|
break;
|
|
end;
|
|
end;
|
|
old := fTask.Caption;
|
|
fTask.Caption := '';
|
|
if (fQueue.Items.Count > 0) then begin
|
|
a := Split(fQueue.Items[0],0);
|
|
n := StrToInt(Split(fQueue.Items[0],1));
|
|
s := Split(fQueue.Items[0],2);
|
|
if (a = 'task') or (a = 'plot') then begin
|
|
if a = 'plot' then begin
|
|
CompleteAct;
|
|
s := 'Loading ' + Plots.Items[Plots.Items.Count-1].Caption;
|
|
end;
|
|
Task(s, n * 1000);
|
|
fQueue.Items.Delete(0);
|
|
end else begin
|
|
raise Exception.Create('bah!');
|
|
end;
|
|
end else with Encumbar do if Position >= Max then begin
|
|
Task('Heading to market to sell loot',4 * 1000);
|
|
fTask.Caption := 'market';
|
|
end else if (Pos('kill|',old) <= 0) and (old <> 'heading') then begin
|
|
if GetI(Inventory, 'Gold') > EquipPrice then begin
|
|
Task('Negotiating purchase of better equipment', 5 * 1000);
|
|
fTask.Caption := 'buying';
|
|
end else begin
|
|
Task('Heading to the killing fields', 4 * 1000);
|
|
fTask.Caption := 'heading';
|
|
end;
|
|
end else begin
|
|
n := GetI(Traits,'Level');
|
|
l := n;
|
|
s := MonsterTask(n);
|
|
n := (2 * InventoryLabelAlsoGameStyle.Tag * n * 1000) div l;
|
|
Task('Executing ' + s, n);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function IndexOf(list: TListView; key: String): Integer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to list.Items.Count-1 do if list.Items.Item[i].Caption = key then begin
|
|
Result := i;
|
|
Exit;
|
|
end;
|
|
with list.Items.Add do begin
|
|
Result := Index;
|
|
Caption := key;
|
|
MakeVisible(false);
|
|
list.Width := list.Width - 1; // trigger an autosize
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.Put(list: TListView; key, value: String);
|
|
begin
|
|
Put(list, IndexOf(list,key), value);
|
|
end;
|
|
|
|
procedure TMainForm.Put(list: TListView; key: String; value: Integer);
|
|
begin
|
|
Put(list,key,IntToStr(value));
|
|
if key = 'STR' then
|
|
Encumbar.Max := 10 + value;
|
|
if list = Inventory then with Encumbar do begin
|
|
Position := Sum(Inventory) - GetI(Inventory,'Gold');
|
|
Hint := IntToStr(Position) + '/' + IntToStr(Max) + ' cubits';
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.Put(list: TListView; pos: Integer; value: String);
|
|
begin
|
|
with list.Items.Item[pos] do begin
|
|
if SubItems.Count < 1
|
|
then SubItems.Add(value)
|
|
else SubItems[0] := value;
|
|
end;
|
|
//list.MultiSelect := true;
|
|
//list.RowSelect := true;
|
|
//list.HideSelection := false;
|
|
list.Items[pos].Selected := true;
|
|
end;
|
|
|
|
function LevelUpTime(level: Integer): Integer; // seconds
|
|
begin
|
|
// 20 minutes per level
|
|
Result := 20 * level * 60;
|
|
end;
|
|
|
|
procedure TMainForm.GoButtonClick(Sender: TObject);
|
|
begin
|
|
with ExpBar do begin
|
|
Position := 0;
|
|
Max := LevelUpTime(1);
|
|
end;
|
|
|
|
fTask.Caption := '';
|
|
fQuest.Caption := '';
|
|
fQueue.Items.Clear;
|
|
|
|
Task('Loading.',2000); // that dot is spotted for later...
|
|
Q('task|10|Experiencing an enigmatic and foreboding night vision');
|
|
Q('task|6|Much is revealed about that wise old bastard you''d underestimated');
|
|
Q('task|6|A shocking series of events leaves you alone and bewildered, but resolute');
|
|
Q('task|4|Drawing upon an unexpected reserve of determination, you set out on a long and dangerous journey');
|
|
Q('task|2|Loading');
|
|
|
|
PlotBar.Max := 26;
|
|
with Plots.Items.Add do begin
|
|
Caption := 'Prologue';
|
|
StateIndex := 0;
|
|
end;
|
|
|
|
StartTimer;
|
|
SaveGame;
|
|
Brag('s');
|
|
end;
|
|
|
|
procedure TMainForm.WinSpell;
|
|
begin
|
|
AddR(Spells, K.Spells.Lines[RandomLow(Min(GetI(Stats,'WIS')+GetI(Traits,'Level'),
|
|
K.Spells.Lines.Count))], 1);
|
|
end;
|
|
|
|
function LPick(list: TStrings; goal: Integer): String;
|
|
var
|
|
i, best, b1: Integer;
|
|
s: String;
|
|
begin
|
|
Result := Pick(list);
|
|
for i := 1 to 5 do begin
|
|
best := StrToInt(Split(Result,1));
|
|
s := Pick(list);
|
|
b1 := StrToInt(Split(s,1));
|
|
if abs(goal-best) > abs(goal-b1) then
|
|
Result := s;
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.WinEquip;
|
|
var
|
|
posn, qual, plus, count: Integer;
|
|
name, modifier: String;
|
|
stuff, better, worse: TStrings;
|
|
begin
|
|
posn := Random(Equips.Items.Count);
|
|
Equips.Tag := posn; // remember as the "best item"
|
|
if posn = 0 then begin
|
|
stuff := K.Weapons.Lines;
|
|
better := K.OffenseAttrib.Lines;
|
|
worse := K.OffenseBad.Lines;
|
|
end else begin
|
|
better := K.DefenseAttrib.Lines;
|
|
worse := K.DefenseBad.Lines;
|
|
if posn = 1
|
|
then stuff := K.Shields.Lines
|
|
else stuff := K.Armors.Lines;
|
|
end;
|
|
name := LPick(stuff,GetI(Traits,'Level'));
|
|
qual := StrToInt(Split(name,1));
|
|
name := Split(name,0);
|
|
plus := GetI(Traits,'Level') - qual;
|
|
if plus < 0 then better := worse;
|
|
count := 0;
|
|
while (count < 2) and (plus <> 0) do begin
|
|
modifier := Pick(better);
|
|
qual := StrToInt(Split(modifier, 1));
|
|
modifier := Split(modifier, 0);
|
|
if Pos(modifier, name) > 0 then Break; // no repeats
|
|
if Abs(plus) < Abs(qual) then Break; // too much
|
|
name := modifier + ' ' + name;
|
|
Dec(plus, qual);
|
|
Inc(count);
|
|
end;
|
|
if plus <> 0 then name := IntToStr(plus) + ' ' + name;
|
|
if plus > 0 then name := '+' + name;
|
|
|
|
Put(Equips, posn, name);
|
|
end;
|
|
|
|
procedure TMainForm.WinStat;
|
|
var
|
|
i,t: Integer;
|
|
function Square(x: Integer): Integer; begin Result := x * x; end;
|
|
begin
|
|
if Odds(1,2)
|
|
then i := Random(Stats.Items.Count)
|
|
else begin
|
|
// favor the best stat so it will tend to clump
|
|
t := 0;
|
|
for i := 0 to 5 do Inc(t,Square(GetI(Stats,i)));
|
|
t := Random(t);
|
|
i := -1;
|
|
while t >= 0 do begin
|
|
Inc(i);
|
|
Dec(t,Square(GetI(Stats,i)));
|
|
end;
|
|
end;
|
|
Add(Stats, Stats.Items[i].Caption, 1);
|
|
end;
|
|
|
|
function TMainForm.SpecialItem: String;
|
|
begin
|
|
Result := InterestingItem + ' of ' +
|
|
Pick(K.ItemOfs.Lines);
|
|
end;
|
|
|
|
function TMainForm.InterestingItem: String;
|
|
begin
|
|
Result := Pick(K.ItemAttrib.Lines) + ' ' +
|
|
Pick(K.Specials.Lines);
|
|
end;
|
|
|
|
function TMainForm.BoringItem: String;
|
|
begin
|
|
Result := Pick(K.BoringItems.Lines);
|
|
end;
|
|
|
|
procedure TMainForm.WinItem;
|
|
begin
|
|
Add(Inventory, SpecialItem, 1);
|
|
end;
|
|
|
|
procedure TMainForm.CompleteQuest;
|
|
var
|
|
lev, level, l, i, montag: Integer;
|
|
m: string;
|
|
begin
|
|
lev := 0; // Quell stupid compiler warning
|
|
with QuestBar do begin
|
|
Position := 0;
|
|
Max := 50 + Random(100);
|
|
end;
|
|
with Quests do begin
|
|
if Items.Count > 0 then begin
|
|
{$IFDEF LOGGING}
|
|
Log('Quest completed: ' + Items[Items.Count-1].Caption);
|
|
{$ENDIF}
|
|
Items[Items.Count-1].StateIndex := 1;
|
|
case Random(4) of
|
|
0: WinSpell;
|
|
1: WinEquip;
|
|
2: WinStat;
|
|
3: WinItem;
|
|
end;
|
|
end;
|
|
while Items.Count > 99 do Items.Delete(0);
|
|
|
|
with Items.Add do begin
|
|
case Random(5) of
|
|
0: begin
|
|
level := GetI(Traits,'Level');
|
|
for i := 1 to 4 do begin
|
|
montag := Random(K.Monsters.Lines.Count);
|
|
m := K.Monsters.Lines[montag];
|
|
l := StrToInt(Split(m,1));
|
|
if (i = 1) or (abs(l - level) < abs(lev - level)) then begin
|
|
lev := l;
|
|
fQuest.Caption := m;
|
|
fQuest.Tag := montag;
|
|
end;
|
|
end;
|
|
Caption := 'Exterminate ' + Definite(Split(fQuest.Caption,0),2);
|
|
end;
|
|
1: begin
|
|
fQuest.Caption := InterestingItem;
|
|
Caption := 'Seek ' + Definite(fQuest.Caption,1);
|
|
fQuest.Caption := '';
|
|
end;
|
|
2: begin
|
|
fQuest.Caption := BoringItem;
|
|
Caption := 'Deliver this ' + fQuest.Caption;
|
|
fQuest.Caption := '';
|
|
end;
|
|
3: begin
|
|
fQuest.Caption := BoringItem;
|
|
Caption := 'Fetch me ' + Indefinite(fQuest.Caption,1);
|
|
fQuest.Caption := '';
|
|
end;
|
|
4: begin
|
|
level := GetI(Traits,'Level');
|
|
for i := 1 to 2 do begin
|
|
montag := Random(K.Monsters.Lines.Count);
|
|
m := K.Monsters.Lines[montag];
|
|
l := StrToInt(Split(m,1));
|
|
if (i = 1) or (abs(l - level) < abs(lev - level)) then begin
|
|
lev := l;
|
|
fQuest.Caption := m;
|
|
end;
|
|
end;
|
|
Caption := 'Placate ' + Definite(Split(fQuest.Caption,0),2);
|
|
fQuest.Caption := '';
|
|
end;
|
|
end;
|
|
{$IFDEF LOGGING}
|
|
Log('Commencing quest: ' + Caption);
|
|
{$ENDIF}
|
|
StateIndex := 0;
|
|
MakeVisible(false);
|
|
end;
|
|
Width := Width - 1; // trigger a column resize
|
|
end;
|
|
SaveGame;
|
|
end;
|
|
|
|
function Rome(var n: Integer; dn: Integer; var s: String; ds: String): Boolean;
|
|
begin
|
|
Result := (n >= dn);
|
|
if Result then begin
|
|
n := n - dn;
|
|
s := s + ds;
|
|
end;
|
|
end;
|
|
|
|
function UnRome(var s: String; dn: Integer; var n: Integer; ds: String): Boolean;
|
|
begin
|
|
Result := (Copy(s,1,Length(ds)) = ds);
|
|
if Result then begin
|
|
s := Copy(s,Length(ds)+1,10000);
|
|
n := n + dn;
|
|
end;
|
|
end;
|
|
|
|
function IntToRoman(n: Integer): String;
|
|
begin
|
|
while Rome(n, 1000, Result, 'M') do ;
|
|
Rome(n, 900, Result, 'CM');
|
|
Rome(n, 500, Result, 'D');
|
|
Rome(n, 400, Result, 'CD');
|
|
while Rome(n, 100, Result, 'C') do ;
|
|
Rome(n, 90, Result, 'XC');
|
|
Rome(n, 50, Result, 'L');
|
|
Rome(n, 40, Result, 'XL');
|
|
while Rome(n, 10, Result, 'X') do ;
|
|
Rome(n, 9, Result, 'IX');
|
|
Rome(n, 5, Result, 'V');
|
|
Rome(n, 4, Result, 'IV');
|
|
while Rome(n, 1, Result, 'I') do ;
|
|
end;
|
|
|
|
function RomanToInt(n: String): Integer;
|
|
begin
|
|
Result := 0;
|
|
while UnRome(n, 1000, Result, 'M') do ;
|
|
UnRome(n, 900, Result, 'CM');
|
|
UnRome(n, 500, Result, 'D');
|
|
UnRome(n, 400, Result, 'CD');
|
|
while UnRome(n, 100, Result, 'C') do ;
|
|
UnRome(n, 90, Result, 'XC');
|
|
UnRome(n, 50, Result, 'L');
|
|
UnRome(n, 40, Result, 'XL');
|
|
while UnRome(n, 10, Result, 'X') do ;
|
|
UnRome(n, 9, Result, 'IX');
|
|
UnRome(n, 5, Result, 'V');
|
|
UnRome(n, 4, Result, 'IV');
|
|
while UnRome(n, 1, Result, 'I') do ;
|
|
end;
|
|
|
|
procedure TMainForm.CompleteAct;
|
|
begin
|
|
PlotBar.Position := 0;
|
|
with Plots do begin
|
|
Items[Items.Count-1].StateIndex := 1;
|
|
PlotBar.Max := 60 * 60 * (1 + 5 * Items.Count); // 1 hr + 5/act
|
|
PlotBar.Hint := 'Cutscene omitted';
|
|
with Items.Add do begin
|
|
Caption := 'Act ' + IntToRoman(Items.Count-1);
|
|
MakeVisible(false);
|
|
StateIndex := 0;
|
|
Width := Width-1;
|
|
end;
|
|
end;
|
|
WinItem;
|
|
WinEquip;
|
|
SaveGame;
|
|
Brag('a');
|
|
end;
|
|
|
|
|
|
{$IFDEF LOGGING}
|
|
procedure TMainForm.Log(line: String);
|
|
var
|
|
stamp: String;
|
|
logname: String;
|
|
log: Text;
|
|
begin
|
|
if FLogEvents then begin
|
|
logname := ChangeFileExt(GameSaveName, '.log');
|
|
DateTimeToString(stamp, '[yyyy-mm-dd hh:nn:ss]', Now);
|
|
AssignFile(log, logname);
|
|
if FileExists(logname) then Append(log) else Rewrite(log);
|
|
WriteLn(log, stamp + ' ' + line);
|
|
Flush(log);
|
|
CloseFile(log);
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TMainForm.ExportCharSheet;
|
|
var
|
|
f: TextFile;
|
|
begin
|
|
AssignFile(f, ChangeFileExt(GameSaveName, '.sheet'));
|
|
Rewrite(f);
|
|
Write(f, CharSheet);
|
|
Flush(f);
|
|
CloseFile(f);
|
|
end;
|
|
|
|
function TMainForm.CharSheet: String;
|
|
var
|
|
i: Integer;
|
|
f: String;
|
|
procedure Wr(a: String); begin f := f + a; end;
|
|
procedure WrLn(a: String); overload; begin Wr(a + #13#10); end;
|
|
procedure WrLn; overload; begin Wr(#13#10); end;
|
|
begin
|
|
Wr(Get(Traits,'Name'));
|
|
if GetHostName <> '' then
|
|
Wr(' [' + GetHostName + ']');
|
|
WrLn;
|
|
WrLn(Get(Traits,'Race') + ' ' + Get(Traits,'Class'));
|
|
WrLn(Format('Level %d (exp. %d/%d)', [GetI(Traits,'Level'), ExpBar.Position, ExpBar.Max]));
|
|
//WrLn('Level ' + Get(Traits,'Level') + ' (' + ExpBar.Hint + ')');
|
|
WrLn;
|
|
with Plots do if Items.Count > 0 then
|
|
WrLn('Plot stage: ' + Items[Items.Count-1].Caption + ' (' + PlotBar.Hint + ')');
|
|
with Quests do if Items.Count > 0 then
|
|
WrLn('Quest: ' + Items[Items.Count-1].Caption + ' (' + QuestBar.Hint + ')');
|
|
WrLn;
|
|
WrLn( 'Stats:');
|
|
WrLn( Format(' STR%7d', [GetI(Stats,'STR')]));
|
|
WrLn( Format(' CON%7d', [GetI(Stats,'CON')]));
|
|
WrLn( Format(' DEX%7d', [GetI(Stats,'DEX')]));
|
|
WrLn( Format(' INT%7d', [GetI(Stats,'INT')]));
|
|
WrLn( Format(' WIS%7d HP Max%7d', [GetI(Stats,'WIS'), GetI(Stats,'HP Max')]));
|
|
WrLn( Format(' CHA%7d MP Max%7d', [GetI(Stats,'CHA'), GetI(Stats,'MP Max')]));
|
|
WrLn;
|
|
WrLn( 'Equipment:');
|
|
for i := 1 to Equips.Items.Count-1 do
|
|
if Get(Equips,i) <> '' then
|
|
WrLn( ' ' + LeftStr(Equips.Items[i].Caption + ' ', 12) + Get(Equips,i));
|
|
WrLn;
|
|
WrLn( 'Spell Book:');
|
|
with Spells do
|
|
for i := 1 to Items.Count-1 do
|
|
WrLn( ' ' + Items[i].Caption + ' ' + Get(Spells,i));
|
|
WrLn;
|
|
WrLn( 'Inventory (' + EncumBar.Hint + '):');
|
|
WrLn( ' ' + Indefinite('gold piece', GetI(Inventory, 'Gold')));
|
|
with Inventory do
|
|
for i := 2 to Items.Count-1 do
|
|
if Pos(' of ', Items[i].Caption) > 0
|
|
then WrLn( ' ' + Definite(Items[i].Caption, GetI(Inventory,i)))
|
|
else WrLn( ' ' + Indefinite(Items[i].Caption, GetI(Inventory,i)));
|
|
WrLn;
|
|
WrLn( '-- ' + DateTimeToStr(Now));
|
|
WrLn( '-- Progress Quest 6.2 - http://progressquest.com/');
|
|
Result := f;
|
|
end;
|
|
|
|
procedure TMainForm.Task(caption: String; msec: Integer);
|
|
begin
|
|
Kill.SimpleText := caption + '...';
|
|
{$IFDEF LOGGING}
|
|
Log(Kill.SimpleText);
|
|
{$ENDIF}
|
|
with TaskBar do begin
|
|
Position := 0;
|
|
Max := msec;
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.Add(list: TListView; key: String; value: Integer);
|
|
var line: String;
|
|
begin
|
|
Put(list, key, value + GetI(list,key));
|
|
if value = 0 then Exit;
|
|
|
|
if value > 0 then line := 'Gained' else line := 'Lost';
|
|
if key = 'Gold' then begin
|
|
key := 'gold piece';
|
|
if value > 0 then line := 'Got paid' else line := 'Spent';
|
|
end;
|
|
if value < 0 then value := -value;
|
|
line := line + ' ' + Indefinite(key, value);
|
|
{$IFDEF LOGGING}
|
|
Log(line);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMainForm.AddR(list: TListView; key: String; value: Integer);
|
|
begin
|
|
Put(list, key, IntToRoman(value + RomanToInt(Get(list,key))));
|
|
end;
|
|
|
|
function TMainForm.Get(list: TListView; key: String): String;
|
|
begin
|
|
Result := Get(list, IndexOf(list,key));
|
|
end;
|
|
|
|
function TMainForm.Get(list: TListView; index: Integer): String;
|
|
begin
|
|
with list.Items.Item[index] do begin
|
|
if SubItems.Count < 1
|
|
then Result := ''
|
|
else Result := SubItems[0];
|
|
end;
|
|
end;
|
|
|
|
function TMainForm.GetI(list: TListView; key: String): Integer;
|
|
begin
|
|
Result := StrToIntDef(Get(list,key),0);
|
|
end;
|
|
|
|
function TMainForm.GetI(list: TListView; index: Integer): Integer;
|
|
begin
|
|
Result := StrToIntDef(Get(list,index),0);
|
|
end;
|
|
|
|
function TMainForm.Sum(list: TListView): Integer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 0 to list.Items.Count - 1 do
|
|
Inc(Result, GetI(list,i));
|
|
end;
|
|
|
|
procedure PutLast(list: TListView; value: String);
|
|
begin
|
|
if list.Items.Count > 0 then
|
|
with list.Items.Item[list.Items.Count-1] do begin
|
|
if SubItems.Count < 1
|
|
then SubItems.Add(value)
|
|
else SubItems[0] := value;
|
|
end;
|
|
list.Width := list.Width - 1; // trigger an autosize
|
|
end;
|
|
|
|
procedure TMainForm.LevelUp;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Add(Traits,'Level',1);
|
|
Add(Stats,'HP Max', GetI(Stats,'CON') div 3 + 1 + Random(4));
|
|
Add(Stats,'MP Max', GetI(Stats,'INT') div 3 + 1 + Random(4));
|
|
for i := 1 to 2 do WinStat;
|
|
WinSpell;
|
|
with ExpBar do begin
|
|
Position := 0;
|
|
Max := LevelUpTime(GetI(Traits,'Level'));
|
|
end;
|
|
SaveGame;
|
|
Brag('l');
|
|
end;
|
|
|
|
procedure TMainForm.ClearAllSelections;
|
|
begin
|
|
Equips.ClearSelection;
|
|
Spells.ClearSelection;
|
|
Stats.ClearSelection;
|
|
Traits.ClearSelection;
|
|
Inventory.ClearSelection;
|
|
Plots.ClearSelection;
|
|
Quests.ClearSelection;
|
|
end;
|
|
|
|
function RoughTime(s: Integer): String;
|
|
begin
|
|
if s < 120 then Result := IntToStr(s) + ' seconds'
|
|
else if s < 60 * 120 then Result := IntToStr(s div 60) + ' minutes'
|
|
else if s < 60 * 60 * 48 then Result := IntToStr(s div 3600) + ' hours'
|
|
else Result := IntToStr(s div (3600 * 24)) + ' days';
|
|
end;
|
|
|
|
procedure TMainForm.Timer1Timer(Sender: TObject);
|
|
var
|
|
gain: Boolean;
|
|
elapsed: Integer;
|
|
begin
|
|
gain := Pos('kill|',fTask.Caption) = 1;
|
|
with TaskBar do begin
|
|
if Position >= Max then begin
|
|
ClearAllSelections;
|
|
|
|
if Kill.SimpleText = 'Loading....' then Max := 0;
|
|
|
|
// gain XP / level up
|
|
if gain then with ExpBar do if Position >= Max
|
|
then LevelUp
|
|
else Position := Position + TaskBar.Max div 1000;
|
|
with ExpBar do Hint := IntToStr(Max-Position) + ' XP needed for next level';
|
|
|
|
// advance quest
|
|
if gain then if Plots.Items.Count > 1 then with QuestBar do if Position >= Max then begin
|
|
CompleteQuest;
|
|
end else if Quests.Items.Count > 0 then begin
|
|
Position := Position + TaskBar.Max div 1000;
|
|
Hint := IntToStr(100 * Position div Max) + '% complete';
|
|
end;
|
|
|
|
// advance plot
|
|
if gain then with PlotBar do if Position >= Max
|
|
then InterplotCinematic
|
|
else Position := Position + TaskBar.Max div 1000;
|
|
|
|
//Time.Caption := FormatDateTime('h:mm:ss',PlotBar.Position / (24.0 * 60 * 60));
|
|
PlotBar.Hint := RoughTime(PlotBar.Max-PlotBar.Position) + ' remaining';
|
|
//PlotBar.Hint := FormatDateTime('h:mm:ss" remaining"',(PlotBar.Max-PlotBar.Position) / (24.0 * 60 * 60));
|
|
|
|
Dequeue();
|
|
end else with TaskBar do begin
|
|
elapsed := LongInt(timeGetTime) - LongInt(Timer1.Tag);
|
|
if elapsed > 100 then elapsed := 100;
|
|
if elapsed < 0 then elapsed := 0;
|
|
Position := Position + elapsed;
|
|
end;
|
|
end;
|
|
Timer1.Tag := timeGetTime;
|
|
end;
|
|
|
|
procedure TMainForm.FormCreate(Sender: TObject);
|
|
begin
|
|
QuestBar.Position := 0;
|
|
PlotBar.Position := 0;
|
|
TaskBar.Position := 0;
|
|
ExpBar.Position := 0;
|
|
Encumbar.Position := 0;
|
|
|
|
FReportSave := true;
|
|
FLogEvents := false;
|
|
FMakeBackups := true;
|
|
FMinToTray := true;
|
|
FExportSheets := false;
|
|
|
|
MakeFileAssociations;
|
|
end;
|
|
|
|
procedure TMainForm.SpeedButton1Click(Sender: TObject);
|
|
begin
|
|
{$IFDEF CHEATS}
|
|
TaskBar.Position := TaskBar.Max;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMainForm.RollCharacter: Boolean;
|
|
var
|
|
f: Integer;
|
|
begin
|
|
Result := true;
|
|
repeat
|
|
if not NewGuyForm.Go then begin
|
|
Result := false;
|
|
Exit;
|
|
end;
|
|
Put(Traits, 'Name', NewGuyForm.Name.Text);
|
|
if FileExists(GameSaveName) and
|
|
(mrNo = MessageDlg('The saved game "' + GameSaveName + '" already exists. Do you want to overwrite it?', mtWarning, [mbYes,mbNo], 0)) then begin
|
|
// go around again
|
|
end else begin
|
|
f := FileCreate(GameSaveName);
|
|
if f = -1 then begin
|
|
ShowMessage('The thought police don''t like the name "' + GameSaveName + '". Choose a name without \\ / : * ? " < > or | in it.');
|
|
end else begin
|
|
FileClose(f);
|
|
Break;
|
|
end;
|
|
end;
|
|
until false;
|
|
|
|
with NewGuyForm do begin
|
|
Put(Traits,'Name',Name.Text);
|
|
Put(Traits,'Race',Race.Items[Race.ItemIndex]);
|
|
Put(Traits,'Class',Klass.Items[Klass.ItemIndex]);
|
|
Put(Traits,'Level',1);
|
|
Put(Stats,'STR',STR.Tag);
|
|
Put(Stats,'CON',CON.Tag);
|
|
Put(Stats,'DEX',DEX.Tag);
|
|
Put(Stats,'INT',INT.Tag);
|
|
Put(Stats,'WIS',WIS.Tag);
|
|
Put(Stats,'CHA',CHA.Tag);
|
|
Put(Stats,'HP Max',Random(8) + CON.Tag div 6);
|
|
Put(Stats,'MP Max',Random(8) + INT.Tag div 6);
|
|
Put(Equips,'Weapon','Sharp Stick');
|
|
Put(Inventory,'Gold',0);
|
|
InventoryLabelAlsoGameStyle.Tag := 3;//GameStyle.Position;
|
|
ClearAllSelections;
|
|
GoButtonClick(NewGuyForm);
|
|
end;
|
|
end;
|
|
|
|
const
|
|
KUsage =
|
|
'Usage: pq [flags] [game.pq3]'#10 +
|
|
#10 +
|
|
'Flags:'#10 +
|
|
' -no-backup Do not make a backup file when saving the game'#10 +
|
|
{$IFDEF LOGGING}
|
|
' -log Create a text log of events as they occur in the game'#10 +
|
|
{$ENDIF}
|
|
' -no-report-save Do not display the "Game saved" message when saving'#10 +
|
|
' -no-tray Do not minimize to the system tray'#10 +
|
|
' -export Export a text character sheet periodically'#10 +
|
|
' -export-only Export a text character sheet now, then exit'#10 +
|
|
' -no-proxy Do not use Internet Explorer proxy settings'#10 +
|
|
' -help Display this chatter (and exit)'#10 ;
|
|
|
|
procedure TMainForm.FormShow(Sender: TObject);
|
|
var
|
|
done, exportandexit: Boolean;
|
|
i: Integer;
|
|
begin
|
|
if Timer1.Enabled then Exit;
|
|
done := false;
|
|
exportandexit := false;
|
|
for i := 1 to ParamCount do begin
|
|
if ParamStr(i) = '-backup'
|
|
then FMakeBackups := true
|
|
{$IFDEF LOGGING}
|
|
else if ParamStr(i) = '-log'
|
|
then FLogEvents := true
|
|
{$ENDIF}
|
|
else if ParamStr(i) = '-no-report-save'
|
|
then FReportSave := false
|
|
else if ParamStr(i) = '-no-tray'
|
|
then FMinToTray := false
|
|
else if ParamStr(i) = '-export'
|
|
then FExportSheets := true
|
|
else if ParamStr(i) = '-export-only'
|
|
then exportandexit := true
|
|
else if ParamStr(i) = '-no-proxy'
|
|
then ProxyOK := false
|
|
else if ParamStr(i) = '-help'
|
|
then begin
|
|
ShowMessage(KUsage);
|
|
Close;
|
|
Exit;
|
|
end else begin
|
|
LoadGame(ParamStr(i));
|
|
if exportandexit then begin
|
|
ExportCharSheet;
|
|
Timer1.Enabled := false;
|
|
Close;
|
|
end;
|
|
Exit;
|
|
end;
|
|
end;
|
|
while not Done do begin
|
|
SetHostName('');
|
|
SetHostAddr('');
|
|
SetLogin('');
|
|
SetPassword('');
|
|
case FrontForm.ShowModal of
|
|
mrOk: begin
|
|
done := RollCharacter;
|
|
end;
|
|
mrRetry: begin
|
|
// load
|
|
if FrontForm.OpenDialog1.Execute then begin
|
|
LoadGame(FrontForm.OpenDialog1.Filename);
|
|
Done := true;
|
|
end;
|
|
end;
|
|
mrYesToAll: begin
|
|
Done := ServerSelectForm.Go;
|
|
end;
|
|
mrCancel: begin
|
|
Close;
|
|
Done := true;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.Button1Click(Sender: TObject);
|
|
begin
|
|
{$IFDEF CHEATS}
|
|
LevelUp;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMainForm.CashInClick(Sender: TObject);
|
|
begin
|
|
{$IFDEF CHEATS}
|
|
WinEquip;
|
|
WinItem;
|
|
WinSpell;
|
|
WinStat;
|
|
Add(Inventory,'Gold',Random(100));
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMainForm.FinishQuestClick(Sender: TObject);
|
|
begin
|
|
{$IFDEF CHEATS}
|
|
QuestBar.Position := QuestBar.Max;
|
|
TaskBar.Position := TaskBar.Max;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMainForm.CheatPlotClick(Sender: TObject);
|
|
begin
|
|
{$IFDEF CHEATS}
|
|
PlotBar.Position := PlotBar.Max;
|
|
TaskBar.Position := TaskBar.Max;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMainForm.SaveGame: Boolean;
|
|
var
|
|
f: TFileStream;
|
|
m: TMemoryStream;
|
|
i: Integer;
|
|
begin
|
|
{$IFDEF LOGGING}
|
|
Log('Saving game: ' + GameSaveName);
|
|
{$ENDIF}
|
|
Result := true;
|
|
try
|
|
if FMakeBackups then begin
|
|
DeleteFile(ChangeFileExt(GameSaveName, '.bak'));
|
|
MoveFile(PChar(GameSaveName), PChar(ChangeFileExt(GameSaveName, '.bak')));
|
|
end;
|
|
f := TFileStream.Create(GameSaveName, fmCreate);
|
|
except
|
|
on EfCreateError do begin
|
|
Result := false;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
//ClearAllSelections;
|
|
m := TMemoryStream.Create;
|
|
for i := 0 to ComponentCount-1 do
|
|
m.WriteComponent(Components[i]);
|
|
|
|
m.Seek(0, soFromBeginning);
|
|
ZCompressStream(m, f);
|
|
|
|
m.Free;
|
|
f.Free;
|
|
end;
|
|
|
|
procedure TMainForm.LoadGame(name: String);
|
|
var
|
|
f: TStream;
|
|
m: TStream;
|
|
i: Integer;
|
|
begin
|
|
FSaveFileName := name;
|
|
m := TMemoryStream.Create;
|
|
f := TFileStream.Create(name, fmOpenRead);
|
|
try
|
|
ZDecompressStream(f, m);
|
|
f.Free;
|
|
except
|
|
on EZCompressionError do begin
|
|
ShowMessage('Error loading game.');
|
|
Close;
|
|
Exit;
|
|
end;
|
|
end;
|
|
Traits.Items.Clear;
|
|
Stats.Items.Clear;
|
|
Equips.Items.Clear;
|
|
m.Seek(0, soFromBeginning);
|
|
for i := 0 to ComponentCount-1 do
|
|
m.ReadComponent(Components[i]);
|
|
m.Free;
|
|
{$IFDEF LOGGING}
|
|
Log('Loaded game: ' + name);
|
|
{$ENDIF}
|
|
StartTimer;
|
|
TriggerAutosizes;
|
|
end;
|
|
|
|
procedure TMainForm.TriggerAutosizes;
|
|
begin
|
|
Plots.Width := 100;
|
|
Quests.Width := 100;
|
|
Inventory.Width := 100;
|
|
Equips.Width := 100;
|
|
Spells.Width := 100;
|
|
Traits.Width := 100;
|
|
Stats.Width := 100;
|
|
end;
|
|
|
|
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
|
|
begin
|
|
if Timer1.Enabled then begin
|
|
Timer1.Enabled := false;
|
|
Shell_NotifyIcon(NIM_DELETE, @FTrayIcon);
|
|
if SaveGame then
|
|
if FReportSave then
|
|
ShowMessage('Game saved as ' + GameSaveName);
|
|
end;
|
|
FReportSave := true;
|
|
Action := caFree;
|
|
end;
|
|
|
|
function TMainForm.GameSaveName: String;
|
|
begin
|
|
if FSaveFileName = '' then begin
|
|
FSaveFileName := Get(Traits,'Name');
|
|
if GetHostName <> '' then
|
|
FSaveFileName := FSaveFileName + ' [' + GetHostName + ']';
|
|
FSaveFileName := FSaveFileName + kFileExt;
|
|
FSaveFileName := ExpandFileName(PChar(FSaveFileName));
|
|
end;
|
|
Result := FSaveFileName;
|
|
end;
|
|
|
|
procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
begin
|
|
if (FindWindow('TAppBuilder', nil) > 0) and (ssCtrl in Shift) and (ssShift in Shift) and (Key = ord('C')) then begin
|
|
{$IFDEF CHEATS}
|
|
Cheats.Visible := not Cheats.Visible;
|
|
{$ENDIF}
|
|
end;
|
|
if (ssCtrl in Shift) and (Key = ord('A')) then begin
|
|
ShowMessage(CharSheet);
|
|
end;
|
|
if GetPasskey = 0 then Exit; // no need for these things
|
|
if (ssCtrl in Shift) and (Key = ord('B')) then begin
|
|
Brag('b');
|
|
Navigate(GetHostAddr + 'name=' + UrlEncode(Get(Traits,'Name')));
|
|
end;
|
|
if (ssCtrl in Shift) and (Key = ord('M')) then begin
|
|
SetMotto(InputBox('Progress Quest', 'Declare your motto!', GetMotto));
|
|
Brag('m');
|
|
Navigate(GetHostAddr + 'name=' + UrlEncode(Get(Traits,'Name')));
|
|
end;
|
|
if (ssCtrl in Shift) and (Key = ord('G')) then begin
|
|
SetGuild(InputBox('Progress Quest', 'Choose a guild.'#13#13'Make sure you undestand the guild rules before you join one. To learn more about guilds, visit http://progressquest.com/guilds.php', GetGuild));
|
|
Guildify;
|
|
end;
|
|
end;
|
|
|
|
procedure Navigate(url: String);
|
|
begin
|
|
ShellExecute(GetDesktopWindow(), 'open', PChar(url), nil, '', SW_SHOW);
|
|
end;
|
|
|
|
function LFSR(pt: String; salt: Integer): Integer;
|
|
var
|
|
k: Integer;
|
|
begin
|
|
Result := salt;
|
|
for k := 1 to Length(pt) do
|
|
Result := Ord(pt[k])
|
|
xor (Result shl 1)
|
|
xor (1 and ((Result shr 31) xor (Result shr 5)));
|
|
for k := 1 to 10 do
|
|
Result := (Result shl 1)
|
|
xor (1 and ((Result shr 31) xor (Result shr 5)));
|
|
end;
|
|
|
|
|
|
procedure TMainForm.Brag(trigger: String);
|
|
var
|
|
url, body: string;
|
|
best, i: Integer;
|
|
const
|
|
flat = 1;
|
|
begin
|
|
if FExportSheets then
|
|
ExportCharSheet;
|
|
if GetPasskey = 0 then Exit; // not a online game!
|
|
url := 'cmd=b&t=' + trigger;
|
|
with Traits do for i := 0 to Items.Count-1 do
|
|
url := url + '&' + LowerCase(Items[i].Caption[1]) + '=' + UrlEncode(Items[i].Subitems[0]);
|
|
url := url + '&x=' + IntToStr(ExpBar.Position);
|
|
url := url + '&i=' + UrlEncode(Get(Equips,Equips.Tag));
|
|
if Equips.Tag > 1 then url := url + '+' + Equips.Items[Equips.Tag].Caption;
|
|
best := 0;
|
|
if Spells.Items.Count > 0 then with Spells do begin
|
|
for i := 1 to Items.Count-1 do
|
|
if (i+flat) * RomanToInt(Get(Spells,i)) >
|
|
(best+flat) * RomanToInt(Get(Spells,best)) then
|
|
best := i;
|
|
url := url + '&z=' + UrlEncode(Items[best].Caption + ' ' + Get(Spells,best));
|
|
end;
|
|
best := 0;
|
|
for i := 1 to 5 do
|
|
if GetI(Stats,i) > GetI(Stats,best) then best := i;
|
|
url := url + '&k=' + Stats.Items[best].Caption + '+' + Get(Stats,best);
|
|
url := url + '&a=' + UrlEncode(Plots.Items[Plots.Items.Count-1].Caption);
|
|
url := url + '&h=' + UrlEncode(GetHostName);
|
|
url := url + RevString;
|
|
url := url + '&p=' + IntToStr(LFSR(url, GetPasskey));
|
|
url := url + '&m=' + UrlEncode(GetMotto);
|
|
url := AuthenticateUrl(GetHostAddr + url);
|
|
try
|
|
body := DownloadString(url);
|
|
if (LowerCase(Split(body,0)) = 'report') then
|
|
ShowMessage(Split(body,1));
|
|
except
|
|
on EWebError do begin
|
|
// 'ats okay.
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TMainForm.AuthenticateUrl(url: String): String;
|
|
begin
|
|
if (GetLogin <> '') or (GetPassword <> '') then
|
|
Result := StuffString(url, 8, 0, GetLogin+':'+GetPassword+'@')
|
|
else
|
|
Result := url;
|
|
end;
|
|
|
|
procedure TMainForm.Guildify;
|
|
var
|
|
url, s,b: string;
|
|
i: Integer;
|
|
begin
|
|
if GetPasskey = 0 then Exit; // not a online game!
|
|
url := 'cmd=guild';
|
|
with Traits do for i := 0 to Items.Count-1 do
|
|
url := url + '&' + LowerCase(Items[i].Caption[1]) + '=' + UrlEncode(Items[i].Subitems[0]);
|
|
url := url + '&h=' + UrlEncode(GetHostName);
|
|
url := url + RevString;
|
|
url := url + '&guild=' + UrlEncode(GetGuild);
|
|
url := url + '&p=' + IntToStr(LFSR(url, GetPasskey));
|
|
url := AuthenticateUrl(GetHostAddr + url);
|
|
try
|
|
b := DownloadString(url);
|
|
s := Take(b);
|
|
if s <> '' then ShowMessage(s);
|
|
s := Take(b);
|
|
if s <> '' then Navigate(s);
|
|
except
|
|
on EWebError do begin
|
|
// 'ats okay.
|
|
Abort;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.OnQueryEndSession(var Msg: TMessage);
|
|
var Action: TCloseAction;
|
|
begin
|
|
FReportSave := false;
|
|
FormClose(Self, Action);
|
|
ReplyMessage(-1);
|
|
end;
|
|
|
|
procedure TMainForm.OnEndSession(var Msg: TMessage);
|
|
var Action: TCloseAction;
|
|
begin
|
|
Msg.Result := 0;
|
|
if Msg.wParam <> 0 then begin
|
|
FReportSave := false;
|
|
FormClose(Self, Action);
|
|
end;
|
|
ReplyMessage(0);
|
|
end;
|
|
|
|
initialization
|
|
RegisterClasses([TMainForm]);
|
|
end.
|
|
|
|
|
|
|