X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_triggers.pas;h=2aa9cfa7fb9b6a17c1cfe81ab4a50f47dcfed398;hb=69e1c288d6d270abd835cde8a87e818d0298799f;hp=2d19853372178e21d863f10c8f39d9d521ccca57;hpb=db4e988645273fe1c11611d84e03f0199cd181f7;p=d2df-sdl.git diff --git a/src/game/g_triggers.pas b/src/game/g_triggers.pas index 2d19853..2aa9cfa 100644 --- a/src/game/g_triggers.pas +++ b/src/game/g_triggers.pas @@ -19,14 +19,18 @@ unit g_triggers; interface uses + Variants, MAPDEF, e_graphics, g_basic, g_sound, - BinEditor, xdynrec; + BinEditor, xdynrec, hashtable, exoma; type + THashStrVariant = specialize THashBase; + TActivator = record UID: Word; TimeOut: Word; end; + PTrigger = ^TTrigger; TTrigger = record public @@ -66,19 +70,18 @@ type 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(Trigger: 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; @@ -114,6 +117,143 @@ const {$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(hsihash, hsiequ); + 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); @@ -122,23 +262,27 @@ end; 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; @@ -692,9 +836,9 @@ begin 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; @@ -1069,18 +1213,53 @@ var 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 @@ -2143,10 +2322,11 @@ 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.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); @@ -2155,16 +2335,20 @@ begin begin Trigger.trigDataRec := nil; end; - result := g_Triggers_Create(Trigger, arridx); + } + result := g_Triggers_Create(Trigger, triggers.itemAt[mapidx], arridx); end; -function g_Triggers_Create(Trigger: TTrigger; forceInternalIndex: Integer=-1): DWORD; +function g_Triggers_Create(Trigger: TTrigger; trec: TDynRecord; forceInternalIndex: Integer=-1): DWORD; var find_id: DWORD; fn, mapw: AnsiString; f, olen: Integer; begin + if (tgscope = nil) then tgscope := TTrigScope.Create(); + if (tgclist = nil) then tgclist := TMyConstList.Create(); + // Íå ñîçäàâàòü âûõîä, åñëè èãðà áåç âûõîäà if (Trigger.TriggerType = TRIGGER_EXIT) and (not LongBool(gGameSettings.Options and GAME_OPTION_ALLOWEXIT)) then @@ -2189,12 +2373,50 @@ 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; + Trigger.mapId := trec.id; + // clone trigger data + if (trec.trigRec = nil) then + begin + gTriggers[find_id].trigDataRec := nil; + //HACK! + if (gTriggers[find_id].TriggerType <> TRIGGER_SECRET) then + begin + e_LogWritefln('trigger of type %s has no triggerdata; wtf?!', [gTriggers[find_id].TriggerType], MSG_WARNING); + end; + end + else + begin + gTriggers[find_id].trigDataRec := trec.trigRec.clone(nil); + end; + with gTriggers[find_id] do begin ID := find_id; @@ -2225,6 +2447,67 @@ begin // update cached trigger variables trigUpdateCacheData(gTriggers[find_id], gTriggers[find_id].trigDataRec); + gTriggers[find_id].userVars := nil; //THashStrVariant.Create(hsihash, hsiequ); + + try + gTriggers[find_id].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'])]); + gTriggers[find_id].exoThink := nil; + end; + else + raise; + end; + try + gTriggers[find_id].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'])]); + gTriggers[find_id].exoCheck := nil; + end; + else + raise; + end; + try + gTriggers[find_id].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'])]); + gTriggers[find_id].exoAction := nil; + end; + else + raise; + end; + try + gTriggers[find_id].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'])]); + gTriggers[find_id].exoInit := nil; + end; + else + raise; + end; + + if (forceInternalIndex < 0) and (gTriggers[find_id].exoInit <> nil) then + begin + //conwritefln('executing trigger init: [%s]', [gTriggers[find_id].exoInit.toString()]); + try + tgscope.me := @gTriggers[find_id]; + gTriggers[find_id].exoInit.value(tgscope); + tgscope.me := nil; + except + tgscope.me := nil; + conwritefln('*** trigger exoactivate error: %s', [gTriggers[find_id].exoInit.toString()]); + exit; + end; + end; + // Çàãðóæàåì çâóê, åñëè ýòî òðèããåð "Çâóê" if (Trigger.TriggerType = TRIGGER_SOUND) and (Trigger.tgcSoundName <> '') then begin @@ -2395,7 +2678,7 @@ begin 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 @@ -2470,7 +2753,7 @@ begin // Îïðåäåëÿåì èçìåíÿåìûå èì òðèããåðû: for b := 0 to High(gTriggers) do begin - if g_Collide(tgctX, tgctY, tgctWidth, tgctHeight, gTriggers[b].X, gTriggers[b].Y, + if g_Collide(tgcTX, tgcTY, tgcTWidth, tgcTHeight, gTriggers[b].X, gTriggers[b].Y, gTriggers[b].Width, gTriggers[b].Height) and ((b <> a) or (tgcWait > 0)) then begin // Can be self-activated, if there is Data.Wait @@ -2813,6 +3096,11 @@ 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; @@ -2827,6 +3115,10 @@ var dw: DWORD; sg: Single; b: Boolean; + kv: THashStrVariant.PEntry; + //it: THashStrVariant.TKeyValEnumerator; + //uname: AnsiString; + t: LongInt; begin // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ count := Length(gTriggers); @@ -2910,6 +3202,35 @@ begin sg := gTriggers[i].Sound.GetPan(); Mem.WriteSingle(sg); end; + // uservars + if (gTriggers[i].userVars = nil) then + begin + Mem.WriteInt(0); + end + else + begin + Mem.WriteInt(gTriggers[i].userVars.count); + for kv in gTriggers[i].userVars.byKeyValue do + begin + //writeln('<', kv.key, '>:<', VarToStr(kv.value), '>'); + Mem.WriteString(kv.key); + t := LongInt(varType(kv.value)); + Mem.WriteInt(t); + case t of + varString: Mem.WriteString(AnsiString(kv.value)); + varBoolean: Mem.WriteBoolean(Boolean(kv.value)); + varShortInt: Mem.WriteInt(Integer(kv.value)); + varSmallint: Mem.WriteInt(Integer(kv.value)); + varInteger: Mem.WriteInt(Integer(kv.value)); + //varInt64: Mem.WriteInt(Integer(kv.value)); + varByte: Mem.WriteInt(Integer(kv.value)); + varWord: Mem.WriteInt(Integer(kv.value)); + varLongWord: Mem.WriteInt(Integer(kv.value)); + //varQWord: + else raise Exception.CreateFmt('cannot save uservar ''%s''', [kv.key]); + end; + end; + end; end; end; @@ -2922,6 +3243,13 @@ var 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; @@ -2942,18 +3270,6 @@ begin if (Trig.TriggerType = TRIGGER_NONE) then continue; // empty one Mem.ReadInt(mapIndex); 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); @@ -3023,7 +3339,36 @@ begin gTriggers[i].Sound.SetPosition(dw); end end; + // uservars + gTriggers[i].userVars.Free(); + gTriggers[i].userVars := nil; + Mem.ReadInt(uvcount); + if (uvcount > 0) then + begin + gTriggers[i].userVars := THashStrVariant.Create(hsihash, hsiequ); + vv := Unassigned; + while (uvcount > 0) do + begin + Dec(uvcount); + uvname := ''; + Mem.ReadString(uvname); + Mem.ReadInt(vt); + case vt of + varString: begin ustr := ''; Mem.ReadString(ustr); vv := ustr; end; + varBoolean: begin Mem.ReadBoolean(ubool); vv := ubool; end; + varShortInt: begin Mem.ReadInt(uint); vv := ShortInt(uint); end; + varSmallint: begin Mem.ReadInt(uint); vv := SmallInt(uint); end; + varInteger: begin Mem.ReadInt(uint); vv := LongInt(uint); end; + varByte: begin Mem.ReadInt(uint); vv := Byte(uint); end; + varWord: begin Mem.ReadInt(uint); vv := Word(uint); end; + varLongWord: begin Mem.ReadInt(uint); 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.