Advertisement
Guest User

\Baldurs Gate 3\Data\Mods\RevealingAuraSE\ScriptExtender\Lua\Server\RevealingAura.lua

a guest
Sep 23rd, 2024
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.14 KB | None | 0 0
  1. -- USER CONFIGURABLE VARIABLES
  2.  
  3. AURA_RADIUS = 25 -- in meters
  4. VALUE_THRESHOLD = 25 -- gold
  5. WEIGHT_THRESHOLD = 2 -- grams
  6.  
  7. -- performance options, if needed
  8. UPDATE_INTERVAL = 250 -- milliseconds
  9. PHASE_DIVISOR = 4 -- look for new items every nth update
  10.  
  11. -- END OF USER CONFIGURABLE VARIABLES
  12.  
  13.  
  14. -- GLOBAL VARIABLES
  15. toggle = false
  16. toggle_tiny = false
  17. toggle_container = false
  18. toggle_default = false
  19. toggle_stealing = false
  20.  
  21. -- CONSTANTS
  22. PING_TIMER = "REVEALING_AURA_PING"
  23. EFFECT_IGNORE = -2
  24. EFFECT_UNDEFINED = -1
  25. EFFECT_NONE = 0
  26. EFFECT_DEFAULT = 1
  27. EFFECT_TINY = 2
  28. EFFECT_UNIMPORTANT = 3
  29. EFFECT_CONTAINER = 4
  30. EFFECT_CONTAINER_VALUABLE = 5
  31. EFFECT_CONTAINER_UNOPENED = 6
  32. EFFECT_VALUABLE = 7
  33. EFFECT_LOCKED = 8
  34. EFFECT_STEALING = 9
  35. EFFECT_SWD = 10
  36.  
  37. EFFECTS = {
  38. [EFFECT_DEFAULT] = "2b440364-c5b7-4243-d3a6-0684052f4d89",
  39. [EFFECT_TINY] = "1ca4cbbb-1738-4dae-85cd-b63e4d3d0689",
  40. [EFFECT_UNIMPORTANT] = "34e00416-686c-48d9-a17c-525d07dacb5d",
  41. [EFFECT_CONTAINER] = "328c4552-ed80-4d3a-8481-fbc036e97750",
  42. [EFFECT_CONTAINER_VALUABLE] = "770335e9-ad2c-453a-972c-74a116be94b3",
  43. [EFFECT_CONTAINER_UNOPENED] = "b0a765e2-d287-4e92-8740-4cd78dc63d6d",
  44. [EFFECT_VALUABLE] = "770335e9-ad2c-453a-972c-74a116be94b3",
  45. [EFFECT_LOCKED] = "8d965c39-6e1e-467f-bbd0-38ab9d19764d",
  46. [EFFECT_STEALING] = "e2f96cad-5343-481a-b023-cead49549ad7",
  47. [EFFECT_SWD] = "b7a2491e-4d4e-6c7d-d469-060ed81b17c0",
  48. }
  49.  
  50. component_layers = {
  51. IsItem={1, 2, 3},
  52. IsCharacter={1, 2}
  53. }
  54.  
  55. TAGS = {
  56. IGNORE_PLATFORM_DESTRUCTION = "cf60d691-4307-4a8c-af4d-0e8781ea2503",
  57. JUNK = "99f4e322-ab2f-48e9-8294-b87ea62212b0",
  58. HIGHLIGHTITEM = "2782dd9d-a96c-48e9-a850-73c79f54bfb6",
  59. SCROLL = "dd86c045-0370-4ec9-b7c5-b0b160706f09",
  60. -- Speak With Dead conditions
  61. HASSWDDIALOG = "21be1053-c642-4081-b64b-56678ae11594",
  62. BEAST = "890b5a2a-e773-48df-b191-c887d87bec16",
  63. UNDEAD = "33c625aa-6982-4c27-904f-e47029a9b140",
  64. CORPSE_SPOKEN = "249ba319-6652-47ec-9f31-ef5be55429c3",
  65. BLOCK_SPEAK_WITH_DEAD = "70f9d685-b791-4ea2-88e4-fc8fc48b4234",
  66. }
  67.  
  68. -- CHARACTER EVENTS
  69.  
  70. Ext.Osiris.RegisterListener("LevelGameplayStarted", 2, "after", function (levelName, isEditorMode)
  71. for _,c in pairs(Osi.DB_PartyMembers:Get(nil)) do
  72. _P("Assigning to " .. c[1])
  73. AddPassive(c[1], "REVEALING_AURA_Passive")
  74. AddPassive(c[1], "REVEALING_AURA_Passive_Tiny")
  75. AddPassive(c[1], "REVEALING_AURA_Passive_Container")
  76. AddPassive(c[1], "REVEALING_AURA_Passive_Default")
  77. AddPassive(c[1], "REVEALING_AURA_Passive_Unimportant")
  78. end
  79.  
  80. Osi.TimerLaunch(PING_TIMER, 1000)
  81. end)
  82.  
  83. Ext.Osiris.RegisterListener("CharacterJoinedParty", 1, "after", function (char)
  84. AddPassive(char, "REVEALING_AURA_Passive")
  85. AddPassive(char, "REVEALING_AURA_Passive_Tiny")
  86. AddPassive(char, "REVEALING_AURA_Passive_Container")
  87. AddPassive(char, "REVEALING_AURA_Passive_Default")
  88. AddPassive(char, "REVEALING_AURA_Passive_Unimportant")
  89.  
  90. local p = Ext.Entity.Get(GetHostCharacter()).ServerToggledPassives.Passives
  91. local c = Ext.Entity.Get(char)
  92.  
  93. c.ServerToggledPassives.Passives.REVEALING_AURA_Passive = p.REVEALING_AURA_Passive or false
  94. c.ServerToggledPassives.Passives.REVEALING_AURA_Passive_Tiny = p.REVEALING_AURA_Passive_Tiny or false
  95. c.ServerToggledPassives.Passives.REVEALING_AURA_Passive_Container = p.REVEALING_AURA_Passive_Container or false
  96. c.ServerToggledPassives.Passives.REVEALING_AURA_Passive_Default = p.REVEALING_AURA_Passive_Default or false
  97. c.ServerToggledPassives.Passives.REVEALING_AURA_Passive_Unimportant = p.REVEALING_AURA_Passive_Unimportant or false
  98. end)
  99.  
  100. Ext.Osiris.RegisterListener("CharacterLeftParty", 1, "before", function (char)
  101. RemovePassive(char, "REVEALING_AURA_Passive")
  102. RemovePassive(char, "REVEALING_AURA_Passive_Tiny")
  103. RemovePassive(char, "REVEALING_AURA_Passive_Container")
  104. RemovePassive(char, "REVEALING_AURA_Passive_Default")
  105. RemovePassive(char, "REVEALING_AURA_Passive_Unimportant")
  106. RemoveStatus(char, "REVEAL_INTERACTIVES")
  107. end)
  108.  
  109. Ext.Osiris.RegisterListener("LevelUnloading", 1, "before", function (levelName)
  110. Osi.TimerCancel(PING_TIMER)
  111. end)
  112.  
  113.  
  114. -- HELPERS
  115.  
  116. function FastHasStatus(item, status)
  117. if item.StatusContainer ~= nil then
  118. for _,s in pairs(item.StatusContainer.Statuses) do
  119. if s == status then
  120. return true
  121. end
  122. end
  123. end
  124. return false
  125. end
  126.  
  127. function FastHasTag(item, tag)
  128. if item.Tag ~= nil then
  129. for _,t in pairs(item.Tag.Tags) do
  130. if t == tag then
  131. return true
  132. end
  133. end
  134. end
  135. return false
  136. end
  137.  
  138. function HasAnyActionType(item)
  139. if item.ActionType ~= nil then
  140. for a,actiontype in pairs(item.ActionType.ActionTypes) do
  141. if actiontype > 0 then
  142. return true
  143. end
  144. end
  145. end
  146. return false
  147. end
  148.  
  149. function HasActionType(item, action)
  150. if item.ActionType ~= nil then
  151. for a,actiontype in pairs(item.ActionType.ActionTypes) do
  152. if actiontype == action then
  153. return true
  154. end
  155. end
  156. end
  157. return false
  158. end
  159.  
  160. function IsValuable(item)
  161. return (
  162. item.ServerItem.ItemType == "VeryRare" or
  163. item.ServerItem.ItemType == "Legendary" or
  164. item.ServerItem.StoryItem or
  165. item.Value.Unique or
  166. item.Value.Value >= VALUE_THRESHOLD or
  167. FastHasTag(item, TAGS["HIGHLIGHTITEM"]) or
  168. FastHasTag(item, TAGS["SCROLL"])
  169. )
  170. end
  171.  
  172. function IsValuableIventories(item, depth)
  173. if depth > 5 then
  174. _P("RevealingAuraSE - Inventory recursion limit reached")
  175. return 0
  176. end
  177.  
  178. local alljunk = true
  179. for _,inv in pairs(item.InventoryOwner.Inventories) do
  180. for _,v in pairs(inv.InventoryContainer.Items) do
  181. local i = v.Item.ServerItem.Item
  182.  
  183. if IsValuable(v.Item) then
  184. return 2
  185. end
  186.  
  187. if alljunk and not FastHasTag(v.Item, TAGS["JUNK"]) then
  188. alljunk = false
  189. end
  190.  
  191. if v.Item.InventoryOwner ~= nil then
  192. -- nested container
  193. local container = IsValuableIventories(v.Item, depth + 1)
  194.  
  195. if container == 2 then
  196. return 2
  197. elseif alljunk and container == 1 then
  198. alljunk = false
  199. end
  200. end
  201. end
  202. end
  203.  
  204. if alljunk then
  205. return 0
  206. end
  207.  
  208. return 1
  209. end
  210.  
  211.  
  212. -- ASSIGN EFFECTS
  213.  
  214. function GetEffect(item, i, last)
  215. if item.IsItem ~= nil then
  216. if last == EFFECT_UNDEFINED then
  217. if FastHasTag(item, TAGS["IGNORE_PLATFORM_DESTRUCTION"]) then
  218. return EFFECT_IGNORE
  219. end
  220. end
  221.  
  222. -- just in case this is applied to an item rather than character
  223. if FastHasStatus(item, 'SPEAK_WITH_DEAD_INTERESTING') then
  224. return EFFECT_NONE
  225. end
  226.  
  227. if i == 1 then
  228. if item.InventoryOwner ~= nil then
  229. -- container
  230. local v = IsValuableIventories(item, 0)
  231.  
  232. if toggle_tiny and v == 2 then
  233. return EFFECT_CONTAINER_VALUABLE
  234. elseif toggle_container and v > 0 then
  235. return EFFECT_CONTAINER
  236. end
  237. elseif toggle_tiny and IsValuable(item) then
  238. return EFFECT_VALUABLE
  239. elseif toggle_tiny and item.Data.Weight ~= nil and
  240. item.ServerItem.CanBePickedUp and
  241. item.Data.Weight > 0 and
  242. item.Data.Weight < WEIGHT_THRESHOLD then
  243. return EFFECT_TINY
  244. end
  245.  
  246. -- if item.ServerItem.Template.Unimportant then
  247. -- return toggle_unimportant and EFFECT_UNIMPORTANT or EFFECT_NONE
  248. -- return EFFECT_NONE
  249. -- end
  250. if item.InventoryOwner ~= nil then
  251. return EFFECT_NONE
  252. else
  253. return toggle_default and EFFECT_DEFAULT or EFFECT_NONE
  254. end
  255. elseif i == 2 then
  256. if item.OwneeCurrent ~= nil and
  257. item.OwneeCurrent.Ownee ~= nil and
  258. Osi.IsPlayer(item.OwneeCurrent.Ownee.Uuid.EntityUuid) == 0 then
  259.  
  260. if toggle_stealing then
  261. return EFFECT_STEALING
  262. else
  263.  
  264. if item.InventoryOwner ~= nil then
  265. -- container
  266. local v = IsValuableIventories(item, 0)
  267.  
  268. if toggle_tiny and v == 2 then
  269. -- return EFFECT_CONTAINER_VALUABLE
  270. return EFFECT_STEALING
  271. elseif toggle_container and v > 0 then
  272. -- return EFFECT_CONTAINER
  273. return EFFECT_NONE
  274. end
  275. return EFFECT_NONE
  276. elseif toggle_tiny and IsValuable(item) then
  277. return EFFECT_STEALING
  278. elseif toggle_tiny and item.Data.Weight ~= nil and
  279. item.ServerItem.CanBePickedUp and
  280. item.Data.Weight > 0 and
  281. item.Data.Weight < WEIGHT_THRESHOLD then
  282. -- return EFFECT_TINY
  283. return EFFECT_STEALING
  284. end
  285.  
  286. -- if item.ServerItem.Template.Unimportant then
  287. -- return toggle_unimportant and EFFECT_UNIMPORTANT or EFFECT_NONE
  288. -- return EFFECT_NONE
  289. -- end
  290.  
  291. return EFFECT_NONE
  292. end
  293. end
  294.  
  295. if item.InventoryOwner ~= nil and item.HasOpened == nil then
  296. local v = IsValuableIventories(item, 0)
  297.  
  298. if v > 0 then
  299. return EFFECT_CONTAINER_UNOPENED
  300. end
  301. end
  302.  
  303. return EFFECT_NONE
  304. elseif i == 3 then
  305. if Osi.IsLocked(item.Uuid.EntityUuid) > 0 and not FastHasStatus(item, 'ARCANE_LOCK') then
  306. return EFFECT_LOCKED
  307. end
  308.  
  309. return EFFECT_NONE
  310. end
  311. elseif item.IsCharacter ~= nil then
  312. if not (Osi.IsDead(item.Uuid.EntityUuid) > 0 or
  313. FastHasStatus(item, 'KNOCKED_OUT') or
  314. FastHasStatus(item, 'KNOCKED_OUT_PERMANENTLY') or
  315. FastHasStatus(item, 'KNOCKED_OUT_TEMPORARILY')
  316. ) then
  317. return EFFECT_NONE
  318. end
  319.  
  320. if i == 1 then
  321. if FastHasStatus(item, 'SPEAK_WITH_DEAD_INTERESTING') then
  322. return EFFECT_NONE
  323. end
  324.  
  325. if FastHasTag(item, TAGS['HASSWDDIALOG']) and
  326. Osi.IsPlayer(item.Uuid.EntityUuid) == 0 and
  327. Osi.IsFreshCorpse(item.Uuid.EntityUuid) > 0 and
  328. not FastHasTag(item, TAGS['BEAST']) and
  329. not FastHasTag(item, TAGS['UNDEAD']) and
  330. not FastHasTag(item, TAGS['CORPSE_SPOKEN']) and
  331. not FastHasTag(item, TAGS['BLOCK_SPEAK_WITH_DEAD']) then
  332. return EFFECT_SWD
  333. end
  334.  
  335. if item.InventoryOwner ~= nil and
  336. item.CanBeLooted ~= nil and
  337. item.Loot ~= nil then
  338. local v = IsValuableIventories(item, 0)
  339.  
  340. if toggle_tiny and v == 2 then
  341. return EFFECT_CONTAINER_VALUABLE
  342. elseif toggle_container and v > 0 then
  343. return EFFECT_CONTAINER
  344. end
  345. end
  346.  
  347. if item.InventoryOwner ~= nil then
  348. return EFFECT_NONE
  349. else
  350. return toggle_default and EFFECT_DEFAULT or EFFECT_NONE
  351. end
  352. end
  353.  
  354. if i == 2 then
  355. if item.Loot ~= nil and item.Loot.Flags & 4 > 0 then
  356.  
  357. if toggle_stealing then
  358. return EFFECT_STEALING
  359. else
  360. if item.InventoryOwner ~= nil then
  361. -- container
  362. local v = IsValuableIventories(item, 0)
  363.  
  364. if toggle_tiny and v == 2 then
  365. -- return EFFECT_CONTAINER_VALUABLE
  366. return EFFECT_STEALING
  367. elseif toggle_container and v > 0 then
  368. -- return EFFECT_CONTAINER
  369. return EFFECT_NONE
  370. end
  371. return EFFECT_NONE
  372. elseif toggle_tiny and IsValuable(item) then
  373. return EFFECT_STEALING
  374. elseif toggle_tiny and item.Data.Weight ~= nil and
  375. item.ServerItem.CanBePickedUp and
  376. item.Data.Weight > 0 and
  377. item.Data.Weight < WEIGHT_THRESHOLD then
  378. -- return EFFECT_TINY
  379. return EFFECT_STEALING
  380. end
  381.  
  382. -- if item.ServerItem.Template.Unimportant then
  383. -- -- return toggle_unimportant and EFFECT_UNIMPORTANT or EFFECT_NONE
  384. -- return EFFECT_NONE
  385. -- end
  386.  
  387. return EFFECT_NONE
  388. end
  389.  
  390. end
  391.  
  392. if item.InventoryOwner ~= nil and
  393. item.CanBeLooted ~= nil and
  394. item.Loot ~= nil and
  395. item.Loot.Flags & 2 == 0 then
  396.  
  397. local v = IsValuableIventories(item, 0)
  398.  
  399. if v > 0 then
  400. return EFFECT_CONTAINER_UNOPENED
  401. end
  402. end
  403.  
  404. return EFFECT_NONE
  405. end
  406. end
  407. end
  408.  
  409. function InRanges(transform, positions)
  410. for k,v in pairs(positions) do
  411. if InRange(transform, v[1], v[2], v[3]) then
  412. return true
  413. end
  414. end
  415. return false
  416. end
  417.  
  418. function InRange(transform, x, y, z)
  419. local delx = math.abs(transform[1] - x)
  420. if delx > AURA_RADIUS then
  421. return false
  422. end
  423.  
  424. local dely = math.abs(transform[2] - y)
  425. if dely > AURA_RADIUS then
  426. return false
  427. end
  428.  
  429. local delz = math.abs(transform[3] - z)
  430. if delz > AURA_RADIUS then
  431. return false
  432. end
  433.  
  434. return math.sqrt(delx^2 + dely^2 + delz^2) < AURA_RADIUS
  435. end
  436.  
  437. function UpdateEffects(effects, item, layers)
  438. for _,i in pairs(layers) do
  439. if effects[i]["effect_idx"] ~= EFFECT_IGNORE then
  440. local effect_idx = GetEffect(item, i, effects[i]["effect_idx"])
  441. if effects[i]["effect_idx"] ~= effect_idx then
  442. if effects[i]["effect_id"] ~= 0 then
  443. StopLoopEffect(effects[i]["effect_id"])
  444. effects[i]["effect_id"] = 0
  445. end
  446.  
  447. effects[i]["effect_idx"] = effect_idx
  448. if effect_idx > EFFECT_NONE then
  449. local effect_id = PlayLoopEffect(item.Uuid.EntityUuid, EFFECTS[effect_idx], "", 1)
  450. if effect_id ~= nil then
  451. effects[i]["effect_id"] = effect_id
  452. end
  453. end
  454. end
  455. end
  456. end
  457. end
  458.  
  459. function RemoveEffects(effects, item, layers)
  460. for _,i in pairs(layers) do
  461. local effect_id = effects[i]["effect_id"]
  462. if effect_id > 0 then
  463. StopLoopEffect(effect_id)
  464. end
  465. end
  466. end
  467.  
  468.  
  469. -- MAIN EVENT LOOP
  470.  
  471. timer_phase = 0
  472. Ext.Osiris.RegisterListener("TimerFinished", 1, "after", function (timer)
  473. if timer == PING_TIMER then
  474. Osi.TimerLaunch(PING_TIMER, UPDATE_INTERVAL)
  475. local start = Ext.Utils.MonotonicTime()
  476. local count = 0
  477.  
  478. local e = Ext.Entity.Get(GetHostCharacter())
  479. local p = e.ServerToggledPassives.Passives
  480.  
  481. if not p.REVEALING_AURA_Passive then
  482. if not toggle then
  483. return
  484. end
  485.  
  486. for component,layers in pairs(component_layers) do
  487. for _,item in pairs(Ext.Entity.GetAllEntitiesWithComponent(component)) do
  488. if item.Vars.Revealing_Aura_Effects then
  489. RemoveEffects(item.Vars.Revealing_Aura_Effects, item, layers)
  490. end
  491. item.Vars.Revealing_Aura_Effects = nil
  492. end
  493. end
  494.  
  495. toggle = false
  496. return
  497. end
  498.  
  499. toggle = e.ServerToggledPassives.Passives.REVEALING_AURA_Passive
  500. toggle_tiny = e.ServerToggledPassives.Passives.REVEALING_AURA_Passive_Tiny
  501. toggle_container = e.ServerToggledPassives.Passives.REVEALING_AURA_Passive_Container
  502. toggle_default = e.ServerToggledPassives.Passives.REVEALING_AURA_Passive_Default
  503. toggle_stealing = e.ServerToggledPassives.Passives.REVEALING_AURA_Passive_Unimportant
  504.  
  505. if timer_phase ~= 0 then
  506. -- fast update loop for existing effects
  507. for component,layers in pairs(component_layers) do
  508. for _,item in pairs(Ext.Entity.GetAllEntitiesWithComponent(component)) do
  509. local effects = item.Vars.Revealing_Aura_Effects
  510. if effects ~= nil then
  511. UpdateEffects(effects, item, layers)
  512. item.Vars.Revealing_Aura_Effects = effects
  513. count = count + 1
  514. end
  515. end
  516. end
  517. else
  518. local positions = {}
  519. for _,c in pairs(Osi.DB_PartyMembers:Get(nil)) do
  520. local x,y,z = GetPosition(c[1])
  521. if x ~= nil then
  522. table.insert(positions, {x,y,z})
  523. end
  524. end
  525.  
  526. for component,layers in pairs(component_layers) do
  527. for _,item in pairs(Ext.Entity.GetAllEntitiesWithComponent(component)) do
  528. if item.Transform ~= nil then
  529. local transform = item.Transform.Transform.Translate
  530. local effects = item.Vars.Revealing_Aura_Effects
  531.  
  532. if item.InventoryMember == nil and InRanges(item.Transform.Transform.Translate, positions) then
  533. count = count + 1
  534. if effects == nil then
  535. effects = {}
  536. for i in pairs(layers) do
  537. effects[i] = {
  538. effect_idx = EFFECT_UNDEFINED,
  539. effect_id = 0
  540. }
  541. end
  542. item.Vars.Revealing_Aura_Effects = effects
  543. end
  544.  
  545. UpdateEffects(effects, item, layers)
  546. -- ensure syncing
  547. item.Vars.Revealing_Aura_Effects = effects
  548. else
  549. if effects ~= nil then
  550. RemoveEffects(effects, item, layers)
  551. item.Vars.Revealing_Aura_Effects = nil
  552. end
  553. end
  554. end
  555. end
  556. end
  557. end
  558.  
  559. timer_phase = (timer_phase + 1) % PHASE_DIVISOR
  560. -- print performance info to the console
  561. -- _P("phase " .. timer_phase .. " loop: " .. count .. ", " .. (Ext.Utils.MonotonicTime() - start) / 1000)
  562. end
  563. end)
  564.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement