index 88e51347a9ef53678adcbdf23bd8e4f0c8b45f16..19edc46fd96b97b9efec5d5f156af12da87ae73d 100644 (file)
--- a/src/game/g_triggers.pas
+++ b/src/game/g_triggers.pas
interface
uses
+ SysUtils, Variants, Classes,
MAPDEF, e_graphics, g_basic, g_sound,
- BinEditor, xdynrec;
+ xdynrec, hashtable, exoma;
type
TActivator = record
UID: Word;
TimeOut: Word;
end;
+
PTrigger = ^TTrigger;
TTrigger = record
public
ActivateType: Byte;
Keys: Byte;
TexturePanelGUID: Integer;
- TexturePanelType: Word;
+ //TexturePanelType: Word;
TimeOut: Word;
ActivateUID: Word;
AutoSpawn: Boolean;
SpawnCooldown: Integer;
SpawnedCount: Integer;
- ShotPanelType: Word;
+ //ShotPanelType: Word;
ShotPanelTime: Integer;
ShotSightTime: Integer;
ShotSightTimeout: Integer;
mapIndex: Integer; // index in fields['trigger'], used in save/load
trigPanelGUID: Integer;
- //TrigData: TTriggerData;
- trigDataRec: TDynRecord; // triggerdata; owned by trigger
+ trigDataRec: TDynRecord; // triggerdata; owned by trigger (cloned)
+ exoInit, exoThink, exoCheck, exoAction: TExprBase;
+
+ userVars: THashStrVariant;
{$INCLUDE ../shared/mapdef_tgc_def.inc}
public
function trigCenter (): TDFPoint; inline;
-
- public
- property trigShotPanelGUID: Integer read trigPanelGUID write trigPanelGUID;
end;
-function g_Triggers_Create(Trigger: TTrigger; forceInternalIndex: Integer=-1): DWORD;
+function g_Triggers_Create (aTrigger: TTrigger; trec: TDynRecord; forceInternalIndex: Integer=-1): DWORD;
procedure g_Triggers_Update();
procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
procedure g_Triggers_OpenAll();
procedure g_Triggers_DecreaseSpawner(ID: DWORD);
procedure g_Triggers_Free();
-procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
-procedure g_Triggers_LoadState(var Mem: TBinMemoryReader);
+procedure g_Triggers_SaveState (st: TStream);
+procedure g_Triggers_LoadState (st: TStream);
var
Math,
g_player, g_map, g_panel, g_gfx, g_game, g_textures,
g_console, g_monsters, g_items, g_phys, g_weapons,
- wadreader, g_main, SysUtils, e_log, g_language,
- g_options, g_net, g_netmsg, utils, xparser;
+ wadreader, g_main, e_log, g_language,
+ g_options, g_net, g_netmsg, utils, xparser, xstreams;
const
TRIGGER_SIGNATURE = $58475254; // 'TRGX'
{$INCLUDE ../shared/mapdef_tgc_impl.inc}
+
+// ////////////////////////////////////////////////////////////////////////// //
+type
+ TTrigScope = class(TExprScope)
+ private
+ plrprops: TPropHash;
+ monsprops: TPropHash;
+ platprops: TPropHash;
+
+ public
+ me: PTrigger;
+
+ public
+ constructor Create ();
+ destructor Destroy (); override;
+
+ function getObj (const aname: AnsiString): TObject; override;
+ function getField (obj: TObject; const afldname: AnsiString): Variant; override;
+ procedure setField (obj: TObject; const afldname: AnsiString; var aval: Variant); override;
+ end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+type
+ TMyConstList = class(TExprConstList)
+ public
+ function valid (const cname: AnsiString): Boolean; override;
+ function get (const cname: AnsiString; out v: Variant): Boolean; override;
+ end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+function TMyConstList.valid (const cname: AnsiString): Boolean;
+begin
+ //writeln('CHECK: ''', cname, '''');
+ result :=
+ (cname = 'player') or
+ (cname = 'self') or
+ false;
+end;
+
+function TMyConstList.get (const cname: AnsiString; out v: Variant): Boolean;
+var
+ eidx: Integer;
+ ebs: TDynEBS;
+begin
+ //if (cname = 'answer') then begin v := LongInt(42); result := true; exit; end;
+ result := false;
+ if (gCurrentMap = nil) then exit;
+ for eidx := 0 to gCurrentMap.mapdef.ebsTypeCount-1 do
+ begin
+ ebs := gCurrentMap.mapdef.ebsTypeAt[eidx];
+ if ebs.has[cname] then
+ begin
+ //writeln('FOUND: ''', cname, '''');
+ v := ebs[cname];
+ result := true;
+ exit;
+ end;
+ end;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+constructor TTrigScope.Create ();
+begin
+ plrprops := TPropHash.Create(TPlayer, 'e');
+ monsprops := TPropHash.Create(TMonster, 'e');
+ platprops := TPropHash.Create(TPanel, 'e');
+ me := nil;
+end;
+
+
+destructor TTrigScope.Destroy ();
+begin
+ platprops.Free();
+ monsprops.Free();
+ plrprops.Free();
+ inherited;
+end;
+
+
+function TTrigScope.getObj (const aname: AnsiString): TObject;
+begin
+ if (aname = 'player') then result := gPlayers[0] //FIXME
+ else if (aname = 'self') or (aname = 'this') then result := TObject(Pointer(PtrUInt(1)))
+ else result := inherited getObj(aname);
+end;
+
+
+function TTrigScope.getField (obj: TObject; const afldname: AnsiString): Variant;
+begin
+ if (obj = gPlayers[0]) then
+ begin
+ if plrprops.get(obj, afldname, result) then exit;
+ end
+ else if (obj = TObject(Pointer(PtrUInt(1)))) then
+ begin
+ if (me <> nil) and (me.userVars <> nil) then
+ begin
+ if me.userVars.get(afldname, result) then exit;
+ end;
+ end;
+ result := inherited getField(obj, afldname);
+end;
+
+
+procedure TTrigScope.setField (obj: TObject; const afldname: AnsiString; var aval: Variant);
+begin
+ if (obj = gPlayers[0]) then
+ begin
+ if plrprops.put(obj, afldname, aval) then exit;
+ end
+ else if (obj = TObject(Pointer(PtrUInt(1)))) then
+ begin
+ if (me <> nil) then
+ begin
+ if (Length(afldname) > 4) and (afldname[1] = 'u') and (afldname[2] = 's') and
+ (afldname[3] = 'e') and (afldname[4] = 'r') then
+ begin
+ if (me.userVars = nil) then me.userVars := THashStrVariant.Create();
+ me.userVars.put(afldname, aval);
+ exit;
+ end;
+ end;
+ end;
+ inherited setField(obj, afldname, aval);
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+var
+ tgscope: TTrigScope = nil;
+ tgclist: TMyConstList = nil;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
function TTrigger.trigCenter (): TDFPoint; inline;
begin
result := TDFPoint.Create(x+width div 2, y+height div 2);
function FindTrigger (): DWORD;
var
- i: Integer;
+ i, olen: Integer;
begin
- for i := 0 to High(gTriggers) do
+ olen := Length(gTriggers);
+
+ for i := 0 to olen-1 do
begin
if gTriggers[i].TriggerType = TRIGGER_NONE then begin result := i; exit; end;
end;
- if (gTriggers = nil) then
- begin
- SetLength(gTriggers, 8);
- result := 0;
- end
- else
+ SetLength(gTriggers, olen+8);
+ result := olen;
+
+ for i := result to High(gTriggers) do
begin
- result := Length(gTriggers);
- SetLength(gTriggers, result+8);
- for i := result to High(gTriggers) do gTriggers[i].TriggerType := TRIGGER_NONE;
+ gTriggers[i].TriggerType := TRIGGER_NONE;
+ gTriggers[i].trigDataRec := nil;
+ gTriggers[i].exoInit := nil;
+ gTriggers[i].exoThink := nil;
+ gTriggers[i].exoCheck := nil;
+ gTriggers[i].exoAction := nil;
+ gTriggers[i].userVars := nil;
end;
end;
begin
if (tgcAmmo = 0) or ((tgcAmmo > 0) and (ShotAmmoCount > 0)) then
begin
- if (trigShotPanelGUID <> -1) and (ShotPanelTime = 0) then
+ if (trigPanelGUID <> -1) and (ShotPanelTime = 0) then
begin
- g_Map_SwitchTextureGUID(ShotPanelType, trigShotPanelGUID);
+ g_Map_SwitchTextureGUID({ShotPanelType,} trigPanelGUID);
ShotPanelTime := 4; // òèêîâ íà âñïûøêó âûñòðåëà
end;
end;
end;
+var
+ tvval: Variant;
begin
result := false;
if g_Game_IsClient then exit;
if not Trigger.Enabled then exit;
if (Trigger.TimeOut <> 0) and (actType <> ACTIVATE_CUSTOM) then exit;
- if gLMSRespawn = LMS_RESPAWN_WARMUP then exit;
+ if (gLMSRespawn = LMS_RESPAWN_WARMUP) then exit;
+
+ if (Trigger.exoCheck <> nil) then
+ begin
+ //conwritefln('exocheck: [%s]', [Trigger.exoCheck.toString()]);
+ try
+ tgscope.me := @Trigger;
+ tvval := Trigger.exoCheck.value(tgscope);
+ tgscope.me := nil;
+ if not Boolean(tvval) then exit;
+ except on e: Exception do
+ begin
+ tgscope.me := nil;
+ conwritefln('trigger exocheck error: %s [%s]', [e.message, Trigger.exoCheck.toString()]);
+ exit;
+ end;
+ end;
+ end;
animonce := False;
coolDown := (actType <> 0);
+ if (Trigger.exoAction <> nil) then
+ begin
+ //conwritefln('exoactivate: [%s]', [Trigger.exoAction.toString()]);
+ try
+ tgscope.me := @Trigger;
+ Trigger.exoAction.value(tgscope);
+ tgscope.me := nil;
+ except on e: Exception do
+ begin
+ tgscope.me := nil;
+ conwritefln('trigger exoactivate error: %s [%s]', [e.message, Trigger.exoAction.toString()]);
+ exit;
+ end;
+ end;
+ end;
+
with Trigger do
begin
case TriggerType of
Dec(idx);
end;
TimeOut := tgcWait;
+ result := true;
end;
end;
end;
if Result {and (Trigger.TexturePanel <> -1)} then
begin
- g_Map_SwitchTextureGUID(Trigger.TexturePanelType, Trigger.TexturePanelGUID, IfThen(animonce, 2, 1));
+ g_Map_SwitchTextureGUID({Trigger.TexturePanelType,} Trigger.TexturePanelGUID, IfThen(animonce, 2, 1));
end;
end;
-function g_Triggers_CreateWithMapIndex (Trigger: TTrigger; arridx, mapidx: Integer): DWORD;
+function g_Triggers_CreateWithMapIndex (aTrigger: TTrigger; arridx, mapidx: Integer): DWORD;
var
triggers: TDynField;
begin
triggers := gCurrentMap['trigger'];
if (triggers = nil) then raise Exception.Create('LOAD: map has no triggers');
if (mapidx < 0) or (mapidx >= triggers.count) then raise Exception.Create('LOAD: invalid map trigger index');
- Trigger.trigDataRec := triggers.itemAt[mapidx];
- if (Trigger.trigDataRec = nil) then raise Exception.Create('LOAD: internal error in trigger loader');
- Trigger.mapId := Trigger.trigDataRec.id;
- Trigger.mapIndex := mapidx;
- if (Trigger.trigDataRec.trigRec <> nil) then
- begin
- Trigger.trigDataRec := Trigger.trigDataRec.trigRec.clone(nil);
- end
- else
- begin
- Trigger.trigDataRec := nil;
- end;
- result := g_Triggers_Create(Trigger, arridx);
+ aTrigger.mapIndex := mapidx;
+ result := g_Triggers_Create(aTrigger, triggers.itemAt[mapidx], arridx);
end;
-function g_Triggers_Create(Trigger: TTrigger; forceInternalIndex: Integer=-1): DWORD;
+function g_Triggers_Create (aTrigger: TTrigger; trec: TDynRecord; forceInternalIndex: Integer=-1): DWORD;
var
find_id: DWORD;
fn, mapw: AnsiString;
f, olen: Integer;
+ ptg: PTrigger;
begin
+ if (tgscope = nil) then tgscope := TTrigScope.Create();
+ if (tgclist = nil) then tgclist := TMyConstList.Create();
+
// Íå ñîçäàâàòü âûõîä, åñëè èãðà áåç âûõîäà
- if (Trigger.TriggerType = TRIGGER_EXIT) and
+ if (aTrigger.TriggerType = TRIGGER_EXIT) and
(not LongBool(gGameSettings.Options and GAME_OPTION_ALLOWEXIT)) then
- Trigger.TriggerType := TRIGGER_NONE;
+ begin
+ aTrigger.TriggerType := TRIGGER_NONE;
+ end;
// Åñëè ìîíñòðû çàïðåùåíû, îòìåíÿåì òðèããåð
- if (Trigger.TriggerType = TRIGGER_SPAWNMONSTER) and
+ if (aTrigger.TriggerType = TRIGGER_SPAWNMONSTER) and
(not LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS)) and
(gGameSettings.GameType <> GT_SINGLE) then
- Trigger.TriggerType := TRIGGER_NONE;
+ begin
+ aTrigger.TriggerType := TRIGGER_NONE;
+ end;
// Ñ÷èòàåì êîëè÷åñòâî ñåêðåòîâ íà êàðòå
- if Trigger.TriggerType = TRIGGER_SECRET then gSecretsCount += 1;
+ if (aTrigger.TriggerType = TRIGGER_SECRET) then gSecretsCount += 1;
if (forceInternalIndex < 0) then
begin
if (forceInternalIndex >= olen) then
begin
SetLength(gTriggers, forceInternalIndex+1);
- for f := olen to High(gTriggers) do gTriggers[f].TriggerType := TRIGGER_NONE;
+ for f := olen to High(gTriggers) do
+ begin
+ gTriggers[f].TriggerType := TRIGGER_NONE;
+ gTriggers[f].trigDataRec := nil;
+ gTriggers[f].exoInit := nil;
+ gTriggers[f].exoThink := nil;
+ gTriggers[f].exoCheck := nil;
+ gTriggers[f].exoAction := nil;
+ gTriggers[f].userVars := nil;
+ end;
end;
+ f := forceInternalIndex;
+ gTriggers[f].trigDataRec.Free();
+ gTriggers[f].exoInit.Free();
+ gTriggers[f].exoThink.Free();
+ gTriggers[f].exoCheck.Free();
+ gTriggers[f].exoAction.Free();
+ gTriggers[f].userVars.Free();
+ gTriggers[f].trigDataRec := nil;
+ gTriggers[f].exoInit := nil;
+ gTriggers[f].exoThink := nil;
+ gTriggers[f].exoCheck := nil;
+ gTriggers[f].exoAction := nil;
+ gTriggers[f].userVars := nil;
find_id := DWORD(forceInternalIndex);
end;
- gTriggers[find_id] := Trigger;
+ gTriggers[find_id] := aTrigger;
+ ptg := @gTriggers[find_id];
- with gTriggers[find_id] do
+ ptg.mapId := trec.id;
+ // clone trigger data
+ if (trec.trigRec = nil) then
+ begin
+ ptg.trigDataRec := nil;
+ //HACK!
+ if (ptg.TriggerType <> TRIGGER_SECRET) then
+ begin
+ e_LogWritefln('trigger of type %s has no triggerdata; wtf?!', [ptg.TriggerType], TMsgType.Warning);
+ end;
+ end
+ else
+ begin
+ ptg.trigDataRec := trec.trigRec.clone(nil);
+ end;
+
+ with ptg^ do
begin
ID := find_id;
// if this type of trigger exists both on the client and on the server
// use an uniform numeration
- if Trigger.TriggerType = TRIGGER_SOUND then
+ ClientID := 0;
+ if (ptg.TriggerType = TRIGGER_SOUND) then
begin
Inc(gTriggerClientID);
ClientID := gTriggerClientID;
- end
- else
- begin
- ClientID := 0;
end;
TimeOut := 0;
ActivateUID := 0;
end;
// update cached trigger variables
- trigUpdateCacheData(gTriggers[find_id], gTriggers[find_id].trigDataRec);
+ trigUpdateCacheData(ptg^, ptg.trigDataRec);
+
+ ptg.userVars := nil;
+
+ try
+ ptg.exoThink := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_think']));
+ except
+ on e: TExomaParseException do
+ begin
+ conwritefln('*** ERROR parsing exoma_think (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_think'])]);
+ ptg.exoThink := nil;
+ end;
+ else
+ raise;
+ end;
+ try
+ ptg.exoCheck := TExprBase.parse(tgclist, VarToStr(trec.user['exoma_check']));
+ except
+ on e: TExomaParseException do
+ begin
+ conwritefln('*** ERROR parsing exoma_check (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_check'])]);
+ ptg.exoCheck := nil;
+ end;
+ else
+ raise;
+ end;
+ try
+ ptg.exoAction := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_action']));
+ except
+ on e: TExomaParseException do
+ begin
+ conwritefln('*** ERROR parsing exoma_action (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_action'])]);
+ ptg.exoAction := nil;
+ end;
+ else
+ raise;
+ end;
+ try
+ ptg.exoInit := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_init']));
+ except
+ on e: TExomaParseException do
+ begin
+ conwritefln('*** ERROR parsing exoma_init (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_init'])]);
+ ptg.exoInit := nil;
+ end;
+ else
+ raise;
+ end;
+
+ if (forceInternalIndex < 0) and (ptg.exoInit <> nil) then
+ begin
+ //conwritefln('executing trigger init: [%s]', [gTriggers[find_id].exoInit.toString()]);
+ try
+ tgscope.me := ptg;
+ ptg.exoInit.value(tgscope);
+ tgscope.me := nil;
+ except
+ tgscope.me := nil;
+ conwritefln('*** trigger exoactivate error: %s', [ptg.exoInit.toString()]);
+ exit;
+ end;
+ end;
// Çàãðóæàåì çâóê, åñëè ýòî òðèããåð "Çâóê"
- if (Trigger.TriggerType = TRIGGER_SOUND) and (Trigger.tgcSoundName <> '') then
+ if (ptg.TriggerType = TRIGGER_SOUND) and (ptg.tgcSoundName <> '') then
begin
// Åùå íåò òàêîãî çâóêà
- if not g_Sound_Exists(Trigger.tgcSoundName) then
+ if not g_Sound_Exists(ptg.tgcSoundName) then
begin
- fn := g_ExtractWadName(Trigger.tgcSoundName);
- if fn = '' then
+ fn := g_ExtractWadName(ptg.tgcSoundName);
+ if (fn = '') then
begin // Çâóê â ôàéëå ñ êàðòîé
mapw := g_ExtractWadName(gMapInfo.Map);
- fn := mapw+':'+g_ExtractFilePathName(Trigger.tgcSoundName);
+ fn := mapw+':'+g_ExtractFilePathName(ptg.tgcSoundName);
end
else // Çâóê â îòäåëüíîì ôàéëå
begin
- fn := GameDir + '/wads/' + Trigger.tgcSoundName;
+ fn := GameDir + '/wads/' + ptg.tgcSoundName;
end;
- if not g_Sound_CreateWADEx(Trigger.tgcSoundName, fn) then
+ //e_LogWritefln('loading trigger sound ''%s''', [fn]);
+ if not g_Sound_CreateWADEx(ptg.tgcSoundName, fn) then
begin
- g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.tgcSoundName]));
+ g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, ptg.tgcSoundName]));
end;
end;
// Ñîçäàåì îáúåêò çâóêà
- with gTriggers[find_id] do
+ with ptg^ do
begin
Sound := TPlayableSound.Create();
- if not Sound.SetByName(Trigger.tgcSoundName) then
+ if not Sound.SetByName(ptg.tgcSoundName) then
begin
Sound.Free();
Sound := nil;
end;
// Çàãðóæàåì ìóçûêó, åñëè ýòî òðèããåð "Ìóçûêà"
- if (Trigger.TriggerType = TRIGGER_MUSIC) and (Trigger.tgcMusicName <> '') then
+ if (ptg.TriggerType = TRIGGER_MUSIC) and (ptg.tgcMusicName <> '') then
begin
// Åùå íåò òàêîé ìóçûêè
- if not g_Sound_Exists(Trigger.tgcMusicName) then
+ if not g_Sound_Exists(ptg.tgcMusicName) then
begin
- fn := g_ExtractWadName(Trigger.tgcMusicName);
+ fn := g_ExtractWadName(ptg.tgcMusicName);
if fn = '' then
begin // Ìóçûêà â ôàéëå ñ êàðòîé
mapw := g_ExtractWadName(gMapInfo.Map);
- fn := mapw+':'+g_ExtractFilePathName(Trigger.tgcMusicName);
+ fn := mapw+':'+g_ExtractFilePathName(ptg.tgcMusicName);
end
else // Ìóçûêà â ôàéëå ñ êàðòîé
begin
- fn := GameDir+'/wads/'+Trigger.tgcMusicName;
+ fn := GameDir+'/wads/'+ptg.tgcMusicName;
end;
- if not g_Sound_CreateWADEx(Trigger.tgcMusicName, fn, True) then
+ if not g_Sound_CreateWADEx(ptg.tgcMusicName, fn, True) then
begin
- g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.tgcMusicName]));
+ g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, ptg.tgcMusicName]));
end;
end;
end;
// Çàãðóæàåì äàííûå òðèããåðà "Òóðåëü"
- if Trigger.TriggerType = TRIGGER_SHOT then
+ if (ptg.TriggerType = TRIGGER_SHOT) then
begin
- with gTriggers[find_id] do
+ with ptg^ do
begin
ShotPanelTime := 0;
ShotSightTime := 0;
ShotSightTimeout := 0;
ShotSightTarget := 0;
ShotSightTargetN := 0;
- ShotAmmoCount := Trigger.tgcAmmo;
+ ShotAmmoCount := ptg.tgcAmmo;
ShotReloadTime := 0;
end;
end;
if ShotPanelTime > 0 then
begin
Dec(ShotPanelTime);
- if ShotPanelTime = 0 then g_Map_SwitchTextureGUID(ShotPanelType, trigShotPanelGUID);
+ if ShotPanelTime = 0 then g_Map_SwitchTextureGUID({ShotPanelType,} trigPanelGUID);
end;
if ShotSightTime > 0 then
begin
SetLength(gTriggers[a].Activators, 0);
end;
gTriggers[a].trigDataRec.Free();
+
+ gTriggers[a].exoThink.Free();
+ gTriggers[a].exoCheck.Free();
+ gTriggers[a].exoAction.Free();
+ gTriggers[a].userVars.Free();
end;
gTriggers := nil;
end;
-procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
+procedure g_Triggers_SaveState (st: TStream);
var
count, actCount, i, j: Integer;
- dw: DWORD;
sg: Single;
b: Boolean;
+ kv: THashStrVariant.PEntry;
+ t: LongInt;
begin
// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ
count := Length(gTriggers);
- Mem := TBinMemoryWriter.Create((count+1)*200);
// Êîëè÷åñòâî òðèããåðîâ
- Mem.WriteInt(count);
+ utils.writeInt(st, LongInt(count));
if (count = 0) then exit;
for i := 0 to High(gTriggers) do
begin
// Ñèãíàòóðà òðèããåðà
- dw := TRIGGER_SIGNATURE; // 'TRGX'
- Mem.WriteDWORD(dw);
+ utils.writeSign(st, 'TRGX');
+ utils.writeInt(st, Byte(0));
// Òèï òðèããåðà
- Mem.WriteByte(gTriggers[i].TriggerType);
+ utils.writeInt(st, Byte(gTriggers[i].TriggerType));
if (gTriggers[i].TriggerType = TRIGGER_NONE) then continue; // empty one
// Ñïåöèàëüíûå äàííûå òðèããåðà: ïîòîì èç êàðòû îïÿòü âûòàùèì; ñîõðàíèì òîëüêî èíäåêñ
- Mem.WriteInt(gTriggers[i].mapIndex);
+ utils.writeInt(st, LongInt(gTriggers[i].mapIndex));
// Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
- Mem.WriteInt(gTriggers[i].X);
- Mem.WriteInt(gTriggers[i].Y);
+ utils.writeInt(st, LongInt(gTriggers[i].X));
+ utils.writeInt(st, LongInt(gTriggers[i].Y));
// Ðàçìåðû
- Mem.WriteWord(gTriggers[i].Width);
- Mem.WriteWord(gTriggers[i].Height);
+ utils.writeInt(st, Word(gTriggers[i].Width));
+ utils.writeInt(st, Word(gTriggers[i].Height));
// Âêëþ÷åí ëè òðèããåð
- Mem.WriteBoolean(gTriggers[i].Enabled);
+ utils.writeBool(st, gTriggers[i].Enabled);
// Òèï àêòèâàöèè òðèããåðà
- Mem.WriteByte(gTriggers[i].ActivateType);
+ utils.writeInt(st, Byte(gTriggers[i].ActivateType));
// Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
- Mem.WriteByte(gTriggers[i].Keys);
+ utils.writeInt(st, Byte(gTriggers[i].Keys));
// ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
- Mem.WriteInt(gTriggers[i].TexturePanelGUID);
+ utils.writeInt(st, LongInt(gTriggers[i].TexturePanelGUID));
// Òèï ýòîé ïàíåëè
- Mem.WriteWord(gTriggers[i].TexturePanelType);
+ //Mem.WriteWord(gTriggers[i].TexturePanelType);
// Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
- Mem.WriteInt(gTriggers[i].trigPanelGUID);
+ utils.writeInt(st, LongInt(gTriggers[i].trigPanelGUID));
// Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
- Mem.WriteWord(gTriggers[i].TimeOut);
+ utils.writeInt(st, Word(gTriggers[i].TimeOut));
// UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
- Mem.WriteWord(gTriggers[i].ActivateUID);
+ utils.writeInt(st, Word(gTriggers[i].ActivateUID));
// Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
actCount := Length(gTriggers[i].Activators);
- Mem.WriteInt(actCount);
+ utils.writeInt(st, LongInt(actCount));
for j := 0 to actCount-1 do
begin
// UID îáúåêòà
- Mem.WriteWord(gTriggers[i].Activators[j].UID);
+ utils.writeInt(st, Word(gTriggers[i].Activators[j].UID));
// Âðåìÿ îæèäàíèÿ
- Mem.WriteWord(gTriggers[i].Activators[j].TimeOut);
+ utils.writeInt(st, Word(gTriggers[i].Activators[j].TimeOut));
end;
// Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
- Mem.WriteBoolean(gTriggers[i].PlayerCollide);
+ utils.writeBool(st, gTriggers[i].PlayerCollide);
// Âðåìÿ äî çàêðûòèÿ äâåðè
- Mem.WriteInt(gTriggers[i].DoorTime);
+ utils.writeInt(st, LongInt(gTriggers[i].DoorTime));
// Çàäåðæêà àêòèâàöèè
- Mem.WriteInt(gTriggers[i].PressTime);
+ utils.writeInt(st, LongInt(gTriggers[i].PressTime));
// Ñ÷åò÷èê íàæàòèé
- Mem.WriteInt(gTriggers[i].PressCount);
+ utils.writeInt(st, LongInt(gTriggers[i].PressCount));
// Ñïàâíåð àêòèâåí
- Mem.WriteBoolean(gTriggers[i].AutoSpawn);
+ utils.writeBool(st, gTriggers[i].AutoSpawn);
// Çàäåðæêà ñïàâíåðà
- Mem.WriteInt(gTriggers[i].SpawnCooldown);
+ utils.writeInt(st, LongInt(gTriggers[i].SpawnCooldown));
// Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
- Mem.WriteInt(gTriggers[i].SpawnedCount);
+ utils.writeInt(st, LongInt(gTriggers[i].SpawnedCount));
// Ñêîëüêî ðàç ïðîèãðàí çâóê
- Mem.WriteInt(gTriggers[i].SoundPlayCount);
+ utils.writeInt(st, LongInt(gTriggers[i].SoundPlayCount));
// Ïðîèãðûâàåòñÿ ëè çâóê?
if (gTriggers[i].Sound <> nil) then b := gTriggers[i].Sound.IsPlaying() else b := false;
- Mem.WriteBoolean(b);
+ utils.writeBool(st, b);
if b then
begin
// Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
- dw := gTriggers[i].Sound.GetPosition();
- Mem.WriteDWORD(dw);
+ utils.writeInt(st, LongWord(gTriggers[i].Sound.GetPosition()));
// Ãðîìêîñòü çâóêà
sg := gTriggers[i].Sound.GetVolume();
- sg := sg / (gSoundLevel/255.0);
- Mem.WriteSingle(sg);
+ sg := sg/(gSoundLevel/255.0);
+ //Mem.WriteSingle(sg);
+ st.WriteBuffer(sg, sizeof(sg)); // sorry
// Ñòåðåî ñìåùåíèå çâóêà
sg := gTriggers[i].Sound.GetPan();
- Mem.WriteSingle(sg);
+ //Mem.WriteSingle(sg);
+ st.WriteBuffer(sg, sizeof(sg)); // sorry
+ end;
+ // uservars
+ if (gTriggers[i].userVars = nil) then
+ begin
+ utils.writeInt(st, LongInt(0));
+ end
+ else
+ begin
+ utils.writeInt(st, LongInt(gTriggers[i].userVars.count)); //FIXME: check for overflow
+ for kv in gTriggers[i].userVars.byKeyValue do
+ begin
+ //writeln('<', kv.key, '>:<', VarToStr(kv.value), '>');
+ utils.writeStr(st, kv.key);
+ t := LongInt(varType(kv.value));
+ utils.writeInt(st, LongInt(t));
+ case t of
+ varString: utils.writeStr(st, AnsiString(kv.value));
+ varBoolean: utils.writeBool(st, Boolean(kv.value));
+ varShortInt: utils.writeInt(st, LongInt(kv.value));
+ varSmallint: utils.writeInt(st, LongInt(kv.value));
+ varInteger: utils.writeInt(st, LongInt(kv.value));
+ //varInt64: Mem.WriteInt(Integer(kv.value));
+ varByte: utils.writeInt(st, LongInt(kv.value));
+ varWord: utils.writeInt(st, LongInt(kv.value));
+ varLongWord: utils.writeInt(st, LongInt(kv.value));
+ //varQWord:
+ else raise Exception.CreateFmt('cannot save uservar ''%s''', [kv.key]);
+ end;
+ end;
end;
end;
end;
-procedure g_Triggers_LoadState (var Mem: TBinMemoryReader);
+procedure g_Triggers_LoadState (st: TStream);
var
count, actCount, i, j, a: Integer;
dw: DWORD;
b: Boolean;
Trig: TTrigger;
mapIndex: Integer;
+ uvcount: Integer;
+ vt: LongInt;
+ vv: Variant;
+ uvname: AnsiString = '';
+ ustr: AnsiString = '';
+ uint: LongInt;
+ ubool: Boolean;
begin
- if (Mem = nil) then exit;
+ assert(st <> nil);
g_Triggers_Free();
// Êîëè÷åñòâî òðèããåðîâ
- Mem.ReadInt(count);
+ count := utils.readLongInt(st);
if (count = 0) then exit;
+ if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid trigger count');
for a := 0 to count-1 do
begin
// Ñèãíàòóðà òðèããåðà
- Mem.ReadDWORD(dw); // 'TRGX'
- if (dw <> TRIGGER_SIGNATURE) then raise EBinSizeError.Create('g_Triggers_LoadState: Wrong Trigger Signature');
+ if not utils.checkSign(st, 'TRGX') then raise XStreamError.Create('invalid trigger signature');
+ if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid trigger version');
// Òèï òðèããåðà
- Mem.ReadByte(Trig.TriggerType);
- // Ñïåöèàëüíûå äàííûå òðèããåðà: èíäåêñ â gCurrentMap.field['triggers']
+ Trig.TriggerType := utils.readByte(st);
if (Trig.TriggerType = TRIGGER_NONE) then continue; // empty one
- Mem.ReadInt(mapIndex);
+ // Ñïåöèàëüíûå äàííûå òðèããåðà: èíäåêñ â gCurrentMap.field['triggers']
+ mapIndex := utils.readLongInt(st);
i := g_Triggers_CreateWithMapIndex(Trig, a, mapIndex);
- {
- if (gTriggers[i].trigData <> nil) then
- begin
- tw := TStrTextWriter.Create();
- try
- gTriggers[i].trigData.writeTo(tw);
- e_LogWritefln('=== trigger #%s loaded ==='#10'%s'#10'---', [mapIndex, tw.str]);
- finally
- tw.Free();
- end;
- end;
- }
- // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
- Mem.ReadInt(gTriggers[i].X);
- Mem.ReadInt(gTriggers[i].Y);
- // Ðàçìåðû:
- Mem.ReadWord(gTriggers[i].Width);
- Mem.ReadWord(gTriggers[i].Height);
- // Âêëþ÷åí ëè òðèããåð:
- Mem.ReadBoolean(gTriggers[i].Enabled);
- // Òèï àêòèâàöèè òðèããåðà:
- Mem.ReadByte(gTriggers[i].ActivateType);
- // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
- Mem.ReadByte(gTriggers[i].Keys);
- // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
- Mem.ReadInt(gTriggers[i].TexturePanelGUID);
- // Òèï ýòîé ïàíåëè:
- Mem.ReadWord(gTriggers[i].TexturePanelType);
- // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
- Mem.ReadInt(gTriggers[i].trigPanelGUID);
- // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
- Mem.ReadWord(gTriggers[i].TimeOut);
- // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
- Mem.ReadWord(gTriggers[i].ActivateUID);
- // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
- Mem.ReadInt(actCount);
- if actCount > 0 then
+ // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
+ gTriggers[i].X := utils.readLongInt(st);
+ gTriggers[i].Y := utils.readLongInt(st);
+ // Ðàçìåðû
+ gTriggers[i].Width := utils.readWord(st);
+ gTriggers[i].Height := utils.readWord(st);
+ // Âêëþ÷åí ëè òðèããåð
+ gTriggers[i].Enabled := utils.readBool(st);
+ // Òèï àêòèâàöèè òðèããåðà
+ gTriggers[i].ActivateType := utils.readByte(st);
+ // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
+ gTriggers[i].Keys := utils.readByte(st);
+ // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
+ gTriggers[i].TexturePanelGUID := utils.readLongInt(st);
+ // Òèï ýòîé ïàíåëè
+ //Mem.ReadWord(gTriggers[i].TexturePanelType);
+ // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
+ gTriggers[i].trigPanelGUID := utils.readLongInt(st);
+ // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
+ gTriggers[i].TimeOut := utils.readWord(st);
+ // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
+ gTriggers[i].ActivateUID := utils.readWord(st);
+ // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
+ actCount := utils.readLongInt(st);
+ if (actCount < 0) or (actCount > 1024*1024) then raise XStreamError.Create('invalid activated object count');
+ if (actCount > 0) then
begin
SetLength(gTriggers[i].Activators, actCount);
for j := 0 to actCount-1 do
begin
// UID îáúåêòà
- Mem.ReadWord(gTriggers[i].Activators[j].UID);
+ gTriggers[i].Activators[j].UID := utils.readWord(st);
// Âðåìÿ îæèäàíèÿ
- Mem.ReadWord(gTriggers[i].Activators[j].TimeOut);
+ gTriggers[i].Activators[j].TimeOut := utils.readWord(st);
end;
end;
- // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
- Mem.ReadBoolean(gTriggers[i].PlayerCollide);
- // Âðåìÿ äî çàêðûòèÿ äâåðè:
- Mem.ReadInt(gTriggers[i].DoorTime);
- // Çàäåðæêà àêòèâàöèè:
- Mem.ReadInt(gTriggers[i].PressTime);
- // Ñ÷åò÷èê íàæàòèé:
- Mem.ReadInt(gTriggers[i].PressCount);
- // Ñïàâíåð àêòèâåí:
- Mem.ReadBoolean(gTriggers[i].AutoSpawn);
- // Çàäåðæêà ñïàâíåðà:
- Mem.ReadInt(gTriggers[i].SpawnCooldown);
- // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
- Mem.ReadInt(gTriggers[i].SpawnedCount);
- // Ñêîëüêî ðàç ïðîèãðàí çâóê:
- Mem.ReadInt(gTriggers[i].SoundPlayCount);
- // Ïðîèãðûâàåòñÿ ëè çâóê?
- Mem.ReadBoolean(b);
+ // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
+ gTriggers[i].PlayerCollide := utils.readBool(st);
+ // Âðåìÿ äî çàêðûòèÿ äâåðè
+ gTriggers[i].DoorTime := utils.readLongInt(st);
+ // Çàäåðæêà àêòèâàöèè
+ gTriggers[i].PressTime := utils.readLongInt(st);
+ // Ñ÷åò÷èê íàæàòèé
+ gTriggers[i].PressCount := utils.readLongInt(st);
+ // Ñïàâíåð àêòèâåí
+ gTriggers[i].AutoSpawn := utils.readBool(st);
+ // Çàäåðæêà ñïàâíåðà
+ gTriggers[i].SpawnCooldown := utils.readLongInt(st);
+ // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
+ gTriggers[i].SpawnedCount := utils.readLongInt(st);
+ // Ñêîëüêî ðàç ïðîèãðàí çâóê
+ gTriggers[i].SoundPlayCount := utils.readLongInt(st);
+ // Ïðîèãðûâàåòñÿ ëè çâóê?
+ b := utils.readBool(st);
if b then
begin
- // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
- Mem.ReadDWORD(dw);
- // Ãðîìêîñòü çâóêà:
- Mem.ReadSingle(vol);
- // Ñòåðåî ñìåùåíèå çâóêà:
- Mem.ReadSingle(pan);
- // Çàïóñêàåì çâóê, åñëè åñòü:
- if gTriggers[i].Sound <> nil then
+ // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
+ dw := utils.readLongWord(st);
+ // Ãðîìêîñòü çâóêà
+ //Mem.ReadSingle(vol);
+ st.ReadBuffer(vol, sizeof(vol)); // sorry
+ // Ñòåðåî ñìåùåíèå çâóêà
+ //Mem.ReadSingle(pan);
+ st.ReadBuffer(pan, sizeof(pan)); // sorry
+ // Çàïóñêàåì çâóê, åñëè åñòü
+ if (gTriggers[i].Sound <> nil) then
begin
gTriggers[i].Sound.PlayPanVolume(pan, vol);
gTriggers[i].Sound.Pause(True);
gTriggers[i].Sound.SetPosition(dw);
end
end;
+ // uservars
+ gTriggers[i].userVars.Free();
+ gTriggers[i].userVars := nil;
+ uvcount := utils.readLongInt(st);
+ if (uvcount < 0) or (uvcount > 1024*1024) then raise XStreamError.Create('invalid number of user vars in trigger');
+ if (uvcount > 0) then
+ begin
+ gTriggers[i].userVars := THashStrVariant.Create();
+ vv := Unassigned;
+ while (uvcount > 0) do
+ begin
+ Dec(uvcount);
+ uvname := utils.readStr(st);
+ vt := utils.readLongInt(st);
+ case vt of
+ varString: begin ustr := utils.readStr(st); vv := ustr; end;
+ varBoolean: begin ubool := utils.readBool(st); vv := ubool; end;
+ varShortInt: begin uint := utils.readLongInt(st); vv := ShortInt(uint); end;
+ varSmallint: begin uint := utils.readLongInt(st); vv := SmallInt(uint); end;
+ varInteger: begin uint := utils.readLongInt(st); vv := LongInt(uint); end;
+ varByte: begin uint := utils.readLongInt(st); vv := Byte(uint); end;
+ varWord: begin uint := utils.readLongInt(st); vv := Word(uint); end;
+ varLongWord: begin uint := utils.readLongInt(st); vv := LongWord(uint); end;
+ else raise Exception.CreateFmt('cannot load uservar ''%s''', [uvname]);
+ end;
+ gTriggers[i].userVars.put(uvname, vv);
+ end;
+ end;
end;
end;
+
end.