www.pudn.com > SiegeOfAvalon.rar > Engine.pas


unit Engine;
{******************************************************************************}
{ }
{ Siege Of Avalon : Open Source Edition }
{ ------------------------------------- }
{ }
{ Portions created by Digital Tome L.P. Texas USA are }
{ Copyright ©1999-2000 Digital Tome L.P. Texas USA }
{ All Rights Reserved. }
{ }
{ Portions created by Team SOAOS are }
{ Copyright (C) 2003 - Team SOAOS. }
{ }
{ }
{ Contributor(s) }
{ -------------- }
{ Dominique Louis <Dominique@SavageSoftware.com.au> }
{ }
{ }
{ }
{ You may retrieve the latest version of this file at the SOAOS project page : }
{ http://www.sourceforge.com/projects/soaos }
{ }
{ The contents of this file maybe used with permission, subject to }
{ the GNU Lesser General Public License Version 2.1 (the "License"); you may }
{ not use this file except in compliance with the License. You may }
{ obtain a copy of the License at }
{ http://www.opensource.org/licenses/lgpl-license.php }
{ }
{ Software distributed under the License is distributed on an }
{ "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or }
{ implied. See the License for the specific language governing }
{ rights and limitations under the License. }
{ }
{ Description }
{ ----------- }
{ }
{ }
{ }
{ }
{ }
{ }
{ }
{ Requires }
{ -------- }
{ DirectX Runtime libraris on Win32 }
{ They are available from... }
{ http://www.microsoft.com. }
{ }
{ Programming Notes }
{ ----------------- }
{ }
{ }
{ }
{ }
{ Revision History }
{ ---------------- }
{ July 13 2003 - DL : Initial Upload to CVS }
{ }
{******************************************************************************}

{$INCLUDE Anigrp30cfg.inc}

interface

uses
Classes,
Anigrp30,
AniDec30,
ExtCtrls,
Windows,
Math,
SysUtils,
INIFiles,
{$IFDEF DX5}
DirectX,
{$ELSE}
DirectDraw,
{$ENDIF}
LogFile,
MMSystem,
Graphics,
Resource,
Titles,
DFX,
MousePtr;

procedure CreateGlobals;
procedure FreeGlobals;
function GetCharactersInRadius( X, Y : longint; Radius : single ) : TStringList;
procedure RunScript( Me : TObject; Script : string );
procedure Converse( ObjectRef : TObject; Conversation : string );
function FormatFP( D : double ) : string;
function UnFormatFP( S : string ) : double;
procedure CheckCache;
procedure GetChapters( INI : TINIFile );
function SymbolReplacement( const Script : string ) : string;

const
PI = 3.1415926535;
pi2 = 2 * PI;

var
Game : TAniView;
GameMap : TAniMap;
Figures, FigureInstances : TStringList;
Sounds : TList;
ActiveTriggers : TList;
SayList : TList;
ShadowImage : IDirectDrawSurface;
GlowImage : TRLESprite;
BaseLightType : longint;
DefaultPath : string;
CachePath : string;
MapKnown : boolean;
Themes : TStringList;
GameName : string;
LVLFile : string;
CurrentScene : string;
CurrentStartingPoint : string;
TravelList : TStringList;
PlotShadows : boolean;
DefaultPants : TLayerResource;
FemDefaultPants : TLayerResource;
ElfDefaultPants : TLayerResource;
RatResource : TCharacterResource;
WolfResource : TCharacterResource;
GIFToPOX : boolean;
AllSpells : boolean;
Bikini : boolean;
Quests : TStringList;
Adventures : TStringList;
MouseCursor : TMousePtr;
ReadCache : boolean;
WriteCache : boolean;
GlobalBrightness : longint;
UseDirectSound : boolean;
MaxCacheSize : int64;
LoadingFromSaveFile : boolean;
SpawnList : TList;
Chapters : int64;
TalkToMe : boolean;
BodyRotResource : TResource;
NoPageNumbers : boolean;
UseSmallFont : boolean;
NoTransit : boolean;
SaveMsg : string;
FullInvMsg : string;
ChestMsg : string;
SOLName : string;
QuickSave : string;
BlackScript : string;
implementation

uses
AniDemo,
Character,
Parts,
Effects,
Display,
Spells1,
music;

type
TCacheInfo = record
Name : ShortString;
Size : longint;
Date : TDateTime;
end;

const
MaxScriptEntry = 40;
Precision = 1000000;

var
ScriptEntryCount : integer;

procedure CreateGlobals;
const
FailName : string = 'Engine.CreateGlobals';
begin
{$IFDEF DODEBUG}
if ( CurrDbgLvl >= DbgLvlSevere ) then
Log.LogEntry( FailName );
{$ENDIF}
try

Sprites := TSpriteManager.create( 400 );
Figures := TStringList.create;
Figures.capacity := 256;
FigureInstances := TStringList.create;
FigureInstances.capacity := 1024;
Sounds := TList.create;
ActiveTriggers := TList.create;
PartManager := TPartManager.Create( ItemDB, XRefDB );
TitlesManager := TTitlesDB.create( TitlesDB );
NPCList := TList.create;
NPCList.capacity := 5;
Themes := TStringList.create;
Themes.Sorted := true;
TravelList := TStringList.Create;
TravelList.Sorted := true;
Quests := TStringList.create;
Adventures := TStringList.Create;
SayList := TList.Create;
SpawnList := TList.create;
ExText.Open( 'Engine' );
SaveMsg := ExText.GetText( 'Save' );
if SaveMsg = '' then
SaveMsg := 'Game Saved';
FullInvMsg := ExText.GetText( 'Full' );
if FullInvMsg = '' then
FullInvMsg := 'I cannot carry anymore';
ChestMsg := ExText.GetText( 'Chest' );
if ChestMsg = '' then
ChestMsg := '>>> Previously missing items placed on ground <<<';
SOLName := ExText.GetText( 'SOL' );
if SOLName = '' then
SOLName := 'Start of Level';
QuickSave := ExText.GetText( 'QuickSave' );
if QuickSave = '' then
QuickSave := 'QuickSave';
ExtExt.Close;

except
on E : Exception do
Log.log( FailName, E.Message, [ ] );
end;
end;

procedure FreeGlobals;
const
FailName : string = 'Engine.FreeGlobals';
begin
{$IFDEF DODEBUG}
if ( CurrDbgLvl >= DbgLvlSevere ) then
Log.LogEntry( FailName );
{$ENDIF}
try

Sprites.free; Sprites := nil;
Figures.free; Figures := nil;
FigureInstances.free; FigureInstances := nil;
Sounds.free; Sounds := nil;
ActiveTriggers.free; ActiveTriggers := nil;
PartManager.free; PartManager := nil;
TitlesManager.Free; TitlesManager := nil;
NPCList.free; NPCList := nil;
Themes.free; Themes := nil;
TravelList.free; TravelList := nil;
Quests.Free; Quests := nil;
Adventures.Free; Adventures := nil;
SayList.Free; SayList := nil;
SpawnList.free; SpawnList := nil;

except
on E : Exception do
Log.log( FailName, E.Message, [ ] );
end;
end;

function GetCharactersInRadius( X, Y : longint; Radius : single ) : TStringList;
var
i, j : integer;
List : TList;
const
FailName : string = 'Engine.GetCharactersInRadius';
begin
result := nil;

{$IFDEF DODEBUG}
if ( CurrDbgLvl >= DbgLvlSevere ) then
Log.LogEntry( FailName );
{$ENDIF}
try

List := Game.FindInRadius( X, Y, Radius );
if assigned( List ) then
begin
for i := 0 to List.count - 1 do
begin
if TAniFigure( List.items[ i ] ) is TCharacter then
begin
if not assigned( result ) then
Result := TStringList.create;
j := result.add( TCharacter( List.items[ i ] ).GUID );
result.objects[ j ] := List.items[ i ];
end;
end;
end;
List.free;

except
on E : Exception do
Log.log( FailName, E.Message, [ ] );
end;
end;

procedure GetChapters( INI : TINIFile );
var
List : TStringList;
i : integer;
S, Key, Value : string;
KeyIndex : int64;
begin
Chapters := 1;
List := TStringList.create;
try
INI.ReadSectionValues( 'Chapters', List );
for i := 0 to List.count - 1 do
begin
try
S := List.Strings[ i ];
Key := Parse( S, 0, '=' );
Value := Parse( S, 1, '=' );
if ( lowercase( copy( Key, 1, 8 ) ) = 'chapter ' ) and ( lowercase( Value ) = 'available' ) then
begin
KeyIndex := StrToInt( copy( Key, 9, length( Key ) - 8 ) ) - 1;
Chapters := Chapters or ( 1 shl KeyIndex );
end;
except
end;
end;
finally
List.free;
end;
end;

function FormatFP( D : double ) : string;
var
L : int64;
begin
L := trunc( D );
result := inttostr( L ) + '.' + Inttostr( trunc( ( D - L ) * Precision ) );
end;

function UnFormatFP( S : string ) : double;
var
i : integer;
S1, S2 : string;
begin
i := Pos( '.', S );
if i > 0 then
begin
if i = 1 then
S1 := '0'
else
S1 := copy( S, 1, i - 1 );
S2 := copy( S, i + 1, Length( S ) - i );
if Length( S2 ) > 6 then
S2 := copy( S2, 1, 6 );
if Length( S2 ) = 0 then
result := StrToInt64( S1 )
else
result := StrToInt64( S1 ) + ( StrToInt64( S2 ) / Power( 10, Length( S2 ) ) );
end
else
result := StrToInt64( S );
end;

function SymbolReplacement( const Script : string ) : string;
var
S, S0, S1, S2 : string;
i, j : integer;
INI : TINIFile;
begin
INI := nil;
result := Script;
i := Pos( '#', result );
while ( i > 0 ) do
begin
S := Parse( result, 1, '#' );
j := Length( S );
if not assigned( INI ) then
INI := TINIFile.create( DefaultPath + 'maps/symbols.ini' );
S1 := Parse( S, 0, '.' );
S2 := Parse( S, 1, '.' );
S0 := INI.ReadString( S1, S2, '' );
result := Copy( result, 1, i - 1 ) + S0 + Copy( result, i + j + 2, Length( result ) - i - j - 1 );
i := Pos( '#', result );
end;
if assigned( INI ) then
INI.free;

i := Pos( #13#10, result ); //Strip CRLFs
while ( i > 0 ) do
begin
result := Copy( result, 1, i - 1 ) + Copy( result, i + 2, Length( result ) - i - 2 );
i := Pos( #13#10, result );
end;

i := Pos( #13, result ); //Strip CRs
while ( i > 0 ) do
begin
result := Copy( result, 1, i - 1 ) + Copy( result, i + 1, Length( result ) - i - 1 );
i := Pos( #13#10, result );
end;

i := Pos( #9, result ); //Strip Tabs
while ( i > 0 ) do
begin
result := Copy( result, 1, i - 1 ) + Copy( result, i + 1, Length( result ) - i - 1 );
i := Pos( #13#10, result );
end;
end;

procedure RunScript( Me : TObject; Script : string );
var
h, i, j, k : integer;
r : Integer;
T : single;
X, Y : Integer;
iLoop : integer;
S0, S1, S2, S3, S4, S5, Command, Token, ObjectName : string;
ObjectRef : TGameObject;
Parms : string;
Event : string;
Effect : TEffect;
IfFailed : boolean;
IfLevel, NewIfLevel : integer;
Object1 : TGameObject;
List, Group : TStringList;
NewEffect : TEffect;
NewItem : TItem;
NewCharacter : TCharacter;
const
FailName : string = 'Engine.RunScript.';
begin
{$IFDEF DODEBUG}
if ( CurrDbgLvl >= DbgLvlSevere ) then
Log.LogEntry( FailName );
{$ENDIF}
try
try
if Script = '' then
exit;
if ScriptEntryCount > MaxScriptEntry then
begin
log.log( 'ERROR: Runscript exceeded maxscript' );
exit; //Prevent infinite loop
end;
inc( ScriptEntryCount );

Script := SymbolReplacement( Script );
IfLevel := 0;
i := 0;
Command := Trim( Parse( Script, i, ';' ) );
while Command <> '' do
begin
IfFailed := false;
j := Pos( '(', Command );
if ( j > 0 ) then
begin
Parms := Trim( Copy( Command, j + 1, length( Command ) - j ) );
j := Pos( ')', Parms );
if ( j > 0 ) then
Parms := Trim( Copy( Parms, 1, j - 1 ) );
end
else
Parms := '';
Token := lowercase( Trim( Parse( Command, 0, '(' ) ) );
j := Pos( '.', Token );
if j > 0 then
begin
ObjectName := Trim( Copy( Token, 1, j - 1 ) );
Token := Trim( Copy( Token, j + 1, length( Token ) - j ) );
ObjectRef := nil;
if ObjectName = 'player' then
ObjectRef := Player
else if ObjectName = 'current' then
ObjectRef := Current
else
begin
j := FigureInstances.IndexOf( ObjectName );
if j >= 0 then
ObjectRef := TGameObject( FigureInstances.objects[ j ] );
end;
end
else
begin
ObjectName := '';
ObjectRef := TGameObject( Me );
end;
if Token = 'doaction' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TSpriteObject then
begin
TSpriteObject( ObjectRef ).DoAction( Parms );
end;
end;
end
else if Token = 'doeffect' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
NewEffect := GetNamedEffect( Parms );
if assigned( NewEffect ) then
begin
TCharacter( ObjectRef ).AddEffect( NewEffect );
end;
end;
end;
end
else if Token = 'causeevent' then
begin
Event := 'On' + Parms;
if ObjectName = '' then
begin
for j := 0 to FigureInstances.count - 1 do
begin
ObjectRef := TGameObject( FigureInstances.objects[ j ] );
if ObjectRef is TSpriteObject then
begin
if TSpriteObject( ObjectRef ).PropertyExists( Event ) then
RunScript( ObjectRef, TSpriteObject( ObjectRef ).Properties[ Event ] );
end;
end;
end
else
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TSpriteObject then
begin
if TSpriteObject( ObjectRef ).PropertyExists( Event ) then
RunScript( ObjectRef, TSpriteObject( ObjectRef ).Properties[ Event ] );
end;
end;
end;
end
else if Token = 'ifprop' then
begin
IfFailed := true;
if assigned( ObjectRef ) then
begin
S1 := Parse( Parms, 1, '=' );
if S1 = '' then
begin
S1 := Parse( Parms, 1, '>' );
if S1 = '' then
begin
S1 := Parse( Parms, 1, '<' );
if S1 = '' then
begin
end
else
begin
S2 := Parse( Parms, 0, '<' );
IfFailed := StrToFloat( ObjectRef.Properties[ S2 ] ) >= StrToFloat( S1 );
end;
end
else
begin
S2 := Parse( Parms, 0, '>' );
IfFailed := StrToFloat( ObjectRef.Properties[ S2 ] ) <= StrToFloat( S1 );
end;
end
else
begin
S2 := Parse( Parms, 0, '=' );
IfFailed := lowercase( ObjectRef.Properties[ S2 ] ) <> lowercase( S1 );
end;
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnotprop' then
begin
IfFailed := false;
if assigned( ObjectRef ) then
begin
S1 := Parse( Parms, 1, '=' );
if S1 = '' then
begin
S1 := Parse( Parms, 1, '>' );
if S1 = '' then
begin
S1 := Parse( Parms, 1, '<' );
if S1 = '' then
begin
end
else
begin
S2 := Parse( Parms, 0, '<' );
IfFailed := StrToFloat( ObjectRef.Properties[ S2 ] ) < StrToFloat( S1 );
end;
end
else
begin
S2 := Parse( Parms, 0, '>' );
IfFailed := StrToFloat( ObjectRef.Properties[ S2 ] ) > StrToFloat( S1 );
end;
end
else
begin
S2 := Parse( Parms, 0, '=' );
IfFailed := lowercase( ObjectRef.Properties[ S2 ] ) = lowercase( S1 );
end;
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'addtitle' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
TCharacter( ObjectRef ).AddTitle( Parms );
end;
end;
end
else if Token = 'removetitle' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
TCharacter( ObjectRef ).RemoveTitle( Parms );
end;
end;
end
else if Token = 'iftitle' then
begin
IfFailed := true;
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
IfFailed := not TCharacter( ObjectRef ).TitleExists( Parms );
end;
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnottitle' then
begin
IfFailed := false;
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
IfFailed := TCharacter( ObjectRef ).TitleExists( Parms );
end;
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifplayer' then
begin
IfFailed := not ( ObjectRef = Player );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnotplayer' then
begin
IfFailed := ( ObjectRef = Player );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifcurrent' then
begin
IfFailed := not ( ObjectRef = Current );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnotcurrent' then
begin
IfFailed := ( ObjectRef = Current );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifalldead' then
begin
IfFailed := not AllDead( Parms );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnotalldead' then
begin
IfFailed := AllDead( Parms );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifcombatmode' then
begin
IfFailed := not Current.CombatMode;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnotcombatmode' then
begin
IfFailed := Current.CombatMode;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifparty' then
begin
IfFailed := not ( NPCList.count > 1 );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnoparty' then
begin
IfFailed := not ( NPCList.count < 2 );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifinparty' then
begin
IfFailed := not ( NPCList.IndexOf( ObjectRef ) > 0 );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnotinparty' then
begin
IfFailed := ( NPCList.IndexOf( ObjectRef ) > 0 );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifpartycount' then
begin
IfFailed := not ( StrToInt( Parms ) = NPCList.count );
if not IfFailed then
inc( IfLevel );
end
else if Token = 'fadetoblack' then
begin
frmMain.Active := False;
frmMain.Timer3.Interval := 200;
frmMain.Timer3.tag := 5;
MouseCursor.Enabled := false;
BlackScript := Parms;
frmMain.Timer3.enabled := true;
end
else if ( Token = 'loadmap' ) or ( Token = 'loadmapext' ) then
begin
if Current.IntendToZone then
begin
S0 := Parse( Parms, 0, '|' );
S1 := Parse( S0, 0, ',' );
S2 := Parse( S0, 1, ',' );
S3 := Parse( S0, 2, ',' );
S4 := Parse( S0, 3, ',' );
k := Pos( '|', Parms );
if ( k = 0 ) or NoTransit then
frmMain.LoadNewMap( S1, S2, S3, S4 )
else
begin
S5 := copy( Parms, k, length( Parms ) - k + 1 );
frmMain.BeginTransit( S1, S2, S3, S4, S5 );
end;
end;
end
else if Token = 'enableconsole' then
begin
frmMain.DisableConsole := false;
end
else if Token = 'disableconsole' then
begin
frmMain.DisableConsole := true;
frmMain.OnKeyDown := nil;
Game.OnMouseDown := nil;

Game.OnMouseUp := nil;
Game.OnMouseMove := nil;
MouseCursor.Enabled := false;

// Game.ForceRefresh:=true;
// Game.Enabled := false;
end
else if Token = 'addtoparty' then
begin
if TCharacter( ObjectRef ).BaseHitPoints < 0 then
begin // jrs 6Nov01 Restore norm/default base hitpoints to all party members
Log.Log( '-- FIXUP, Party.HitPoints was ' + FloatToStr( TCharacter( ObjectRef ).HitPoints ) + ' and BaseHitPoints was ' + FloatToStr( TCharacter( ObjectRef ).BaseHitPoints ) );
TCharacter( ObjectRef ).HitPoints := 20;
TCharacter( ObjectRef ).CalcStats; // just to be sure
Log.Log( '-- Party.HitPoints is now ' + FloatToStr( TCharacter( ObjectRef ).HitPoints ) + ' and BaseHitPoints is now ' + FloatToStr( TCharacter( ObjectRef ).BaseHitPoints ) );
end;
frmMain.AddToParty( ObjectRef );
end
else if Token = 'removefromparty' then
begin
if NPCList.Count > 1 then
begin
frmMain.ChangeFocus( player );
frmMain.RemoveFromParty( ObjectRef );
end;
end
else if Token = 'removeallpartymembers' then
begin
if NPCList.Count > 1 then
begin
frmMain.ChangeFocus( player );
while NPCList.Count > 1 do
if NPCList.Items[ 1 ] <> player then
begin
TCharacter( NPCList.Items[ 1 ] ).Alliance := '';
frmMain.RemoveFromParty( NPCList.Items[ 1 ] );
end;
end;
end
else if Token = 'freezeparty' then
begin
if NPCList.Count > 1 then
begin
frmMain.ChangeFocus( player );
for iLoop := 0 to NPCList.Count - 1 do
if NPCList.Items[ iLoop ] <> player then
begin
TCharacter( NPCList.Items[ iLoop ] ).Frozen := true;
end;
end
end
else if Token = 'unfreezeparty' then
begin
for iLoop := 0 to NPCList.Count - 1 do
if NPCList.Items[ iLoop ] <> player then
begin
TCharacter( NPCList.Items[ iLoop ] ).Frozen := false;
end;
end
else if Token = 'savegame' then
begin
frmMain.SaveAGame( Parms );
end
else if ( Token = 'setproperty' ) or ( Token = 'setprop' ) then
begin
if assigned( ObjectRef ) then
begin
S0 := Parse( Parms, 0, '=' );
S1 := Parse( Parms, 1, '=' );
ObjectRef.Properties[ S0 ] := S1;
end;
end
else if ( Token = 'meander' ) then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
if StrToInt( Parms ) <> 0 then
begin
r := random( StrToInt( Parms ) );
T := pi2 * random( 360 ) / 360;
X := round( r * cos( T ) ) + TCharacter( ObjectRef ).X;
Y := round( r * sin( T ) ) + TCharacter( ObjectRef ).Y;
TCharacter( ObjectRef ).walkTo( X, Y, 16 );
end;
end;
end
else if ( Token = 'adjustspeed' ) then
begin
if assigned( ObjectRef ) then
begin
TCharacterResource( TCharacter( ObjectRef ).Resource ).speed := StrToInt( Parms );
end;
end
else if ( Token = 'adjusthealth' ) then
begin
if assigned( ObjectRef ) then
begin
TCharacter( ObjectRef ).TakeDamage( TCharacter( ObjectRef ), StrToInt( Parms ), 0, False );
// TCharacter(ObjectRef).Wounds := TCharacter(ObjectRef).Wounds + StrToInt(Parms);
end;
end
else if ( Token = 'adjustmaxhitpoints' ) then
begin
if assigned( ObjectRef ) then
begin
TCharacter( ObjectRef ).Hitpoints := TCharacter( ObjectRef ).Hitpoints + StrToInt( Parms );
if TCharacter( ObjectRef ).Hitpoints < 1 then
TCharacter( ObjectRef ).Hitpoints := 1;
end;
end
else if ( Token = 'adjustmana' ) then
begin
if assigned( ObjectRef ) then
begin
TCharacter( ObjectRef ).drain := TCharacter( ObjectRef ).drain + StrToInt( Parms );
if TCharacter( ObjectRef ).drain < 0 then
TCharacter( ObjectRef ).drain := 0;
if TCharacter( ObjectRef ).drain > TCharacter( ObjectRef ).Mana then
TCharacter( ObjectRef ).drain := TCharacter( ObjectRef ).Mana;
end;
end
else if ( Token = 'setgroupproperty' ) or ( Token = 'setgroupprop' ) then
begin
S2 := Parse( Parms, 0, ',' );
S3 := Parse( Parms, 1, ',' );
S0 := Parse( S3, 0, '=' );
S1 := Parse( S3, 1, '=' );
List := GetGroup( nil, S2 );
if assigned( List ) then
begin
for k := 0 to List.count - 1 do
TGameObject( List.objects[ k ] ).Properties[ S0 ] := S1;
end;
end
else if Token = 'say' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TSpriteObject then
begin
TSpriteObject( ObjectRef ).Say( Parms, clWhite );
end;
end;
end
else if Token = 'converse' then
begin
Converse( ObjectRef, Parms );
end
else if Token = 'merchant' then
begin
if ObjectRef is TCharacter then
frmMain.BeginMerchant( TCharacter( ObjectRef ) );
end
else if Token = 'cleartrack' then
begin
if ObjectRef is TCharacter then
TCharacter( ObjectRef ).track := nil; ;
end
else if Token = 'changetheme' then
begin
frmMain.CurrentTheme := Parms;
end
else if Token = 'playmp3' then
begin
if Assigned( MusicLib ) then
begin
MusicLib.OpenThisSong( SoundPath + 'theme/' + Parms );
MusicLib.PlayThisSong;
frmMain.SoundTimer.Enabled := false;
end;
end
else if Token = 'endmp3' then
begin
if Assigned( MusicLib ) then
begin
frmMain.SoundTimer.Enabled := false;
end;
end

else if Token = 'setdeathscreen' then
begin
frmMain.DeathScreen := Parms;
end
else if Token = 'showending' then
begin
frmMain.ShowEnding;
end
else if Token = 'showmessage' then
begin
S1 := Parse( Parms, 0, ',' ); //Message
S2 := Parse( Parms, 1, ',' ); //Time
try
frmMain.ShowQuickMessage( S1, StrToInt( S2 ) );
except
end;
end
else if Token = 'makeenemy' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
TCharacter( ObjectRef ).MakeEnemy( Parms );
end;
end;
end
else if Token = 'makefriend' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
TCharacter( ObjectRef ).MakeAlly( Parms );
end;
end;
end
else if Token = 'makeneutral' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
TCharacter( ObjectRef ).MakeNeutral( Parms );
end;
end;
end
else if Token = 'alert' then
begin
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
S1 := Parse( Parms, 0, ',' ); //Group to Alert
List := GetGroup( nil, S1 );
if assigned( List ) then
begin
for k := 0 to List.count - 1 do
begin
if List.Objects[ k ] is TCharacter then
begin
TCharacter( List.Objects[ k ] ).Track := TCharacter( ObjectRef );
end;
end;
end;
end;
end;
end
else if Token = 'spawn' then
begin
S1 := Parse( Parms, 0, ',' ); //Number of characters
S2 := Parse( Parms, 1, ',' ); //PathCorner Group
S3 := Parse( Parms, 2, ',' ); //Base GUID
S4 := Parse( Parms, 3, ',' ); //Interval
S5 := Parse( Parms, 4, ',' ); //Character group to clone from
h := StrToInt( S4 );
List := GetGroup( nil, S2 );
if assigned( List ) then
try
Group := GetGroup( nil, S5 );
if assigned( Group ) then
try
for j := Group.count - 1 downto 0 do
begin
if not ( Group.Objects[ j ] is TCharacter ) then
Group.Delete( j );
end;
for j := 1 to strtoint( S1 ) do
begin
k := random( Group.count );
TCharacter( Group.Objects[ k ] ).Clone( TObject( NewCharacter ), S3 + inttostr( j ) );
k := random( List.count );
NewCharacter.SetPos( TGameObject( List.objects[ k ] ).X, TGameObject( List.objects[ k ] ).Y, TGameObject( List.objects[ k ] ).z );
SpawnList.add( NewCharacter );
NewCharacter.SpawnCount := ( j - 1 ) * h;
end;
finally
Group.free;
end;
finally
List.free;
end;
end
else if Token = 'giveitem' then
begin
if assigned( ObjectRef ) then
begin
S1 := Parse( Parms, 0, ',' ); //Item Name
S2 := Parse( Parms, 1, ',' ); //Recipient
Object1 := GetGUID( S2 );
if assigned( Object1 ) then
TransferItem( ObjectRef, Object1, S1, true );
end;
end
else if Token = 'takeitem' then
begin
if assigned( ObjectRef ) then
begin
S1 := Parse( Parms, 0, ',' ); //Item Name
S2 := Parse( Parms, 1, ',' ); //Giver
Object1 := GetGUID( S2 );
if assigned( Object1 ) then
TransferItem( Object1, ObjectRef, S1, true );
end;
end
else if Token = 'ifgiveitem' then
begin
IfFailed := true;
if assigned( ObjectRef ) then
begin
S1 := Parse( Parms, 0, ',' ); //Item Name
S2 := Parse( Parms, 1, ',' ); //Recipient
Object1 := GetGUID( S2 );
if assigned( Object1 ) then
IfFailed := not TransferItem( ObjectRef, Object1, S1, false );
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'iftakeitem' then
begin
IfFailed := true;
if assigned( ObjectRef ) then
begin
S1 := Parse( Parms, 0, ',' ); //Item Name
S2 := Parse( Parms, 1, ',' ); //Giver
Object1 := GetGUID( S2 );
if assigned( Object1 ) then
IfFailed := not TransferItem( Object1, ObjectRef, S1, false );
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'removeitem' then
begin
if assigned( ObjectRef ) then
begin
S1 := Parse( Parms, 0, ',' ); //Item Name
if ObjectRef is TCharacter then
begin
TCharacter( ObjectRef ).RemoveItem( S1 );
end
else if ObjectRef is TContainer then
begin
TContainer( ObjectRef ).RemoveItem( S1 );
end;
end;
end
else if Token = 'ifpartymember' then
begin
IfFailed := true;
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
IfFailed := not TCharacter( ObjectRef ).PartyMember;
end;
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnotpartymember' then
begin
IfFailed := false;
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
IfFailed := TCharacter( ObjectRef ).PartyMember;
end;
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifhasitem' then
begin
IfFailed := true;
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
IfFailed := not TCharacter( ObjectRef ).HasItem( Parms );
end
else if ObjectRef is TContainer then
begin
IfFailed := not TContainer( ObjectRef ).HasItem( Parms );
end;
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnothasitem' then
begin
IfFailed := false;
if assigned( ObjectRef ) then
begin
if ObjectRef is TCharacter then
begin
IfFailed := TCharacter( ObjectRef ).HasItem( Parms );
end
else if ObjectRef is TContainer then
begin
IfFailed := TContainer( ObjectRef ).HasItem( Parms );
end;
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifchapterexists' then
begin
IfFailed := true;
try
S1 := Parse( Parms, 0, ',' );
IfFailed := ( int64( 1 shl ( strtoint( S1 ) - 1 ) ) and Chapters ) = 0;
except
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'ifnotchapterexists' then
begin
IfFailed := false;
try
S1 := Parse( Parms, 0, ',' );
IfFailed := ( int64( 1 shl ( strtoint( S1 ) - 1 ) ) and Chapters ) <> 0;
except
end;
if not IfFailed then
inc( IfLevel );
end
else if Token = 'setmaxparty' then
begin
try
MaxPartyMembers := strtoint( Parms );
except
end;
end
else if Token = 'journalentry' then
begin
frmMain.AddLogEntry( Parms );
end
else if Token = 'addquest' then
begin
frmMain.AddQuest( Parms );
end
else if Token = 'removequest' then
begin
j := Quests.IndexOf( Parms );
if j >= 0 then
Quests.Delete( j );
end
else if Token = 'adventure' then
begin
frmMain.AddAdventure( Parms );
end
else if Token = 'additem' then
begin
S1 := Parse( Parms, 0, ',' ); //Item Name
S2 := Parse( Parms, 1, ',' ); //GUID for new item
if ObjectRef is TCharacter then
begin
NewItem := PartManager.LoadItem( S1, TCharacterResource( TCharacter( ObjectRef ).Resource ).NakedName );
if assigned( NewItem ) then
begin
j := FigureInstances.add( S2 );
FigureInstances.objects[ j ] := NewItem;
NewItem.Resource := PartManager.GetLayerResource( NewItem.LayeredImage );
Game.AddFigure( NewItem );
if TCharacter( ObjectRef ).FindFreeInventoryXY( NewItem ) then
begin
TCharacter( ObjectRef ).Inventory.Add( NewItem );
NewItem.Enabled := False;
end
else
begin
NewItem.SetPos( ObjectRef.X, ObjectRef.Y, ObjectRef.Z );
NewItem.Enabled := True;
NewItem.Init;
end;
end;
end
else if ObjectRef is TContainer then
begin
NewItem := PartManager.LoadItem( S1, '' );
if assigned( NewItem ) then
begin
j := FigureInstances.add( S2 );
FigureInstances.objects[ j ] := NewItem;
NewItem.Resource := PartManager.GetLayerResource( NewItem.LayeredImage );
Game.AddFigure( NewItem );
if TContainer( ObjectRef ).FindFreeInventoryXY( NewItem ) then
begin
TContainer( ObjectRef ).Inventory.Add( NewItem );
NewItem.Enabled := False;
end
else
begin
NewItem.SetPos( ObjectRef.X, ObjectRef.Y, ObjectRef.Z );
NewItem.Enabled := True;
NewItem.Init;
end;
end;
end;
end
else if Token = 'moveto' then
begin
if assigned( ObjectRef ) and ( ObjectRef is TSpriteObject ) then
begin
Object1 := GetGUID( Parms );
if assigned( Object1 ) then
begin
if ObjectRef is TCharacter then
TCharacter( ObjectRef ).WalkTo( TGameObject( Object1 ).X, TGameObject( Object1 ).Y, 64 )
else if ( ObjectRef is TGameObject ) then
TSpriteObject( ObjectRef ).FindPathTo( TGameObject( Object1 ).X, TGameObject( Object1 ).Y, nil, 64 );
end;
end;
end
else if Token = 'teleport' then
begin
List := GetGroup( nil, Parms );
if assigned( List ) then
begin
try
j := 0;
for k := 0 to List.count - 1 do
begin
if k >= NPCList.count then
break;
if List.objects[ k ] is TPathCorner then
begin
TCharacter( NPCList.items[ j ] ).SetPos( TPathCorner( List.objects[ k ] ).X, TPathCorner( List.objects[ k ] ).Y, TPathCorner( List.objects[ k ] ).Z );
TCharacter( NPCList.items[ j ] ).Stand;
inc( j );
end;
end;
finally
List.free;
end;
end
end
else if Token = 'moveparty' then
begin
List := GetGroup( nil, Parms );
if assigned( List ) then
begin
try
j := 0;
for k := 0 to List.count - 1 do
begin
if k >= NPCList.count then
break;
if List.objects[ k ] is TPathCorner then
begin
if TCharacter( NPCList.items[ j ] ) <> player then
begin
TCharacter( NPCList.items[ j ] ).SetPos( TPathCorner( List.objects[ k ] ).X, TPathCorner( List.objects[ k ] ).Y, TPathCorner( List.objects[ k ] ).Z );
TCharacter( NPCList.items[ j ] ).Stand;
end;
inc( j );
end;
end;
finally
List.free;
end;
end
end
else if Token = 'setpos' then
begin
if assigned( ObjectRef ) then
begin
Object1 := GetGUID( Parms );
if assigned( Object1 ) and ( Object1 is TGameObject ) then
begin
TSpriteObject( ObjectRef ).SetPos( TGameObject( Object1 ).X, TGameObject( Object1 ).Y, TSpriteObject( ObjectRef ).Z );
if ObjectRef is TCharacter then
TCharacter( ObjectRef ).Stand;
end;
end;
end
else if Token = 'reinit' then
begin
if assigned( ObjectRef ) and ( ObjectRef is TCharacter ) then
begin
TCharacter( ObjectRef ).AI.Init;
end;
end
else if Token = 'invis' then
begin
if assigned( ObjectRef ) and ( ObjectRef is TCharacter ) then
begin
TCharacter( ObjectRef ).Alpha := 1;
TCharacter( ObjectRef ).SpecialEffect := seTranslucent;
end;
end
else if Token = 'cast' then
begin
if assigned( ObjectRef ) and ( ObjectRef is TCharacter ) then
begin
if assigned( TCharacter( ObjectRef ).CurrentSpell ) then
begin
Object1 := GetGUID( Parms );
if assigned( Object1 ) and ( Object1 is TSpriteObject ) then
TCharacter( Objectref ).Cast( TSpriteObject( Object1 ) );
end;
end;
end
else if Token = 'endif' then
begin
dec( IfLevel );
if IfLevel < 0 then
exit;
end
else if Token = 'else' then
begin
dec( IfLevel );
if IfLevel < 0 then
exit;
NewIfLevel := IfLevel;
repeat
inc( i );
Command := lowercase( Trim( Parse( Script, i, ';' ) ) );
if Command = '' then
exit
else
begin
S1 := Parse( Command, 1, '.' );
if S1 = '' then
S1 := Command;
if Copy( S1, 1, 2 ) = 'if' then
inc( NewIfLevel )
else if S1 = 'endif' then
dec( NewIfLevel );
end;
until ( NewIfLevel = IfLevel ) and ( S1 = 'endif' );
end;

if IfFailed then
begin
NewIfLevel := IfLevel + 1;
repeat
inc( i );
Command := lowercase( Trim( Parse( Script, i, ';' ) ) );
if Command = '' then
exit
else
begin
S1 := Parse( Command, 1, '.' );
if S1 = '' then
S1 := Command;
if Copy( S1, 1, 2 ) = 'if' then
inc( NewIfLevel )
else if S1 = 'endif' then
dec( NewIfLevel );
end;
until ( ( NewIfLevel = IfLevel ) and ( S1 = 'endif' ) ) or ( ( NewIfLevel = IfLevel + 1 ) and ( S1 = 'else' ) );
if S1 <> 'else' then
inc( IfLevel );
end;
inc( i );
Command := Trim( Parse( Script, i, ';' ) );
end;

except
on E : Exception do
Log.log( FailName, E.Message, [ ] );
end;
finally
dec( ScriptEntryCount );
end;

end;

procedure Converse( ObjectRef : TObject; Conversation : string );
const
FailName : string = 'Engine.Converse.';
begin
{$IFDEF DODEBUG}
if ( CurrDbgLvl >= DbgLvlSevere ) then
Log.LogEntry( FailName );
{$ENDIF}
try

frmMain.BeginConverse( TGameObject( ObjectRef ), Conversation );

except
on E : Exception do
Log.log( FailName, E.Message, [ ] );
end;
end;

function GetFileList( const cDir, cExt : string ) : TStringList;
var
found : integer;
SearchRec : TSearchRec;
sDir, sExt : string;
begin
if cDir[ length( cDir ) ] = '/' then
sDir := copy( cDir, 1, length( cDir ) - 1 )
else
sDir := cDir;
sExt := lowercase( cExt );
if sExt[ 1 ] <> '.' then
sExt := '.' + sExt;

result := TStringList.create;
found := FindFirst( sDir + '/*' + sExt, 0, SearchRec );
while found = 0 do
begin
result.add( SearchRec.Name );
found := FindNext( SearchRec );
end;
FindClose( SearchRec );
end;

procedure CheckCache;
var
F : file;
// INI: TINIFile;
FileList : TStringList;
TotalSize : int64;
Dir : string;
i, j : integer;
L : longint;
// L64: int64;
CacheList, pList, pList1, pList2 : ^TCacheInfo;
CacheItem : TCacheInfo;
Count : integer;
begin
Dir := DefaultPath + 'cache/';
FileList := GetFileList( Dir, '.zit' );
try
Count := FileList.count;
GetMem( CacheList, Count * sizeof( TCacheInfo ) );
pList := CacheList;
TotalSize := 0;
//Calculate TotalSize, Load Name and Size fields
for i := 0 to Count - 1 do
begin
pList^.Name := ChangeFileExt( FileList.strings[ i ], '' );


AssignFile( F, Dir + FileList.strings[ i ] );
try
Reset( F, 1 );
L := filesize( F );
CloseFile( F );
except
L := 0;
end;

AssignFile( F, Dir + ChangeFileExt( FileList.strings[ i ], '.pit' ) );
try
Reset( F, 1 );
L := L + filesize( F );
CloseFile( F );
except
end;

AssignFile( F, Dir + ChangeFileExt( FileList.strings[ i ], '.dit' ) );
try
Reset( F, 1 );
L := L + filesize( F );
CloseFile( F );
except
end;

AssignFile( F, Dir + ChangeFileExt( FileList.strings[ i ], '.cit' ) );
try
Reset( F, 1 );
L := L + filesize( F );
if filesize( F ) > 12 then
begin
Seek( F, 12 );
BlockRead( F, pList^.Date, sizeof( TDateTime ) );
end
else
begin
pList^.Date := 0;
end;
CloseFile( F );
except
end;
//Log.Log(pList^.Name+' - '+DateTimeToStr(pList^.Date)+' ('+inttostr(L)+')');

pList^.Size := L;
TotalSize := TotalSize + L;
inc( pList );
end;
finally
FileList.free;
end;

if TotalSize > MaxCacheSize then
begin
Log.Log( 'Clearing cache...' );
//Load Date field
// INI:=TINIFile.create(DefaultPath + 'siege.ini');

// try
{ pList:=CacheList;
for i:=1 to Count do begin
S:=INI.ReadString('Cache',pList^.Name,'');
try
L64:=StrToInt64(S);
except
L64:=0;
end;
pList^.Date:=TDateTime(addr(L64)^);
inc(pList);
end; }

//Sort by Date
pList := CacheList;
for i := 1 to Count - 1 do
begin
pList1 := pList;
pList2 := pList;
for j := i + 1 to Count do
begin
inc( pList1 );
if pList1.Date < pList2.Date then
pList2 := pList1;
end;
if pList2 <> pList then
begin
CacheItem := pList^;
pList^ := pList2^;
pList2^ := CacheItem;
end;
inc( pList );
end;

pList := CacheList;
while ( TotalSize > MaxCacheSize ) do
begin
Log.Log( ' ' + pList^.Name );
try
DeleteFile( Dir + pList^.Name + '.zit' );
except
end;
try
DeleteFile( Dir + pList^.Name + '.pit' );
except
end;
try
DeleteFile( Dir + pList^.Name + '.dit' );
except
end;
try
DeleteFile( Dir + pList^.Name + '.cit' );
except
end;
dec( TotalSize, pList^.Size );
// INI.DeleteKey('Cache',pList^.Name);
inc( pList );
end;
// finally
// INI.free
// end;
end;
FreeMem( CacheList );
end;

end.