-
Notifications
You must be signed in to change notification settings - Fork 1
/
DScript.nut
2298 lines (1923 loc) · 80.9 KB
/
DScript.nut
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*#########################################
DScript Version 0.42a
Use at your liking.
All Squirrel scripts get combined together so you can use the scripts in here via extends in other .nut files as well.
Check out the more advanced version in the Scripts-In-progress branch.
##########################################*/
#################BASE FUNCTIONS###############
const DtR = 0.01745 // DegToRad PI/180
#######Getting Parameter functions####
function DGetAllDescendants(at,objset) //Emulation of the "@"-parameter. BruteForce crawl. Don't know if there is a better way.
{
foreach ( l in Link.GetAll("~MetaProp",at)) //~MetaProp links are invisible in the editor but form the hirarchy of our archetypes. Counterintuitivly going from Ancestor to Descendant.
{
local id=LinkDest(l);
if (id>0){objset.append(id)}
else {DGetAllDescendants(id,objset)}
}
}
############################################
function DCheckString(r,adv=false) //Analysis of a given string parameter depending on its prefixed parameter.
{ // adv(anced)=true returns the result in an array instead of a single entity
//handling non strings
switch (typeof(r))
{
case "array":
if (adv){return r}else{return r.pop()}
case "float":
case "integer":
if (adv){return [r]}else{return r}
case "null":
case "bool":
return r //low prio TODO: Even if there should be no such situation, add adv.
}
//Convenient sugar code for you
switch (r)
{
case "[me]":
{if (adv){return [self]}else{return self}}
case "[source]":
{if (adv){return [SourceObj]}else{return SourceObj}}
case "":
{if (adv){print("DScript Warning: Returning empty string array, that's kinda strange.");return [""]}else{return ""}}
}
//Operator check.
local objset=[]
switch (r[0])
{
case '&': //Linked Objects of &LinkType. Returns Array
foreach ( l in Link.GetAll(r.slice(1),self)){objset.append(LinkDest(l))}
break
case '*': //Object of Type, without descendants
r=r.slice(1)
if (r[0]=='-') //Single Archetype
{
r=r.tointeger()
}
else //If obj is a concrete one it will still work
{
r=ObjID(r)
}
foreach ( l in Link.GetAll("~MetaProp",ObjID(r.slice(1))))
{
local id=LinkDest(l);
if (id>0){objset.append(id)}
}
break
case '@': //Object of Type, with descendants. See first function above.
r=r.slice(1)
if (r[0]=='-') //Example @-441
{
r=r.tointeger()
}
else //@switches
{
r=ObjID(r)
}
DGetAllDescendants(r,objset)
break
case '$': //Looks for QVar variable
objset.append(Quest.Get(r.slice(1)))
break
case '^': //Read line below ;) Not sure if this works for T1/G
objset.append(Object.FindClosestObjectNamed(self, r.slice(1)))
break
// Add/Remove subset with + and +- operator
case '+':
local ar= split(r,"+")
ar.remove(0)
foreach (t in ar) //Loops back into this function to get your specific set
{
if (t[0]!= '-')
{objset.extend(DCheckString(t,1))}
else // +- operator, remove subset
{
local removeset = DCheckString(t.slice(1),1)
local idx=null
foreach (k in removeset)
{
idx = objset.find(k)
if (idx!=null) {objset.remove(idx)}
}
}
}
break
// integer, float, vector check if they come as string.
case '#':
objset.append(r.slice(1).tointeger())
break
case '.': //Float. Not sure if this is the best operator choice.
objset.append(r.slice(1).tofloat())
break
case '<': //vector
local ar= split(r,"<,")
return vector(ar[1].tofloat(),ar[2].tofloat(),ar[3].tofloat())
default :
objset.append(r) //problem for example +42 gives back a "42"-string which is not an object... and TurnOn can't be converted into an integer.
} //TODO: Where did I put the workaround? Possible with Try..catch or??? Fixed in new version.
if (adv){return objset}else{return objset.pop()} //Return an array or single entity
}
function DGetParam(par,def=null,DN=null,adv=false) //Function to return parameters, returns given default value. if adv=1 an array of objects will be returned.
{
if(!DN){DN=userparams()} //The Design Note has to be passed on to a) save userparams() calls and b)for this function to work for artificial tables and class objects as well.
if (par in DN)
{
return DCheckString(DN[par],adv) //will return arrayed or single objs(adv=1).
}
else //Default Value
{
return DCheckString(def,adv)
}
}
function DGetStringParam(param,def,str,adv=false,cuts=";=") //Like the above function but works with strings instead of a table. To get an (object) array set adv=1.
{
str=str.tostring()
local div = split(str,cuts); //Puts the values into arrays which where before sperated by your characters specified by cuts
local key = div.find(param);
if (key)
{return DCheckString(div[key+1],adv)} //Parameter=Value form [...ParameterIndex,ParameterIndex+1,...] indexed paris.
else
{return DCheckString(def,adv)}
}
/*#########Carry over Data via TimerData Functions###################
Q: How to carry over data from one Script to another when there is a delay?
PROBLEM 1: The SourceObject is not carried over when the message get's delayed via a StandardTimer. => A: Save as a global variable.
PROBLEM 2: That data is LOST when the game is gets closed and reloaded.
This functions allows the sending and retrieving of multiple values via a timer which is save game persistent.
*/
function DArrayToString(ar,o="+") //Creates a long string out of your array data separated by +
{
local data=""
local l = ar.len()-1
for(local i = 0; i< l; i++)
{
data += ar[i]+o //appends your next indexed value and your divide operator
}
return data +=ar[l] //Returns your string
}
function DSetTimerDataTo(To,name,delay,...) //Target to another object. '...' Means it takes an unspecified amount of data -> vargv
{
local data = DArrayToString(vargv)
return SetOneShotTimer(To,name,delay,data)
}
function DSetTimerData(name,delay,...) //Target is self
{
local data = DArrayToString(vargv)
return SetOneShotTimer(name,delay,data)
}
function DGetTimerData(m,KeyValue=false) //Get back your data after timeout
{
local o="+"
if (KeyValue){o="+="} //Is your data only seperated by + or by Key1=Value1+Key2=....
return split(m,o) //low prio todo: While IMO not necessary add general operator option.
}
##########################################
###########Section Counter and Capacitor Checks#######
/* Script activation Count and Capacitors (activations in a limited time frame) are handled via Object Data, in this section they are set and controlled.*/
function DCapacitorCheck(script,DN,OnOff="") //Capacitors only, general "" or "On/Off" specific
{
local Capa = GetData(script+OnOff+"Capacitor")+1 //NewValue
//DEBUG print(script+"Capa= "+Capa+"/"+DGetParam(script+OnOff+"Capacitor",null,DN)+" Timer:"+DGetParam(script+OnOff+"CapacitorFalloff",null,DN))
if (Capa == DGetParam(script+OnOff+"Capacitor",0,DN).tointeger()) //Reached Threshold? //DHub compability
{
SetData(script+OnOff+"Capacitor",0) //Reset Capacitor and terminate now unnecessary FalloffTimer
if (DGetParam(script+OnOff+"CapacitorFalloff",false,DN))
{KillTimer(ClearData(script+OnOff+"FalloffTimer"))}
return null //Don't abort
}
else
{
if (DGetParam(script+OnOff+"CapacitorFalloff",false,DN))
{
if(IsDataSet(script+OnOff+"FalloffTimer")){KillTimer(GetData(script+OnOff+"FalloffTimer"))} //reseting timer and killing old ones.
SetData(script+OnOff+"FalloffTimer",SetOneShotTimer(script+"Falloff",DGetParam(script+OnOff+"CapacitorFalloff",false,DN).tofloat(),OnOff))
}
SetData(script+OnOff+"Capacitor",Capa)
return true //Abort
}
}
function DCountCapCheck(script,DN,func) //Does all the checks and delays before the execution of a Script
{
//Checks if a Capacitor is set and if it is reached with the function above. func=1 means a TurnOn
//Strange to look at it with the null statements I know. But this setup enables that the three different Setups can't interfere with each other.
//Abuses (null==null)==true, Once abort is false it can't be true anymore, while beeing null(undecided) it can be changed by the OnOff checks.
local abort = null
if (IsDataSet(script+"Capacitor")){if(DCapacitorCheck(script,DN)){abort = true}else{abort=false}}
if (IsDataSet(script+"OnCapacitor")&&func==1){if(DCapacitorCheck(script,DN,"On")){if (abort==null){abort = true}}else{abort=false}}
if (IsDataSet(script+"OffCapacitor")&&func==0){if(DCapacitorCheck(script,DN,"Off")){if (abort==null){abort = true}}else{abort=false}}
if (abort){return} //If abort changed to true.
//Is a Counter Set?
if (IsDataSet(script+"Counter")) //low prio todo: add DHub compability
{
local CountOnly = DGetParam(script+"CountOnly",0,DN) //Count only ONs or OFFs
if (CountOnly == 0 || CountOnly+func == 2) //Disabled or On(CountOnly1+Func1=2), Off(CountOnly2+Func0=2)
{
local Count = SetData(script+"Counter",GetData(script+"Counter")+1)
if (Count > DGetParam(script+"Count",0,DN).tointeger()){return} //Over the Max abort.
}
}
//Use a Negative Fail chance to increase Counter and Capacitor even if it could fail later.
local FailChance = DGetParam(script+"FailChance",0,DN).tointeger()
if (FailChance < 0) {if (FailChance <= Data.RandInt(-100,0)){return}}
// All Checks green! Then Go or Delay it?
local d = DGetParam(script+"Delay",false,DN).tofloat()
if (d)
{
//Stop old timers if ExlusiveDelay is set.
if (IsDataSet(script+"DelayTimer")&& DGetParam(script+"ExclusiveDelay",false,DN))
{
KillTimer(GetData(script+"DelayTimer"))
}
//Stop Infinite Repeat
if (IsDataSet(script+"InfRepeat"))
{
if (GetData(script+"InfRepeat") != func) //Inverse Command received => end repeat and clean up. Same func gets ignored -> Activation is solely handled via the reapeating DelayTimer.
{
KillTimer(GetData(script+"DelayTimer"))
ClearData(script+"DelayTimer")
ClearData(script+"InfRepeat")
}
}
else
//Start DelayTimer -> Above timer message handle activation when received.
{
local r=DGetParam(script+"Repeat",0,DN).tointeger()
if (r==-1){SetData(script+"InfRepeat",func)} //If infinite repats store if they are ON or OFF.
//Store the Timer inside the ObjectsData and start it with all necessary information inside the timers name.
SetData(script+"DelayTimer",DSetTimerData(script+"Delayed",d,func,SourceObj,r,d))
}
}
else //No Delay. Excute the scripts ON or OFF functions.
{if (func){this.DoOn(DN)}else{this.DoOff(DN)}}
}
##########
#################################
function DBaseFunction(DN,script) //this got turned into a global function so DHub can use it.
#################################
{
##Handle Special Messages
local bmsg=message()
local mssg =bmsg.message
if (mssg == "ResetCount") //ResetCount is not script specific! low prio todo: do
{if (IsDataSet(script+"Counter")){SetData(script+"Counter",0)}}
if (mssg=="Timer") //Check if they are special timers like Capacitor Falloff or DataTimers
{
local msg = bmsg.name //Name of the Timer
if (msg==script+"Falloff") //Ending FalloffCapacitor Timer. This is script specific.
{
local cfo = bmsg.data //ON or OFF or "" //Check between On/Off/""Falloff
local dat=GetData(script+cfo+"Capacitor")-1
if (dat>-1) //low prio TODO: One 'wasted' timer? fixed in next ver.
{
SetData(script+cfo+"Capacitor",dat) //Reduce Capacitor by 1 and start a new Timer. The Timer(ID) is stored to catch it.
SetData(script+cfo+"FalloffTimer",SetOneShotTimer(script+"Falloff",DGetParam(script+cfo+"CapacitorFalloff",0,DN).tofloat(),cfo))}
else
{
ClearData(script+cfo+"FalloffTimer") //No more Timer, clear pointer.
}
}
//DELAY AND REPEAT
if (msg==script+"Delayed") //Delayed Activation now initiate script.
{
local ar =DGetTimerData(bmsg.data) //Get Stored Data, [ON/OFF,Source,More Repeats to do?,timerdelay]
ar[0]= ar[0].tointeger() //func Off(0), ON(1)
SourceObj=ar[1].tointeger()
if (ar[0]){this.DoOn(DN)}else{this.DoOff(DN)}
ar[2] = ar[2].tointeger()
if (ar[2]!=0) //Are there Repeats left? If yes start new Timer
{
if (ar[2]!=-1){ar[2]-=1} //-1 infinite repeats.
ar[3]=ar[3].tofloat() //Now start a new timer with savegame persistent data.
SetData(script+"DelayTimer",DSetTimerData(script+"Delayed",ar[3],ar[0],SourceObj,ar[2],ar[3]))
}
else
{
ClearData(script+"DelayTimer") //Clean up behind yourself!
}
}
}
//Gets the correct [source] for most non standard messages.
switch (bmsg.getclass()) // I think checking the instance is easier than string comparison.
{
case sFrobMsg :
SourceObj = bmsg.Frobber
break
case sPhysMsg :
if (bmsg.collType){
SourceObj = bmsg.collObj #NOTE can be a real object OR a texture
break
}
if (bmsg.contactType){
SourceObj = bmsg.contactType // ContactCreate
break
}
// "PhysFellAsleep", "PhysWokeUp", "PhysMadePhysical", "PhysMadeNonPhysical" "PhysEnter", "PhysExit" left.
SourceObj = bmsg.transObj // For first two it's 0. TODO: test nonphysikal
break
case sContainerScrMsg : // Send to the Container when it contains something
SourceObj = bmsg.containee
break
case sContainedScrMsg :
SourceObj = bmsg.container
break
case sDamageScrMsg :
case sSlayMsg :
SourceObj = bmsg.culprit // In case of player this is the weapon not player itself. TODO what about AIs?
break // TLG: Joke in source: "Culprit: Mr. Green kind: With the candlestick damage type."
case sAttackMsg :
SourceObj = bmsg.weapon
break
case sStimMsg :
SourceObj = sLink(bmsg.source).From() #NOTE Source / Sensor links go From the sending object To the StimArchetype. Good to know ;)
break
case sMovingTerrainMsg :
SourceObj = bmsg.waypoint
break
case sWaypointMsg :
SourceObj = bmsg.moving_terrain
break
case sRoomMsg:
// "ObjRoomTransit" is sent to the object. All other messages from API-reference are sent to the room.
if (bmsg.TransitionType == eRoomChange.kRoomTransit) {
// As there are two options lets check for possible links which define priority by the user.
foreach (link in ["Route", "~Population"]){
foreach (room in [bmsg.ToObjId, bmsg.FromObjId]){
if (Link.AnyExist(link, self, room)){
SourceObj = room
break
}
}
}
SourceObj = bmsg.ToObjId // no link found return ToObjRoom
}
else // No Transit message means self is a room and we use the object as source.
SourceObj = bmsg.MoveObjId
break
default:
SourceObj = bmsg.from
} //TODO: Add stim, room, obb...
###
//Let it fail?
local FailChance = DGetParam(script+"FailChance",0,DN)
if (FailChance > 0)
{if (FailChance >= Data.RandInt(0,100)){return}}
###
//React to the received message? Checks if the script actually has a ON/OFF function and if the message is in the set of specified commands. And Yes a DScript can perform it's ON and OFF action if both accepct the same message.
if ("DoOn" in this)
{
if (DGetParam(script+"On",DGetParam("DefOn","TurnOn",this,1),DN,1).find(mssg)!=null){DCountCapCheck(script,DN,1)}
}
if ("DoOff" in this)
{
if (DGetParam(script+"Off",DGetParam("DefOff","TurnOff",this,1),DN,1).find(mssg)!=null){DCountCapCheck(script,DN,0)}
}
}
##################END OF GLOBAL FUNCTIONS###############################
############################################
class DBaseTrap extends SqRootScript
############################################
{
/*A Base script. Has no function on it's own but is the framework for nearly all others.
Handles custom [ScriptName]ON/OFF parameters specified in the Design Note and calls the DoON/OFF actions of the specific script via the functions above.
If no parameter is set the scripts normaly respond to TurnOn and TurnOff, if you instead want another default activation message you can specify this with DefOn="CustomMessage" or DefOff="TurnOn" anywhere in your script class but outside of functions. Messages specified in the Design Note have priority.
In the constructor() function it handles the necessary ObjectData needed for Counters and Capacitors.
*/
SourceObj=0 //if a message is delayed the source object is lost, it will be stored inside the timer and then when the timer triggers it will be made availiable again.
constructor() //Setting up save game persistent data.
{
if (!IsEditor()){return} //Initial data is set in the Editor. NOTE! possible TODO: Counter, Capacitor objects will not work when created in game!
local DN = userparams();
local script = GetClassName()
if (DGetParam(script+"Count",0,DN)){SetData(script+"Counter",0)}else{ClearData(script+"Counter")} //Automatic clean up.
if (DGetParam(script+"Capacitor",1,DN) != 1){SetData(script+"Capacitor",0)}else{ClearData(script+"Capacitor")}
if (DGetParam(script+"OnCapacitor",1,DN) != 1){SetData(script+"OnCapacitor",0)}else{ClearData(script+"OnCapacitor")}
if (DGetParam(script+"OffCapacitor",1,DN) != 1){SetData(script+"OffCapacitor",0)}else{ClearData(script+"OffCapacitor")}
}
function OnMessage() //Message receiving.
{
DBaseFunction(userparams(),GetClassName())
}
}
##################
//This is just a script for testing purposes, please ignore
class DLowerTrap extends DBaseTrap
{
DefOn = "TurnOn" //Default On message that this script is waiting for but differing from the standard TurnOn
constructor()
{
}
function OnMessage() //General catching for testing.
{
local DN=userparams()
base.OnMessage()
local script="DLowerTrap"
//DarkUI.TextMessage("Capacitor:= "+GetData(script+"Capacitor")+"/"+DGetParam(script+"Capacitor",1,DN)+" OnCap= "+GetData(script+"OnCapacitor")+"/"+DGetParam(script+"OnCapacitor",1,DN)+" OffCap= "+GetData(script+"OffCapacitor")+"/"+DGetParam(script+"OffCapacitor",1,DN)+" Counter= "+GetData(script+"Counter")+"/"+DGetParam(script+"Count",0,DN))
local from = Camera.GetFacing()
from = vector(sin(from.y)*cos(from.z),sin(from.y)*sin(from.z),cos(from.y))
DarkUI.TextMessage(from)
}
function OnTimer()
{
}
function DoOn(DN)
{
SetOneShotTimer("fd",1)
Link.BroadcastOnAllLinks(self,"TurnOn","ControlDevice")
}
function DoOff(DN)
{
Link.BroadcastOnAllLinks(self,"TurnOff","ControlDevice")
}
}
############################################
#### Real Scripts ####
############################################
##############################################
class DRelayTrap extends DBaseTrap
##############################################
/*
A relay with all the DBaseTrap features. Reacts to the messages specified by DRelayTrapOn/Off and it will relay the message specified with DRelayTrapTOn. Respectively the on TurnOff to be sent messages can be specified with DRelayTrapTOff. By default these are "TurnOn" and "TurnOff".
NEW: With the + operator you can define multiple On, TOn, Off and TOff messages!
With DRelayTrap[On/Off]Target (NEW: Also [On/Off]TDest works as an alternative) you can specify where to sent the message(s) to. Default are ControlDevice linked objects. If a DRelayTrapOnTarget is specified then it will take priority over DRelayTrapTarget.
As a TOn, TOff message you can also send a Stim to do this, first enter the intensity surrounded by square brackets, followed by the stim name. For example: [ScriptName]TOn="[5.00]WaterStim".
NEW v.30: DRelayTrapToQVar="QVarName"; will store the ObjectsID into the specified QVar. It then can for example be targeted via $QVarName. Usefull to always sent a command to a specific but variable object.
Design Note example:
NVRelayTrapOn="+TurnOn+BashStimStimulus";NVRelayTrapTOn="+TurnOn+[5]FireStim";NVRelayTrapOnTarget="+player+^ZombieTypes"
What will happen:
On TurnOn or when bashed it will send a TurnOn and a FireStim with intensity 5 to the player and the closest Zombie.(relative to the object with the script)
As nothing else is specified on TurnOff will send a TurnOff to all ControlDevice linked objects.
________________
SQUIRREL NOTE: Can be used as RootScript to use the DSendMessage; DRelayMessages functions. As an example see DStdButton.
#################################################### */
{
function DSendMessage(t,msg) //Send a message or a Stim to the target.
{
if (msg[0]!='[') //Test if normal or "[Intensity]Stimulus" format.
{SendMessage(t,msg)}
else
{
local ar=split(msg,"[]")
ar.remove(0)
if (!GetDarkGame()) //T1/G compability; different versions one which allows to set the source.
{ActReact.Stimulate(t,ar[1],ar[0].tofloat(),self)}
else
{ActReact.Stimulate(t,ar[1],ar[0].tofloat())}
}
}
function DRelayMessages(OnOff,DN) //Sents each message to each target
{
local script = GetClassName()
foreach (msg in DGetParam(script+"T"+OnOff,"Turn"+OnOff,DN,1)) //Determins the messages to be sent, TurnOn/Off is default.
{
foreach (t in DGetParam(script+OnOff+"Target",DGetParam(script+OnOff+"TDest",DGetParam(script+"Target",DGetParam(script+"TDest","&ControlDevice",DN,1),DN,1),DN,1),DN,1)) //Priority Order: [ScriptName] Tartget > TDest > Default: &ControlDevice
{
DSendMessage(t,msg)
}
}
}
function DoOn(DN)
{
if (DGetParam("DRelayTrapToQVar",false,DN)) //If specified will store the ObejctsID into the specified QVar
{
Quest.Set(DGetParam("DRelayTrapToQVar",null,DN),SourceObj,eQuestDataType.kQuestDataUnknown)
}
DRelayMessages("On",DN)
}
function DoOff(DN)
{
if (DGetParam("DRelayTrapToQVar",false,DN))
{
Quest.Set(DGetParam("DRelayTrapToQVar",null,DN),SourceObj,eQuestDataType.kQuestDataUnknown)
}
DRelayMessages("Off",DN)
}
}
#########################################################
class DHub extends SqRootScript //NOT A BASE SCRIPT
#########################################################
{
/*
A powerful multi message script. Each incoming message can be completely handled differently. See it as multiple DRelayTraps in one object.
Valuable Parameters.
Relay=Message you want to send
Target= where
Delay=
DelayMax //Enables a random delay between Delay and DelayMax
ExclusiveDelay=1 //Abort future messages
Repeat= //-1 until the message is received again.
Count= //How often the script will work. Receiving ResetCounter will reset this
Capacitor= //Will only relay when the messages is received that number of times
CapacitorFalloff= //Every __ms reduces the stored capacitor by 1
FailChance //Chance to fail a relay. if negative it will affect Count even if the message is not sent
Every Parameter can be set as default for every message with DHubParameterName or individualy for every message (have obv. priority)
Design Note example:
DHubYourMessage="TOn=RelayMessage;TDest=DestinationObject;Delay
DHubTurnOn="Relay=TurnOff;To=player;Delay=5000;Repeat=3"
*/
/* THIS IS NOT IMPLEMENTED fully:
If DHubCount is a negative number a trap wide counter will be used. Non negative Message_Count parameters will behave normally.
NOTE: Using Message_Count with a negative values is not advised. It will not cause an error but could set a wrong starting count if that Message is the first valid one.
Examples:
DHubTurnOn="Count=1" will only once relay a message after receiving TurnOn
DHubCountNormal
--
DHubCount=-3;
DHubTurnOff="==";
DHubTurnOn="==";
DHubTweqStarting="Count=0"
Relaying TurnOn or TurnOff BOTH will increase the counter until 3 messages in total have been relayed.
TweqStarting messages will not increase the counter and will still be relayed when 3 other messages have been relayed.
Possible Future addition:
non zero Counts will increase the hub Count; and could additionally be blocked then, too.
if (CountMax < 0){CountData= "DHubCounter"}else{CountData="DHub"+msg+"Counter"}
//first time setting script var or else grabbing Data
if (IsDataSet(CountData)){CurCount=GetData(CountData)}
else {SetData(CountData,Count)}
*/
//Storing the default values in an array which form an artifical DesignNote.
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
DefDN=["Relay","TurnOn","Target","&ControlDevice","Count",0,"Capacitor",1,"CapacitorFalloff",0,"FailChance",0,"Delay",0,"DelayMax",0,"Repeat",0,"ExclusiveDelay",0]
SourceObj=null
DefOn=null
i=null
constructor() //Initializing Skript Data
{
local ie=!IsEditor()
local DN=userparams()
local def = 0
//Not implemented yet
/*if (DGetParam("DHubCount",0,DN)<0){SetData("DHubCounter",0)}else{ClearData("DHubCounter")}
if (DGetParam("DHubCapacitor",1,DN) < 0){SetData("DHubCapacitor",0)}else{ClearData("DHubCapacitor")}*/
foreach (k,v in DN) //Checks each Key,Value pais in the DesignNote and if they are a DHub Statement.
{
if (startswith(k,"DHub"))
{//DefDN[ 1 2 3 4 5 6 7 8 9 10]
def = [null,"DHubRelay","DHubTarget","DHubCount","DHubCapacitor","DHubCapacitorFalloff","DHubFailChance","DHubDelay","DHubDelayMax","DHubRepeat","DHubExclusiveDelay"].find(k)
if (!def) //Value found
{
if (ie){continue} //Initial data is set in the Editor. And data changes during game. Continue to recreate the DefDN.
if (DGetStringParam("Count",DGetParam("DHubCount",0,DN),v))
{
SetData(k+"Counter",0)
}
else {ClearData(k+"Counter")}
if (DGetStringParam("Capacitor",DGetParam("DHubCapacitor",1,DN),v) != 1)
{
SetData(k+"Capacitor",0)
}
else {ClearData(k+"Capacitor")}
}
else //Make a default array.
{
DefDN[def*2-1]=v //Writes the specified value into the corresponding slot in our artifical DefDesignNote.
}
}
}
}
##############
function OnMessage() //Similar to the base functions in the first part.
{
local lhub = userparams();
local bmsg = message()
local msg = bmsg.message
local command = ""
local l=endswith(msg,"ResetCount")
local msg2=msg
//Check special Messages and set [source]
//Reset single counts and repeats.
if (l || endswith(msg,"StopRepeat"))
{
msg2 = msg.slice(0,-10)
if (msg2 != "")
{
msg2= "DHub"+msg2
if (l)
{SetData(msg2+"Counter",0)}
else
{
if (IsDataSet(msg2+"DelayTimer"))
{
KillTimer(GetData(msg2+"DelayTimer"))
ClearData(msg2+"DelayTimer")
if (IsDataSet(msg2+"InfRepeat")){ClearData(msg2+"InfRepeat")}
}
}
}
else
{
if (l)
foreach (k,v in lhub)
{
if (startswith(k,"DHub")&& !([null,"DHubRelay","DHubCount","DHubOn","DHubTarget","DHubCapacitor","DHubCapacitorFalloff","DHubFailChance","DHubDelay","DHubDelayMax","DHubRepeat","DHubExclusiveDelay"].find(k)))
{
if (IsDataSet(k+"Counter")){SetData(k+"Counter",0)}
}
}
else
{
foreach (k,v in lhub)
{
if (startswith(k,"DHub")&& !([null,"DHubRelay","DHubCount","DHubOn","DHubTarget","DHubCapacitor","DHubCapacitorFalloff","DHubFailChance","DHubDelay","DHubDelayMax","DHubRepeat","DHubExclusiveDelay"].find(k)))
{
if (IsDataSet(k+"InfRepeat"))
{
KillTimer(GetData(k+"DelayTimer"))
ClearData(k+"DelayTimer")
ClearData(k+"InfRepeat")
}
}
}
}
}
}
if (typeof bmsg == "sFrobMsg")
{SourceObj=bmsg.Frobber}
else{SourceObj = bmsg.from}
//End special message check.
DefOn="null" //Reset so a Timer won't activate it
if (msg=="Timer")
{
local msgn = bmsg.name
if (endswith(msgn,"Falloff")||endswith(msgn,"Delayed"))
{
msgn= msgn.slice(0,-7) //Both words have 7 characters. TODO: Is the next line correct, DelayedDelayed??
if (endswith(msgn,"Delayed"))
{SourceObj=split(bmsg.data,"+")[1].tointeger()}
command = DGetParam(msgn,false,lhub) //Check if the found command is specified.
if (command)
{
local SubDN ={}
local CArray = split(command,";=")
l = CArray.len()
for (local v=0;v<20;v+=2) //Setting default parameter.
{
SubDN[msgn+DefDN[v]]<-DefDN[v+1]
}
for (local v=0;v<l;v=v+2)
{
SubDN[msgn+CArray[v]]=CArray[v+1]
}
DBaseFunction(SubDN,msgn)
}
}
}
command = DGetParam("DHub"+msg,null,lhub)
if (command!=null)
{
i=1
local SubDN ={}
local CArray=split(command,";=")
local FailChance=0
msg2=msg
DefOn=msg2
//Creating a "Design Note" for every action and passing it on.
while (command)
{
if (i!=1){msg2=msg+i; CArray = split(command,";=")}
l = CArray.len()
SubDN.clear()
for (local v=0;v<20;v+=2) //Setting default parameter. There are 10 Key,Value pairs = 20 arrays slots.
{
SubDN["DHub"+msg2+DefDN[v]]<-DefDN[v+1]
}
if (command!="==")
{
for (local v=0;v<l;v+=2) //Setting custom parameter. SubDN is now a 20 entry table
{
SubDN["DHub"+msg2+CArray[v]]=CArray[v+1]
}
}
//Fail Chance.
FailChance=DGetParam("DHub"+msg2+"FailChance",DefDN[11],SubDN).tointeger() //sucks a bit to have this in the loop.
if (FailChance == 0){DCountCapCheck("DHub"+msg2,SubDN,1)}
else {if (!(FailChance >= Data.RandInt(0,100))){DCountCapCheck("DHub"+msg2,SubDN,1)}}
i++
command = DGetParam("DHub"+msg+i,false,lhub) //Next command. If not found the loop ends.
}
}
}
//Here the Message is sent.
function DoOn(DN)
{
local baseDN=userparams()
local m=message()
local mssg=m.message
local idx=""
if (i!=1){idx=i}
if (mssg=="Timer")
{
if (endswith(m.name,"Delayed"))
{
mssg= m.name.slice(4,-7)
idx=""
}
}
foreach (msg in DGetParam("DHub"+mssg+idx+"Relay",0,DN,1))
{
foreach (t in DGetParam("DHub"+mssg+idx+"Target",0,DN,1))
{
if (msg[0]!='[') //As in DRelayTrap it checks for a Stimulus
{SendMessage(t,msg)}
else
{
local ar=split(msg,"[]")
//ar.remove(0)
if (!GetDarkGame()) //T1/G compability = 0
{ActReact.Stimulate(t,ar[2],ar[1].tofloat(),self)}
else
{ActReact.Stimulate(t,ar[2],ar[1].tofloat())}
}
}
}
}
}
################################
## END of HUB
################################
#########################################
class DStdButton extends DRelayTrap
#########################################
/*Has all the StdButton features - even TrapControlFlags work. Once will lock the Object - as well as the DRelayTrap features, so basically this can save some script markers which only wait for a Button TurnOn
Additional:
If the button is LOCKED the joint will not activate and the Schema specified by DStdButtonLockSound will be played, by default "noluck" the wrong lockpick sound.
NOTE: As this is a DRelayTrap script as well it can be actiavted via TurnOn; but the Default message is "DIOn" (I=Internal); sending this message will bypass the Lock check and TrapControlFlags.
######################################### */
{
###StdController
DefOn="DIOn" //A sucessfull Button Push will sent a DIOn to itself and then trigger underlaying DBase&DRelayTrap features
DefOff="DIOff"
function OnBeginScript()
{
if(Property.Possessed(self,"CfgTweqJoints")) // Standard procedure to have other property as well.
Property.Add(self,"JointPos");
Physics.SubscribeMsg(self,ePhysScriptMsgType.kCollisionMsg); //Remember that Buttons can be activated by walking against them. Actiavting the OnPhysCollision() below. TODO: Make this optional.
}
function OnEndScript()
{
Physics.UnsubscribeMsg(self,ePhysScriptMsgType.kCollisionMsg);//I'm not sure why they always clean them up, but I keep it that way.
}
function ButtonPush(DN)
{
//Play Sound when locked and standard Event Activate sound. TODO: Check for sounds but should be fine.
if (Property.Get(self,"Locked"))
{
Sound.PlaySchemaAtObject(self,DGetParam("DStdButtonLockSound","noluck",DN),self)
return
}
Sound.PlayEnvSchema(self,"Event Activate",self,null,eEnvSoundLoc.kEnvSoundAtObjLoc)
ActReact.React("tweq_control",1.0,self,0,eTweqType.kTweqTypeJoints,eTweqDo.kTweqDoActivate)
DarkGame.FoundObject(self); //Marks Secret found if there is one associated with the button press. TODO: T1 comability?
local trapflags=0
local on = true
if(Property.Possessed(self,"TrapFlags"))
trapflags=Property.Get(self,"TrapFlags");
//NOTE: TrapControlFlags are set as bits.
if((on && !(trapflags & TRAPF_NOON)) ||
(!on && !(trapflags & TRAPF_NOOFF)))
{
if(trapflags & TRAPF_INVERT)
on=!on;
//Link.BroadcastOnAllLinks(self,on?"TurnOn":"TurnOff","ControlDevice");
SendMessage(self,on?"DIOn":"DIOff")
}
if(trapflags & TRAPF_ONCE)
Property.SetSimple(self,"Locked",true);
}
function OnPhysCollision()
{
if(message().collSubmod==4) //Collision with the button part.
{
if(! (Object.InheritsFrom(message().collObj,"Avatar")
|| Object.InheritsFrom(message().collObj,"Creature"))) //TODO: This is the standard function but I wanna look at it again.
{
ButtonPush(userparams());
}
}
}
function OnFrobWorldEnd()
{
ButtonPush(userparams());
}
}
####################################################################
class DArmAttachment extends DBaseTrap
####################################################################
/*
Attaches another object to the players arm.
So when using an empty hand model you can create your own custom weapons. It can be given to any weapon-ready object together with Inventory-> Limb Object: emptyhan or BJACHAND (Both Models included, made by Jason Otto and Soul Tear).
OR you can also add it to normal weapons to attach a Shield or Light or whatever creative stuff you come up with
Parameters:
DArmAttachmentUseObject:
= 0 (Default): Will create a dummy object and give it the Shape->Model: DArmAttachmentModel
= 1 Will create a copy of a real object/archetype specified in DArmAttachmentModel. If you want to attach special effects for example.
= 2 (experimental and not really working): Same as 1 but the object will be really physical -> Real collision sounds based on the object.
= 3 (experimental little working): Same as 2 but works even better. Disadvantage: Errors in DromED, Model will pass through walls/objects.
DArmAttachmentModel: model name(0) or object Archtype (1) depending on (DArmAttachmentUseObject)
DArmAttachmentRot and DArmAttachmentPos: The model will most likely need some adjustments in position and Rotation. Both parameters take 3 arguments separated by , like this: "90,0,0" or "0.1,-0.6,-0.43". They stand for HPB Roation respectively xyz translation. But most like don't behave like you expect it. Best method how to figure out the numbers is to use it in combination with set game_mode_backup 0 (NEVER SAVE AFTER!) and modify the DetailAttachment Link. It's trial and error.
NOTE: You will also need to do some Hierarchy changes to adjust the sound and motions. Also I would say this script is not 100% finished please give feedback.
TIP: Remember you can use multiple different melee weapons by using the cycle command. Or design them so that you can also use them from the normal inventory.
####################################################################*/
{
DefOn="InvSelect"
function DoOn(DN)
{
SetOneShotTimer("Equip",0.5)
}
function OnTimer()
{
if (message().name == "Equip")
{
local DN=userparams()
local o = null
local t = DGetParam("DArmAttachmentUseObject",false,DN)
local m = DGetParam("DArmAttachmentModel",self,DN)
// print("m1= "+m)
//TODO: Switch better maybe?
if (m ==self && !t)
{m = Property.Get(self,"ModelName")}
t = t.tointeger()
print("m2= "+m)
if (t)
{
o = Object.Create(m)
Property.SetSimple(o,"RenderType",0)
}
else
{
o = Object.Create(-1)