{
OpenRPG - A engine for making RPG audiogames
Based on the Last Crusade audiogame of Patrick Dwyer & Peter S. VanLund
Idealized in Delphi/Pascal by Lucas Alexandre - Sociedade dos Negão
}

program OpenRPG;

{$APPTYPE CONSOLE}

    uses classes, console, GameUI, rfile, rgame, rvars, SDL, SDL_Mixer, sound,
    sysUtils, windows;

function fileSum(filename: string): integer;
var i, j: integer;
count: integer;
begin
count:=0;
ClearSession;
value.LoadFromFile(filename);
for i:=0 to value.Count-1 do
begin
if pos('[CHECKSUM]',value[i])<>0 then
begin
value.Delete(i);
value.Delete(i);
break;
end;
end;
for i:=0 to value.Count-1 do
for j:=1 to length(value[i]) do count:=count+ord(value[i][j]);
fileSum:=count;
end;

procedure MenuMusic;
begin
OpenMusic('sounds\main_theme.ogg');
PlayMusic(-1);
end;

procedure LoadGame(saved: boolean=false);
var i: integer;
procedure loadSaved;
Const
gfile='player.sav';
var title: string;
i: integer;
begin
title:=game.title;
ReadSession(gfile,'GAME');
game.title:=readValue('title');
if game.title<>title then halt;
game.load:=readValue('load');
game.battle_music:=readValue('battle_music');
ClearSession;
ReadSession(gfile,'PLAYER');
with player^ do
begin
attack:=readValue('attack');
block:=readValue('block');
defense:=readValue('defense');
hp:=strToInt(ReadValue('hp'));
gold:=strToInt(readValue('gold'));
map:=readValue('map');
lastBlock:=readValue('lastblock');
end;
ClearSession;
ReadSession(gfile,'MAP');
with map^ do
begin
name:=readValue('name');
bg:=readValue('bg');
end;
ClearSession;
ReadSession(gfile,'BLOCK');
with block^ do
begin
bg:=readValue('bg');
end;
ClearSession;
ReadSession(gfile,'TALK');
for i:=0 to value.Count-1 do
if value[i]='' then break else
player^.talk.Add(value[i]);
ClearSession;
ReadSession(gfile,'DEFEAT');
for i:=0 to value.Count-1 do
if value[i]='' then break else
player^.defeat.Add(value[i]);
ClearSession;
ReadSession(gfile,'COLLECTED');
for i:=0 to value.Count-1 do
if value[i]='' then break else
player^.collected.Add(value[i]);
ClearSession;
ReadSession(gfile,'ITEM');
for i:=0 to value.Count-1 do
if value[i]='' then break else
player^.item.Add(value[i]);
ClearSession;
ReadSession(gfile,'CHECKSUM');
i:=strToInt(readValue('SUM'));
if i<>fileSum('player.sav') then halt;
end;
begin
Rebuild;
MusicFadeOut(3000);
sleep(3000);
if not Saved then
play('sounds\introduction.ogg',0);
for i:=1 to 6 do sleep(499);
play('sounds\loading.wav',0);
ClearSession;
if not ReadSession(game.load,'MAP') then exit;
map^.name:=ReadValue('name');
if saved then loadSaved;
play('sounds\entering.wav',0);
play(map^.name,0);
player^.hp:=100;
if not findItem(player^.attack) then player^.item.Add(player^.attack);
if not findItem(player^.defense) then player^.item.Add(player^.defense);
end;

procedure SaveGame;
var i: integer;
sum: integer;
begin
try
assign(gamefile,'player.sav');
rewrite(gamefile);
with game do
begin
writeln(gamefile,'[GAME]');
writeln(gamefile,'title='+title);
writeln(gamefile,'load='+load);
writeln(gamefile,'battle_music='+battle_music);
writeln(gamefile,'');
end;
with player^ do
begin
writeln(gamefile,'[PLAYER]');
writeln(gamefile,'attack='+attack);
writeln(gamefile,'block='+block);
writeln(gamefile,'defense='+defense);
writeln(gamefile,'gold='+intToStr(gold));
writeln(gamefile,'hp='+intToStr(hp));
writeln(gamefile,'map='+map);
writeln(gamefile,'lastblock='+lastBlock);
writeln(gamefile,'');
end;
with map^ do
begin
writeln(gamefile,'[MAP]');
writeln(gamefile,'name='+name);
writeln(gamefile,'bg='+bg);
writeln(gamefile,'');
end;
with block^ do
begin
writeln(gamefile,'[BLOCK]');
writeln(gamefile,'bg='+bg);
writeln(gamefile,'');
end;
writeln(gamefile,'[TALK]');
for i:=0 to player^.talk.Count-1 do writeln(gamefile,player^.talk[i]);
writeln(gamefile,'');
writeln(gamefile,'[COLLECTED]');
for i:=0 to player^.collected.Count-1 do writeln(gamefile,player^.collected[i]);
writeln(gamefile,'');
writeln(gamefile,'[DEFEAT]');
for i:=0 to player^.defeat.Count-1 do writeln(gamefile,player^.defeat[i]);
writeln(gamefile,'');
writeln(gamefile,'[ITEM]');
for i:=0 to player^.item.Count-1 do writeln(gamefile,player^.item[i]);
writeln(gamefile,'');
close(gamefile);
sum:=fileSum('player.sav');
append(gamefile);
writeln(gamefile,'[CHECKSUM]');
writeln(gamefile,'SUM='+intToStr(sum));
close(gamefile);
play('sounds\saved.wav',0);
Except
end;
end;

procedure Replace(item: string);
var type1, type2: string;
level1, level2: string;
name1, name2: string;
c: char;
begin
name2:=readValue('name');
type2:=readValue('type');
if type2='weapon' then
level2:=readValue('attack')
else if type2='armor' then
level2:=readValue('defense');
ClearSession;
if type2='weapon' then
readSession('itens.rpg',player^.attack)
else
if type2='armor' then
readSession('itens.rpg',player^.defense);
name1:=readValue('name');
type1:=readValue('type');
if type1='weapon' then
level1:=readValue('attack')
else
if type1='armor' then
level1:=readValue('defense');
play('sounds\g_replace.wav',0);
play(name1,0);
play('sounds\level.wav',0);
speakNumber(strToInt(level1));
play('sounds\g_with.wav',0);
play(name2,0);
play('sounds\level.wav',0);
speakNumber(strToInt(level2));
repeat
c:=upcase(readkey);
until (c='Y') or (c='N');
if c='Y' then
begin
if type2='weapon' then
player^.attack:=item
else
if type2='armor' then
player^.defense:=item;
play('sounds\g_equipped.wav',0);
play(name2,0);
end;
end;

procedure Cure;
var i: integer;
begin
for i:=0 to player^.item.Count-1 do
begin
ClearSession;
ReadSession('itens.rpg',player^.item[i]);
if readValue('type')='cure' then
begin
deleteItem(player^.item[i]);
player^.hp:=player^.hp+strToInt(readValue('hp'));
if player^.hp>100 then player^.hp:=100;
play(readValue('sound'),0);
play('sounds\player_nh.wav',0);
speakNumber(player^.hp);
play('sounds\hp.wav',0);
end;
end;
end;

procedure GetItens(battle: boolean=false);
var s: string;
i,j,k: integer;
label noitems;
begin
s:=ReadValue('contains');
if s<>'' then
begin
k:=0;
for j:=1 to subValues(s) do
begin
if inList(@player^.collected,subValue(s,j),player^.block,player^.map) then
inc(k);
end;
if k=subValues(s) then
goto noitems;
for i:=1 to subValues(s) do
begin
if inList(@player^.collected,subValue(s,i),player^.block,game.load) then
continue;
player^.collected.Add(subValue(s,i)+':'+player^.block+'@'+game.load);
player^.item.Add(subValue(s,i));
ClearSession;
ReadSession('itens.rpg',subValue(s,i));
if battle then
play('sounds\collected.wav',0)
else play('sounds\g_found.wav',0);
play(readValue('name'),0);
if readValue('type')='gold' then
begin
player^.gold:=player^.gold+strToInt(readValue('price'));
play('sounds\player_nh.wav',0);
speakNumber(player^.gold);
play('sounds\gold.wav',0);
end;
if (readValue('type')='weapon') or (readValue('type')='armor') then
Replace(subValue(s,i));
end;
if not battle then play('sounds\g_nomore.wav',0);
exit;
end
else
if not battle then goto noitems;

noitems:
play('sounds\g_noitems.wav',0);
end;

procedure leprechaun;
var c: char;
label again;
procedure lepgame;
var r: Cardinal;
begin
randomize; r:=1+random(2);
play('sounds\rules_lep.wav',0);
play('sounds\123_lep.wav',0);
repeat
c:=upcase(readkey);
until (c in ['1'..'3']);
if c=intToStr(r) then
begin
r:=25+random(75);
player^.gold:=player^.gold+r;
play('sounds\pwin_lep.wav',0);
play('sounds\player_nh.wav',0);
speakNumber(player^.gold);
play('sounds\gold.wav',0);
end
else
begin
player^.gold:=player^.gold-50;
play('sounds\plose_lep.wav',0);
play('sounds\player_nh.wav',0);
speakNumber(player^.gold);
play('sounds\gold.wav',0);
end;
end;
procedure noplay;
var r: integer;
s: string;
begin
randomize; r:=random(2);
s:=readValue('mad');
if s='1' then
begin
if r=0 then
play('sounds\noplaygood_lep.wav',0)
else
if r>0 then
begin
l:=false;
play(readValue('mad_sound'),0);
player^.gold:=player^.gold div 2;
play('sounds\lepgone.wav',0);
play('sounds\player_nh.wav',0);
speakNumber(player^.gold);
play('sounds\gold.wav',0);
ClearSession;
readSession(player^.map,player^.block);
player^.defeat.Add(readValue('person')+':'+player^.block+'@'+player^.map);
end;
end
else
if (s='0') or (s='') then
play('sounds\noplaygood_lep.wav',0);
end;
begin
play('sounds\encountered.wav',0);
play(readValue('name'),0);
play('sounds\play_lep.wav',0);
repeat
c:=upcase(readkey);
until (c='Y') or (c='N');
if (c='Y') and (player^.gold<50) then
begin
play('sounds\more_lep.wav',0);
exit;
end;
if c='Y' then
lepgame   else
begin
noplay;
exit;
end;

again:
play('sounds\again_lep.wav',0);
repeat
c:=upcase(readkey);
until (c='Y') or (c='N');
if c='Y' then
begin
if (c='Y') and (player^.gold<50) then
begin
play('sounds\more_lep.wav',0);
exit;
end;
lepgame; goto again;
end
else
play('sounds\goodbye_lep.wav',0);
end;

procedure deathLep;
var r: integer;
begin
randomize; r:=random(3);
if r<=1 then
begin
ClearSession;
readSession(player^.map,player^.block);
player^.defeat.Add(readValue('person')+':'+player^.block+'@'+player^.map);
play('sounds\survive_lep.wav',0);
play('sounds\lepgone.wav',0);
player^.hp:=player^.hp div 2;
play('sounds\player_nh.wav',0);
speakNumber(player^.hp);
play('sounds\hp.wav',0);
end
else
if r>=2 then
begin
ClearSession;
readSession(player^.map,player^.block);
player^.defeat.Add(readValue('person')+':'+player^.block+'@'+player^.map);
randomize;
player^.gold:=player^.gold+200+random(50);
play('sounds\death_lep.wav',0);
play('sounds\collected.wav',0);
play('sounds\pot_gold.wav',0);
play('sounds\player_nh.wav',0);
speakNumber(player^.gold);
play('sounds\gold.wav',0);
end;
end;

function pressed(key: integer): boolean;
var value: integer;
begin
pressed:=false;
value:=GetKeyState(key);
if (value=1) or (value<0) then
pressed:=true;
end;

function processBlock: boolean;
var s, t: string;
b: boolean;
c: char;
r: integer;

a: integer;
d: integer;
ahp: integer;
enemy: string;
pa: integer;
pd: integer;
v: string;
w: string;
label action;
function run: boolean;
var r: integer;
begin
run:=false;
randomize;
r:=random(3);
if r=1 then
begin
run:=true;
play('sounds\run_yes.wav',0);
MusicFadeOut(1000);
sleep(1000);
player^.block:=LastBlock;
exit;
end
else
play('sounds\run_no.wav',0);
end;
procedure LoadMusic;
begin
if not ReadSession(player^.map,player^.block) then exit;
block^.bg:=readValue('bg');
OpenMusic(block^.bg);
PlayMusic(-1);
end;
function CheckVictory: boolean;
begin
CheckVictory:=false;
if (ahp<=0) then
begin
b:=false;
play('sounds\victory.wav',0);
ClearSession;
ReadSession(player^.map,player^.block);
readSession('characters.rpg',readValue('person'));
if readValue('contains')<>'' then
GetItens(true);
MusicFadeOut(1000);
sleep(1000);
player^.defeat.Add(enemy+':'+player^.block+'@'+game.load);
CheckVictory:=true;
end;
end;
procedure processVendor;
var c: char;
i: integer;
products: TStringList;
procedure infoItem;
begin
if readValue('type')='weapon' then
begin
play('sounds\level.wav',0);
speakNumber(strToInt(readValue('attack')));
end
else
if readValue('type')='armor' then
begin
play('sounds\level.wav',0);
speakNumber(strToInt(readValue('defense')));
end;
end;
label again;
begin
products:=TStringList.Create;
play('sounds\v_welcome.wav',0);
play(readValue('name'),0);
play('sounds\v_today.wav',0);
repeat
c:=upcase(readkey);
until (c='Y') or (c='N');
if c='Y' then
begin
play('sounds\player_nh.wav',0);
speaknumber(player^.gold);
play('sounds\gold.wav',0);
products.CommaText:=readValue('products');
for i:=0 to products.Count-1 do
begin
clearSession;
readSession('itens.rpg',products[i]);
if (findItem(products[i])) and (readValue('type')<>'cure') then continue;
again:
play('sounds\v_purchase.wav',0);
play(readValue('name'),0);
infoItem;
play('sounds\v_for.wav',0);
speakNumber(strToInt(readValue('price')));
play('sounds\gold.wav',0);
repeat
c:=upcase(readkey);
until (c='N') or (c='Y');
if c='Y' then
begin
if player^.gold<strToInt(readValue('price')) then
begin
play('sounds\v_cannot.wav',0);
continue;
end;
player^.gold:=player^.gold-strToInt(readValue('price'));
player^.item.Add(products[i]);
play('sounds\player_nh.wav',0);
speakNumber(player^.gold);
play('sounds\v_goldremain.wav',0);
if readValue('type')='cure' then goto again;
end;
end;
end;
products.Free;
play('sounds\v_goodbye.wav',0);
end;
function CheckDeath: boolean;
begin
CheckDeath:=false;
if (player^.hp<=0) then
begin
play('sounds\player_x.wav',0);
processBlock:=false;
CheckDeath:=true;
exit;
end;
end;
procedure attack1;
begin
play(v,0);
r:=random(pa+4);
r:=r-d;
if r<=0 then
play('sounds\player_m.wav',0)
else
begin
ahp:=ahp-r;
play('sounds\player_a.wav',0);
speakNumber(r);
if CheckVictory then
begin
LoadMusic;
exit;
end;
play('sounds\enemy_nh.wav',0);
speakNumber(ahp);
play('sounds\hp_remain.wav',0);
end;
end;
procedure attack2;
var s: string;
begin
ClearSession;
ReadSession(player^.map,player^.block);
s:=ReadValue('person');
ClearSession;
ReadSession('characters.rpg',s);
randomize;
play(w,0);
r:=random(strToInt(readValue('attack'))+4);
r:=r-pd;
if r<=0 then
play('sounds\enemy_m.wav',0)
else
begin
player^.hp:=player^.hp-r;
play('sounds\player_l.wav',0);
speakNumber(r);
if r>1 then
play('sounds\hp.wav',0)
else play('sounds\hp_1.wav',0);
if CheckDeath then
begin
MusicFadeOut(3000);
sleep(3000);
exit;
end;
play('sounds\player_nh.wav',0);
speakNumber(player^.hp);
if player^.hp>1 then
play('sounds\hp_remain.wav',0)
else play('sounds\hp_remain1.wav',0);
end;
end;
begin
processBlock:=true;
if player^.hp<=0 then exit;
ClearSession;
if not ReadSession(player^.map,player^.block) then exit;
s:=readValue('person');
if s='' then LoadMusic;
if s<>'' then
begin
ClearSession;
readSession('characters.rpg',s);
t:=readValue('type');
if t='leprechaun' then
begin
LoadMusic;
if inList(@player^.defeat,s,player^.block,game.load) then
goto action;
Leprechaun;
l:=true;
end;
if t='enemy' then
begin
if inList(@player^.defeat,s,player^.block,game.load) then
begin
LoadMusic;
goto action;
end;
enemy:=s;
OpenMusic(game.battle_music);
playMusic(-1);
play('sounds\encountered.wav',0);
play(readValue('name'),0);
play('sounds\enemy_a.wav',0);
speakNumber(strToInt(readValue('attack')));
play('sounds\enemy_d.wav',0);
speakNumber(strToInt(readValue('defense')));
play('sounds\enemy_h.wav',0);
t:=readValue('hp');
speakNumber(strToInt(t));
ahp:=strToInt(t);
b:=true;
a:=strToInt(readValue('attack'));
d:=strToInt(readValue('defense'));
w:=readValue('sound');
ClearSession;
ReadSession('itens.rpg',player^.attack);
v:=readValue('sound');
pa:=strToInt(readValue('attack'));
ClearSession;
ReadSession('itens.rpg',player^.defense);
pd:=strToInt(readValue('defense'));
if strToInt(t)>1 then
play('sounds\hp.wav',0)
else play('sounds\hp_1.wav',0);
play('sounds\run_away.wav',0);
repeat
c:=upcase(readkey);
until (c='R') or (c='Y') or (C='N') or (C='A');
if (c='Y') or (c='R') then
if run then exit;
attack2;
end;
if player^.hp<=0 then
begin
processBlock:=false;
 exit;
end;
if t='vendor' then
begin
LoadMusic;
processVendor;
end;
if t='friend' then
begin
LoadMusic;
if inList(@player^.talk,s,player^.block,game.load) then
begin
play('sounds\talkto.wav',0);
play(readValue('name'),0);
c:=upcase(readkey);
if c<>'Y' then goto action;
end
else
begin
play('sounds\encountered.wav',0);
play(readValue('name'),0);
end;
player^.talk.Add(s+':'+player^.block+'@'+player^.map);
play(readValue('dialog'),0);
end;
end;

action:
c:=upcase(readkey);
if b then
if (c<>'A') and (c<>'R') and (c<>'P') then c:=#0;
if c='P' then Cure;
if c=#32 then
begin
ClearSession;
readSession('itens.rpg',player^.attack);
play('sounds\player_s.wav',0);
SpeakNumber(strToInt(readValue('attack')));
ClearSession;
ReadSession('itens.rpg',player^.defense);
play('sounds\player_d.wav',0);
SpeakNumber(strToInt(readValue('defense')));
play('sounds\player_nh.wav',0);
speakNumber(player^.hp);
if player^.hp>1 then
play('sounds\hp.wav',0)
else play('sounds\hp1.wav',0);
end;
if (rawKey in [37..40]) and (not b) then
begin
if rawKey=37 then c:='o';
if rawKey=38 then c:='n';
if rawKey=39 then c:='l';
if rawKey=40 then c:='s';
ClearSession;
ReadSession(player^.map,player^.block);
s:=ReadValue(c);
if s<>'' then
begin
l:=false;
play('sounds\g_heading.wav',0);
LastBlock:=player^.block;
case c of
'n': play('sounds\g_north.wav',0);
's': play('sounds\g_south.wav',0);
'l': play('sounds\g_east.wav',0);
'o': play('sounds\g_west.wav',0);
end;
MusicFadeOut(1000);
sleep(1000);
player^.block:=s;
exit;
end;
end;
if (c='R') and (b) then
begin
if not run then
attack2 else exit;
end;
if c='S' then
begin
ClearSession;
ReadSession(player^.map,player^.block);
GetItens;
end;
if c='V' then SaveGame;
if (c='A') and (l) then
begin
if  inList(@player^.defeat,readValue('person'),player^.block,player^.map) then goto action;
l:=false;
deathLep;
end;
if c=#27 then
begin
processBlock:=false;
exit;
end;
if (c='A') and (b) then
begin
attack1; if b then attack2;
if not b then exit;
end;
c:=#0;
goto action;

end;

function menu: boolean;
var c: char;
begin
menu:=true;
if (c=#0) or (c='1') or (c='2') or (c='3') then
play('sounds\menu.wav',0);
c:=upcase(readkey);
case c of
#27: halt;
'1':
begin
LoadGame;
while processBlock do;
MenuMusic;
play('sounds\menu.wav',0);
end;
'2':
begin
loadGame(true);
while processBlock do;
MenuMusic;
play('sounds\menu.wav',0);
end;
'3': begin
play('sounds\dir_skip.wav',0);
play('sounds\dir_spacebar.wav',0);
play('sounds\dir_characters.wav,0',0);
play('sounds\dir_enemy.wav',0);
play('sounds\dir_items.wav',0);
play('sounds\dir_location.wav',0);
play('sounds\dir_save.wav',0);
play('sounds\dir_quit.wav',0);
end;
end;
c:=#0;
end;

begin
try
if not OpenCFG then halt;
clrscr;
GameInfo;
with game do
begin
if title='' then title:='OpenRPG';
if load='' then exit;
SetWindowTitle(title);
MenuMusic;
play('sounds\title.wav',0);
play('sounds\menu.wav',0);
end;
while menu do;
Except
end;
end.
