@fps-games/vfx 0.1.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +41 -0
  3. package/dist/effects/coin-burst/index.js +6 -2
  4. package/dist/effects/comet-trail-flame/index.js +3 -1
  5. package/dist/effects/debris-smoke/index.js +1 -0
  6. package/dist/effects/electric-arc/index.js +1 -0
  7. package/dist/effects/energy-shield/index.js +1 -0
  8. package/dist/effects/explosion/index.js +3 -2
  9. package/dist/effects/hit-flash/index.js +12 -4
  10. package/dist/effects/laser-beam/index.js +14 -2
  11. package/dist/effects/lumber_vehicle_upgrade_aura/index.js +1 -0
  12. package/dist/effects/monster-hit/index.js +2 -1
  13. package/dist/effects/player-upgrade/index.js +5 -3
  14. package/dist/effects/pulse-laser-volley/index.js +14 -4
  15. package/dist/effects/thruster-flame/index.js +3 -1
  16. package/dist/effects/treasure-attention/index.d.ts +54 -0
  17. package/dist/effects/treasure-attention/index.js +535 -0
  18. package/dist/effects/ufo-thread-flame/index.js +1 -0
  19. package/dist/effects/vehicle-upgrade/index.js +4 -1
  20. package/dist/effects/weapon-trail/index.js +8 -3
  21. package/dist/effects/wood_crusher_sawdust/index.d.ts +4 -0
  22. package/dist/effects/wood_cutter_sawdust/index.d.ts +4 -0
  23. package/dist/index.js +85 -17
  24. package/dist/types.d.ts +80 -0
  25. package/index.json +19 -1
  26. package/package.json +9 -4
  27. package/packages/effects/treasure-attention/vfx-params.json +49 -0
  28. package/tooling/vfx-cli.mjs +8 -1
  29. package/docs/AUTHORING_EFFECTS.md +0 -297
  30. package/docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md +0 -1580
  31. package/docs/VFX_LIBRARY_EXECUTION_PLAN.md +0 -229
  32. package/docs/VFX_LIBRARY_PLAN.md +0 -546
  33. package/docs/vfx-ab/RESULTS.md +0 -35
  34. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/after.png +0 -0
  35. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/before.png +0 -0
  36. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/after.png +0 -0
  37. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/before.png +0 -0
  38. package/docs/vfx-integration_chatgpt.md +0 -462
  39. package/docs/weapon-trail-visualization.html +0 -241
  40. package/packages/EffectPackageService.ts +0 -240
  41. package/packages/SceneVfxService.ts +0 -96
  42. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/runtime/simpleMeshEffect.v1.js +0 -333
  43. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.frag.glsl +0 -25
  44. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.vert.glsl +0 -11
  45. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/textures/effect_jianbian.png +0 -0
  46. package/packages/build.mesh.yellow_build_upgrade_indicator/effect.json +0 -927
  47. package/packages/build.mesh.yellow_build_upgrade_indicator/media/contact-sheet.png +0 -0
  48. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.gif +0 -0
  49. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.mp4 +0 -0
  50. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.webm +0 -0
  51. package/packages/build.mesh.yellow_build_upgrade_indicator/media/thumbnail.png +0 -0
  52. package/packages/build.mesh.yellow_build_upgrade_indicator/metadata/description.json +0 -197
  53. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-effect.original.json +0 -895
  54. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-normalized-vfx-to-babylon-report.json +0 -241
  55. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-overrides.json +0 -542
  56. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-tuning-report.json +0 -69
  57. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/asset-map.json +0 -125
  58. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/lineage.json +0 -44
  59. package/packages/build.particle.golden_upgrade_starburst/assets/runtime/particleEmitter.v1.js +0 -210
  60. package/packages/build.particle.golden_upgrade_starburst/assets/textures/effect_guangdian.png +0 -0
  61. package/packages/build.particle.golden_upgrade_starburst/effect.json +0 -880
  62. package/packages/build.particle.golden_upgrade_starburst/media/contact-sheet.png +0 -0
  63. package/packages/build.particle.golden_upgrade_starburst/media/preview.gif +0 -0
  64. package/packages/build.particle.golden_upgrade_starburst/media/preview.mp4 +0 -0
  65. package/packages/build.particle.golden_upgrade_starburst/media/preview.webm +0 -0
  66. package/packages/build.particle.golden_upgrade_starburst/media/thumbnail.png +0 -0
  67. package/packages/build.particle.golden_upgrade_starburst/metadata/description.json +0 -161
  68. package/packages/build.particle.golden_upgrade_starburst/provenance/03-effect.original.json +0 -762
  69. package/packages/build.particle.golden_upgrade_starburst/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  70. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-overrides.json +0 -436
  71. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-tuning-report.json +0 -40
  72. package/packages/build.particle.golden_upgrade_starburst/provenance/asset-map.json +0 -115
  73. package/packages/build.particle.golden_upgrade_starburst/provenance/lineage.json +0 -40
  74. package/packages/build.particle.orange_upgrade_transform_sparks/assets/runtime/particleEmitter.v1.js +0 -210
  75. package/packages/build.particle.orange_upgrade_transform_sparks/assets/textures/b5b27ab1-e740-4398-b407-848fc2b2c897.png +0 -0
  76. package/packages/build.particle.orange_upgrade_transform_sparks/effect.json +0 -887
  77. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.gif +0 -0
  78. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.mp4 +0 -0
  79. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.webm +0 -0
  80. package/packages/build.particle.orange_upgrade_transform_sparks/media/thumbnail.png +0 -0
  81. package/packages/build.particle.orange_upgrade_transform_sparks/metadata/description.json +0 -179
  82. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-effect.original.json +0 -859
  83. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  84. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-overrides.json +0 -478
  85. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-tuning-report.json +0 -40
  86. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/asset-map.json +0 -114
  87. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/lineage.json +0 -41
  88. package/packages/build.particle.wood_chip_dust_burst/assets/runtime/particleEmitter.v1.js +0 -210
  89. package/packages/build.particle.wood_chip_dust_burst/assets/textures/effect_yanwu4.png +0 -0
  90. package/packages/build.particle.wood_chip_dust_burst/assets/textures/suipian3.png +0 -0
  91. package/packages/build.particle.wood_chip_dust_burst/effect.json +0 -1400
  92. package/packages/build.particle.wood_chip_dust_burst/media/contact-sheet.png +0 -0
  93. package/packages/build.particle.wood_chip_dust_burst/media/preview.gif +0 -0
  94. package/packages/build.particle.wood_chip_dust_burst/media/preview.mp4 +0 -0
  95. package/packages/build.particle.wood_chip_dust_burst/media/preview.webm +0 -0
  96. package/packages/build.particle.wood_chip_dust_burst/media/thumbnail.png +0 -0
  97. package/packages/build.particle.wood_chip_dust_burst/metadata/description.json +0 -173
  98. package/packages/build.particle.wood_chip_dust_burst/provenance/03-effect.original.json +0 -1277
  99. package/packages/build.particle.wood_chip_dust_burst/provenance/03-normalized-vfx-to-babylon-report.json +0 -329
  100. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-overrides.json +0 -749
  101. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-tuning-report.json +0 -60
  102. package/packages/build.particle.wood_chip_dust_burst/provenance/asset-map.json +0 -146
  103. package/packages/build.particle.wood_chip_dust_burst/provenance/lineage.json +0 -69
  104. package/packages/effects/building-upgrade/effect.json +0 -6
  105. package/packages/effects/building-upgrade/index.ts +0 -387
  106. package/packages/effects/character-entrance/effect.json +0 -6
  107. package/packages/effects/character-entrance/index.ts +0 -308
  108. package/packages/effects/coin-burst/index.ts +0 -43
  109. package/packages/effects/comet-trail-flame/assets/textures/trail_5x4.webp +0 -0
  110. package/packages/effects/comet-trail-flame/index.ts +0 -106
  111. package/packages/effects/conveyor-upgrade/effect.json +0 -6
  112. package/packages/effects/conveyor-upgrade/index.ts +0 -401
  113. package/packages/effects/debris-smoke/assets/models/rock_debris.glb +0 -0
  114. package/packages/effects/debris-smoke/assets/textures/smoke22.webp +0 -0
  115. package/packages/effects/debris-smoke/index.ts +0 -101
  116. package/packages/effects/door_open_gold_flakes/assets/runtime/goldFlakes.v1.js +0 -397
  117. package/packages/effects/door_open_gold_flakes/door_open_gold_flakes.json +0 -498
  118. package/packages/effects/door_open_gold_flakes/index.ts +0 -11
  119. package/packages/effects/electric-arc/assets/textures/spark_4x4.webp +0 -0
  120. package/packages/effects/electric-arc/index.ts +0 -62
  121. package/packages/effects/energy-shield/effect.json +0 -7
  122. package/packages/effects/energy-shield/index.ts +0 -431
  123. package/packages/effects/explosion/assets/textures/baozha29.webp +0 -0
  124. package/packages/effects/explosion/assets/textures/glow01.webp +0 -0
  125. package/packages/effects/explosion/assets/textures/lizi01.webp +0 -0
  126. package/packages/effects/explosion/assets/textures/xulie_lizibaozha02_5x5.webp +0 -0
  127. package/packages/effects/explosion/index.ts +0 -114
  128. package/packages/effects/ground-tile-upgrade/effect.json +0 -6
  129. package/packages/effects/ground-tile-upgrade/index.ts +0 -410
  130. package/packages/effects/hit-flash/index.ts +0 -69
  131. package/packages/effects/laser-beam/assets/textures/laser_glow.webp +0 -0
  132. package/packages/effects/laser-beam/assets/textures/laser_noise.webp +0 -0
  133. package/packages/effects/laser-beam/index.ts +0 -6
  134. package/packages/effects/laser-beam-factory.ts +0 -135
  135. package/packages/effects/lumber-cart-suction/effect.json +0 -6
  136. package/packages/effects/lumber-cart-suction/index.ts +0 -399
  137. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb +0 -0
  138. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/.active +0 -1
  139. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/1781589063890_upload.glb +0 -0
  140. package/packages/effects/lumber-debris-scatter/assets/runtime/solidParticleScatter.v1.js +0 -236
  141. package/packages/effects/lumber-debris-scatter/effect.json +0 -228
  142. package/packages/effects/lumber-debris-scatter/index.ts +0 -780
  143. package/packages/effects/lumber_vehicle_gear_spin/assets/runtime/gearSpin.v1.js +0 -202
  144. package/packages/effects/lumber_vehicle_gear_spin/index.ts +0 -11
  145. package/packages/effects/lumber_vehicle_gear_spin/lumber_vehicle_gear_spin.json +0 -338
  146. package/packages/effects/lumber_vehicle_upgrade_aura/assets/runtime/upgradeAura.v1.js +0 -353
  147. package/packages/effects/lumber_vehicle_upgrade_aura/index.ts +0 -11
  148. package/packages/effects/lumber_vehicle_upgrade_aura/lumber_vehicle_upgrade_aura.json +0 -365
  149. package/packages/effects/lumber_vehicle_wood_chips/assets/runtime/solidParticleScatter.v1.js +0 -310
  150. package/packages/effects/lumber_vehicle_wood_chips/index.ts +0 -11
  151. package/packages/effects/lumber_vehicle_wood_chips/lumber_vehicle_wood_chips.json +0 -335
  152. package/packages/effects/monster-hit/index.ts +0 -128
  153. package/packages/effects/player-upgrade/effect.json +0 -6
  154. package/packages/effects/player-upgrade/index.ts +0 -635
  155. package/packages/effects/pulse-laser-volley/assets/textures/bolt_guangshu.webp +0 -0
  156. package/packages/effects/pulse-laser-volley/index.ts +0 -204
  157. package/packages/effects/resource-collect-sparkles/effect.json +0 -6
  158. package/packages/effects/resource-collect-sparkles/index.ts +0 -254
  159. package/packages/effects/run-footstep-dust/assets/runtime/runFootstepDust.v1.js +0 -215
  160. package/packages/effects/run-footstep-dust/effect.json +0 -183
  161. package/packages/effects/run-footstep-dust/index.ts +0 -377
  162. package/packages/effects/space-shared.ts +0 -120
  163. package/packages/effects/thruster-flame/index.ts +0 -305
  164. package/packages/effects/tree-chop-dust/assets/textures/tree_chop_dust_atlas.png +0 -0
  165. package/packages/effects/tree-chop-dust/effect.json +0 -8
  166. package/packages/effects/tree-chop-dust/index.ts +0 -202
  167. package/packages/effects/tree-leaf-chop-burst/assets/runtime/particleEmitter.v1.js +0 -303
  168. package/packages/effects/tree-leaf-chop-burst/assets/textures/tree_leaf_chop_burst_leaf_atlas.png +0 -0
  169. package/packages/effects/tree-leaf-chop-burst/effect.json +0 -498
  170. package/packages/effects/tree-leaf-chop-burst/index.ts +0 -294
  171. package/packages/effects/tree_chop_dust_burst/assets/runtime/particleEmitter.v1.js +0 -318
  172. package/packages/effects/tree_chop_dust_burst/assets/textures/tree_chop_dust_burst_dust.png +0 -0
  173. package/packages/effects/tree_chop_dust_burst/index.ts +0 -13
  174. package/packages/effects/tree_chop_dust_burst/tree_chop_dust_burst.json +0 -412
  175. package/packages/effects/ufo-thread-flame/assets/textures/flame_flow.webp +0 -0
  176. package/packages/effects/ufo-thread-flame/assets/textures/flame_spike.webp +0 -0
  177. package/packages/effects/ufo-thread-flame/index.ts +0 -152
  178. package/packages/effects/vehicle-exhaust-smoke/assets/textures/vehicle_exhaust_smoke_atlas.png +0 -0
  179. package/packages/effects/vehicle-exhaust-smoke/effect.json +0 -8
  180. package/packages/effects/vehicle-exhaust-smoke/index.ts +0 -298
  181. package/packages/effects/vehicle-upgrade/effect.json +0 -6
  182. package/packages/effects/vehicle-upgrade/index.ts +0 -401
  183. package/packages/effects/weapon-trail/assets/textures/trail_wedge.png +0 -0
  184. package/packages/effects/weapon-trail/index.ts +0 -313
  185. package/packages/effects/wood-crusher-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  186. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  187. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  188. package/packages/effects/wood-crusher-sawdust/index.ts +0 -43
  189. package/packages/effects/wood-crusher-sawdust/wood_crusher_sawdust.json +0 -592
  190. package/packages/effects/wood-cutter-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  191. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  192. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  193. package/packages/effects/wood-cutter-sawdust/index.ts +0 -43
  194. package/packages/effects/wood-cutter-sawdust/wood_cutter_sawdust.json +0 -592
  195. package/packages/effects/wood-sawdust-effect-factory.ts +0 -194
  196. package/packages/effects/wood_crusher_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  197. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  198. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  199. package/packages/effects/wood_crusher_sawdust/index.ts +0 -29
  200. package/packages/effects/wood_crusher_sawdust/wood_crusher_sawdust.json +0 -592
  201. package/packages/effects/wood_cutter_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  202. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  203. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  204. package/packages/effects/wood_cutter_sawdust/index.ts +0 -29
  205. package/packages/effects/wood_cutter_sawdust/wood_cutter_sawdust.json +0 -592
  206. package/packages/effects/wood_peel_material_test/assets/runtime/woodPeelMaterialTest.v1.js +0 -196
  207. package/packages/effects/wood_peel_material_test/index.ts +0 -11
  208. package/packages/effects/wood_peel_material_test/wood_peel_material_test.json +0 -136
  209. package/packages/env.particle.forest_edge_fog/assets/runtime/particleEmitter.v1.js +0 -210
  210. package/packages/env.particle.forest_edge_fog/assets/textures/f12a23c4-b924-4322-a260-3d982428f1e8.png +0 -0
  211. package/packages/env.particle.forest_edge_fog/assets/textures/yuan.png +0 -0
  212. package/packages/env.particle.forest_edge_fog/effect.json +0 -854
  213. package/packages/env.particle.forest_edge_fog/media/preview.gif +0 -0
  214. package/packages/env.particle.forest_edge_fog/media/preview.mp4 +0 -0
  215. package/packages/env.particle.forest_edge_fog/media/preview.webm +0 -0
  216. package/packages/env.particle.forest_edge_fog/media/thumbnail.png +0 -0
  217. package/packages/env.particle.forest_edge_fog/metadata/description.json +0 -148
  218. package/packages/env.particle.forest_edge_fog/provenance/03-effect.original.json +0 -827
  219. package/packages/env.particle.forest_edge_fog/provenance/03-normalized-vfx-to-babylon-report.json +0 -196
  220. package/packages/env.particle.forest_edge_fog/provenance/04-preview-overrides.json +0 -432
  221. package/packages/env.particle.forest_edge_fog/provenance/04-preview-tuning-report.json +0 -28
  222. package/packages/env.particle.forest_edge_fog/provenance/asset-map.json +0 -124
  223. package/packages/env.particle.forest_edge_fog/provenance/lineage.json +0 -41
  224. package/packages/generation.ts +0 -14
  225. package/packages/index.ts +0 -14
  226. package/packages/legacy-particle-runtime.ts +0 -113
  227. package/packages/manifestRuntimePackage.ts +0 -519
  228. package/packages/registry.ts +0 -85
  229. package/packages/rendering.ts +0 -13
  230. package/packages/resource.particle.viscous_oil_suction/assets/runtime/top_disk_suction/particleEmitter.v1.js +0 -424
  231. package/packages/resource.particle.viscous_oil_suction/assets/textures/Glow ad.png +0 -0
  232. package/packages/resource.particle.viscous_oil_suction/effect.json +0 -1254
  233. package/packages/resource.particle.viscous_oil_suction/media/preview.gif +0 -0
  234. package/packages/resource.particle.viscous_oil_suction/media/preview.mp4 +0 -0
  235. package/packages/resource.particle.viscous_oil_suction/media/preview.webm +0 -0
  236. package/packages/resource.particle.viscous_oil_suction/media/thumbnail.png +0 -0
  237. package/packages/resource.particle.viscous_oil_suction/metadata/description.json +0 -122
  238. package/packages/resource.particle.viscous_oil_suction/provenance/03-effect.original.json +0 -1051
  239. package/packages/resource.particle.viscous_oil_suction/provenance/03-normalized-vfx-to-babylon-report.json +0 -200
  240. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-overrides.json +0 -581
  241. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-tuning-report.json +0 -28
  242. package/packages/resource.particle.viscous_oil_suction/provenance/asset-map.json +0 -111
  243. package/packages/resource.particle.viscous_oil_suction/provenance/lineage.json +0 -45
  244. package/packages/types.ts +0 -107
  245. package/tooling/check-dist-runtime.mjs +0 -166
  246. package/tooling/generate-index.mjs +0 -55
  247. package/tooling/test-guardrails.mjs +0 -157
  248. package/tooling/validate-boundary.mjs +0 -92
  249. package/tooling/validate-effects.mjs +0 -304
  250. package/tooling/validate.mjs +0 -53
@@ -1,635 +0,0 @@
1
- import { Color3, Color4 } from '@babylonjs/core/Maths/math.color';
2
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import { Constants } from '@babylonjs/core/Engines/constants';
4
- import { DynamicTexture } from '@babylonjs/core/Materials/Textures/dynamicTexture';
5
- import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
6
- import { CreateCylinder } from '@babylonjs/core/Meshes/Builders/cylinderBuilder';
7
- import { CreateDisc } from '@babylonjs/core/Meshes/Builders/discBuilder';
8
- import { CreatePlane } from '@babylonjs/core/Meshes/Builders/planeBuilder';
9
- import { CreateTorus } from '@babylonjs/core/Meshes/Builders/torusBuilder';
10
- import type { Mesh } from '@babylonjs/core/Meshes/mesh';
11
- import { ParticleSystem } from '@babylonjs/core/Particles/particleSystem';
12
- import type { Scene } from '@babylonjs/core/scene';
13
- import type { VfxColorValue, VfxEffectPackage, VfxParamValues } from '../../types';
14
- import { configureVfxParticleSystem, VFX_RENDERING_GROUP_ID } from '../../rendering';
15
- import manifest from './effect.json';
16
-
17
- interface PlayerUpgradeParams extends VfxParamValues {
18
- lifetime: number;
19
- scale: number;
20
- initialHeight: number;
21
- color: VfxColorValue;
22
- opacity: number;
23
- strandCount: number;
24
- strandLength: number;
25
- strandHeight: number;
26
- strandRadius: number;
27
- strandWidth: number;
28
- riseSpeed: number;
29
- ringCount: number;
30
- ringMaxRadius: number;
31
- ringThickness: number;
32
- ringInterval: number;
33
- ringOpacity: number;
34
- glowIntensity: number;
35
- glowHeight: number;
36
- glowWidth: number;
37
- }
38
-
39
- interface RingState {
40
- mesh: Mesh;
41
- material: StandardMaterial;
42
- startDelay: number;
43
- duration: number;
44
- }
45
-
46
- interface StrandMeshState {
47
- mesh: Mesh;
48
- material: StandardMaterial;
49
- height: number;
50
- startDelay: number;
51
- duration: number;
52
- }
53
-
54
- const strandTextureByScene = new WeakMap<Scene, DynamicTexture>();
55
- const glowTextureByScene = new WeakMap<Scene, DynamicTexture>();
56
- const haloTextureByScene = new WeakMap<Scene, DynamicTexture>();
57
-
58
- const DEFAULT_PARAMS: PlayerUpgradeParams = {
59
- lifetime: 2.4,
60
- scale: 1,
61
- initialHeight: 0.02,
62
- color: { r: 1, g: 0.78, b: 0.08 },
63
- opacity: 1,
64
- strandCount: 120,
65
- strandLength: 0.72,
66
- strandHeight: 1.55,
67
- strandRadius: 0.58,
68
- strandWidth: 0.075,
69
- riseSpeed: 1.45,
70
- ringCount: 4,
71
- ringMaxRadius: 2.15,
72
- ringThickness: 0.09,
73
- ringInterval: 0.28,
74
- ringOpacity: 1,
75
- glowIntensity: 1.85,
76
- glowHeight: 2.15,
77
- glowWidth: 1.08,
78
- };
79
-
80
- export const playerUpgradeEffectPackage: VfxEffectPackage<PlayerUpgradeParams> = {
81
- id: 'player-upgrade',
82
- nameZh: '人物&NPC升级',
83
- manifest,
84
- defaultParams: DEFAULT_PARAMS,
85
- debugAnchor: 'player',
86
- debugParams: [
87
- { key: 'lifetime', label: '生命周期', kind: 'lifetime', min: 0.2, max: 6, step: 0.01 },
88
- { key: 'scale', label: '整体缩放', kind: 'scale', min: 0.1, max: 5, step: 0.01 },
89
- { key: 'initialHeight', label: '初始高度', kind: 'number', min: -1, max: 3, step: 0.01 },
90
- { key: 'color', label: '金光颜色', kind: 'color' },
91
- { key: 'opacity', label: '金丝透明度', kind: 'opacity', min: 0, max: 1, step: 0.01 },
92
- { key: 'strandCount', label: '金丝数量', kind: 'number', min: 0, max: 240, step: 1 },
93
- { key: 'strandLength', label: '金丝长度', kind: 'number', min: 0.05, max: 3, step: 0.01 },
94
- { key: 'strandHeight', label: '金丝上升高度', kind: 'number', min: 0.2, max: 5, step: 0.01 },
95
- { key: 'strandRadius', label: '金丝包围半径', kind: 'number', min: 0.05, max: 2, step: 0.01 },
96
- { key: 'strandWidth', label: '金丝粗细', kind: 'number', min: 0.005, max: 0.2, step: 0.001 },
97
- { key: 'riseSpeed', label: '金丝上升速度', kind: 'number', min: 0.05, max: 6, step: 0.01 },
98
- { key: 'ringCount', label: '亮环次数', kind: 'number', min: 0, max: 10, step: 1 },
99
- { key: 'ringMaxRadius', label: '亮环最大半径', kind: 'number', min: 0.1, max: 5, step: 0.01 },
100
- { key: 'ringThickness', label: '亮环粗细', kind: 'number', min: 0.005, max: 0.3, step: 0.001 },
101
- { key: 'ringInterval', label: '亮环间隔', kind: 'number', min: 0.02, max: 1.2, step: 0.01 },
102
- { key: 'ringOpacity', label: '亮环透明度', kind: 'opacity', min: 0, max: 1, step: 0.01 },
103
- { key: 'glowIntensity', label: '光芒强度', kind: 'number', min: 0, max: 3, step: 0.01 },
104
- { key: 'glowHeight', label: '光柱高度', kind: 'number', min: 0.2, max: 5, step: 0.01 },
105
- { key: 'glowWidth', label: '光柱宽度', kind: 'number', min: 0.1, max: 4, step: 0.01 },
106
- ],
107
- create({ scene, root, position, params }) {
108
- const effectScale = Math.max(0.05, params.scale);
109
- markVfxRoot(root);
110
- root.position.copyFrom(position).addInPlace(new Vector3(0, params.initialHeight * effectScale, 0));
111
- const strands = createStrandSystem(scene, root, params);
112
- const strandMeshes = createStrandMeshes(scene, root, params);
113
- const rings = createExpandingRings(scene, root, params);
114
- const glowAura = createGlowAura(scene, root, params);
115
- let disposed = false;
116
- const disposeTimer = window.setTimeout(() => disposeAll(), getDisposeDelaySeconds(params) * 1000);
117
-
118
- strands.start();
119
-
120
- return {
121
- root,
122
- dispose() {
123
- disposeAll();
124
- },
125
- };
126
-
127
- function disposeAll(): void {
128
- if (disposed) return;
129
- disposed = true;
130
- window.clearTimeout(disposeTimer);
131
- strands.stop();
132
- strands.dispose();
133
- strandMeshes.dispose();
134
- rings.dispose();
135
- glowAura.dispose();
136
- cleanupPlanarShadowArtifacts(scene);
137
- root.dispose();
138
- }
139
- },
140
- };
141
-
142
- function markVfxRoot(root: unknown): void {
143
- const target = root as { metadata?: Record<string, unknown> | null };
144
- target.metadata = {
145
- ...(target.metadata ?? {}),
146
- disablePlanarShadow: true,
147
- disablePostOutlineDepth: true,
148
- };
149
- }
150
-
151
- function prepareVfxMesh(mesh: Mesh): void {
152
- mesh.isPickable = false;
153
- mesh.checkCollisions = false;
154
- mesh.receiveShadows = false;
155
- mesh.metadata = {
156
- ...((mesh.metadata as Record<string, unknown> | null) ?? {}),
157
- disablePlanarShadow: true,
158
- disablePostOutlineDepth: true,
159
- };
160
- }
161
-
162
- function cleanupPlanarShadowArtifacts(scene: Scene): void {
163
- for (const mesh of [...scene.meshes]) {
164
- if (mesh.name.startsWith('player-upgrade.') && mesh.name.includes('_planarShadow')) {
165
- mesh.dispose();
166
- }
167
- }
168
-
169
- for (const node of [...scene.transformNodes]) {
170
- if (node.name.startsWith('player-upgrade.') && node.name.includes('_shadow_root')) {
171
- node.dispose();
172
- }
173
- }
174
- }
175
-
176
- function createGlowAura(scene: Scene, root: { getAbsolutePosition(): Vector3 }, params: PlayerUpgradeParams): { dispose(): void } {
177
- const effectScale = Math.max(0.05, params.scale);
178
- const color = params.color;
179
- const glowHeight = Math.max(0.1, params.glowHeight) * effectScale;
180
- const glowWidth = Math.max(0.05, params.glowWidth) * effectScale;
181
- const glowIntensity = Math.max(0, params.glowIntensity);
182
- const meshes: Mesh[] = [];
183
- const materials: StandardMaterial[] = [];
184
-
185
- for (let index = 0; index < 3; index += 1) {
186
- const mesh = CreatePlane(`player-upgrade.soft-glow-column.${index}`, { size: 1 }, scene);
187
- prepareVfxMesh(mesh);
188
- mesh.parent = root as any;
189
- mesh.position.y = glowHeight * 0.48;
190
- mesh.rotation.y = (index * Math.PI) / 3;
191
- mesh.scaling.set(glowWidth, glowHeight, 1);
192
- mesh.renderingGroupId = VFX_RENDERING_GROUP_ID;
193
-
194
- const material = new StandardMaterial(`player-upgrade.soft-glow-column-material.${index}`, scene);
195
- configureTexturedGlowMaterial(material, color, getGlowTexture(scene), 2.8);
196
- material.alpha = 0;
197
- mesh.material = material;
198
-
199
- meshes.push(mesh);
200
- materials.push(material);
201
- }
202
-
203
- const halo = CreateDisc('player-upgrade.ground-halo', { radius: 1, tessellation: 96 }, scene);
204
- prepareVfxMesh(halo);
205
- halo.parent = root as any;
206
- halo.position.y = 0.025 * effectScale;
207
- halo.rotation.x = Math.PI / 2;
208
- halo.scaling.set(glowWidth * 1.12, glowWidth * 1.12, 1);
209
- halo.renderingGroupId = VFX_RENDERING_GROUP_ID;
210
-
211
- const haloMaterial = new StandardMaterial('player-upgrade.ground-halo-material', scene);
212
- configureTexturedGlowMaterial(haloMaterial, color, getHaloTexture(scene), 2.4);
213
- haloMaterial.alpha = 0;
214
- halo.material = haloMaterial;
215
- meshes.push(halo);
216
- materials.push(haloMaterial);
217
-
218
- let age = 0;
219
- let disposed = false;
220
- const observer = scene.onBeforeRenderObservable.add(() => {
221
- if (disposed) return;
222
- const deltaSeconds = Math.min(0.05, scene.getEngine().getDeltaTime() / 1000);
223
- age += deltaSeconds;
224
- const lifetime = Math.max(0.2, params.lifetime);
225
- const t = Math.min(1, age / lifetime);
226
- const fadeIn = Math.min(1, t / 0.12);
227
- const fadeOut = Math.max(0, 1 - Math.max(0, t - 0.72) / 0.28);
228
- const pulse = 0.86 + Math.sin(age * 13) * 0.1 + Math.sin(age * 23) * 0.04;
229
- const columnAlpha = Math.min(1, glowIntensity * params.opacity * 0.56 * fadeIn * fadeOut * pulse);
230
- const haloAlpha = Math.min(1, glowIntensity * params.ringOpacity * 0.68 * fadeIn * fadeOut);
231
-
232
- for (let index = 0; index < materials.length; index += 1) {
233
- materials[index].alpha = index === materials.length - 1 ? haloAlpha : columnAlpha;
234
- }
235
-
236
- const haloRadius = glowWidth * 1.12 * (1 + easeOutCubic(t) * 0.34);
237
- halo.scaling.set(haloRadius, haloRadius, 1);
238
-
239
- if (age >= lifetime + 0.35) dispose();
240
- });
241
-
242
- function dispose(): void {
243
- if (disposed) return;
244
- disposed = true;
245
- scene.onBeforeRenderObservable.remove(observer);
246
- for (const mesh of meshes) mesh.dispose();
247
- for (const material of materials) material.dispose();
248
- }
249
-
250
- return { dispose };
251
- }
252
-
253
- function createStrandSystem(scene: Scene, root: { getAbsolutePosition(): Vector3 }, params: PlayerUpgradeParams): ParticleSystem {
254
- const effectScale = Math.max(0.05, params.scale);
255
- const count = Math.max(0, Math.round(params.strandCount));
256
- const color = params.color;
257
- const bright = brighten(color, 0.72);
258
- const strandLength = Math.max(0.05, params.strandLength) * effectScale;
259
- const strandRadius = Math.max(0, params.strandRadius) * effectScale;
260
- const strandWidth = Math.max(0.001, params.strandWidth) * effectScale;
261
-
262
- const system = new ParticleSystem('player-upgrade.gold-strands', Math.max(1, count), scene);
263
- configureVfxParticleSystem(system);
264
- system.particleTexture = getStrandTexture(scene);
265
- system.particleTexture.hasAlpha = true;
266
- system.emitter = root as any;
267
- system.manualEmitCount = count;
268
- system.emitRate = count * 60;
269
- system.targetStopDuration = 0.05;
270
- system.disposeOnStop = false;
271
- system.blendMode = ParticleSystem.BLENDMODE_ADD;
272
- system.minLifeTime = Math.max(0.12, params.lifetime * 0.52);
273
- system.maxLifeTime = Math.max(system.minLifeTime + 0.01, params.lifetime * 0.95);
274
- system.minSize = Math.max(0.04, strandLength * 0.42);
275
- system.maxSize = Math.max(system.minSize, strandLength * 0.9);
276
- system.minScaleX = strandWidth;
277
- system.maxScaleX = strandWidth * 1.8;
278
- system.minScaleY = 1;
279
- system.maxScaleY = 1.6;
280
- system.minEmitPower = Math.max(0.01, params.riseSpeed * effectScale * 0.55);
281
- system.maxEmitPower = Math.max(system.minEmitPower, params.riseSpeed * effectScale * 1.28);
282
- system.gravity = new Vector3(0, 0.34 * effectScale, 0);
283
- system.updateSpeed = 1 / 60;
284
- system.color1 = toColor4(color, params.opacity * 0.72);
285
- system.color2 = toColor4(bright, params.opacity);
286
- system.colorDead = toColor4(color, 0);
287
- system.addColorGradient(0, toColor4(color, 0));
288
- system.addColorGradient(0.13, toColor4(bright, params.opacity));
289
- system.addColorGradient(0.72, toColor4(color, params.opacity * 0.58));
290
- system.addColorGradient(1, toColor4(color, 0));
291
- system.addVelocityGradient(0, 1);
292
- system.addVelocityGradient(0.66, 0.62);
293
- system.addVelocityGradient(1, 0.16);
294
-
295
- let positionIndex = 0;
296
- let directionIndex = 0;
297
- system.startPositionFunction = (worldMatrix, positionToUpdate, particle, isLocal) => {
298
- const index = particle.id + positionIndex;
299
- positionIndex += 1;
300
- const angle = sequence01(index, 0) * Math.PI * 2;
301
- const radius = Math.sqrt(sequence01(index, 1)) * strandRadius;
302
- const local = new Vector3(
303
- Math.cos(angle) * radius,
304
- sequence01(index, 2) * 0.08 * effectScale,
305
- Math.sin(angle) * radius,
306
- );
307
- if (isLocal) positionToUpdate.copyFrom(local);
308
- else Vector3.TransformCoordinatesToRef(local, worldMatrix, positionToUpdate);
309
- };
310
- system.startDirectionFunction = (worldMatrix, directionToUpdate, particle, isLocal) => {
311
- const index = particle.id + directionIndex;
312
- directionIndex += 1;
313
- const sway = strandRadius * 0.16;
314
- const local = new Vector3(
315
- (sequence01(index, 3) - 0.5) * sway,
316
- 0.86 + sequence01(index, 4) * 0.52,
317
- (sequence01(index, 5) - 0.5) * sway,
318
- );
319
- if (isLocal) directionToUpdate.copyFrom(local);
320
- else Vector3.TransformNormalToRef(local, worldMatrix, directionToUpdate);
321
- };
322
-
323
- return system;
324
- }
325
-
326
- function createStrandMeshes(scene: Scene, root: { getAbsolutePosition(): Vector3 }, params: PlayerUpgradeParams): { dispose(): void } {
327
- const effectScale = Math.max(0.05, params.scale);
328
- const count = Math.min(90, Math.max(0, Math.round(params.strandCount * 0.62)));
329
- const color = params.color;
330
- const radius = Math.max(0.04, params.strandRadius * effectScale);
331
- const strandLength = Math.max(0.05, params.strandLength * effectScale);
332
- const riseHeight = Math.max(0.1, params.strandHeight * effectScale);
333
- const width = Math.max(0.005, params.strandWidth * effectScale * 1.25);
334
- const speedScale = Math.max(0.2, params.riseSpeed / DEFAULT_PARAMS.riseSpeed);
335
- const duration = Math.max(0.22, params.lifetime * 0.72 / speedScale);
336
- const states: StrandMeshState[] = [];
337
-
338
- for (let index = 0; index < count; index += 1) {
339
- const angle = sequence01(index, 0) * Math.PI * 2;
340
- const localRadius = radius * lerp(0.42, 1.08, sequence01(index, 1));
341
- const mesh = CreateCylinder(
342
- `player-upgrade.gold-strand-mesh.${index}`,
343
- { height: 1, diameter: width, tessellation: 6 },
344
- scene,
345
- );
346
- prepareVfxMesh(mesh);
347
- mesh.parent = root as any;
348
- mesh.position.x = Math.cos(angle) * localRadius;
349
- mesh.position.z = Math.sin(angle) * localRadius;
350
- mesh.position.y = 0;
351
- mesh.renderingGroupId = VFX_RENDERING_GROUP_ID;
352
- mesh.isVisible = false;
353
-
354
- const material = new StandardMaterial(`player-upgrade.gold-strand-mesh-material.${index}`, scene);
355
- configureGlowMaterial(material, color, 2.8);
356
- material.alpha = 0;
357
- mesh.material = material;
358
-
359
- states.push({
360
- mesh,
361
- material,
362
- height: strandLength * lerp(0.68, 1.18, sequence01(index, 2)),
363
- startDelay: sequence01(index, 3) * Math.min(0.34, params.lifetime * 0.18),
364
- duration: duration * lerp(0.78, 1.12, sequence01(index, 4)),
365
- });
366
- }
367
-
368
- let age = 0;
369
- let disposed = false;
370
- const observer = scene.onBeforeRenderObservable.add(() => {
371
- if (disposed) return;
372
- const deltaSeconds = Math.min(0.05, scene.getEngine().getDeltaTime() / 1000);
373
- age += deltaSeconds;
374
- for (const state of states) {
375
- const localAge = age - state.startDelay;
376
- if (localAge < 0 || localAge > state.duration) {
377
- state.mesh.isVisible = false;
378
- state.material.alpha = 0;
379
- continue;
380
- }
381
- const t = Math.min(1, localAge / state.duration);
382
- const grow = Math.min(1, t / 0.32);
383
- const fadeOut = Math.max(0, 1 - Math.max(0, t - 0.66) / 0.34);
384
- const alpha = params.opacity * fadeOut * Math.min(1, t / 0.12);
385
- const currentHeight = state.height * grow;
386
- state.mesh.isVisible = alpha > 0.01;
387
- state.mesh.scaling.set(1, currentHeight, 1);
388
- state.mesh.position.y = currentHeight * 0.5 + riseHeight * easeOutCubic(t);
389
- state.material.alpha = Math.min(1, alpha * 1.35);
390
- }
391
- if (age >= params.lifetime + 0.45) dispose();
392
- });
393
-
394
- function dispose(): void {
395
- if (disposed) return;
396
- disposed = true;
397
- scene.onBeforeRenderObservable.remove(observer);
398
- for (const state of states) {
399
- state.mesh.dispose();
400
- state.material.dispose();
401
- }
402
- }
403
-
404
- return { dispose };
405
- }
406
-
407
- function createExpandingRings(scene: Scene, root: { getAbsolutePosition(): Vector3 }, params: PlayerUpgradeParams): { dispose(): void } {
408
- const effectScale = Math.max(0.05, params.scale);
409
- const ringCount = Math.max(0, Math.round(params.ringCount));
410
- const ringDuration = Math.max(0.18, Math.min(0.9, params.lifetime * 0.38));
411
- const minRadius = 0.14 * effectScale;
412
- const maxRadius = Math.max(minRadius + 0.01, params.ringMaxRadius * effectScale);
413
- const thickness = Math.max(0.002, params.ringThickness * effectScale);
414
- const color = params.color;
415
- const states: RingState[] = [];
416
-
417
- for (let index = 0; index < ringCount; index += 1) {
418
- const mesh = CreateTorus(
419
- `player-upgrade.expanding-ring.${index}`,
420
- { diameter: 1, thickness, tessellation: 96 },
421
- scene,
422
- );
423
- prepareVfxMesh(mesh);
424
- mesh.parent = root as any;
425
- mesh.position.y = 0.03 * effectScale + index * 0.012 * effectScale;
426
- mesh.renderingGroupId = VFX_RENDERING_GROUP_ID;
427
- mesh.isVisible = false;
428
-
429
- const material = new StandardMaterial(`player-upgrade.ring-material.${index}`, scene);
430
- configureGlowMaterial(material, color, 2.4);
431
- material.alpha = 0;
432
- mesh.material = material;
433
-
434
- states.push({
435
- mesh,
436
- material,
437
- startDelay: index * Math.max(0.02, params.ringInterval),
438
- duration: ringDuration,
439
- });
440
- }
441
-
442
- let age = 0;
443
- let disposed = false;
444
- const observer = scene.onBeforeRenderObservable.add(() => {
445
- if (disposed) return;
446
- const deltaSeconds = Math.min(0.05, scene.getEngine().getDeltaTime() / 1000);
447
- age += deltaSeconds;
448
- for (const state of states) {
449
- const localAge = age - state.startDelay;
450
- if (localAge < 0 || localAge > state.duration) {
451
- state.mesh.isVisible = false;
452
- state.material.alpha = 0;
453
- continue;
454
- }
455
- const t = Math.min(1, localAge / state.duration);
456
- const eased = easeOutCubic(t);
457
- const radius = lerp(minRadius, maxRadius, eased);
458
- const alpha = params.ringOpacity * (1 - t) * Math.min(1, t / 0.12);
459
- state.mesh.isVisible = alpha > 0.01;
460
- state.mesh.scaling.set(radius, radius, radius);
461
- const ringColor = brighten(color, 0.1 + 0.08 * (1 - t));
462
- state.material.diffuseColor = toColor3(ringColor);
463
- state.material.emissiveColor = toGlowColor3(ringColor, 3.4);
464
- state.material.alpha = Math.min(1, alpha * 1.45);
465
- }
466
- if (age >= getLastRingEndTime(params, ringDuration) + 0.1) dispose();
467
- });
468
-
469
- function dispose(): void {
470
- if (disposed) return;
471
- disposed = true;
472
- scene.onBeforeRenderObservable.remove(observer);
473
- for (const state of states) {
474
- state.mesh.dispose();
475
- state.material.dispose();
476
- }
477
- }
478
-
479
- return { dispose };
480
- }
481
-
482
- function getStrandTexture(scene: Scene): DynamicTexture {
483
- const existing = strandTextureByScene.get(scene);
484
- if (existing) return existing;
485
- const texture = new DynamicTexture('player_upgrade_gold_strand_texture', { width: 48, height: 192 }, scene, true);
486
- const context = texture.getContext();
487
- context.clearRect(0, 0, 48, 192);
488
- const glow = context.createLinearGradient(0, 0, 0, 192);
489
- glow.addColorStop(0, 'rgba(255,255,255,0)');
490
- glow.addColorStop(0.18, 'rgba(255,255,255,0.9)');
491
- glow.addColorStop(0.62, 'rgba(255,255,255,0.5)');
492
- glow.addColorStop(1, 'rgba(255,255,255,0)');
493
- context.fillStyle = glow;
494
- context.fillRect(19, 0, 10, 192);
495
- context.fillStyle = 'rgba(255,255,255,0.95)';
496
- context.fillRect(23, 8, 2, 168);
497
- texture.hasAlpha = true;
498
- texture.update(false);
499
- strandTextureByScene.set(scene, texture);
500
- return texture;
501
- }
502
-
503
- function getGlowTexture(scene: Scene): DynamicTexture {
504
- const existing = glowTextureByScene.get(scene);
505
- if (existing) return existing;
506
-
507
- const texture = new DynamicTexture('player_upgrade_soft_glow_texture', { width: 256, height: 512 }, scene, true);
508
- const context = texture.getContext();
509
- context.clearRect(0, 0, 256, 512);
510
-
511
- const vertical = context.createLinearGradient(0, 0, 0, 512);
512
- vertical.addColorStop(0, 'rgba(255,255,255,0)');
513
- vertical.addColorStop(0.16, 'rgba(255,255,255,0.28)');
514
- vertical.addColorStop(0.46, 'rgba(255,255,255,0.72)');
515
- vertical.addColorStop(0.78, 'rgba(255,255,255,0.22)');
516
- vertical.addColorStop(1, 'rgba(255,255,255,0)');
517
- context.fillStyle = vertical;
518
- context.fillRect(0, 0, 256, 512);
519
-
520
- const radial = context.createRadialGradient(128, 262, 6, 128, 262, 120);
521
- radial.addColorStop(0, 'rgba(255,255,255,1)');
522
- radial.addColorStop(0.18, 'rgba(255,255,255,0.76)');
523
- radial.addColorStop(0.48, 'rgba(255,255,255,0.22)');
524
- radial.addColorStop(1, 'rgba(255,255,255,0)');
525
- const compositeContext = context as unknown as CanvasRenderingContext2D;
526
- compositeContext.globalCompositeOperation = 'destination-in';
527
- context.fillStyle = radial;
528
- context.fillRect(0, 0, 256, 512);
529
- compositeContext.globalCompositeOperation = 'source-over';
530
-
531
- texture.hasAlpha = true;
532
- texture.update(false);
533
- glowTextureByScene.set(scene, texture);
534
- return texture;
535
- }
536
-
537
- function getHaloTexture(scene: Scene): DynamicTexture {
538
- const existing = haloTextureByScene.get(scene);
539
- if (existing) return existing;
540
-
541
- const texture = new DynamicTexture('player_upgrade_ground_halo_texture', { width: 256, height: 256 }, scene, true);
542
- const context = texture.getContext();
543
- context.clearRect(0, 0, 256, 256);
544
-
545
- const halo = context.createRadialGradient(128, 128, 18, 128, 128, 128);
546
- halo.addColorStop(0, 'rgba(255,255,255,0.74)');
547
- halo.addColorStop(0.34, 'rgba(255,255,255,0.42)');
548
- halo.addColorStop(0.58, 'rgba(255,255,255,0.16)');
549
- halo.addColorStop(0.76, 'rgba(255,255,255,0.04)');
550
- halo.addColorStop(1, 'rgba(255,255,255,0)');
551
- context.fillStyle = halo;
552
- context.fillRect(0, 0, 256, 256);
553
-
554
- texture.hasAlpha = true;
555
- texture.update(false);
556
- haloTextureByScene.set(scene, texture);
557
- return texture;
558
- }
559
-
560
- function getDisposeDelaySeconds(params: PlayerUpgradeParams): number {
561
- const ringDuration = Math.max(0.18, Math.min(0.9, params.lifetime * 0.38));
562
- return Math.max(0.2, params.lifetime) + getLastRingEndTime(params, ringDuration) + 0.35;
563
- }
564
-
565
- function getLastRingEndTime(params: PlayerUpgradeParams, ringDuration: number): number {
566
- return Math.max(0, Math.round(params.ringCount) - 1) * Math.max(0.02, params.ringInterval) + ringDuration;
567
- }
568
-
569
- function toColor4(color: VfxColorValue, alpha: number): Color4 {
570
- return new Color4(clamp01(color.r), clamp01(color.g), clamp01(color.b), clamp01(alpha));
571
- }
572
-
573
- function toColor3(color: VfxColorValue): Color3 {
574
- return new Color3(clamp01(color.r), clamp01(color.g), clamp01(color.b));
575
- }
576
-
577
- function toGlowColor3(color: VfxColorValue, intensity: number): Color3 {
578
- return new Color3(
579
- Math.max(0, color.r * intensity),
580
- Math.max(0, color.g * intensity),
581
- Math.max(0, color.b * intensity),
582
- );
583
- }
584
-
585
- function configureGlowMaterial(material: StandardMaterial, color: VfxColorValue, intensity: number): void {
586
- material.disableLighting = true;
587
- material.backFaceCulling = false;
588
- material.diffuseColor = toColor3(color);
589
- material.emissiveColor = toGlowColor3(color, intensity);
590
- material.specularColor = Color3.Black();
591
- material.alphaMode = Constants.ALPHA_ADD;
592
- material.transparencyMode = StandardMaterial.MATERIAL_ALPHABLEND;
593
- }
594
-
595
- function configureTexturedGlowMaterial(
596
- material: StandardMaterial,
597
- color: VfxColorValue,
598
- texture: DynamicTexture,
599
- intensity: number,
600
- ): void {
601
- configureGlowMaterial(material, color, intensity);
602
- material.diffuseTexture = texture;
603
- material.opacityTexture = texture;
604
- material.useAlphaFromDiffuseTexture = true;
605
- }
606
-
607
- function brighten(color: VfxColorValue, amount: number): VfxColorValue {
608
- return {
609
- r: lerp(color.r, 1, amount),
610
- g: lerp(color.g, 1, amount),
611
- b: lerp(color.b, 1, amount),
612
- };
613
- }
614
-
615
- function easeOutCubic(t: number): number {
616
- return 1 - (1 - t) ** 3;
617
- }
618
-
619
- function lerp(min: number, max: number, t: number): number {
620
- return min + (max - min) * t;
621
- }
622
-
623
- function clamp01(value: number): number {
624
- return Math.min(1, Math.max(0, value));
625
- }
626
-
627
- function fract(value: number): number {
628
- return value - Math.floor(value);
629
- }
630
-
631
- function sequence01(index: number, axis: number): number {
632
- if (index === 0) return 0.5;
633
- const strides = [0.618033988749895, 0.414213562373095, 0.732050807568877, 0.324717957244746, 0.754877666246693, 0.569840290998053];
634
- return fract(0.5 + index * strides[axis % strides.length] + axis * 0.173205080756888);
635
- }