-
Notifications
You must be signed in to change notification settings - Fork 5
/
LibQuestData.lua
1149 lines (1039 loc) · 43.8 KB
/
LibQuestData.lua
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
--[[
LibQuestData
by Sharlikran
https://sharlikran.github.io/
--]]
-- Init
local lib = _G["LibQuestData"]
local internal = _G["LibQuestData_Internal"]
-- Libraries
local LMP = LibMapPins
local GPS = LibGPS3
local LMD = LibMapData
-- Default table
local quest_data_index_default = {
-- [lib.quest_data_index.quest_name] = "", Depreciated
-- [lib.quest_data_index.quest_giver] = "", Depreciated
[lib.quest_data_index.quest_type] = -1, -- Undefined
[lib.quest_data_index.quest_repeat] = -1, -- Undefined
[lib.quest_data_index.game_api] = 100003, -- 100003 means unverified
[lib.quest_data_index.quest_line] = 10000, -- QuestLine (10000 = not assigned/not verified. 10001 = not part of a quest line/verified)
[lib.quest_data_index.quest_number] = 10000, -- Quest Number In QuestLine (10000 = not assigned/not verified)
[lib.quest_data_index.quest_series] = 0, -- None = 0, Cadwell's Almanac = 1, Undaunted = 2, AD = 3, DC = 4, EP = 5
[lib.quest_data_index.quest_display_type] = -1, -- ZONE_DISPLAY_TYPE_ZONE_STORY, ZONE_DISPLAY_TYPE_DUNGEON << -1 = Undefined >>
}
local quest_map_pin_index_default = {
[lib.quest_map_pin_index.local_x] = -10, -- GetMapPlayerPosition() << -10 = Undefined >>
[lib.quest_map_pin_index.local_y] = -10, -- GetMapPlayerPosition() << -10 = Undefined >>
[lib.quest_map_pin_index.global_x] = -10, -- LocalToGlobal(GetMapPlayerPosition()) << -10 = Undefined >>
[lib.quest_map_pin_index.global_y] = -10, -- LocalToGlobal(GetMapPlayerPosition()) << -10 = Undefined >>
[lib.quest_map_pin_index.quest_id] = -1, -- -1 Unknown, Quest ID i.e. 6404 for "The Dragonguard"
[lib.quest_map_pin_index.quest_giver] = -1, -- Arbitrary number pointing to an NPC Name 81004, "Abnur Tharn" << -1 = Undefined >>
}
-------------------------------------------------
----- Helpers ----
-------------------------------------------------
function internal:has_vampirisum()
-- Noxiphilic Sanguivoria 40360
for index = 1, GetNumBuffs("player") do
local buffName, timeStarted, timeEnding, buffSlot, stackCount, iconFilename, buffType, effectType, abilityType, statusEffectType, abilityId, canClickOff, castByPlayer = GetUnitBuffInfo("player", index)
if abilityId == 40360 then return true end
end
return false
end
function internal:has_lupinus()
-- Sanies Lupinus 40521
for index = 1, GetNumBuffs("player") do
local buffName, timeStarted, timeEnding, buffSlot, stackCount, iconFilename, buffType, effectType, abilityType, statusEffectType, abilityId, canClickOff, castByPlayer = GetUnitBuffInfo("player", index)
if abilityId == 40521 then return true end
end
return false
end
-- Function to check for empty table
function internal:is_empty_or_nil(t)
if t == nil or t == "" then return true end
return type(t) == "table" and ZO_IsTableEmpty(t) or false
end
function internal:missing_gps_data(info)
if info[lib.quest_map_pin_index.global_x] == -10 then
--d("global_x was -10 so undefined")
return true
end
return false
end
function internal:missing_quest_giver(info)
if info[lib.quest_map_pin_index.quest_giver] == -1 then
--d("quest_giver was -1 so undefined")
return true
end
if type(info[lib.quest_map_pin_index.quest_giver]) == "string" then
--d("Quest Giver was a string")
return true
end
return false
end
function internal:has_undefined_data(info)
if info[lib.quest_map_pin_index.global_x] == -10 then
--d("global_x was -10 so undefined")
return true
end
if info[lib.quest_map_pin_index.quest_giver] == -1 then
--d("quest_giver was -1 so undefined")
return true
end
if type(info[lib.quest_map_pin_index.quest_giver]) == "string" then
--d("Quest Giver was a string")
return true
end
return false
end
function internal:quest_in_range(map_data_quest, quest_data)
local distance = zo_round(GPS:GetLocalDistanceInMeters(map_data_quest[lib.quest_map_pin_index.local_x],
map_data_quest[lib.quest_map_pin_index.local_y], quest_data[lib.quest_map_pin_index.local_x],
quest_data[lib.quest_map_pin_index.local_y]))
if distance <= 25 then
--internal.dm("Debug", "Distance was within range")
return true
else
--internal.dm("Debug", "Distance outside of range")
return false
end
end
-------------------------------------------------
----- General Quest Info ----
-------------------------------------------------
function lib:get_quest_list(zone)
local all_zone_quests = {}
local subzone_quests = {}
local new_element = {}
local quest_name_in_zone_list = {}
-- get all the quetsts in the zone
if internal:zone_has_quest_locations(zone) then
all_zone_quests = internal:get_zone_quests(zone)
end
-- make a table of the questId values as a way to prevent them from duplicating maybe ??
for key, quest_info in pairs(all_zone_quests) do
local questId = quest_info[lib.quest_map_pin_index.quest_id]
lib.quest_in_zone_list[questId] = true
end
internal.dm("Debug", "get_quest_list")
--internal.dm("Debug", zone)
if internal:subzone_has_conversions(zone) then
-- all_zone_quests = internal:add_subzone_quests(zone)
local subzone_list = internal.subzone_list[zone]
--internal.dm("Debug", subzone_list)
for subzone, conversion in pairs(subzone_list) do
-- use the texture name as the zone and get the quests as if subzone = zone
--internal.dm("Debug", subzone)
local subzone_quests = internal:get_zone_quests(subzone)
for i, quest in ipairs(subzone_quests) do
--internal.dm("Debug", quest)
local questId = quest[lib.quest_map_pin_index.quest_id]
--[[If the quest info is not nil and the quest info from the subzone is not already in the list of quests
then add the quest from the subzone. This will prevent adding a quest from the subzone with
the subzone scale information to the map when there is already a fake pin.]]--
if not internal:is_empty_or_nil(quest) and not lib.quest_in_zone_list[questId] then
local new_element = ZO_DeepTableCopy(quest)
--internal.dm("Verbose", quest[lib.quest_map_pin_index.local_x])
--internal.dm("Verbose", quest[lib.quest_map_pin_index.local_y])
new_element[lib.quest_map_pin_index.local_x] = (quest[lib.quest_map_pin_index.local_x] * conversion.zoom_factor) + conversion.x
new_element[lib.quest_map_pin_index.local_y] = (quest[lib.quest_map_pin_index.local_y] * conversion.zoom_factor) + conversion.y
--internal.dm("Verbose", new_element[lib.quest_map_pin_index.local_x])
--internal.dm("Verbose", new_element[lib.quest_map_pin_index.local_y])
table.insert(all_zone_quests, new_element)
local questId = new_element[lib.quest_map_pin_index.quest_id]
lib.quest_in_zone_list[questId] = true
end
end
end
end
--internal.dm("Debug", all_zone_quests)
local new_all_zone_quests = {}
for key, quest_info in pairs(all_zone_quests) do
local questId = quest_info[lib.quest_map_pin_index.quest_id]
local questName = lib:get_quest_name(questId, lib.effective_lang)
local questInfo = lib.quest_data[questId]
local questSeries = nil
if questInfo then
questSeries = questInfo[lib.quest_data_index.quest_series]
else
questSeries = 0
end
local playerAlliance = GetUnitAlliance("player")
local playerGuildRankData = nil
local playerGuildQuestLevelRequirement = nil
if questSeries >= lib.quest_series_type.quest_type_guild_mage or questSeries == lib.quest_series_type.quest_type_undaunted then
-- this is set in function update_guild_skillline_data()
playerGuildRankData = lib.quest_guild_rank_data[questSeries].rank
if lib.guild_rank_quest_list[questSeries][questId] then
playerGuildQuestLevelRequirement = lib.guild_rank_quest_list[questSeries][questId]
end
end
local playerQualifies = true
if playerGuildQuestLevelRequirement and playerGuildRankData then
playerQualifies = playerGuildQuestLevelRequirement <= playerGuildRankData
end
local removedQuest = false
if lib.known_removed_quest[questId] then
removedQuest = true
end
--[[
Pledges can be obtained from any location. Before this can be used All quests
specific to an Alliance have to be manually reviewed. Otherwise Pledges
will not show on the map.
if questSeries >= 3 and questSeries <= 5 then
playerQualifies = lib.playerAlliance[playerAlliance] == questSeries
end
]]--
if IsInImperialCity() or IsInCyrodiil() then
--internal.dm("Debug", "The word cyrodiil was found.")
if questSeries >= lib.quest_series_type.quest_type_ad and questSeries <= lib.quest_series_type.quest_type_ep then
playerQualifies = lib.playerAlliance[playerAlliance] == questSeries
end
else
--internal.dm("Debug", "The word cyrodiil was not found.")
end
local prerequisiteCompleted = internal:prerequisites_completed(questId)
local showBreadcrumbQuest = internal:show_breadcrumb_quest(questId)
local showCompanionQuest = true
if lib:is_companion_rapport_quest(questId) then showCompanionQuest = internal:check_companion_rapport_requirements(questId) end
local showCertificationQuest = GetUnitLevel("player") >= 6 and lib.quest_certifications[questId]
--HasQuest(pinData.q)
-- /script d(HasCompletedQuest(3686))
if questSeries == lib.playerAlliance[playerAlliance] then
internal.dm("Debug", string.format("[%s] %s : Completed(%s), Prerequisite (%s), Breadcrumb(%s), Companion(%s)", questId, GetQuestName(questId), tostring(HasCompletedQuest(questId)), tostring(prerequisiteCompleted), tostring(showBreadcrumbQuest), tostring(showCompanionQuest)))
end
if not quest_name_in_zone_list[questName] and playerQualifies and not removedQuest and prerequisiteCompleted and showBreadcrumbQuest and showCompanionQuest then
-- name didn't exist keep it for sure
quest_name_in_zone_list[questName] = questId
table.insert(new_all_zone_quests, quest_info)
else
-- name exists already
local questExists = questId == quest_name_in_zone_list[questName]
local questUnknown = questName == lib.unknown_quest_name_string[lib.effective_lang]
if questUnknown and playerQualifies and not removedQuest and prerequisiteCompleted and showBreadcrumbQuest and showCompanionQuest then
--[[ the name of the quest in unknown because a localization
has not been provided
]]--
table.insert(new_all_zone_quests, quest_info)
elseif questExists and playerQualifies and not removedQuest and prerequisiteCompleted and showBreadcrumbQuest and showCompanionQuest then
--[[ the name is in the table, and the ID matches so keep it
because it is another quest giver in a different place
Otherwise it is a quest with the same name but a different
Quest ID
]]--
table.insert(new_all_zone_quests, quest_info)
end
end
end
-- clear quest_in_zone_list after everything is built
lib.quest_in_zone_list = {}
--internal.dm("Debug", all_zone_quests)
return new_all_zone_quests
end
LMD:RegisterCallback(LMD.callbackType.EVENT_ZONE_CHANGED,
function()
lib.questGiverName = nil
local zone = LMP:GetZoneAndSubzone(true, false, true)
lib.zone_quests = lib:get_quest_list(zone)
end)
LMD:RegisterCallback(LMD.callbackType.EVENT_LINKED_WORLD_POSITION_CHANGED,
function()
lib.questGiverName = nil
local zone = LMP:GetZoneAndSubzone(true, false, true)
lib.zone_quests = lib:get_quest_list(zone)
end)
LMD:RegisterCallback(LMD.callbackType.OnWorldMapChanged,
function()
lib.questGiverName = nil
local zone = LMP:GetZoneAndSubzone(true, false, true)
lib.zone_quests = lib:get_quest_list(zone)
end)
LMD:RegisterCallback(LMD.callbackType.WorldMapSceneStateChange,
function()
lib.questGiverName = nil
local zone = LMP:GetZoneAndSubzone(true, false, true)
lib.zone_quests = lib:get_quest_list(zone)
end)
--[[ get whether or not it is a cadwell quest
return true if it is a cadwell quest
]]--
function lib:is_cadwell_quest(id)
if type(id) == "number" then
local c = lib.quest_cadwell[id] or false
return c
end
end
--[[ get whether or not it is a companion quest
return true if it is a companion quest
]]--
function lib:is_companion_quest(id)
if type(id) == "number" then
local c = lib.quest_companion[id] or false
return c
end
end
--[[ get whether or not it is a companion quest
return true if it is a companion quest
]]--
function lib:is_companion_rapport_quest(id)
if type(id) == "number" then
local c = lib.quest_companion_rapport[id] or false
return c
end
end
--[[ get whether or not it is a prologue quest
return true if it is a companion quest
]]--
function lib:is_prologue_quest(id)
if type(id) == "number" then
local c = lib.prologue_quest_list[id] or false
return c
end
end
--[[ get whether or not it is a quest started with a quest object
return true if it is
]]--
function lib:is_object_quest_starter(id)
if type(id) == "number" then
local c = lib.object_quest_starter_list[id] or false
return c
end
end
-------------------------------------------------
----- Assign Quest Flag ----
-------------------------------------------------
function lib:assign_quest_flag(questId, hidden_quest)
local completed_quest = lib.completed_quests[questId] or false
local started_quest = lib.started_quests[questId] or false
local skill_quest = lib.quest_rewards_skilpoint[questId] or false
local cadwell_quest = lib:is_cadwell_quest(questId) or false
local companion_quest = lib:is_companion_quest(questId) or false
local quest_type_data = lib:get_quest_data(questId)
local quest_type = quest_type_data[lib.quest_data_index.quest_type]
local repeatable_type = quest_type_data[lib.quest_data_index.quest_repeat]
local quest_display_type = quest_type_data[lib.quest_data_index.quest_display_type]
--internal.dm("Debug", completed_quest)
--internal.dm("Debug", started_quest)
--internal.dm("Debug", repeatable_type)
--internal.dm("Debug", skill_quest)
--internal.dm("Debug", cadwell_quest)
--internal.dm("Debug", quest_type)
--internal.dm("Debug", quest_display_type)
local fcpq = false -- flag_completed_quest
local fucq = false -- flag_uncompleted_quest
local fhdq = false -- flag_hidden_quest
local fstq = false -- flag_started_quest
local fguq = false -- flag_guild_quest
local fdaq = false -- flag_daily_quest
local fskq = false -- flag_skill_quest
local fcwq = false -- flag_cadwell_quest
local fcaq = false -- flag_companion_quest
local fduq = false -- flag_dungeon_quest
local fhoq = false -- flag_holiday_quest
local fzrq = false -- flag_zone_raid_quest
local fzsq = false -- flag_zone_story_quest
local fprq = false -- flag_type_prologue
local fpgq = false -- flag_type_pledge
if completed_quest then fcpq = true end
if not completed_quest then fucq = true end
if started_quest then fstq = true end
if skill_quest then fskq = true end
if cadwell_quest then fcwq = true end
if companion_quest then fcaq = true end
if hidden_quest then fhdq = true end
if quest_type == QUEST_TYPE_DUNGEON then fduq = true end
if quest_type == QUEST_TYPE_HOLIDAY_EVENT then fhoq = true end
if quest_type == QUEST_TYPE_PROLOGUE then fprq = true end
if quest_type == QUEST_TYPE_UNDAUNTED_PLEDGE then fpgq = true end
local repeatableType = repeatable_type == QUEST_REPEAT_REPEATABLE or repeatable_type == QUEST_REPEAT_DAILY
local pvpQuest = quest_type == QUEST_TYPE_AVA or quest_type == QUEST_TYPE_AVA_GROUP
if quest_type == QUEST_TYPE_GUILD then fguq = true end
local repeatableGuildQuest = repeatableType and fguq
local nonRepeatableGuildQuest = repeatable_type == QUEST_REPEAT_NOT_REPEATABLE and fguq
if repeatableType then fdaq = true end
if pvpQuest and repeatableType then fdaq = true end
if quest_display_type == ZONE_DISPLAY_TYPE_ZONE_STORY then fzsq = true end
if quest_display_type == ZONE_DISPLAY_TYPE_RAID then
fzrq = true
fdaq = false
end
--[[
holliday, daily, and WEEKLY quests are unique
holliday happens only durring the event
Weekly can be done once per week, like a trial quest
daily can be done once per day
]]--
if fhoq then
return lib.flag_holiday_quest
end
if fpgq then
return lib.flag_type_pledge
end
if repeatableGuildQuest or (not fcpq and nonRepeatableGuildQuest) then
return lib.flag_guild_quest
end
if fdaq and (not fhoq or not fpgq or not repeatableGuildQuest) then
return lib.flag_daily_quest
end
--[[
Completed takes precedence over other states
isCompletedQuest: if not holiday, pledge, or daily and not repeatable guild quest
isCompletedNonRepeatableGuildQuest: when it is completed and it is a
non-repeatable guild quest
]]--
local isCompletedQuest = fcpq and (not fhoq or not fpgq or not fdaq or not repeatableGuildQuest)
local isCompletedNonRepeatableGuildQuest = fcpq and nonRepeatableGuildQuest
if isCompletedQuest or isCompletedNonRepeatableGuildQuest then
return lib.flag_completed_quest
end
--[[
Only Uncompleted quests can be hidden so check for
hidden first since you can click them to unhide them.
Hidden should take precedence over uncompleted.
]]--
if fhdq then
return lib.flag_hidden_quest
end
--[[
Started quests should not be hidden and started
at the same time. The event for on_quest_added
should take care of this.
]]--
if fstq then
return lib.flag_started_quest
end
--[[
Cadwell and Skill quests are sort of unique. Completed
should take precedence and if uncompleted these should
take precedence over uncompleted.
]]--
if fzsq then
return lib.flag_zone_story_quest
end
if fskq then
return lib.flag_skill_quest
end
if fcaq then
return lib.flag_companion_quest
end
if fcwq then
return lib.flag_cadwell_quest
end
--[[
new quest types
]]--
if fzrq then
return lib.flag_zone_raid_quest
end
if fduq then
return lib.flag_dungeon_quest
end
if fprq then
return lib.flag_type_prologue
end
--[[
Hopefully this is last
]]--
if fucq then
return lib.flag_uncompleted_quest
end
--[[
Only one flag per quest and I hope it never
gets here and is set to 0
]]--
return 0
end
-------------------------------------------------
----- Extended Quest Data ----
-------------------------------------------------
-- returns ID number only for use with lib.quest_givers[lang]
function lib:get_quest_npc_id(location)
if location[lib.quest_map_pin_index.quest_giver] then
-- can return -1 if that value is in the table
return location[lib.quest_map_pin_index.quest_giver]
else
return -1 -- undefined
end
end
--[[
returns QuestType. ZOS API does not specify whether or not it is a Writ or daily.
]]--
function lib:get_quest_type(quest_id)
if type(quest_id) == "number" and lib.quest_data[quest_id] then
-- can return -1 if that value is in the table
return lib.quest_data[quest_id][lib.quest_data_index.quest_type]
else
return -1 -- undefined
end
end
--[[
returns QuestRepeatableType. ZOS API does not specify whether or not it is a Writ or daily.
]]--
function lib:get_quest_repeat(quest_id)
if type(quest_id) == "number" and lib.quest_data[quest_id] then
-- can return -1 if that value is in the table
return lib.quest_data[quest_id][lib.quest_data_index.quest_repeat]
else
return -1 -- undefined
end
end
--[[
returns the the API the information was gathered with. 100003 means
unverified. 100030 or higher means data collected just prior to Greymoor
]]--
function lib:get_game_api(quest_id)
if type(quest_id) == "number" and lib.quest_data[quest_id] then
-- can return -1 if that value is in the table
return lib.quest_data[quest_id][lib.quest_data_index.game_api]
else
return -1 -- undefined
end
end
--[[
Arbitrary number for Quest Line used with Destinations.
]]--
function lib:get_quest_line(quest_id)
if type(quest_id) == "number" and lib.quest_data[quest_id] then
-- can return -1 if that value is in the table
return lib.quest_data[quest_id][lib.quest_data_index.quest_line]
else
return 10000 -- undefined
end
end
--[[
Arbitrary number for Quest Number used with Destinations.
]]--
function lib:get_quest_number(quest_id)
if type(quest_id) == "number" and lib.quest_data[quest_id] then
-- can return -1 if that value is in the table
return lib.quest_data[quest_id][lib.quest_data_index.quest_number]
else
return 10000 -- undefined
end
end
--[[
Returne number from lib.dest_quest_series_index
]]--
function lib:get_quest_series(quest_id)
if type(quest_id) == "number" and lib.quest_data[quest_id] then
-- can return -1 if that value is in the table
return lib.quest_data[quest_id][lib.quest_data_index.quest_series]
else
return 0 -- None
end
end
-------------------------------------------------
----- Lookup By ID: returns name ----
-------------------------------------------------
-- I want this to be nil rather then unknown npc
function lib:get_quest_giver(id, lang)
lang = lang or lib.effective_lang
return lib.quest_givers[lang][id]
end
function lib:get_quest_name(id, lang)
local unknown = lib.unknown_quest_name_string[lib.effective_lang]
lang = lang or lib.effective_lang
return lib.quest_names[lang][id] or unknown
end
-------------------------------------------------
----- Lookup By Name: returns table of IDs ----
-------------------------------------------------
function lib:get_questids_table(name, lang)
local lang = lang or lib.effective_lang
if type(name) == "string" then
return lib.name_to_questid_table[lang][name]
end
end
function lib:get_npcids_table(name, lang)
local lang = lang or lib.effective_lang
if type(name) == "string" then
return lib.name_to_npcid_table[lang][name]
end
end
-------------------------------------------------
----- Generate QuestID Table By Language ----
-------------------------------------------------
--[[
Build ID table is indexed by the quest name, only default language
is built by default. Author must build other languages as needed.
--]]
-- contains_id is a helper for building the lookup tables
local function contains_id(quent_ids, id_to_find)
local found_id = false
for questname, quest_ids in pairs(quent_ids) do
-- print(questname)
for _, quest_id in pairs(quest_ids) do
-- print(quest_id)
if quest_id == id_to_find then
found_id = true
end
end
end
return found_id
end
function lib:build_questid_table(lang)
local lang = lang or lib.effective_lang
local built_table = {}
for var1, var2 in pairs(lib.quest_names[lang]) do
-- print(var2)
-- print(var2)
if built_table[var2] == nil then built_table[var2] = {} end
if contains_id(built_table, var1) then
-- print("Var 1 is in ids")
else
-- print("Var 1 is not in ids")
table.insert(built_table[var2], var1)
end
end
lib.name_to_questid_table[lang] = built_table
end
function lib:build_npcid_table(lang)
local lang = lang or lib.effective_lang
local built_table = {}
for var1, var2 in pairs(lib.quest_givers[lang]) do
-- print(var2)
-- print(var2)
if built_table[var2] == nil then built_table[var2] = {} end
if contains_id(built_table, var1) then
-- print("Var 1 is in ids")
else
-- print("Var 1 is not in ids")
table.insert(built_table[var2], var1)
end
end
lib.name_to_npcid_table[lang] = built_table
end
function lib:build_questlist_skilpoint()
local built_table = {}
for index = 1, #lib.quest_has_skill_point do
built_table[lib.quest_has_skill_point[index]] = true
end
lib.quest_rewards_skilpoint = built_table
end
function lib:set_conditional_quests(quest_id)
if lib.conditional_quest_list[quest_id] then
for key, conditional_quest_id in pairs(lib.conditional_quest_list[quest_id]) do
lib.completed_quests[conditional_quest_id] = true
end
end
end
function lib:set_achievement_quests(quest_id)
if lib.achievement_quest_list[quest_id] then
local _, _, _, _, completed, _, _ = GetAchievementInfo(lib.achievement_quest_list[quest_id])
if not completed then
lib.completed_quests[quest_id] = true
end
end
end
--[[
Get a table of quest IDs when given a quest name
Use: Drawing a pin with a different color if the player has started the
quest and has not finished it yet. Loop over the table
example:
for _, id in ipairs(ids) do
started_quests[id] = true
end
--]]
function lib:is_quest_started(quest_id)
local started = lib.started_quests
for id, state in pairs(started) do
if id == quest_id then return true end
end
return false
end
local function build_completed_quests()
lib.completed_quests = {}
local fakeQuestId
local apiQuestname
for i = 0, 7500 do
fakeQuestId = i
apiQuestname = GetQuestName(fakeQuestId)
if apiQuestname ~= "" then
if HasCompletedQuest(fakeQuestId) then
if lib.quest_names[lib.effective_lang][fakeQuestId] ~= apiQuestname then
--internal.dm("Debug", "~= quest_name")
LibQuestData_SavedVariables["quest_names"][fakeQuestId] = apiQuestname
end
if lib.quest_names[lib.effective_lang][fakeQuestId] == nil then
--internal.dm("Debug", "== nil")
LibQuestData_SavedVariables["quest_names"][fakeQuestId] = apiQuestname
end
lib.completed_quests[fakeQuestId] = true
lib:set_conditional_quests(fakeQuestId)
end
end
end
end
--[[ update the completed quest list without loop
when the player completes a quest
]]--
local function update_completed_quests(quest_id)
lib.completed_quests[quest_id] = true
end
local function update_started_quests()
-- Get names of started quests from quest journal, get quest ID from lookup table
lib.started_quests = {}
for i = 1, GetNumJournalQuests() do
if IsValidQuestIndex(i) then
local name = GetJournalQuestName(i)
local ids = lib:get_questids_table(name)
if ids ~= nil then
-- Add all IDs for that quest name to list
for _, id in ipairs(ids) do
lib.started_quests[id] = true
end
end
end
end
end
local function update_guild_skillline_data()
for i = 1, GetNumSkillLines(SKILL_TYPE_GUILD) do
local skillLineName, skillLineLevel, _, skillLineId = GetSkillLineInfo(SKILL_TYPE_GUILD, i)
for key, guild_name in pairs(lib.quest_guild_names) do
if skillLineName == guild_name then
lib.quest_guild_rank_data[key].name = skillLineName
lib.quest_guild_rank_data[key].rank = skillLineLevel
end
end
end
end
local function on_skill_rank_update(eventCode, skillType, skillLineIndex, rank)
update_guild_skillline_data()
end
EVENT_MANAGER:RegisterForEvent(lib.libName .. "_skill_rank_updater", EVENT_SKILL_RANK_UPDATE, on_skill_rank_update)
local function on_quest_added(eventCode, journalIndex, questName, objectiveName)
update_started_quests()
end
EVENT_MANAGER:RegisterForEvent(lib.libName .. "_quest_added_updater", EVENT_QUEST_ADDED, on_quest_added)
local function on_quest_removed(eventCode, isCompleted, journalIndex, questName, zoneIndex, poiIndex, questID)
update_started_quests()
if isCompleted then
update_completed_quests(questID)
end
end
EVENT_MANAGER:RegisterForEvent(lib.libName .. "_quest_removed_updater", EVENT_QUEST_REMOVED, on_quest_removed)
-- Event handler function for EVENT_PLAYER_ACTIVATED
local function update_quest_information()
internal.dm("Debug", "update_quest_information")
local eight_field_data = {
quest_name = 1, -- Depreciated, use lib:get_quest_name(id, lang)
quest_giver = 2, -- Depreciated, see quest_map_pin_index
quest_type = 3, -- MAIN_STORY, DUNGEON << -1 = Undefined >>
quest_repeat = 4, -- quest_repeat_daily, quest_repeat_not_repeatable = 0, quest_repeat_repeatable << -1 = Undefined >>
game_api = 5, -- 100003 means unverified, 100030 or higher means recent quest data
quest_line = 6, -- QuestLine (10000 = not assigned/not verified. 10001 = not part of a quest line/verified)
quest_number = 7, -- Quest Number In QuestLine (10000 = not assigned/not verified)
quest_series = 8, -- None = 0, Cadwell's Almanac = 1, Undaunted = 2, AD = 3, DC = 4, EP = 5.
}
local seven_field_data = {
quest_name = 1, -- Depreciated, use lib:get_quest_name(id, lang)
-- quest_giver = 2, Depreciated, see quest_map_pin_index
quest_type = 2, -- MAIN_STORY, DUNGEON << -1 = Undefined >>
quest_repeat = 3, -- quest_repeat_daily, quest_repeat_not_repeatable = 0, quest_repeat_repeatable << -1 = Undefined >>
game_api = 4, -- 100003 means unverified, 100030 or higher means recent quest data
quest_line = 5, -- QuestLine (10000 = not assigned/not verified. 10001 = not part of a quest line/verified)
quest_number = 6, -- Quest Number In QuestLine (10000 = not assigned/not verified)
quest_series = 7, -- None = 0, Cadwell's Almanac = 1, Undaunted = 2, AD = 3, DC = 4, EP = 5.
}
local five_field_location = {
local_x = 1, -- GetMapPlayerPosition() << -10 = Undefined >>
local_y = 2, -- GetMapPlayerPosition() << -10 = Undefined >>
global_x = 3, -- LocalToGlobal(GetMapPlayerPosition()) << -10 = Undefined >>
global_y = 4, -- LocalToGlobal(GetMapPlayerPosition()) << -10 = Undefined >>
quest_id = 5, -- Number index of quest name i.e. 6404 for "The Dragonguard" << -1 = Undefined >>
}
local nine_field_location = {
local_x = 1, -- GetMapPlayerPosition() << -10 = Undefined >>
local_y = 2, -- GetMapPlayerPosition() << -10 = Undefined >>
global_x = 3, -- LocalToGlobal(GetMapPlayerPosition()) << -10 = Undefined >>
global_y = 4, -- LocalToGlobal(GetMapPlayerPosition()) << -10 = Undefined >>
quest_id = 5, -- Number index of quest name i.e. 6404 for "The Dragonguard" << -1 = Undefined >>
world_x = 6, -- Depreciated, WorldX 3D Location << -10 = Undefined >>
world_y = 7, -- Depreciated, WorldY 3D Location << -10 = Undefined >>
world_z = 8, -- Depreciated, WorldZ 3D Location << -10 = Undefined >>
quest_giver = 9, -- Updated, was 9 now 6, Arbitrary number pointing to an NPC Name 81004, "Abnur Tharn" << -1 = Undefined >>
}
local all_locations = LibQuestData_SavedVariables["location_info"]
local all_quest_data = ZO_DeepTableCopy(LibQuestData_SavedVariables["quest_info"])
local all_quest_names = LibQuestData_SavedVariables["quest_names"]
local all_objectives = LibQuestData_SavedVariables["objective_info"]
local all_quest_givers = LibQuestData_SavedVariables["giver_names"]
local all_reward_info = LibQuestData_SavedVariables["reward_info"]
local rebuilt_data = {}
local rebuilt_locations = {}
local current_data = {}
local givername
local npc_id
local saved_reward_info
local currentQuestInfoFormat = false
local quests_from_zone = {}
for zone, zone_quests in pairs(all_locations) do
for index, quest in pairs(zone_quests) do
current_data = {}
if #quest == 5 then
current_data[lib.quest_map_pin_index.local_x] = quest[five_field_location.local_x]
current_data[lib.quest_map_pin_index.local_y] = quest[five_field_location.local_y]
current_data[lib.quest_map_pin_index.global_x] = quest[five_field_location.global_x]
current_data[lib.quest_map_pin_index.global_y] = quest[five_field_location.global_y]
current_data[lib.quest_map_pin_index.quest_id] = quest[five_field_location.quest_id]
givername = -1
--internal.dm("Debug", quest[five_field_location.quest_id])
if all_quest_data[quest[five_field_location.quest_id]] then
if #all_quest_data[quest[five_field_location.quest_id]] == 8 then
givername = all_quest_data[quest[five_field_location.quest_id]][eight_field_data.quest_giver]
end
end
current_data[lib.quest_map_pin_index.quest_giver] = givername
end
if #quest == 9 then
current_data[lib.quest_map_pin_index.local_x] = quest[nine_field_location.local_x]
current_data[lib.quest_map_pin_index.local_y] = quest[nine_field_location.local_y]
current_data[lib.quest_map_pin_index.global_x] = quest[nine_field_location.global_x]
current_data[lib.quest_map_pin_index.global_y] = quest[nine_field_location.global_y]
current_data[lib.quest_map_pin_index.quest_id] = quest[nine_field_location.quest_id]
current_data[lib.quest_map_pin_index.quest_giver] = quest[nine_field_location.quest_giver]
quest = current_data
end
if #quest == 6 then
current_data = quest
if type(current_data[lib.quest_map_pin_index.quest_giver]) == "string" then
npc_id = lib:get_npcids_table(current_data[lib.quest_map_pin_index.quest_giver])
if npc_id then
current_data[lib.quest_map_pin_index.quest_giver] = npc_id[1]
end
end
if (type(current_data[lib.quest_map_pin_index.quest_giver]) == "string") or (current_data[lib.quest_map_pin_index.quest_giver] == -1) then
npc_id = lib:get_giver_when_object(current_data[lib.quest_map_pin_index.quest_id])
if not internal:is_empty_or_nil(npc_id) then
current_data[lib.quest_map_pin_index.quest_giver] = npc_id
end
end
end
if rebuilt_locations[zone] == nil then rebuilt_locations[zone] = {} end
table.insert(rebuilt_locations[zone], current_data)
end
end
for index, data in pairs(all_quest_data) do
current_data = {}
if #data == 8 then
current_data[lib.quest_data_index.quest_type] = data[eight_field_data.quest_type]
current_data[lib.quest_data_index.quest_repeat] = data[eight_field_data.quest_repeat]
current_data[lib.quest_data_index.game_api] = data[eight_field_data.game_api]
current_data[lib.quest_data_index.quest_line] = data[eight_field_data.quest_line]
current_data[lib.quest_data_index.quest_number] = data[eight_field_data.quest_number]
current_data[lib.quest_data_index.quest_series] = data[eight_field_data.quest_series]
end
-- old data when the name was part of the data
local oldSevenFieldData = type(data[seven_field_data.quest_name]) == "string"
if #data == 7 and oldSevenFieldData then
--internal.dm("Debug", "Old 7 field data")
current_data[lib.quest_data_index.quest_type] = data[seven_field_data.quest_type]
current_data[lib.quest_data_index.quest_repeat] = data[seven_field_data.quest_repeat]
current_data[lib.quest_data_index.game_api] = data[seven_field_data.game_api]
current_data[lib.quest_data_index.quest_line] = data[seven_field_data.quest_line]
current_data[lib.quest_data_index.quest_number] = data[seven_field_data.quest_number]
current_data[lib.quest_data_index.quest_series] = data[seven_field_data.quest_series]
end
if (#data == 6) or (#data == 7 and not oldSevenFieldData) then
--internal.dm("Debug", "Previous 6 field data or New 7 field data")
current_data = data
currentQuestInfoFormat = true
end
if not lib.quest_data[index] then
if rebuilt_data[index] == nil then rebuilt_data[index] = {} end
rebuilt_data[index] = current_data
elseif lib.quest_data[index] and not lib.quest_data[index][lib.quest_data_index.quest_display_type] and currentQuestInfoFormat then
if rebuilt_data[index] == nil then rebuilt_data[index] = {} end
rebuilt_data[index] = current_data
else
local newerAPI = false
if current_data[lib.quest_data_index.game_api] > lib.quest_data[index][lib.quest_data_index.game_api] then newerAPI = true end
--[[ Disabled for now to prevent adding Alliance data to PVP quests since that was manually updated 7/17/23
local hasAva = false
if current_data[lib.quest_data_index.quest_series] >= lib.quest_series_type.quest_type_ad and current_data[lib.quest_data_index.quest_series] <= lib.quest_series_type.quest_type_ep then
hasAva = true
end
]]--
if newerAPI then
if rebuilt_data[index] == nil then rebuilt_data[index] = {} end
rebuilt_data[index] = current_data
end
end
end
LibQuestData_SavedVariables["location_info"] = rebuilt_locations
LibQuestData_SavedVariables["quest_info"] = rebuilt_data
for index, data in pairs(all_quest_names) do
if lib.quest_names[lib.effective_lang][index] then
if LibQuestData_SavedVariables["quest_names"][index] == lib.quest_names[lib.effective_lang][index] then
--internal.dm("Debug", "quest_name matched")
LibQuestData_SavedVariables["quest_names"][index] = nil
end
end
end
for index, data in pairs(all_quest_givers) do
npc_id = lib:get_npcids_table(data)
if npc_id then
if lib.quest_givers[lib.effective_lang][npc_id[1]] == data then
LibQuestData_SavedVariables["giver_names"][index] = nil
end
end
end
saved_reward_info = {}
for quest_id, rewards in pairs(all_reward_info) do
for index, reward_data in pairs(rewards) do
npc_id = lib:get_npcids_table(data)
-- when the value is 9
if reward_data == REWARD_TYPE_PARTIAL_SKILL_POINTS then
if not lib.quest_rewards_skilpoint[quest_id] then
saved_reward_info[quest_id] = rewards
end
end
end
end
LibQuestData_SavedVariables["reward_info"] = saved_reward_info
current_data = {}
local added = false
local in_range_missing = false
local in_range_good_data = false
local in_range_missing_giver = false
local quest_not_found
local total_count = 0