-
Notifications
You must be signed in to change notification settings - Fork 6
/
acr_mod_events_i.NSS
2212 lines (1831 loc) · 81.1 KB
/
acr_mod_events_i.NSS
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
////////////////////////////////////////////////////////////////////////////////
//
// System Name : ALFA Core Rules
// Filename : acr_mod_events_i.nss
// $Revision:: 616 $ current version of the file
// $Date:: 2009-12-29#$ date the file was created or modified
// Author : Ronan & Cipher & AcadiusLost
//
// Description
// This file contains functions which process all module event scripts for
// ALFA modules.
//
// Of note is the presence of only a single loop in each event. Subsystems
// residing in an event should not contain their own inventory or effect loops,
// instead the subsystems should share a single loop. This is done for reasons
// of efficiency, NWScript is not quick when doing things like looping through
// items in an inventory, or objects in an area.
//
// Revision History
// 2006/08/18 Cipher Added pc initialization, rest processing, & minor reformatting
// 2006/09/09 Cipher Changed ALFA prefix to ACR
// 2006/09/15 Ronan Added game constant cache system, and updated to ACR_ convention
// 2006/09/15 Cipher Added support for scrying spells
// 2006/09/15 Cipher Added death system functions
// 2006/12/22 Cipher Added OnModuleStart and OnPCLoaded event handlers
// 2007/01/20 Cipher Moved ExecuteScript() item script to ACR_ModuleOnActivateItem()
// 2007/03/31 AcadiusLost commented out calls to Death system scripts, for now.
// 2007/04/15 AcadiusLost edited OnActivate to explicitly pass oItem in ExecuteScript
// 2007/04/26 AcadiusLost Added Module Switches to OnModuleLoad handler
// 2007/05/03 AcadiusLost Added death effect to ACR_ModuleOnPlayerDying()- temporarily
// 2007/05/03 Cipher Added call to ACR_PlayerOnDying() and initialized spell hooking
// 2007/05/25 Cipher Added NWNX initialization and 1984 logging hooks
// 2007/07/01 Cipher Added OnPCLoaded() event calls. This solves PC instantiation workarounds
// 2007/07/13 Cipher Added SQL table creation function call
// 2007/07/14 AcadiusLost Moved DM item granting to OnPCLoaded, added starting equipment handling.
// Also changed ACR_ModuleOnPlayerDeath() to use GetLastHostileActor(), and
// ACR_ModuleOnUnacquireItem() to check GetIsObjectValid(oItem), and ACR_ModuleOnAcquireItem
// to use GetItemPossessor(GetModuleItemAquired()) to avoid strange cases.
// 2007/07/18 AcadiusLost Moved ACR_RestOnClientEnter to OnPCLoaded
// 2007/07/21 Cipher Revised ACR_SQLGetData() calls, skip item acquisition logging on logins
// 2007/09/02 Cipher Added rebuild journal quest entries on login
// 2007/09/15 AcadiusLost Pushed back RestOnClientEnter with a DelayCommand() to give PC status
// adjust a chance to apply damage before calculating offcamera rest healing.
// 2007/09/15 Cipher Moved journal rebuild to the OnPCLoaded event
// 2007/09/20 Cipher Added quest hook for item bounties
// 2007/09/21 Cipher Moved inventory item checks on login to the ItemOnAcquired function to avoid redundancy
// 2007/10/28 AcadiusLost Merged with DMFI 1.05 for OnPCLoad event. Added "#include "dmfi_inc_initial"
// 2007/10/31 AcadiusLost Changed DMFI call to an ExecuteScript() so it isn't needed to compile the ACR.
// 2007/11/02 AcadiusLost Shifted DMFI call outside the DM/Player conditional so it is called for both.
// 2007/11/15 AcadiusLost removed module switch for UMD scrolls (adding ALFA scrolls)
// 2007/11/25 AcadiusLost added OnEquip handling for Martial Bastardswords
// 2007/11/30 AcadiusLost added OnModuleLoad call to initialize chat plugin cache
// 2007/12/16 AcadiusLost added calls to acr_items_i to handle item scripts, moved bastardsword code.
// 2007/12/17 AcadiusLost - pushed death and resting includes to acr_items_i, shifted item activation code as well.
// 2008/03/19 AcadiusLost - shifted death logging into acr_death_i, due to addition of "death floor"
// 2008/04/16 Cipher Fixed logout logging.
// 2008/04/16 Cipher Fixed acquire logging.
// 2008/08/27 AcadiusLost - added hooks for nonlethal damage system
// 2008/09/19 AcadiusLost - prepare for Custom skills, regions, logging fix for NLD
// 2008/09/21 AcadiusLost - Nonlethal OnExit event added.
// 2008/12/08 AcadiusLost - Added handling for server names with apostrophes eg. "Baldur's Gate"
// Item acquire/unacquire optimizations (commented out SQL illegal item checks), deprecated storageobjects
// also amended spawn debug wand tag for GetItemPossessedBy() for DMs.
// 2008/12/10 AcadiusLost - optimized class checking OnPCLoaded()
// 2009/01/19 AcadiusLost - switched Servers table update to take GetName(oModule) to allow synchronicity with pwdata entries.
// 2009/02/07 AcadiusLost - Changed initialization of the AutoSave() cycle for PCs.
// 2009/03/10 AcadiusLost - Added initialization for the DM Client Extension suite in OnClientEnter.
// 2009/07/07 AcadiusLost - updated to add custom skill framework with 1.23
// 2009/07/18 AcadiusLost - Added language and region hooks into PC Load and Levelup events.
// 2009/08/03 AcadiusLost - Added IP-logging onLogin
// 2009/08/08 AcadiusLost - Added handling for OnChat event (1.23)
// 2009/12/29 AcadiusLost - Removed autoassignment of subdual widget OnPCLoaded (no longer needed)
// 2011/05/29 Ronan - Added OnModuleLoad parameters for global module settings.
// 2011/06/04 Zelknolf - Added hook for display of NPC creator GUI
// 2011/06/24 AcadiusLost - merged in fix for clearing IsOnline bits in Characters table OnModuleLoad()
// 2011/06/25 Ronan - Added in the service heartbeats for asynchronous SQL writes.
// 2011/07/02 Basilica - Added persistent database callouts for cache management.
// 2011/12/26 Basilica - Added server admin command directives.
// 2012/01/02 Basilica - Added latency monitor support.
// 2012/01/05 Basilica - Added server IPC callouts.
// 2012/01/09 Basilica - CE integration.
// 2012/01/14 Basilica - Changed to use ACR_RPXPMarkActive for marking RPXP.
// 2012/01/19 Basilica - Added callout for area instancing system.
// 2012/03/03 Basilica - Added callout for SCliExt OnLeave.
// 2012/04/15 Basilica - Added infrastructure for deleting unneeded static objects at module startup.
// 2012/04/16 Basilica - Log module and HAK build date to server log at module startup.
// 2012/04/28 Basilica - Prevent creation of new characters or players with trailing spaces.
// 2014/01/16 Basilica - Added module event hook for override patchable scripts.
// 2014/08/01 Basilica - Added initialization callout to ACR_DatabaseConnector.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef ACR_MOD_EVENTS_I
#define ACR_MOD_EVENTS_I
////////////////////////////////////////////////////////////////////////////////
// Constants ///////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
const string _ONLOAD_DEBUG = "ACR_ModuleOnModuleLoad";
const string ACR_MOD_STATUS = "ACR_MOD_STATUS";
//! Local variable definitions
const string ACR_MOD_SPELLBOOK = "ACR_MOD_SPELLBOOK";
const string ACR_MOD_HOLYSYMBOL = "ACR_MOD_HOLYSYMBOL";
const string ACR_MOD_STARTINGGOLD = "ACR_MOD_STARTINGGOLD";
const string ACR_MOD_STARTARMORX = "ACR_MOD_STARTARMORX";
const string ACR_MOD_FIRSTAID_IT = "ACR_MOD_FIRSTAID_IT";
const string ACR_MOD_LAST_TELL_TO = "ACR_MOD_LAST_TELL_TO";
const string ACR_MOD_LAST_TELL_FROM = "ACR_MOD_LAST_TELL_FROM";
const string ACR_MOD_SUPPRESS_LANGUAGE = "ACR_MOD_SUPPRESS_LANGUAGE";
//! ACR_MOD_STATUS Module Status Flags (bitmask)
const int ACR_MOD_NWNX_FAILED = 0x01;
//! The time period for OnModuleLoad() rescheduling, necessary for NWNX failures.
const float ACR_MOD_RELOAD_CYCLE = 10.0;
const float ACR_TALK_SPHERE_RADIUS = 20.0f;
const float ACR_WHISPER_SPHERE_RADIUS = 3.0f;
const int ACR_ASSEMBLY_LOADER_INITIALIZE = 0;
const int ACR_CANDLEKEEP_INITIALIZE_ARCHIVES = 0;
const int ACR_CHOOSERCREATOR_INITIALIZE = 0;
const int ACR_DATABASE_CONNECTOR_INITIALIZE = 0;
//! Define to 1 to enable language handling debugging.
#define DEBUG_LANG 0
////////////////////////////////////////////////////////////////////////////////
// Structures //////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Global Variables ////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Function Prototypes /////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Runs the ALFA OnModuleLoad event.
void ACR_ModuleOnModuleLoad();
void ACR_ModuleOnModuleStart();
void ACR_ModuleOnClientEnter();
void ACR_ModuleOnClientLeave();
void ACR_ModuleOnAcquireItem();
void ACR_ModuleOnUnacquireItem();
void ACR_ModuleOnActivateItem();
void ACR_ModuleOnPCLoaded();
void ACR_ModuleOnPlayerDeath();
void ACR_ModuleOnPlayerDying();
void ACR_ModuleOnPlayerLevelUp();
void ACR_ModuleOnPlayerRespawn();
void ACR_ModuleOnPlayerRest();
void ACR_ModuleOnHeartbeat();
void ACR_ModuleOnUserDefined();
void ACR_ModuleOnPlayerEquip();
void ACR_ModuleOnPlayerUnequip();
void ACR_ModuleOnCutsceneAbort();
// special handler for the OnChat event (new in 1.23)
int ACR_ModuleOnChat(object oSpeaker, object oTarget, int nMode, string sText);
//! Returns the state of the desired module status bit
//! - nStatus: One of the status flags defined for ACR_MOD_STATUS
//! - returns: TRUE is set, FALSE if not set
int ACR_GetModuleStatus(int nStatus);
//! Sets the state of the desired module status bit
//! - nStatus: One of the status flags defined for ACR_MOD_STATUS
//! - returns: Nothing
void ACR_SetModuleStatus(int nStatus);
//! Called when a new PC is created.
//! - oPC : Player to initialize.
void ACR_HandlePCCreation( object oPC );
// Private
//! Start up latency and health monitoring for the server
void ACR_ModuleInitHealthChecks();
//! Perform startup time area cleanup.
void ACR_PerformModuleStartupCleanup();
//! Sweep unneeded objects from the area, such as walkmesh helpers.
//! - AreaObject: the area to remove unneeded objects from.
void ACR_SweepAreaOfUnneededObjects(object AreaObject);
//! Wrapper for ACR_ModuleInitHealthChecks().
void ACR_StartModuleInitHealthChecks();
//! Parse a new language
string _ParseLang(object pc, string text);
//! Parse a new language
int _HandleLang(object pc, string text, int mode, string lang, object DMFIToolOwner);
//! Check if a mouthpiece should have messages relayed from a speaker.
//! - oMouthPiece: Supplies the mouthpiece, i.e. controlling object (e.g. DM).
//! - oSpeaker: Supplies the actual physical speaker (e.g. controlled object).
//! - fDistance: Supplies the distance for the range check.
//! - Returns: TRUE if the objects were out of range to receive the message
// through conventional means, i.e. we should relay it.
int _MouthPieceNotInRange(object oMouthPiece, object oSpeaker, float fDistance);
//! Wrapper for SpeakString that suppresses voice throw reception.
//! - sToSpeak: The string to send.
//! - nTalkVolume: The volume to use.
void _SpeakString(string sToSpeak, int nTalkVolume);
//! Distribute translated chat message to DM avatars as they do not reliably
// appear to be enumerated within an object shape region.
//! - sTranslatedMessage: The message to distribute.
//! - oSpeaker: The speaking object.
//! - fRange: The maximum range to deliver the message at.
void _DistributeTranslationToDMAvatars(string sTranslatedMessage, object oSpeaker, float fRange);
//! Distribute translated chat message to a mouthpiece object (i.e. controlling
// DMs or players).
//! - sTranslatedMessage: The message to distribute to objects that can
// translate the language.
//! - sScrambledMessage: The message to distribute to objects that cannot
// translate the language.
//! - oSpeaker: The speaking object.
//! - fRange: The maximum range to deliver the message at.
//! - oListener: The object that might is within hearing range of the message.
void _DistributeTranslationToMouthPiece(string sTranslatedMessage, string sScrambledMessage, object oSpeaker, float fRange, object oListener);
//! Distribute translated chat message to appropriate recipients, for party.
//! - sTranslatedMessage: The message to distribute to objects that can
// translate the language.
//! - sScrambledMessage: The message to distribute to objects that cannot
// translate the language.
//! - sLanguage: The language (e.g. Elven).
//! - oSpeaker: The speaking object.
void _DistributeTranslationParty(string sTranslatedMessage, string sScrambledMessage, string sLanguage, object oSpeaker);
//! Distribute translated chat message to appropriate recipients, for talk or
// whisper.
//! - sTranslatedMessage: The message to distribute to objects that can
// translate the language.
//! - sScrambledMessage: The message to distribute to objects that cannot
// translate the language.
//! - sLanguage: The language (e.g. Elven).
//! - oSpeaker: The speaking object.
//! - fRange: The range to distribute the message at.
void _DistributeTranslationSpoken(string sTranslatedMessage, string sScrambledMessage, string sLanguage, object oSpeaker, float fRange);
//! Distribute translated chat message to appropriate recipients.
//! - sOriginalMessage: The message to distribute to objects that can
// translate the language.
//! - sScrambledMessage: The message to distribute to objects that cannot
// translate the language.
//! - sLanguage: The language (e.g. Elven).
//! - oSpeaker: The speaking object.
//! - nMode: The chat mode (CHAT_MODE_WHISPER, CHAT_MODE_TALK,
// CHAT_MODE_PARTY)
void _DistributeTranslation(string sOriginalMessage, string sScrambledMessage, string sLanguage, object oSpeaker, int nMode);
//! Force linkage to functions that CLR scripts will want to call in the
// context of acf_mod_onmoduleload.nss. Note that this function is never
// really run but must make a reference to every symbol that should be force
// included so that it can be resolved at runtime.
void ACR_ModuleInit_ForceLinkageForCLRScripts();
//! Show player UIs.
void _createPlayerGUIs( object oPC );
//! Update player UIs for mod events.
void _updatePlayerGUIs( object oPC );
//! Load CLR script assemblies.
void _LoadAssemblies();
// This is needed to establish the server and gameobject structures that
// ACR_CreatureBehavior uses.
void ACR_InitializeCreatureBehavior();
//! Hook for override script files deployed by the content patcher to
//! Returns TRUE to continue processing, else FALSE to stop. The return value
// is only used if Precall is TRUE.
//! - Event: Supplies the name of the event (ACR_MODULE_ON_* from
// acr_mod_event_hook_i.nss).
//! - Precall: Supplies TRUE if the call happens before normal processing, else
// FALSE if it happens after normal processing.
int ACR_ModuleOverrideEvent(string Event, int Precall);
////////////////////////////////////////////////////////////////////////////////
// Includes ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#include "acr_settings_i"
#include "acr_debug_i"
#include "acr_time_i"
#include "acr_xp_i"
//#include "acr_storageobj_i"
#include "acr_spawn_i"
#include "acr_db_persist_i"
//#include "acr_game_const_i"
#include "acr_scry_i"
#include "acr_tools_i"
#include "acr_1984_i"
#include "acr_quest_i"
#include "acr_chat_i"
#include "acr_xp_i"
#include "acr_items_i"
#include "acr_skills_i"
#include "acr_nonlethal_i"
#include "acr_language_i"
#include "acr_feat_events_i"
#include "acr_srvadmin_i"
#include "acr_server_ipc_i"
#include "acr_version_i"
#include "acr_scliext_i"
#include "acr_area_instance_i"
#include "acr_wealth_i"
#include "acr_gui_i"
#include "acr_language_i"
#include "acr_pchide_i"
#include "acr_movement_i"
#include "acf_spawn_i"
#include "acf_settings_i"
#include "x2_inc_switches"
#include "dmfi_inc_initial"
#include "dmfi_inc_lang"
#include "acr_roll_i"
#include "nwnx_craft_system"
#include "acr_craft_creation_i"
#include "acr_notifications_i"
#include "nwnx_objectattributes_include"
#include "acr_mod_event_hook_i"
////////////////////////////////////////////////////////////////////////////////
// Function Definitions ////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void _SpeakString(string sToSpeak, int nTalkVolume)
{
// Note - there is a bug in the NWScript plugin that under these conditions:
//
// "If a script is executed from a script situation (DelayCommand,
// AssignCommand, and so forth, other 'action' types in NWScript), and the
// script makes a recursive call to itself, then the recursion is not
// properly detected and a new, stacked program object for the script is
// not created. This results in the recursive instance using the global
// variables and OBJECT_SELF of the previous instance (modifications are
// visible between the two, in particular, if the recursive call sets a new
// OBJECT_SELF then OBJECT_SELF appears to have changed in the calling
// instance).
//
// The problem occurs because the NWScript JIT backend does not correctly
// increment the internal nesting level from a script when the script is
// called because of a script situation. If the script had already been
// recursively invoked, the nesting level would have necessitated a new
// script object being temporarily created, as entry into a script from
// the entrypoint always increments the nesting level across the call.
//
// This will be fixed in a future release."
//
// Work around the bug for now by capturing OBJECT_SELF up front and not
// using globals in this AssignCommand action, as the SpeakString engine
// function will cause a recursive call to the OnChat script, which we're
// already running from.
object Self = OBJECT_SELF;
#if DEBUG_LANG
WriteTimestampedLogEntry("_SpeakString(" + GetName(Self) + ": Speaking " + sToSpeak + ", volume: " + IntToString(nTalkVolume) + ".");
#endif
SetLocalInt(Self, ACR_MOD_SUPPRESS_LANGUAGE, 1);
SpeakString(sToSpeak, nTalkVolume);
DeleteLocalInt(Self, ACR_MOD_SUPPRESS_LANGUAGE);
// If the NWScript plugin bug is present, this shows the name of the module
// and not the name of the object that spoke above.
#if DEBUG_LANG
WriteTimestampedLogEntry("_SpeakString finished for " + GetName(OBJECT_SELF) + ".");
#endif
}
void _DistributeTranslationToDMAvatars(string sTranslatedMessage, object oSpeaker, float fRange)
{
object oPC;
object oOriginArea = GetArea(oSpeaker);
for (oPC = GetFirstPC(FALSE); GetIsObjectValid(oPC); oPC = GetNextPC(FALSE))
{
if (!GetIsDM(oPC) && !GetIsDMPossessed(oPC))
{
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationToDMAvatars: " + GetName(oPC) + " is not a DM");
#endif
continue;
}
if (GetArea(oPC) != oOriginArea)
{
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationToDMAvatars: " + GetName(oPC) + " is in the wrong area");
#endif
continue;
}
if (GetDistanceBetween(oSpeaker, oPC) > fRange)
{
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationToDMAvatars: " + GetName(oPC) + " is out of range");
#endif
continue;
}
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationToDMAvatars: Relay message to " + GetName(oPC));
#endif
SendMessageToPC(oPC, sTranslatedMessage);
}
}
void _DistributeTranslationToMouthPieces(string sTranslatedMessage, string sScrambledMessage, object oSpeaker, float fRange, object oListener)
{
object oMouthPiece = GetLocalObject(oListener, "MouthPiece");
string sHeardDescriptor;
if (oMouthPiece == OBJECT_INVALID)
{
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationToMouthPieces: No mouthpiece for " + GetName(oListener));
#endif
return;
}
// If the mouthpiece object would normally receive the message then do not
// synthesize a new report.
if (!_MouthPieceNotInRange(oMouthPiece, oSpeaker, fRange))
{
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationToMouthPieces: Listener " + GetName(oListener) + " mouthpiece " + GetName(oMouthPiece) + " is in normal hearing range not relaying");
#endif
return;
}
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationToMouthPieces: Distributing message to " + (GetIsDM(oMouthPiece) ? "DM" : "player") + " mouthpiece " + GetName(oMouthPiece) + ".");
#endif
sHeardDescriptor = " <i><c=gray>(" + GetName(oListener) + ")</c</i>";
// Choose whether to send the translated or scrambled message, and hand it
// on.
//
// TODO: If we wanted to support having familiars that understood a
// language, this is where we would do it. Right now we only allow a
// DM mouthpiece to receive translated messages.
if (GetIsDM(oMouthPiece))
SendMessageToPC(oMouthPiece, sTranslatedMessage + sHeardDescriptor);
else
SendMessageToPC(oMouthPiece, sScrambledMessage + sHeardDescriptor);
}
void _DistributeTranslationParty(string sTranslatedMessage, string sScrambledMessage, string sLanguage, object oSpeaker)
{
int bTranslate = (sLanguage != "common");
object oListener;
if (!bTranslate)
{
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationParty: No translation required for untranslated message.");
#endif
return;
}
for (oListener = GetFirstPC(FALSE);
GetIsObjectValid(oListener);
oListener = GetNextPC(FALSE))
{
if ((ACR_IsLanguageKnown(oListener, sLanguage) && GetFactionEqual(oSpeaker, oListener))
|| ACR_GetIsDMControlled(oListener) || oListener == oSpeaker)
{
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationParty: Distributing message to party or DM recipient " + GetName(oListener));
#endif
SendMessageToPC(oListener, sTranslatedMessage);
}
}
}
void _DistributeTranslationSpoken(string sTranslatedMessage, string sScrambledMessage, string sLanguage, object oSpeaker, float fRange)
{
int bTranslate = (sLanguage != "common");
location lSpeaker = GetLocation(oSpeaker);
object oListener;
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationSpoken: Distributing message of language " + sLanguage);
#endif
// First, distribute the message to objects in range. Note that DM avatars
// are not reliably enumerated here so they are handled separately.
//
// Additionally, distribute the message to mouthpiece listeners if they are
// within range and would not normally receive the message.
for (oListener = GetFirstObjectInShape(SHAPE_SPHERE, fRange, lSpeaker);
GetIsObjectValid(oListener);
oListener = GetNextObjectInShape(SHAPE_SPHERE, fRange, lSpeaker))
{
if (bTranslate && ACR_IsLanguageKnown(oListener, sLanguage) && !GetIsDM(oListener))
{
#if DEBUG_LANG
WriteTimestampedLogEntry("_DistributeTranslationSpoken: Distributing message to non-DM listener " + GetName(oListener) + ".");
#endif
SendMessageToPC(oListener, sTranslatedMessage);
}
_DistributeTranslationToMouthPieces(sTranslatedMessage, sScrambledMessage, oSpeaker, fRange, oListener);
}
if (bTranslate)
_DistributeTranslationToDMAvatars(sTranslatedMessage, oSpeaker, fRange);
}
void _DistributeTranslation(string sOriginalMessage, string sScrambledMessage, string sLanguage, object oSpeaker, int nMode)
{
string sDisplayLanguage;
string sModeColor;
string sModeDescriptor;
string sTranslatedMessage;
string sUntranslatedMessage;
float fRange;
int nTalkVolume;
// Distribute the translated version of the message.
switch (nMode)
{
case CHAT_MODE_PARTY:
sModeColor = "FFFFFF";
sModeDescriptor = ", Party";
fRange = 0.0f;
nTalkVolume = TALKVOLUME_TALK;
break;
case CHAT_MODE_TALK:
sModeColor = "FFFFFF";
sModeDescriptor = "";
fRange = ACR_TALK_SPHERE_RADIUS;
nTalkVolume = TALKVOLUME_TALK;
break;
case CHAT_MODE_WHISPER:
sModeColor = "808080";
sModeDescriptor = ", Whisper";
fRange = ACR_WHISPER_SPHERE_RADIUS;
nTalkVolume = TALKVOLUME_WHISPER;
break;
case CHAT_MODE_SILENT_TALK:
return;
default:
WriteTimestampedLogEntry("_DistributeTranslation: Unhandled chat mode " + IntToString(nMode) + "!");
return;
}
sTranslatedMessage = "<color=#D7C5B9>"+GetName(oSpeaker)+" : </color><color=#" + sModeColor + "> ["+_CapitalizeWord(sLanguage)+sModeDescriptor + "] "+sOriginalMessage;
sUntranslatedMessage = "<color=#D7C5B9>"+GetName(oSpeaker)+" : </color><color=#" + sModeColor + "> [Untranslated"+sModeDescriptor + "] "+sScrambledMessage;
switch (nMode)
{
case CHAT_MODE_PARTY:
_DistributeTranslationParty(sTranslatedMessage, sUntranslatedMessage, sLanguage, oSpeaker);
break;
case CHAT_MODE_TALK:
case CHAT_MODE_WHISPER:
_DistributeTranslationSpoken(sTranslatedMessage, sUntranslatedMessage, sLanguage, oSpeaker, fRange);
break;
}
// Finally, distribute the scrambled version of the message.
if (GetIsPC(oSpeaker) || ACR_GetIsDMControlled(oSpeaker))
SendChatMessage(oSpeaker, oSpeaker, nMode, sScrambledMessage, FALSE);
else
AssignCommand(oSpeaker, _SpeakString(sScrambledMessage, nTalkVolume));
}
void ACR_CheckBodyPartsOnLevelUp(object oPC)
{
int nRace = GetRacialType(oPC);
int nWings = 0;
if(GetLevelByClass(CLASS_TYPE_FAVORED_SOUL, oPC) == 17)
{
if(GetAlignmentGoodEvil(oPC) == ALIGNMENT_EVIL)
{
if(nRace == RACIAL_TYPE_DWARF)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_DWARF_LEATHER_FEMALE;
else
nWings = WING_MODEL_DWARF_LEATHER_MALE;
}
else if(nRace == RACIAL_TYPE_GNOME)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_GNOME_LEATHER_FEMALE;
else
nWings = WING_MODEL_GNOME_LEATHER_MALE;
}
else if(nRace == RACIAL_TYPE_HALFORC)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_HALFORC_LEATHER_FEMALE;
else
nWings = WING_MODEL_HALFORC_LEATHER_MALE;
}
else
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_HUMAN_LEATHER_FEMALE;
else
nWings = WING_MODEL_HUMAN_LEATHER_MALE;
}
}
else
{
if(nRace == RACIAL_TYPE_DWARF)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_DWARF_WHITE_FEMALE;
else
nWings = WING_MODEL_DWARF_WHITE_MALE;
}
else if(nRace == RACIAL_TYPE_GNOME)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_GNOME_WHITE_FEMALE;
else
nWings = WING_MODEL_GNOME_WHITE_MALE;
}
else if(nRace == RACIAL_TYPE_HALFORC)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_HALFORC_WHITE_FEMALE;
else
nWings = WING_MODEL_HALFORC_WHITE_MALE;
}
else
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_HUMAN_WHITE_FEMALE;
else
nWings = WING_MODEL_HUMAN_WHITE_MALE;
}
}
XPObjectAttributesSetWingVariation(oPC, nWings);
}
if(GetLevelByClass(CLASS_TYPE_DRAGONDISCIPLE, oPC) == 9)
{
if(nRace == RACIAL_TYPE_DWARF)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_DWARF_RED_FEMALE;
else
nWings = WING_MODEL_DWARF_RED_MALE;
}
else if(nRace == RACIAL_TYPE_GNOME)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_GNOME_RED_FEMALE;
else
nWings = WING_MODEL_GNOME_RED_MALE;
}
else if(nRace == RACIAL_TYPE_HALFORC)
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_HALFORC_RED_FEMALE;
else
nWings = WING_MODEL_HALFORC_RED_MALE;
}
else
{
if(GetGender(oPC) == GENDER_FEMALE)
nWings = WING_MODEL_HUMAN_RED_FEMALE;
else
nWings = WING_MODEL_HUMAN_RED_MALE;
}
XPObjectAttributesSetWingVariation(oPC, nWings);
}
}
void ACR_ModuleOnModuleLoad()
{
// Set global module settings.
SetServerData(ACR_SERVER_ID, ACR_SERVER_NAME, ACR_SERVER_REGION, ACR_SERVER_IP);
_LoadAssemblies();
// Check and warn the user if the module wasn't built with the same ACR
// version as a script contained within the hak. (This script is built
// with the module and returns the module's view of the version number. The
// acr_version_check script is built with the hak and returns the hak's view
// of the version number.)
ClearScriptParams();
AddScriptParameterString(ACR_GetVersion());
if (ExecuteScriptEnhanced("acr_version_check", OBJECT_SELF) != TRUE)
{
WriteTimestampedLogEntry("acr_mod_events_i: ** WARNING **: Module ACR version doesn't match alfa2_acr.hak ACR version! This is usually caused by compiling the module against a different set of script headers than alfa2_acr.hak was constructed against.");
}
object oModule = GetModule();
// ORDER MATTERS !!
// Start the debuging system and create debugging ids for the module event scripts
ACR_InitializeDebugging();
ACR_CreateDebugSystem(_ONLOAD_DEBUG, DEBUG_TARGET_LOG | DEBUG_TARGET_DB, DEBUG_TARGET_LOG | DEBUG_TARGET_DB, DEBUG_TARGET_LOG | DEBUG_TARGET_DB);
string sServerId = IntToString(ACR_GetServerId());
ACR_PrintDebugMessage("acr_mod_events_i: ALFA " + sServerId + " loading the ALFA Core Rules.", _ONLOAD_DEBUG, DEBUG_LEVEL_INFO);
// Check NWNX status
if (! NWNXInstalled())
{
// record the status
ACR_SetModuleStatus(ACR_MOD_NWNX_FAILED);
ACR_PrintDebugMessage("acr_mod_events_i: NWNX unavailable on ALFA server " + sServerId + ". Rescheduling OnModuleLoad(). Server restart may be required.", _ONLOAD_DEBUG, DEBUG_LEVEL_FATAL);
// reschedule the OnModuleLoad() handler
DelayCommand(ACR_MOD_RELOAD_CYCLE, ACR_ModuleOnModuleLoad());
return;
}
else
{
// create the SQL tables if need be
ACR_CreateSQLTables();
}
// Initialize asynchronous writes
ACR_StartAsyncService();
// Load database-side configuration directives
ACR_LoadDatabaseConfiguration();
// Initialize spell hook
if ( GetLocalString( oModule, "X2_S_UD_SPELLSCRIPT" ) != "acf_spellhook" ) {
SetLocalString( oModule, "X2_S_UD_SPELLSCRIPT", "acr_spellhook" );
}
// Initialize storage objects used by many systems.
// ACR_InitializeStorageObjects();
// Initialize game engine constants.
//ACR_InitializeGameConstants();
// Initialize the spawn system.
ACR_InitializeSpawns(_SPAWN_AREA_DESPAWN_DELAY, _SPAWN_REFRESH_DELAY, _SPAWN_PRESPAWN_SEAMLESS, _SPAWN_PRESPAWN_PREDICTION);
InitializeInfestations();
// Initialize the time system.
ACR_InitializeTime();
// ACR_PrintDebugMessage("acr_mod_events_i: Starting date: " + GetDateAsString(), _ONLOAD_DEBUG, DEBUG_LEVEL_INFO);
// ACR_PrintDebugMessage("acr_mod_events_i: Sarting time: " + GetTimeAsString(), _ONLOAD_DEBUG, DEBUG_LEVEL_INFO);
ACR_PrintDebugMessage("acr_mod_events_i: Time compression ratio: " + FloatToString(ACR_GetGameToRealTimeRatio()), _ONLOAD_DEBUG, DEBUG_LEVEL_INFO);
// Initialize one-time PC status setups
ACR_PCOnModuleLoad();
// Recreate player corpses after we've given the database a minute to catch its breath.
DelayCommand(60.0f, ACR_RestoreCorpsesOnModuleLoad());
// Set bioware/obsidian module switches as desired:
SetModuleSwitch(MODULE_SWITCH_ENABLE_INVISIBLE_GLYPH_OF_WARDING, TRUE);
SetModuleSwitch(MODULE_SWITCH_ENABLE_CROSSAREA_WALKWAYPOINTS, TRUE);
//SetModuleSwitch(MODULE_SWITCH_ENABLE_UMD_SCROLLS, TRUE);
SetModuleSwitch(MODULE_SWITCH_ENABLE_NPC_AOE_HURT_ALLIES, TRUE);
SetModuleSwitch(MODULE_SWITCH_ENABLE_MULTI_HENCH_AOE_DAMAGE, TRUE);
SetModuleSwitch(MODULE_SWITCH_AOE_HURT_NEUTRAL_NPCS, TRUE);
SetModuleSwitch(MODULE_VAR_AI_STOP_EXPERTISE_ABUSE, TRUE);
SetModuleSwitch(MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS, TRUE);
string sSID = IntToString(ACR_SERVER_ID);
// grab the server name from the module properties so it will be updated.
string sServerName = GetName(oModule);
// Try and discover the external address of the server from the database
// connection. If that fails, fall back to the hardcoded default in the
// configuration file.
//
// This is now superseded by the server communicator in most cases.
string sExtAddress = ACR_GetServerAddressFromDatabase();
if (sExtAddress == "")
sExtAddress = ACR_SERVER_IP;
// check that the server information is in the database
ACR_SQLQuery("SELECT * FROM servers WHERE ID=" + sSID);
// create the record if it does not already exist
if (ACR_SQLFetch() != SQL_SUCCESS)
{
ACR_SQLQuery("INSERT INTO servers (ID, Name, IPAddress) VALUES(" + sSID + ",'" + ACR_SQLEncodeSpecialChars(sServerName) + "','" + sExtAddress + "')");
}
else
{
// update server name and ip address if they've changed
if (ACR_SQLGetData(1) != sServerName)
{
ACR_SQLQuery("UPDATE servers SET Name='" + ACR_SQLEncodeSpecialChars(sServerName) + "' WHERE ID=" + sSID);
}
}
// EDIT: Only where ServerID = the ID of the server that has just finished loading.
ACR_SQLQuery("UPDATE characters SET IsOnline=0 WHERE ServerID=" + sSID);
// Initialize chatlogging buffer
ACR_InitializeChat();
// Initialize server IPC support
ACR_ServerIPC_OnModuleLoad();
// This should be the last time in this function.
ACR_PrintDebugMessage("acr_mod_events_i: ALFA server " + sServerId + " loaded.", _ONLOAD_DEBUG, DEBUG_LEVEL_INFO);
// Start the health monitor (in a DelayCommand to reset the instruction/loop
// count limit).
DelayCommand(0.1f, ACR_StartModuleInitHealthChecks());
// Delete unnecessary objects, such as walkmesh helpers, from statically
// placed areas.
ACR_PerformModuleStartupCleanup();
ACR_LogEvent(OBJECT_INVALID, ACR_LOG_SERVER_LOAD, "Server " + sServerId + " loaded module " + GetName(GetModule()) + ".");
WriteTimestampedLogEntry("Module compiled on: " + ACR_GetBuildDate() + ", HAK compiled on: " + ACR_GetHAKBuildDate() + ", module ACR version: " + ACR_GetVersion());
// Increment the global server startup counter statistic.
ACR_IncrementStatistic("MODULE_LOADS");
// The following function call is never called. This is intentional; the
// purpose of this line is to ensure that the compiler cannot elide any
// symbols referenced by ACR_ModuleInit_ForceLinkageForCLRScripts.
if (Random(2) == 42)
ACR_ModuleInit_ForceLinkageForCLRScripts();
// This is needed to establish the server and gameobject structures that
// ACR_CreatureBehavior uses.
ACR_InitializeCreatureBehavior();
}
void ACR_ModuleOnModuleStart()
{
}
void ACR_ModuleOnClientEnter()
{
object oPC = GetEnteringObject(), oCorpse = OBJECT_INVALID;
if (!ACR_ModuleOverrideEvent(ACR_MODULE_ON_CLIENT_ENTER, TRUE))
return;
// exit if the database is unavailable
if (ACR_GetModuleStatus(ACR_MOD_NWNX_FAILED))
{
ACR_PrintDebugMessage("acr_mod_events_i: ERROR - NWNX Unavailable on " + GetName(oPC) + " login.", _ONLOAD_DEBUG, DEBUG_LEVEL_FATAL);
// boot players and log the error
if (! GetIsDM(oPC))
{
SendMessageToPC(oPC, "Booted because NWNX isn't running on the server (server internal error).");
BootPC(oPC);
return;
}
}
// Code executed for DMs and PCs goes here
// initialize the player - RUN THIS BEFORE INITIALIZING OTHER SYSTEMS
if (!ACR_PCOnClientEnter(oPC))
{
return;
}
// Zero out the RP XP timer. Though this looks ill-placed, we need it to detect redundant
// tickers.
SetLocalInt(oPC, _RPXP_HB, 0);
// notify database system of login
ACR_DBPersistOnIncomingPlayer(oPC);
//initialize for chatlogging
ACR_ChatOnClientEnter(oPC);
// Initialize DM Client Extension pack (optional)
ExecuteScript("wand_init", oPC);
if (ACR_GetIsDMControlled(oPC))
{
// log the entry
ACR_LogEvent(oPC, ACR_LOG_LOGIN, "Dungeon Master: " + ACR_SQLEncodeSpecialChars(GetName(oPC)) + " from IP: "+GetPCIPAddress(oPC));
ACR_IncrementStatistic("DM_LOGINS");
}
else
{
// log the entry
ACR_LogEvent(oPC, ACR_LOG_LOGIN, "Character: " + ACR_SQLEncodeSpecialChars(GetName(oPC))+ " from IP: "+GetPCIPAddress(oPC));
// manage dead PCs - important for this to be done first
ACR_DeathOnClientEnter(oPC);
ACR_SkillsOnClientEnter(oPC);
// If this is a first login, run the character creation function.
if ( GetLocalInt( oPC, ACR_NEW_CHARACTER ) ) {
ACR_HandlePCCreation( oPC );
DeleteLocalInt( oPC, ACR_NEW_CHARACTER );
}
}
// Do custom feat stuff.
ACR_FeatOnClientEnter(oPC);
// Let the IPC subsystem know of the login event.
ACR_ServerIPC_OnClientEnter(oPC);
// Let the wealth subsystem know of the login event.
ACR_WealthOnClientEnter(oPC);
ACR_IncrementStatistic("PLAYER_LOGINS");
ACR_ModuleOverrideEvent(ACR_MODULE_ON_CLIENT_ENTER, FALSE);
}
void _createPlayerGUIs( object oPC )
{
// ACR_OpenUniversalGUI( oPC );
if(GetHasFeat(FEAT_ACR_COMBAT_EXPERTISE, oPC) || GetHasFeat(FEAT_ACR_POWER_ATTACK, oPC))
{
DisplayGuiScreen(oPC, "SCREEN_MODEBAR_2", FALSE, "modebar_2.xml");
}
if (ACR_GetIsDMControlled(oPC))
{
DisplayGuiScreen(oPC, "zspawn_b", FALSE, "zspawn_b.xml");
}
}
void _updatePlayerGUIs( object oPC ) {
if ( GetHasFeat( FEAT_ACR_COMBAT_EXPERTISE, oPC ) ) {
_HandlePowExpGUIEvent( oPC, POWER_EXPERT_EVENT_OPEN_MENU, 0 );
_HandlePowExpGUIEvent( oPC, POWER_EXPERT_EVENT_COMBAT_EXPERTISE_MENU, 0 );
}
if ( GetHasFeat( FEAT_ACR_POWER_ATTACK, oPC ) ) {
_HandlePowExpGUIEvent( oPC, POWER_EXPERT_EVENT_OPEN_MENU, 0 );
_HandlePowExpGUIEvent( oPC, POWER_EXPERT_EVENT_POWER_ATTACK_MENU, 0 );
}
}
void ACR_ModuleOnPCLoaded()
{
object oPC = GetEnteringObject();
if (!ACR_ModuleOverrideEvent(ACR_MODULE_ON_PC_LOADED, TRUE))
return;
CraftingOnPCLoaded(oPC);
HorseOnPCLoaded(oPC);
// process DMs
if (ACR_GetIsDMControlled(oPC))
{
ACR_PCOnPCLoadedAsDM(oPC);
if (GetItemPossessedBy(oPC, "abr_spawn_debug") == OBJECT_INVALID) { CreateItemOnObject("acr_spawn_debug", oPC); }