forked from wofsauge/External-Item-Descriptions
-
Notifications
You must be signed in to change notification settings - Fork 0
/
eid_api.lua
1817 lines (1647 loc) · 71.1 KB
/
eid_api.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
-- Init legacy global variables for other mods to hand over Descriptions
if not __eidItemDescriptions then
__eidItemDescriptions = {}
end
setmetatable(__eidItemDescriptions,
{ __newindex = function(_, k, v) EID:addCollectible(k, v) end })
if not __eidTrinketDescriptions then
__eidTrinketDescriptions = {}
end
setmetatable(__eidTrinketDescriptions,
{ __newindex = function(_, k, v) EID:addTrinket(k, v) end })
if not __eidCardDescriptions then
__eidCardDescriptions = {}
end
setmetatable(__eidCardDescriptions,
{ __newindex = function(_, k, v) EID:addCard(k, v) end })
if not __eidPillDescriptions then
__eidPillDescriptions = {}
end
setmetatable(__eidPillDescriptions,
{ __newindex = function(_, k, v) EID:addPill(k, v) end })
if not __eidItemTransformations then
__eidItemTransformations = {}
end
setmetatable(__eidItemTransformations,
{ __newindex = function(_, k, v) EID:assignTransformation("collectible", k, v) end })
if not __eidEntityDescriptions then
__eidEntityDescriptions = {}
end
setmetatable(__eidEntityDescriptions,
{ __newindex = function(_, k, v)
local entityID = {}
for id in string.gmatch(k, "([^.]+)") do
table.insert(entityID, id)
end
EID:addEntity(entityID[1], entityID[2], entityID[3], v[1], v[2])
end })
---------------------------------------------------------------------------
-------------------------Handle API Functions -----------------------------
local nullVector = Vector(0,0)
local game = Game()
local maxCardID = Card.NUM_CARDS - 1
local maxPillID = PillColor.NUM_PILLS - 1
local dynamicSpriteCache = {} -- used to store sprite objects of collectible icons etc.
-- Adds a description for a collectible. Optional parameters: itemName, language
function EID:addCollectible(id, description, itemName, language)
itemName = itemName or nil
language = language or "en_us"
local modName = EID._currentMod
-- Glitched Items exception so they don't have a mod name
if id > 4294960000 then modName = nil end
EID.descriptions[language].custom["5.100." .. id] = {id, itemName, description, modName}
end
-- Adds a description for a trinket. Optional parameters: itemName, language
function EID:addTrinket(id, description, itemName, language)
itemName = itemName or nil
language = language or "en_us"
EID.descriptions[language].custom["5.350." .. id] = {id, itemName, description, EID._currentMod}
end
-- Adds information about appending text and multiplying numbers in a modded trinket's Golden/Mom's Box description. All three variables are optional, set to ""/0 or nil to not include them
-- appendText: Text to be appended onto the description. Can be one string, or a table of two strings; one for doubling and one for tripling
-- numbersToMultiply: The number inside the text that should be multiplied. can be one number, or a table of numbers
-- maxMultiplier: is what tripling (Golden+Mom's Box) should multiply the numbers by (normally 3)
-- (If it's less than 2, it also applies to doubling)
-- Example: My modded trinket gives +0.5 range and when tripled, adds homing instead of tripling the range boost
-- EID:addGoldenTrinketMetadata(Isaac.GetTrinketIdByName("Cool Trinket"), {"", "Homing tears"}, 0.5, 2)
function EID:addGoldenTrinketMetadata(id, appendText, numbersToMultiply, maxMultiplier, language)
maxMultiplier = maxMultiplier or 3
language = language or "en_us"
if appendText == "" then appendText = nil
elseif type(appendText) == "string" then appendText = {appendText} end
if numbersToMultiply == 0 then numbersToMultiply = nil
elseif type(numbersToMultiply) == "number" then numbersToMultiply = {numbersToMultiply} end
EID.GoldenTrinketData[id] = {t = numbersToMultiply, mult = maxMultiplier, append = appendText and true}
if appendText then
EID.descriptions[language].goldenTrinketEffects[id] = { appendText[1], appendText[1], appendText[2] or appendText[1] }
end
end
-- Add a fully custom data table to the table of Golden Trinket effects
-- Check the comments above EID.GoldenTrinketData in eid_data.lua for some info about what is possible
-- You may also want to add text entries into EID.descriptions[languageCode].goldenTrinketEffects
function EID:addGoldenTrinketTable(id, dataTable)
EID.GoldenTrinketData[id] = dataTable
end
-- Adds a description for a card/rune. Optional parameters: itemName, language
function EID:addCard(id, description, itemName, language)
itemName = itemName or nil
language = language or "en_us"
EID.descriptions[language].custom["5.300." .. id] = {id, itemName, description, EID._currentMod}
end
-- Adds a metadata for a card. Used for Blank Card/Clear Rune. Optional parameters: isRune
-- Avalilable values for mimicCharge : 1~12
function EID:addCardMetadata(id, mimicCharge, isRune)
if isRune then
EID.blankCardHidden[id] = true
EID.runeIDs[id] = true
end
EID.cardMetadata[id] = {
mimiccharge = type(mimicCharge) == "number" and mimicCharge or -1
}
end
-- Adds a description for a pilleffect id. Optional parameters: itemName, language
function EID:addPill(id, description, itemName, language)
itemName = itemName or nil
language = language or "en_us"
EID.descriptions[language].custom["5.70." .. id+1] = {id+1, itemName, description, EID._currentMod}
end
-- Adds a metadata for a pilleffect. Used for Placebo/False PHD. Optional parameters: class
-- Avalilable values for mimicCharge : 1~12
-- For class value, "3-" ~ "3+" are available, although False PHD only cares for negative values.
-- "3-" : Major negative effect - Gives +0.6 Damage / "2-", "1-" : Minor negative effect - Spawns a Black Heart
function EID:addPillMetadata(id, mimicCharge, class)
EID.pillMetadata[id] = {
mimiccharge = type(mimicCharge) == "number" and mimicCharge or -1,
class = class or "0",
}
end
-- Adds a character specific description for the item "Birthright". Optional parameters: playerName, language
function EID:addBirthright(characterId, description, playerName, language)
playerName = playerName or nil
language = language or "en_us"
EID.descriptions[language].birthright[characterId + 1] = {playerName, "", description}
end
-- Creates a new transformation with a given unique name and a display name
function EID:createTransformation(uniqueName, displayName, language)
language = language or "en_us"
if EID.CustomTransformations[uniqueName] == nil then
EID.CustomTransformations[uniqueName] = {}
end
EID.CustomTransformations[uniqueName][language] = displayName
end
-- Assigns transformations to an entity (Adds to existing transformations)
-- valid target types: [collectible, trinket, card, pill, entity]
-- when type = entity, targetIdentifier must be in the format "ID.Variant.subtype". for any other type, it can just be the id
-- EXAMPLE: EID:assignTransformation("collectible", 1, "My Transformation")
function EID:assignTransformation(targetType, targetIdentifier, transformationString)
local entryID = EID:getIDVariantString(targetType)
if entryID ~= nil then
entryID = entryID.."."..targetIdentifier
else
entryID = targetIdentifier
end
EID:removeEntryFromString(EID.CustomTransformAssignments, entryID, transformationString)
if EID.CustomTransformAssignments[entryID] == nil then
EID.CustomTransformAssignments[entryID] = transformationString
else
EID.CustomTransformAssignments[entryID] = EID.CustomTransformAssignments[entryID]..","..transformationString
end
EID:removeEntryFromString(EID.CustomTransformRemovals, entryID, transformationString)
end
-- Try to automatically assign vanilla transformations to the entity
function EID:tryAutodetectTransformationsCollectible(collectibleID)
if not REPENTANCE then return end
local config = EID.itemConfig:GetCollectible(collectibleID)
local transformations = {}
transformations[EID.TRANSFORMATION.ANGEL] = config:HasTags(ItemConfig.TAG_ANGEL) or nil
transformations[EID.TRANSFORMATION.BOB] = config:HasTags(ItemConfig.TAG_BOB) or nil
transformations[EID.TRANSFORMATION.BOOKWORM] = config:HasTags(ItemConfig.TAG_BOOK) or nil
transformations[EID.TRANSFORMATION.CONJOINED] = config:HasTags(ItemConfig.TAG_BABY) or nil
transformations[EID.TRANSFORMATION.GUPPY] = config:HasTags(ItemConfig.TAG_GUPPY) or nil
transformations[EID.TRANSFORMATION.LEVIATHAN] = config:HasTags(ItemConfig.TAG_DEVIL) or nil
transformations[EID.TRANSFORMATION.LORD_OF_THE_FLIES] = config:HasTags(ItemConfig.TAG_FLY) or nil
transformations[EID.TRANSFORMATION.MOM] = config:HasTags(ItemConfig.TAG_MOM) or nil
transformations[EID.TRANSFORMATION.MUSHROOM] = config:HasTags(ItemConfig.TAG_MUSHROOM) or nil
transformations[EID.TRANSFORMATION.POOP] = config:HasTags(ItemConfig.TAG_POOP) or nil
transformations[EID.TRANSFORMATION.SPIDERBABY] = config:HasTags(ItemConfig.TAG_SPIDER) or nil
transformations[EID.TRANSFORMATION.SPUN] = config:HasTags(ItemConfig.TAG_SYRINGE) or nil
-- these dont have a tag : ADULT, STOMPY, SUPERBUM
for k, _ in pairs(transformations) do
EID:assignTransformation("collectible", collectibleID, k)
end
end
-- Removes a transformation of an entity
-- valid target types: [collectible, trinket, card, pill, entity]
-- when type = entity, targetIdentifier must be in the format "ID.Variant.subtype". for any other type, it can just be the id
-- EXAMPLE: EID:removeTransformation("collectible", 1, "My Transformation")
function EID:removeTransformation(targetType, targetIdentifier, transformationString)
local entryID = EID:getIDVariantString(targetType)
if entryID ~= nil then
entryID = entryID.."."..targetIdentifier
else
entryID = targetIdentifier
end
EID:removeEntryFromString(EID.CustomTransformRemovals, entryID, transformationString)
if EID.CustomTransformRemovals[entryID] == nil then
EID.CustomTransformRemovals[entryID] = transformationString
else
EID.CustomTransformRemovals[entryID] = EID.CustomTransformRemovals[entryID]..","..transformationString
end
EID:removeEntryFromString(EID.CustomTransformAssignments, entryID, transformationString)
end
-- Removes a given value from the string inside a table. Example: "1,2,3", removing 2 will return "1,3"
function EID:removeEntryFromString(sourceTable, entryKey, entryValue)
if sourceTable[entryKey] == nil then return end
local newEntry = ""
for str in string.gmatch(sourceTable[entryKey], "([^,]+)") do
local addToList = true
for removeStr in string.gmatch(entryValue, "([^,]+)") do
if removeStr == str then
addToList = false
end
end
if addToList then
newEntry = newEntry..","..str
end
end
newEntry = newEntry:sub(2)
if newEntry == "" then newEntry = nil end
sourceTable[entryKey] = newEntry
end
-- Adds a description for a an Entity. Optional parameters: language, transformations
-- when subtype is -1 or empty, it will affect all subtypes of that entity
function EID:addEntity(id, variant, subtype, entityName, description, language)
subtype = subtype or nil
language = language or "en_us"
if id == EntityType.ENTITY_EFFECT then
EID.effectList[variant] = true
end
EID.descriptions[language].custom[id .. "." .. variant .. "." .. subtype] = {
subtype,
entityName,
description
}
end
-- Adds a new icon object with the shortcut defined in the "shortcut" variable (e.g. "{{shortcut}}" = your icon)
-- Shortcuts are case Sensitive! Shortcuts can be overriden with this function to allow for full control over everything
-- Setting "animationFrame" to -1 will play the animation. The spriteObject needs to be of class Sprite() and have an .anm2 loaded
-- default values: leftOffset= -1 , topOffset = 0
function EID:addIcon(shortcut, animationName, animationFrame, width, height, leftOffset, topOffset, spriteObject)
leftOffset = leftOffset or -1
topOffset = topOffset or 0
EID.InlineIcons[shortcut] = {animationName, animationFrame, width, height, leftOffset, topOffset, spriteObject}
end
-- Adds a new color object with the shortcut defined in the "shortcut" variable (e.g. "{{shortcut}}" = your color)
-- Shortcuts are case Sensitive! Shortcuts can be overriden with this function to allow for full control over everything
-- Define a callback to let it be called when interpreting the color-markup. define a kColor otherwise for a simple color change
function EID:addColor(shortcut, kColor, callback)
if callback ~= nil then
EID.InlineColors[shortcut] = callback
else
EID.InlineColors[shortcut] = kColor
end
end
-- Overrides all potentially displayed texts and permanently displays the given texts. Can be turned of again using the "EID:hidePermanentText()" function
function EID:displayPermanentText(descriptionObject, permName1, permName2)
descriptionObject.PermanentTextEnglish = EID:getDescriptionEntryEnglish(permName1, permName2)
EID.permanentDisplayTextObj = descriptionObject
EID.isDisplayingPermanent = true
end
-- Hides permanently displayed text objects if they exist.
function EID:hidePermanentText()
EID.permanentDisplayTextObj = nil
EID.isDisplayingPermanent = false
end
-- function to turn entity type names into actual ingame ID.Variant pairs
function EID:getIDVariantString(typeName)
if typeName == "collectible" or typeName == "collectibles" then return "5.100"
elseif typeName == "trinket" or typeName == "trinkets" then return "5.350"
elseif typeName == "card" or typeName == "cards" then return "5.300"
elseif typeName == "pill" or typeName == "pills" or typeName == "horsepills" or typeName == "horsepill" then return "5.70"
elseif typeName == "sacrifice" then return "-999.-1"
elseif typeName == "dice" then return "1000.76"
end
return nil
end
-- function to turn entity typ and variants into their EID table-name
function EID:getTableName(Type, Variant, SubType)
local idString = Type.."."..Variant
if idString == "5.100" then return "collectibles"
elseif idString == "5.350" then return "trinkets"
elseif idString == "5.300" then return "cards"
elseif idString == "5.70" then
if SubType <2049 then
return "pills"
else
return "horsepills"
end
elseif idString == "-999.-1" then return "sacrifice"
elseif idString == "1000.76" then return "dice"
else return "custom"
end
end
-- Loads a given font from a given file path and use it to render text
function EID:loadFont(fontFileName)
EID.font:Load(fontFileName)
EID.font:SetMissingCharacter(2)
if not EID.font:IsLoaded() then
Isaac.DebugString("EID - ERROR: Could not load font from '" .. EID.modPath .. "resources/font/default.fnt" .. "'")
return false
end
return true
end
-- Returns if EID is displaying text right now
function EID:isDisplayingText()
return EID.isDisplaying
end
-- Returns true, if curse of blind is active
function EID:hasCurseBlind()
return game:GetLevel():GetCurses() & LevelCurse.CURSE_OF_BLIND > 0
end
-- returns the current text position
function EID:getTextPosition()
local posVector = Vector(EID.UsedPosition.X, EID.UsedPosition.Y)
-- Only apply position modifiers when not in Local Mode
if EID.CurrentScaleType == "Size" then
for _, modifier in pairs(EID.PositionModifiers) do
posVector = posVector + modifier
end
end
return posVector
end
-- Adds a text position modifier Vector, which will be applied to the text position variable
-- Useful to add small offsets. For example: for schoolbag HUD
function EID:addTextPosModifier(identifier, modifierVector)
EID.PositionModifiers[identifier] = modifierVector
end
-- Removes a text position modifier Vector
-- Useful to remove small offsets. For example: for schoolbag HUD
function EID:removeTextPosModifier(identifier)
EID.PositionModifiers[identifier] = nil
end
-- Changes the initial position of all eid descriptions
-- Useful to totally alter and override the current initial Overlay position
function EID:alterTextPos(newPosVector)
EID.UsedPosition = newPosVector
end
-- returns the entity that is currently described. returns last described entity if currently not displaying text
function EID:getLastDescribedEntity()
return EID.lastDescriptionEntity
end
-- Appends a given string to the description of a given Description object
function EID:appendToDescription(descObj, appendString)
descObj.Description = descObj.Description..appendString
end
-- returns the description object of a specific entity
-- falls back to english if the objID isnt available
function EID:getDescriptionObjByEntity(entity)
return EID:getDescriptionObj(entity.Type, entity.Variant, entity.SubType, entity)
end
-- returns the description object of the specified entity
-- falls back to english if the objID isnt available
-- entity is optional
function EID:getDescriptionObj(Type, Variant, SubType, entity, checkModifiers)
local description = {}
description.ObjType = Type
description.ObjVariant = Variant
description.ObjSubType = SubType
description.fullItemString = Type.."."..Variant.."."..SubType
description.Name = EID:getObjectName(Type, Variant, SubType)
description.Entity = entity or nil
local tableEntry = EID:getDescriptionData(Type, Variant, SubType)
description.Description = tableEntry and tableEntry[3] or EID:getXMLDescription(Type, Variant, SubType)
description.Transformation = EID:getTransformation(Type, Variant, SubType)
description.ModName = tableEntry and tableEntry[4]
description.Quality = EID:getObjectQuality(description)
description.Icon = EID:getObjectIcon(description)
if checkModifiers ~= false then
for _,modifier in ipairs(EID.DescModifiers) do
local result = modifier.condition(description)
if type(result) == "table" then
for _,callback in ipairs(result) do
-- If the modifier loads a different description obj (which also goes through the modifier checks), we should stop our checks so text doesn't get printed twice
if description.ObjSubType ~= SubType then break end
description = callback(description)
end
elseif result then
description = modifier.callback(description)
end
end
end
return description
end
-- returns description Object from the legacy mod descriptions if they exist
function EID:getLegacyModDescription(Type, Variant, SubType)
local tableName = EID:getTableName(Type, Variant, SubType)
local customDesc = __eidEntityDescriptions[Type.."."..Variant.."."..SubType]
if tableName == "collectibles" and __eidItemDescriptions[SubType] then
return {"","",__eidItemDescriptions[SubType]}
elseif tableName == "trinkets" and __eidTrinketDescriptions[SubType] then
return{"","", __eidTrinketDescriptions[SubType]}
elseif tableName == "cards" and __eidCardDescriptions[SubType] then
return {"","",__eidCardDescriptions[SubType]}
elseif tableName == "pills" and __eidPillDescriptions[SubType-1] then
return {"","",__eidPillDescriptions[SubType-1]}
elseif customDesc~=nil then
return {"", customDesc[1], customDesc[2]}
end
return nil
end
-- returns the specified object table in the current language.
-- falls back to english if it doesnt exist, unless specified otherwise
function EID:getDescriptionEntry(objTable, objID, noFallback)
if not objID then
return EID.descriptions[EID:getLanguage()][objTable] or EID.descriptions["en_us"][objTable]
else
local translatedTable = EID.descriptions[EID:getLanguage()][objTable]
if noFallback then return translatedTable and translatedTable[objID]
else return (translatedTable and translatedTable[objID]) or (EID.descriptions["en_us"][objTable] and EID.descriptions["en_us"][objTable][objID]) end
end
end
function EID:getDescriptionEntryEnglish(objTable, objID)
if not objID then
return EID.descriptions["en_us"][objTable]
else
return EID.descriptions["en_us"][objTable] and EID.descriptions["en_us"][objTable][objID]
end
end
-- returns the description data table related to a given id, variant and subtype
-- falls back to english if it doesnt exist
function EID:getDescriptionData(Type, Variant, SubType)
local fullString = Type.."."..Variant
local adjustedID = EID:getAdjustedSubtype(Type, Variant, SubType)
local moddedDesc = EID:getDescriptionEntry("custom", fullString.."."..adjustedID)
local tableName = EID:getTableName(Type, Variant, SubType)
local legacyModdedDescription = EID:getLegacyModDescription(Type, Variant, adjustedID)
local defaultDesc = EID:getDescriptionEntry(tableName, adjustedID)
return moddedDesc or legacyModdedDescription or defaultDesc
end
-- Returns an adjusted SubType id for special cases like Horse Pills and Golden Trinkets
function EID:getAdjustedSubtype(Type, Variant, SubType)
local tableName = EID:getTableName(Type, Variant, SubType)
if tableName == "trinkets" then
if SubType > 32768 then
return SubType - 32768
end
elseif tableName == "sacrifice" then
return math.min(#EID.descriptions["en_us"].sacrifice, SubType)
elseif tableName == "pills" or tableName == "horsepills" then
-- The effect of a pill varies depending on what player is looking at it in co-op
-- EID.pillPlayer is a way to recheck a pill for what different players will turn it into
local player = EID.pillPlayer or EID.player
if REPENTANCE and SubType % PillColor.PILL_GIANT_FLAG == PillColor.PILL_GOLD then
return 9999
end
local pool = game:GetItemPool()
if REPENTANCE and player:GetPlayerType() == PlayerType.PLAYER_THESOUL_B then
SubType = pool:GetPillEffect(SubType, player:GetOtherTwin() or player) + 1
else SubType = pool:GetPillEffect(SubType, player) + 1 end
end
return SubType
end
-- Get the transformation uniqueName / ID of a given entity
-- Example: EID:getTransformation(5,100,34) will return "12" which is the id for Bookworm
function EID:getTransformation(id, variant, subType)
local adjustedSubtype = EID:getAdjustedSubtype(id, variant, subType)
local entityString = id.."."..variant.."."..adjustedSubtype
local listToTest = ""
local default = EID.EntityTransformations[entityString]
if default~= nil then listToTest = default end
if id == 5 and variant == 100 then
local customLegacy = __eidItemTransformations[adjustedSubtype]
if customLegacy~= nil then listToTest = listToTest..","..customLegacy end
end
local custom = EID.CustomTransformAssignments[entityString]
if custom~= nil then listToTest = listToTest..","..custom end
local transformationList = ""
local removedList = EID.CustomTransformRemovals[entityString]
for transform in string.gmatch(listToTest, "([^,]+)") do
local isRemoved = false
if removedList ~= nil then
for removedTransform in string.gmatch(removedList, "([^,]+)") do
if transform == removedTransform then isRemoved = true end
end
end
if not isRemoved then transformationList = transformationList..","..transform end
end
return transformationList
end
--Get the name of the given transformation by its uniqueName / ID
function EID:getTransformationName(id)
local str = "Custom"
if tonumber(id) == nil then
-- get translated custom name
local customTransform = EID.CustomTransformations[id]
if customTransform ~= nil then
return customTransform[EID:getLanguage()] or customTransform["en_us"] or id
end
return id
end
return EID:getDescriptionEntry("transformations")[tonumber(id) + 1] or str
end
-- tries to get the ingame name of an item based on its ID
function EID:getObjectName(Type, Variant, SubType)
local tableName = EID:getTableName(Type, Variant, SubType)
local tableEntry = EID:getDescriptionData(Type, Variant, SubType)
local name = nil
if tableEntry ~= nil then
if tableEntry[2] ~= nil and tableEntry[2] ~= "" and tableEntry[2] ~= EID.descriptions["en_us"][tableName][SubType] then
name = tableEntry[2]
end
end
if tableName == "collectibles" then
if EID.itemConfig:GetCollectible(SubType) == nil then return Type.."."..Variant.."."..SubType end
local vanillaName = EID.itemConfig:GetCollectible(SubType).Name
return name or (not string.find(vanillaName, "^#") and vanillaName) or (EID.descriptions["en_us"][tableName][SubType] and EID.descriptions["en_us"][tableName][SubType][2]) or vanillaName
elseif tableName == "trinkets" then
local adjustedSubtype = EID:getAdjustedSubtype(Type, Variant, SubType)
local vanillaName = EID.itemConfig:GetTrinket(adjustedSubtype).Name
return name or (not string.find(vanillaName, "^#") and vanillaName) or EID.descriptions["en_us"][tableName][adjustedSubtype][2] or vanillaName
elseif tableName == "cards" then
local vanillaName = EID.itemConfig:GetCard(SubType).Name
return name or (not string.find(vanillaName, "^#") and vanillaName) or EID.descriptions["en_us"][tableName][SubType][2] or vanillaName
elseif tableName == "pills" or tableName == "horsepills" then
local adjustedSubtype = EID:getAdjustedSubtype(Type, Variant, SubType)
return EID:getPillName(adjustedSubtype, tableName == "horsepills")
elseif tableName == "sacrifice" then
return EID:getDescriptionEntry("sacrificeHeader").." ("..SubType.."/"..#EID.descriptions["en_us"].sacrifice..")"
elseif tableName == "dice" then
return EID:getDescriptionEntry("diceHeader").." ("..SubType..")"
elseif tableName == "custom" then
local xmlName = EID.XMLEntityNames[Type.."."..Variant] or EID.XMLEntityNames[Type.."."..Variant.."."..SubType]
return name or xmlName or Type.."."..Variant.."."..SubType
end
return Type.."."..Variant.."."..SubType
end
-- returns the name of a pill based on the pilleffect id
function EID:getPillName(pillID, isHorsepill)
local moddedDesc = EID:getDescriptionEntry("custom", "5.70."..pillID)
local legacyModdedDescription = EID:getLegacyModDescription(5, 70, pillID)
local tableName = isHorsepill and "horsepills" or "pills"
local defaultDesc = EID:getDescriptionEntry(tableName, pillID)
local name = moddedDesc or legacyModdedDescription or defaultDesc
local vanillaName = ""
if pillID == 9999 then
vanillaName = "Golden Pill" -- only used for languages that haven't defined a Golden Pill name
else
vanillaName = EID.itemConfig:GetPillEffect(pillID - 1).Name
end
name = name and name[2] or (not string.find(vanillaName, "^#") and vanillaName) or EID.descriptions["en_us"][tableName][pillID][2] or vanillaName
return string.gsub(name,"I'm Excited!!!","I'm Excited!!") -- prevent markup trigger
end
-- tries to get the ingame description of an object, based on their description in the XML files
function EID:getXMLDescription(Type, Variant, SubType)
local tableName = EID:getTableName(Type, Variant, SubType)
local desc= nil
if SubType == 0 then return "(no description available)" end
if tableName == "collectibles" then
desc = EID.itemConfig:GetCollectible(SubType).Description
elseif tableName == "trinkets" then
desc = EID.itemConfig:GetTrinket(SubType).Description
elseif tableName == "cards" then
desc = EID.itemConfig:GetCard(SubType).Description
end
return desc or "(no description available)"
end
-- check if an entity is part of the describable entities
function EID:hasDescription(entity)
if entity and EID:IsGridEntity(entity) then
if entity and EID.GridEntityWhitelist[entity:GetType()] then
for _, func in ipairs(EID.GridEntityWhitelist[entity:GetType()]) do
if func(entity) then
return true
end
end
end
return false
end
local isAllowed = false
local entityString = entity.Type .. "." .. entity.Variant .. "." .. entity.SubType
if EID.IgnoredEntities[entity.Type .. "." .. entity.Variant] or EID.IgnoredEntities[entityString] then return false end
if EID.Config["EnableEntityDescriptions"] and EID:getTableName(entity.Type, entity.Variant, entity.SubType) == "custom" then
isAllowed = __eidEntityDescriptions[entityString] ~= nil
isAllowed = isAllowed or EID:getDescriptionData(entity.Type, entity.Variant, entity.SubType) ~= nil
isAllowed = isAllowed or type(entity:GetData()["EID_Description"]) ~= type(nil)
end
if entity.Type == EntityType.ENTITY_PICKUP then
isAllowed = isAllowed or (entity.Variant == PickupVariant.PICKUP_COLLECTIBLE and EID.Config["DisplayItemInfo"])
isAllowed = isAllowed or (entity.Variant == 110 and entity:GetSprite():IsPlaying("Idle") and EID.Config["DisplayItemInfo"]) -- Broken Shovel
isAllowed = isAllowed or (entity.Variant == PickupVariant.PICKUP_TRINKET and EID.Config["DisplayTrinketInfo"])
isAllowed = isAllowed or (entity.Variant == PickupVariant.PICKUP_TAROTCARD and EID.Config["DisplayCardInfo"])
isAllowed = isAllowed or (entity.Variant == PickupVariant.PICKUP_PILL and EID.Config["DisplayPillInfo"])
return isAllowed and (entity.SubType > 0 or
-- For Flip descriptions, allow 5.100.0 pedestals to have descriptions under VERY specific criteria!
(REPENTANCE and EID:getEntityData(entity, "EID_FlipItemID") and EID:PlayersHaveCollectible(CollectibleType.COLLECTIBLE_FLIP)))
end
if entity.Type == 6 and entity.Variant == 16 and EID.Config["DisplayCraneInfo"] and REPENTANCE then
isAllowed = not entity:GetSprite():IsPlaying("Broken") and not entity:GetSprite():IsPlaying("Prize") and not entity:GetSprite():IsPlaying("OutOfPrizes") and (EID.CraneItemType[entity.InitSeed.."Drop"..entity.DropSeed] or EID.CraneItemType[tostring(entity.InitSeed)])
end
if entity.Type == 1000 then
if (entity.Variant == 161 and entity.SubType <= 2) or (entity.Variant == EffectVariant.DICE_FLOOR and EID.Config["DisplayDiceInfo"]) then
isAllowed = true
end
end
return isAllowed
end
-- Replaces shorthand-representations of a character with the internal reference
function EID:replaceShortMarkupStrings(text)
for _, pair in ipairs(EID.TextReplacementPairs) do
text = string.gsub(text, pair[1], pair[2])
end
return text
end
-- Replaces name markup objects with the actual name
function EID:replaceNameMarkupStrings(text)
for word in string.gmatch(text, "{{Name.-}}") do
local strTrimmed = string.gsub(word, "{{Name(.-)}}", function(a) return a end)
local indicator = string.sub(strTrimmed, 1, 1)
local id = tonumber(string.sub(strTrimmed, 2, -1))
local name = ""
if tonumber(indicator) then
local entityID = {}
for e in string.gmatch(strTrimmed, "([^.]*)") do
table.insert(entityID, tonumber(e))
end
name = EID:getObjectName(entityID[1], entityID[2], entityID[3])
elseif indicator == "C" then -- Collectible
name = "{{Collectible"..id.."}}"..EID:getObjectName(5, 100, id)
elseif indicator == "T" then -- Trinket
name = "{{Trinket"..id.."}}"..EID:getObjectName(5, 350, id)
elseif indicator == "P" then -- Pills
name = "{{Pill"..id.."}}"..EID:getPillName(id, false)
elseif indicator == "K" then -- Card
name = "{{Card"..id.."}}"..EID:getObjectName(5, 300, id)
end
text = string.gsub(text, word, "{{ColorYellow}}"..name.."{{CR}}", 1)
end
return text
end
-- Generates a string with the defined pixel-length using a custom 1px wide character
-- This will only work for this specific custom font
function EID:generatePlaceholderString(length)
return string.rep("¤", length)
end
-- Returns the inlineIcon object of a given Iconstring
-- can be used to validate an iconstring
function EID:getIcon(str)
if str == nil then
return EID.InlineIcons["ERROR"]
end
local strTrimmed = string.gsub(str,"{{(.-)}}",function(a) return a end )
if #strTrimmed <= #str then
local itemIconObj = EID:createItemIconObject(strTrimmed)
if itemIconObj then return itemIconObj end
if type(EID.InlineIcons[strTrimmed]) == "function" then
return EID.InlineIcons[strTrimmed](str) or EID.InlineIcons["ERROR"]
end
return EID.InlineIcons[strTrimmed] or EID.InlineIcons["ERROR"]
else
return EID.InlineIcons["ERROR"]
end
end
-- Tries to read special markup used to generate icons for all Collectibles/Trinkets and the default Cards/Pills
-- Returns an inlineIcon Object or nil if no parsing was possible
function EID:createItemIconObject(str)
local collID,numReplace = string.gsub(str, "Collectible", "")
local item = nil
if numReplace > 0 and collID ~= "" and tonumber(collID) ~= nil then
item = EID.itemConfig:GetCollectible(tonumber(collID))
end
local trinketID,numReplace2 = string.gsub(str, "Trinket", "")
if numReplace2 > 0 and trinketID ~= "" and tonumber(trinketID) ~= nil then
item = EID.itemConfig:GetTrinket(tonumber(trinketID))
end
local cardID,numReplace3 = string.gsub(str, "Card", "")
if numReplace3 > 0 and cardID ~= "" and tonumber(cardID) ~= nil then
if tonumber(cardID) > maxCardID then return EID.InlineIcons[str] or EID.InlineIcons["Card"] end
return {"Cards", tonumber(cardID)-1, 8, 8, 0, 1, EID.CardPillSprite}
end
local pillID,numReplace4 = string.gsub(str, "Pill", "")
if numReplace4 > 0 and pillID ~= "" and tonumber(pillID) ~= nil then
if tonumber(pillID) > maxPillID then return EID.InlineIcons[str] or EID.InlineIcons["Pill"] end
return {"Pills", tonumber(pillID)-1, 9, 8, 0, 1, EID.CardPillSprite}
end
if item == nil then
return nil
end
if dynamicSpriteCache[str] then
return dynamicSpriteCache[str]
else
local spriteDummy = Sprite()
spriteDummy:Load("gfx/eid_inline_icons.anm2", true)
spriteDummy:ReplaceSpritesheet(1, item.GfxFileName)
spriteDummy:LoadGraphics()
local newDynamicSprite = {"ItemIcon", 0, 11, 8, -2, -2, spriteDummy}
dynamicSpriteCache[str] = newDynamicSprite
return newDynamicSprite
end
end
-- Returns the icon for a given transformation name or ID
function EID:getTransformationIcon(str)
if str == nil then
return EID.InlineIcons["ERROR"]
end
if tonumber(str) ~= nil then
str = EID.descriptions["en_us"].transformations[tonumber(str + 1)]
end
local transformSprite = EID:getIcon(str:gsub(" ", ""))
if transformSprite[1] == "ERROR" then
transformSprite = EID:getIcon("CustomTransformation")
end
return transformSprite
end
-- Returns the width of a given string in Pixels
function EID:getStrWidth(str)
return EID.font:GetStringWidthUTF8(str)
end
-- Searches thru the given string and replaces Iconplaceholders with icons.
-- Returns 2 values. the string without the placeholders but with an accurate space between lines. and a table of all Inline Sprites
function EID:filterIconMarkup(text, textPosX, textPosY)
local spriteTable = {}
for word in string.gmatch(text, "{{.-}}") do
local textposition = string.find(text, word)
local callback = EID._NextIconModifier
EID._NextIconModifier = nil
local lookup = EID:getIcon(word)
local preceedingTextWidth = EID:getStrWidth(string.sub(text, 0, textposition - 1)) * EID.Scale
table.insert(spriteTable, {lookup, preceedingTextWidth, callback})
text = string.gsub(text, word, EID:generatePlaceholderString(lookup[3]), 1)
end
return text, spriteTable
end
--renders a list of given inline sprite objects returned by the "EID:filterIconMarkup()" function
-- Table entry format: {EID.InlineIcons Object, Width of text preceeding the icon}
function EID:renderInlineIcons(spriteTable, posX, posY)
for _, sprite in ipairs(spriteTable) do
local Xoffset = sprite[1][5] or -1
local Yoffset = sprite[1][6] or 0
local spriteObj = (type(sprite[1][7]) == "function" and sprite[1][7]()) or sprite[1][7] or EID.InlineIconSprite
if sprite[1][2] >= 0 then
spriteObj:SetFrame(sprite[1][1], sprite[1][2])
elseif not spriteObj:IsPlaying(sprite[1][1]) or spriteObj:IsFinished(sprite[1][1]) then
spriteObj:Play(sprite[1][1],true)
else
spriteObj:Update()
end
EID:renderIcon(spriteObj, posX + sprite[2] + Xoffset * EID.Scale, posY + Yoffset * EID.Scale, sprite[3], sprite[1][1], sprite[1][2])
end
end
-- helper function to render Icons in specific EID settins
function EID:renderIcon(spriteObj, posX, posY, callback, animName, animFrame)
spriteObj.Scale = Vector(EID.Scale, EID.Scale)
spriteObj.Color = Color(1, 1, 1, EID.Config["Transparency"], 0, 0, 0)
if callback then
callback(spriteObj)
end
if EID.CachingDescription then
table.insert(EID.CachedIcons[#EID.CachedIcons], {spriteObj, posX, posY, callback, animName, animFrame})
end
spriteObj:Render(Vector(posX, posY), nullVector, nullVector)
end
-- Returns the icon used for the bulletpoint. It will look at the first word in the given string.
-- Also returns the first word if it was rejected (so it can be removed from the line)
function EID:handleBulletpointIcon(text)
local firstWord = EID:removeColorMarkup(string.match(text, "([^%s]+)"))
if EID:getIcon(firstWord) ~= EID.InlineIcons["ERROR"] and string.find(firstWord, "{{.-}}")~=nil then
if not EID.Config["StatAndPickupBulletpoints"] and EID.StatPickupBulletpointBlacklist[firstWord] then
return "\007", firstWord
end
return firstWord
end
return "\007"
end
-- Gets a KColor from a Markup-string (example Input: "{{ColorText}}")
-- Returns the KColor object and a boolean value indicating if the given string was a color markup or not
local colorFunc = nil
function EID:getColor(str, baseKColor)
local color = baseKColor
local isColorMarkup = false
if str ~= nil then
local strTrimmed = string.gsub(str,"{{(.-)}}",function(a) return a end, 1)
if #strTrimmed <= #str then
if type(EID.InlineColors[strTrimmed]) == "function" then
colorFunc = EID.InlineColors[strTrimmed]
color = EID.InlineColors[strTrimmed](color)
else
if EID.InlineColors[strTrimmed] then colorFunc = nil end
color = EID.InlineColors[strTrimmed] or color
end
isColorMarkup = type(EID.InlineColors[strTrimmed]) ~= type(nil)
end
end
color = EID:copyKColor(color)
color.Alpha = math.min(color.Alpha, EID.Config["Transparency"])
return color, isColorMarkup
end
-- Filters a given string and looks for Colormarkup. Splits the text into subsections limited by them.
-- Returns: Table of subsections of the text, their respective KColor, and the width of the subsection
function EID:filterColorMarkup(text, baseKColor)
local textPartsTable = {}
local lastColor = baseKColor
local lastFunc = colorFunc
local lastPosition = 0
for word in string.gmatch(text, "{{.-}}") do
local textposition = string.find(text, word)
local lookup, isColor = EID:getColor(word, lastColor)
if isColor then
local preceedingText = string.sub(text, lastPosition, textposition - 1)
local preceedingTextWidth = EID:getStrWidth(preceedingText) * EID.Scale
lastPosition = textposition
table.insert(textPartsTable, {preceedingText, lastColor, preceedingTextWidth, lastFunc})
lastColor = lookup
lastFunc = colorFunc
text = string.gsub(text, word, "", 1)
end
end
table.insert(textPartsTable, {string.sub(text, lastPosition), lastColor, 0, lastFunc})
return textPartsTable
end
-- A simple function to remove color markup, to preserve bulletpoint icons after start-of-line color markup
function EID:removeColorMarkup(text)
for word in string.gmatch(text, "{{Color.-}}") do
text = string.gsub(text, word, "", 1)
end
return text
end
-- A simple function to replace all markup {{ }} with placeholder strings, to use in fitTextToWidth
function EID:replaceAllMarkupWithSpaces(text, checkBulletpoint)
if checkBulletpoint then
-- Check for the text to just be a bulletpoint icon, which should be considered as zero width
-- (fixTextToWidth uses this function one word at a time)
if EID:getIcon(text:gsub(" ", "")) ~= EID.InlineIcons["ERROR"] and string.find(text, "{{.-}}")~=nil then
return ""
end
end
for word in string.gmatch(text, "{{.-}}") do
local lookup = EID:getIcon(word)
if lookup[1] ~= "ERROR" then
text = string.gsub(text, word, EID:generatePlaceholderString(lookup[3]), 1)
else
text = string.gsub(text, word, "", 1)
end
end
return text
end
-- Fits a given string to a specific width
-- returns the string as a table of lines
function EID:fitTextToWidth(str, textboxWidth, breakUtf8Chars)
local formattedLines = {}
local curLength = 0
local text = {}
-- the first word we run into might actually be a bulletpoint icon, which should be zero width
local isBulletpoint = true
local cursor = 1
local word_begin_index = 1
local byte = string.byte -- for speed up
local sub = string.sub
local byte_space = string.byte(' ')
while cursor <= #str do
-- ascii word: 0x0xxxxxxx
-- utf8 word (sequence): 0x11xxxxxx 0x10xxxxxx 0x10xxxxxx ... 0x10xxxxxx
-- see https://en.wikipedia.org/wiki/UTF-8
-- we can only break after space, or before 0x11xxxxxx
local cur, next = byte(str,cursor), byte(str,cursor+1)
if
-- cond#1: we can break at the end of string
cursor == #str or
-- cond#2: we can break after space
cur == byte_space or
-- handle utf8 characters
(breakUtf8Chars and(
-- cond#3: we can break if the next character is 0x11xxxxxx
((next & 0xC0) == 0xC0) or
-- cond#4: we can also break if the current is 0x10xxxxxx while the next is ascii but not space
(((cur & 0xC0) == 0x80) and (next~= byte_space and (next & 0x80) == 0x00))
))
then
--[[
-------------------ascii only---------------
word will be separated by spaces.
wordA |wordB |wordC |^%@&Q%#^&#@!! |aksldj
↑
cond#2
we may break after every space.
spaces | | | | | |woops
↑
cond#2
-------------------UTF8 only----------------
word is 0x11xxxxxx followed by several 0x10xxxxxx until the next 0x11xxxxxx (not included)
你|好|,|世|界|!|↑|↑|↑|↑
↑
cond#3
byte data of a word:
[0x11xxxxxx,0x10xxxxxx,0x10xxxxxx, (next is 0x11xxxxxx, cond#3)]
--------------------ascii->utf8------------
utf8 word must start with 0x11xxxxxx, so cond#3 split before it.
word|世|界
| ↑
↑ cond#3
cond#3
world |means |世|界
↑
cond#2
-------------------utf8->ascii--------------
世|界|是|world
| ↑
↑ cond#4
cond#3
世|界|是 |world
| ↑
↑ cond#2
cond#3
]]
-- we can break after str[cursor]
local word = sub(str, word_begin_index, cursor)
local wordFiltered = EID:replaceAllMarkupWithSpaces(word, isBulletpoint)
isBulletpoint = false
local wordLength = EID:getStrWidth(wordFiltered)
if curLength + wordLength <= textboxWidth or curLength < 17 then
table.insert(text, word)
curLength = curLength + wordLength
else
table.insert(formattedLines, table.concat(text))
text = { word }
curLength = wordLength
end
-- next word starts here
word_begin_index = cursor + 1
end
cursor = cursor + 1
end