@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.
- package/CHANGELOG.md +115 -0
- package/README.md +41 -0
- package/dist/effects/coin-burst/index.js +6 -2
- package/dist/effects/comet-trail-flame/index.js +3 -1
- package/dist/effects/debris-smoke/index.js +1 -0
- package/dist/effects/electric-arc/index.js +1 -0
- package/dist/effects/energy-shield/index.js +1 -0
- package/dist/effects/explosion/index.js +3 -2
- package/dist/effects/hit-flash/index.js +12 -4
- package/dist/effects/laser-beam/index.js +14 -2
- package/dist/effects/lumber_vehicle_upgrade_aura/index.js +1 -0
- package/dist/effects/monster-hit/index.js +2 -1
- package/dist/effects/player-upgrade/index.js +5 -3
- package/dist/effects/pulse-laser-volley/index.js +14 -4
- package/dist/effects/thruster-flame/index.js +3 -1
- package/dist/effects/treasure-attention/index.d.ts +54 -0
- package/dist/effects/treasure-attention/index.js +535 -0
- package/dist/effects/ufo-thread-flame/index.js +1 -0
- package/dist/effects/vehicle-upgrade/index.js +4 -1
- package/dist/effects/weapon-trail/index.js +8 -3
- package/dist/effects/wood_crusher_sawdust/index.d.ts +4 -0
- package/dist/effects/wood_cutter_sawdust/index.d.ts +4 -0
- package/dist/index.js +85 -17
- package/dist/types.d.ts +80 -0
- package/index.json +19 -1
- package/package.json +9 -4
- package/packages/effects/treasure-attention/vfx-params.json +49 -0
- package/tooling/vfx-cli.mjs +8 -1
- package/docs/AUTHORING_EFFECTS.md +0 -297
- package/docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md +0 -1580
- package/docs/VFX_LIBRARY_EXECUTION_PLAN.md +0 -229
- package/docs/VFX_LIBRARY_PLAN.md +0 -546
- package/docs/vfx-ab/RESULTS.md +0 -35
- package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/after.png +0 -0
- package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/before.png +0 -0
- package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/after.png +0 -0
- package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/before.png +0 -0
- package/docs/vfx-integration_chatgpt.md +0 -462
- package/docs/weapon-trail-visualization.html +0 -241
- package/packages/EffectPackageService.ts +0 -240
- package/packages/SceneVfxService.ts +0 -96
- package/packages/build.mesh.yellow_build_upgrade_indicator/assets/runtime/simpleMeshEffect.v1.js +0 -333
- package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.frag.glsl +0 -25
- package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.vert.glsl +0 -11
- package/packages/build.mesh.yellow_build_upgrade_indicator/assets/textures/effect_jianbian.png +0 -0
- package/packages/build.mesh.yellow_build_upgrade_indicator/effect.json +0 -927
- package/packages/build.mesh.yellow_build_upgrade_indicator/media/contact-sheet.png +0 -0
- package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.gif +0 -0
- package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.mp4 +0 -0
- package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.webm +0 -0
- package/packages/build.mesh.yellow_build_upgrade_indicator/media/thumbnail.png +0 -0
- package/packages/build.mesh.yellow_build_upgrade_indicator/metadata/description.json +0 -197
- package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-effect.original.json +0 -895
- package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-normalized-vfx-to-babylon-report.json +0 -241
- package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-overrides.json +0 -542
- package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-tuning-report.json +0 -69
- package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/asset-map.json +0 -125
- package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/lineage.json +0 -44
- package/packages/build.particle.golden_upgrade_starburst/assets/runtime/particleEmitter.v1.js +0 -210
- package/packages/build.particle.golden_upgrade_starburst/assets/textures/effect_guangdian.png +0 -0
- package/packages/build.particle.golden_upgrade_starburst/effect.json +0 -880
- package/packages/build.particle.golden_upgrade_starburst/media/contact-sheet.png +0 -0
- package/packages/build.particle.golden_upgrade_starburst/media/preview.gif +0 -0
- package/packages/build.particle.golden_upgrade_starburst/media/preview.mp4 +0 -0
- package/packages/build.particle.golden_upgrade_starburst/media/preview.webm +0 -0
- package/packages/build.particle.golden_upgrade_starburst/media/thumbnail.png +0 -0
- package/packages/build.particle.golden_upgrade_starburst/metadata/description.json +0 -161
- package/packages/build.particle.golden_upgrade_starburst/provenance/03-effect.original.json +0 -762
- package/packages/build.particle.golden_upgrade_starburst/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
- package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-overrides.json +0 -436
- package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-tuning-report.json +0 -40
- package/packages/build.particle.golden_upgrade_starburst/provenance/asset-map.json +0 -115
- package/packages/build.particle.golden_upgrade_starburst/provenance/lineage.json +0 -40
- package/packages/build.particle.orange_upgrade_transform_sparks/assets/runtime/particleEmitter.v1.js +0 -210
- package/packages/build.particle.orange_upgrade_transform_sparks/assets/textures/b5b27ab1-e740-4398-b407-848fc2b2c897.png +0 -0
- package/packages/build.particle.orange_upgrade_transform_sparks/effect.json +0 -887
- package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.gif +0 -0
- package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.mp4 +0 -0
- package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.webm +0 -0
- package/packages/build.particle.orange_upgrade_transform_sparks/media/thumbnail.png +0 -0
- package/packages/build.particle.orange_upgrade_transform_sparks/metadata/description.json +0 -179
- package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-effect.original.json +0 -859
- package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
- package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-overrides.json +0 -478
- package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-tuning-report.json +0 -40
- package/packages/build.particle.orange_upgrade_transform_sparks/provenance/asset-map.json +0 -114
- package/packages/build.particle.orange_upgrade_transform_sparks/provenance/lineage.json +0 -41
- package/packages/build.particle.wood_chip_dust_burst/assets/runtime/particleEmitter.v1.js +0 -210
- package/packages/build.particle.wood_chip_dust_burst/assets/textures/effect_yanwu4.png +0 -0
- package/packages/build.particle.wood_chip_dust_burst/assets/textures/suipian3.png +0 -0
- package/packages/build.particle.wood_chip_dust_burst/effect.json +0 -1400
- package/packages/build.particle.wood_chip_dust_burst/media/contact-sheet.png +0 -0
- package/packages/build.particle.wood_chip_dust_burst/media/preview.gif +0 -0
- package/packages/build.particle.wood_chip_dust_burst/media/preview.mp4 +0 -0
- package/packages/build.particle.wood_chip_dust_burst/media/preview.webm +0 -0
- package/packages/build.particle.wood_chip_dust_burst/media/thumbnail.png +0 -0
- package/packages/build.particle.wood_chip_dust_burst/metadata/description.json +0 -173
- package/packages/build.particle.wood_chip_dust_burst/provenance/03-effect.original.json +0 -1277
- package/packages/build.particle.wood_chip_dust_burst/provenance/03-normalized-vfx-to-babylon-report.json +0 -329
- package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-overrides.json +0 -749
- package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-tuning-report.json +0 -60
- package/packages/build.particle.wood_chip_dust_burst/provenance/asset-map.json +0 -146
- package/packages/build.particle.wood_chip_dust_burst/provenance/lineage.json +0 -69
- package/packages/effects/building-upgrade/effect.json +0 -6
- package/packages/effects/building-upgrade/index.ts +0 -387
- package/packages/effects/character-entrance/effect.json +0 -6
- package/packages/effects/character-entrance/index.ts +0 -308
- package/packages/effects/coin-burst/index.ts +0 -43
- package/packages/effects/comet-trail-flame/assets/textures/trail_5x4.webp +0 -0
- package/packages/effects/comet-trail-flame/index.ts +0 -106
- package/packages/effects/conveyor-upgrade/effect.json +0 -6
- package/packages/effects/conveyor-upgrade/index.ts +0 -401
- package/packages/effects/debris-smoke/assets/models/rock_debris.glb +0 -0
- package/packages/effects/debris-smoke/assets/textures/smoke22.webp +0 -0
- package/packages/effects/debris-smoke/index.ts +0 -101
- package/packages/effects/door_open_gold_flakes/assets/runtime/goldFlakes.v1.js +0 -397
- package/packages/effects/door_open_gold_flakes/door_open_gold_flakes.json +0 -498
- package/packages/effects/door_open_gold_flakes/index.ts +0 -11
- package/packages/effects/electric-arc/assets/textures/spark_4x4.webp +0 -0
- package/packages/effects/electric-arc/index.ts +0 -62
- package/packages/effects/energy-shield/effect.json +0 -7
- package/packages/effects/energy-shield/index.ts +0 -431
- package/packages/effects/explosion/assets/textures/baozha29.webp +0 -0
- package/packages/effects/explosion/assets/textures/glow01.webp +0 -0
- package/packages/effects/explosion/assets/textures/lizi01.webp +0 -0
- package/packages/effects/explosion/assets/textures/xulie_lizibaozha02_5x5.webp +0 -0
- package/packages/effects/explosion/index.ts +0 -114
- package/packages/effects/ground-tile-upgrade/effect.json +0 -6
- package/packages/effects/ground-tile-upgrade/index.ts +0 -410
- package/packages/effects/hit-flash/index.ts +0 -69
- package/packages/effects/laser-beam/assets/textures/laser_glow.webp +0 -0
- package/packages/effects/laser-beam/assets/textures/laser_noise.webp +0 -0
- package/packages/effects/laser-beam/index.ts +0 -6
- package/packages/effects/laser-beam-factory.ts +0 -135
- package/packages/effects/lumber-cart-suction/effect.json +0 -6
- package/packages/effects/lumber-cart-suction/index.ts +0 -399
- package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb +0 -0
- package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/.active +0 -1
- package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/1781589063890_upload.glb +0 -0
- package/packages/effects/lumber-debris-scatter/assets/runtime/solidParticleScatter.v1.js +0 -236
- package/packages/effects/lumber-debris-scatter/effect.json +0 -228
- package/packages/effects/lumber-debris-scatter/index.ts +0 -780
- package/packages/effects/lumber_vehicle_gear_spin/assets/runtime/gearSpin.v1.js +0 -202
- package/packages/effects/lumber_vehicle_gear_spin/index.ts +0 -11
- package/packages/effects/lumber_vehicle_gear_spin/lumber_vehicle_gear_spin.json +0 -338
- package/packages/effects/lumber_vehicle_upgrade_aura/assets/runtime/upgradeAura.v1.js +0 -353
- package/packages/effects/lumber_vehicle_upgrade_aura/index.ts +0 -11
- package/packages/effects/lumber_vehicle_upgrade_aura/lumber_vehicle_upgrade_aura.json +0 -365
- package/packages/effects/lumber_vehicle_wood_chips/assets/runtime/solidParticleScatter.v1.js +0 -310
- package/packages/effects/lumber_vehicle_wood_chips/index.ts +0 -11
- package/packages/effects/lumber_vehicle_wood_chips/lumber_vehicle_wood_chips.json +0 -335
- package/packages/effects/monster-hit/index.ts +0 -128
- package/packages/effects/player-upgrade/effect.json +0 -6
- package/packages/effects/player-upgrade/index.ts +0 -635
- package/packages/effects/pulse-laser-volley/assets/textures/bolt_guangshu.webp +0 -0
- package/packages/effects/pulse-laser-volley/index.ts +0 -204
- package/packages/effects/resource-collect-sparkles/effect.json +0 -6
- package/packages/effects/resource-collect-sparkles/index.ts +0 -254
- package/packages/effects/run-footstep-dust/assets/runtime/runFootstepDust.v1.js +0 -215
- package/packages/effects/run-footstep-dust/effect.json +0 -183
- package/packages/effects/run-footstep-dust/index.ts +0 -377
- package/packages/effects/space-shared.ts +0 -120
- package/packages/effects/thruster-flame/index.ts +0 -305
- package/packages/effects/tree-chop-dust/assets/textures/tree_chop_dust_atlas.png +0 -0
- package/packages/effects/tree-chop-dust/effect.json +0 -8
- package/packages/effects/tree-chop-dust/index.ts +0 -202
- package/packages/effects/tree-leaf-chop-burst/assets/runtime/particleEmitter.v1.js +0 -303
- package/packages/effects/tree-leaf-chop-burst/assets/textures/tree_leaf_chop_burst_leaf_atlas.png +0 -0
- package/packages/effects/tree-leaf-chop-burst/effect.json +0 -498
- package/packages/effects/tree-leaf-chop-burst/index.ts +0 -294
- package/packages/effects/tree_chop_dust_burst/assets/runtime/particleEmitter.v1.js +0 -318
- package/packages/effects/tree_chop_dust_burst/assets/textures/tree_chop_dust_burst_dust.png +0 -0
- package/packages/effects/tree_chop_dust_burst/index.ts +0 -13
- package/packages/effects/tree_chop_dust_burst/tree_chop_dust_burst.json +0 -412
- package/packages/effects/ufo-thread-flame/assets/textures/flame_flow.webp +0 -0
- package/packages/effects/ufo-thread-flame/assets/textures/flame_spike.webp +0 -0
- package/packages/effects/ufo-thread-flame/index.ts +0 -152
- package/packages/effects/vehicle-exhaust-smoke/assets/textures/vehicle_exhaust_smoke_atlas.png +0 -0
- package/packages/effects/vehicle-exhaust-smoke/effect.json +0 -8
- package/packages/effects/vehicle-exhaust-smoke/index.ts +0 -298
- package/packages/effects/vehicle-upgrade/effect.json +0 -6
- package/packages/effects/vehicle-upgrade/index.ts +0 -401
- package/packages/effects/weapon-trail/assets/textures/trail_wedge.png +0 -0
- package/packages/effects/weapon-trail/index.ts +0 -313
- package/packages/effects/wood-crusher-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
- package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
- package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
- package/packages/effects/wood-crusher-sawdust/index.ts +0 -43
- package/packages/effects/wood-crusher-sawdust/wood_crusher_sawdust.json +0 -592
- package/packages/effects/wood-cutter-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
- package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
- package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
- package/packages/effects/wood-cutter-sawdust/index.ts +0 -43
- package/packages/effects/wood-cutter-sawdust/wood_cutter_sawdust.json +0 -592
- package/packages/effects/wood-sawdust-effect-factory.ts +0 -194
- package/packages/effects/wood_crusher_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
- package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
- package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
- package/packages/effects/wood_crusher_sawdust/index.ts +0 -29
- package/packages/effects/wood_crusher_sawdust/wood_crusher_sawdust.json +0 -592
- package/packages/effects/wood_cutter_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
- package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
- package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
- package/packages/effects/wood_cutter_sawdust/index.ts +0 -29
- package/packages/effects/wood_cutter_sawdust/wood_cutter_sawdust.json +0 -592
- package/packages/effects/wood_peel_material_test/assets/runtime/woodPeelMaterialTest.v1.js +0 -196
- package/packages/effects/wood_peel_material_test/index.ts +0 -11
- package/packages/effects/wood_peel_material_test/wood_peel_material_test.json +0 -136
- package/packages/env.particle.forest_edge_fog/assets/runtime/particleEmitter.v1.js +0 -210
- package/packages/env.particle.forest_edge_fog/assets/textures/f12a23c4-b924-4322-a260-3d982428f1e8.png +0 -0
- package/packages/env.particle.forest_edge_fog/assets/textures/yuan.png +0 -0
- package/packages/env.particle.forest_edge_fog/effect.json +0 -854
- package/packages/env.particle.forest_edge_fog/media/preview.gif +0 -0
- package/packages/env.particle.forest_edge_fog/media/preview.mp4 +0 -0
- package/packages/env.particle.forest_edge_fog/media/preview.webm +0 -0
- package/packages/env.particle.forest_edge_fog/media/thumbnail.png +0 -0
- package/packages/env.particle.forest_edge_fog/metadata/description.json +0 -148
- package/packages/env.particle.forest_edge_fog/provenance/03-effect.original.json +0 -827
- package/packages/env.particle.forest_edge_fog/provenance/03-normalized-vfx-to-babylon-report.json +0 -196
- package/packages/env.particle.forest_edge_fog/provenance/04-preview-overrides.json +0 -432
- package/packages/env.particle.forest_edge_fog/provenance/04-preview-tuning-report.json +0 -28
- package/packages/env.particle.forest_edge_fog/provenance/asset-map.json +0 -124
- package/packages/env.particle.forest_edge_fog/provenance/lineage.json +0 -41
- package/packages/generation.ts +0 -14
- package/packages/index.ts +0 -14
- package/packages/legacy-particle-runtime.ts +0 -113
- package/packages/manifestRuntimePackage.ts +0 -519
- package/packages/registry.ts +0 -85
- package/packages/rendering.ts +0 -13
- package/packages/resource.particle.viscous_oil_suction/assets/runtime/top_disk_suction/particleEmitter.v1.js +0 -424
- package/packages/resource.particle.viscous_oil_suction/assets/textures/Glow ad.png +0 -0
- package/packages/resource.particle.viscous_oil_suction/effect.json +0 -1254
- package/packages/resource.particle.viscous_oil_suction/media/preview.gif +0 -0
- package/packages/resource.particle.viscous_oil_suction/media/preview.mp4 +0 -0
- package/packages/resource.particle.viscous_oil_suction/media/preview.webm +0 -0
- package/packages/resource.particle.viscous_oil_suction/media/thumbnail.png +0 -0
- package/packages/resource.particle.viscous_oil_suction/metadata/description.json +0 -122
- package/packages/resource.particle.viscous_oil_suction/provenance/03-effect.original.json +0 -1051
- package/packages/resource.particle.viscous_oil_suction/provenance/03-normalized-vfx-to-babylon-report.json +0 -200
- package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-overrides.json +0 -581
- package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-tuning-report.json +0 -28
- package/packages/resource.particle.viscous_oil_suction/provenance/asset-map.json +0 -111
- package/packages/resource.particle.viscous_oil_suction/provenance/lineage.json +0 -45
- package/packages/types.ts +0 -107
- package/tooling/check-dist-runtime.mjs +0 -166
- package/tooling/generate-index.mjs +0 -55
- package/tooling/test-guardrails.mjs +0 -157
- package/tooling/validate-boundary.mjs +0 -92
- package/tooling/validate-effects.mjs +0 -304
- package/tooling/validate.mjs +0 -53
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,121 @@
|
|
|
12
12
|
|
|
13
13
|
<!-- release.mjs 在此行下方插入新版本区块 -->
|
|
14
14
|
|
|
15
|
+
## v0.3.3 — 2026-06-30
|
|
16
|
+
|
|
17
|
+
### 改动
|
|
18
|
+
- fix(npm): exclude source and previews from package
|
|
19
|
+
|
|
20
|
+
### 特效
|
|
21
|
+
- 无新增 / 删除(仍 37 个)。
|
|
22
|
+
|
|
23
|
+
## v0.3.2 — 2026-06-30
|
|
24
|
+
|
|
25
|
+
### 改动
|
|
26
|
+
- ci: publish npm without provenance
|
|
27
|
+
|
|
28
|
+
### 特效
|
|
29
|
+
- 无新增 / 删除(仍 37 个)。
|
|
30
|
+
|
|
31
|
+
## v0.3.1 — 2026-06-30
|
|
32
|
+
|
|
33
|
+
### 改动
|
|
34
|
+
- ci: publish npm package on version tag
|
|
35
|
+
- docs: 集成依赖示例 bump 到 v0.3.0(最新)
|
|
36
|
+
- release(v0.3.0): minor — +1 effect(s) commit dist (做法A)
|
|
37
|
+
- feat(treasure-attention): 宝藏注意力光效进 @fps/vfx (#8)
|
|
38
|
+
- fix(release): git() 对 stdio:inherit 的 null 返回兜底
|
|
39
|
+
- release(v0.2.0): minor — +13 effect(s) commit dist (做法A)
|
|
40
|
+
- docs: 集成指南版本号指向 v0.2.0 + gitignore 验证产物
|
|
41
|
+
- docs: 新项目集成指南 INTEGRATION.md + 根 README
|
|
42
|
+
- fix(service): 监听 root 销毁清理 activeHandles 防泄漏 (#11 M4, #9-D)
|
|
43
|
+
- fix(upgrade): initialHeight 走 spawnParams.offsetY 存活通道 (#11 M12/M13, #9-F)
|
|
44
|
+
- fix(vfx): 纹理泄漏/重建 + renderingGroupId 全库丢弃 + 默认值 footgun (#11 M1/M2/M5/M6/M7-M10)
|
|
45
|
+
- feat(placement): 契约增量 + laser/pulse 定向武器 (#9-A/B/C, #11 M14)
|
|
46
|
+
- fix(player-upgrade): 金丝粒子 dispose(false) 防共享纹理被销毁 (#11 H1)
|
|
47
|
+
- fix(laser): inputs.aim lookAt 用 Space.WORLD(挂点上的 root)
|
|
48
|
+
- feat(placement): inputs 通道(运行时外部信息)+ laser 迁移到 inputs.aim (#9 P1)
|
|
49
|
+
- feat(placement): 给 16 个 demo 特效标注 placement (#9 P1)
|
|
50
|
+
- feat(placement): 放置系统定稿设计 + 契约加 placement 标记 (#9 P1)
|
|
51
|
+
- fix(spawn): 修复挂载时特效双偏移(契约:root 摆放归 service 独占) (#9 P0)
|
|
52
|
+
- feat(spawn): 完整旋转+缩放+可选挂载 spawn transform (#9 P0)
|
|
53
|
+
- feat(energy-shield): add hemisphere glass dome effect
|
|
54
|
+
- refactor(weapon-trail): 彻底重命名 axe-trail → weapon-trail (#5)
|
|
55
|
+
- chore(axe-trail): 显示名 斧刃拖尾 → 武器拖尾 (#5)
|
|
56
|
+
- fix(monster-hit): 修复重复播放不出现 + 闪光光条发不出 (#5)
|
|
57
|
+
- feat(monster-hit): 移植怪物受击特效(闪光+血)进 @fps/vfx (#5)
|
|
58
|
+
- build: 重建 dist + axe-trail 可视化文档 + 刷新 index (#5)
|
|
59
|
+
- feat(axe-trail): 双层亮芯+光晕 / 能量流动 / 跟随挂载点 / 刃头火花 (#5)
|
|
60
|
+
- feat(axe-trail): 加 bloom 辉光 + HDR 亮度 + 加色混合,转能量光风 (#5)
|
|
61
|
+
- feat(axe-trail): 加「拖尾朝向」水平面/竖直,默认水平面 (#5)
|
|
62
|
+
- feat(weapon): 移植斧刃挥砍拖尾 axe-trail 进 @fps/vfx (#5)
|
|
63
|
+
- chore(lumber): 升级特效改名为通用语义 (#5)
|
|
64
|
+
- feat(space): 持续型特效补「生命周期模式」通用参数(对齐 lumber) (#5)
|
|
65
|
+
- refactor(space): 重组特效 — 删溅射副激光 + 合并爆炸/碎石烟雾 (#5)
|
|
66
|
+
- chore(space): 调整 4 个特效中文名 (#5)
|
|
67
|
+
- refactor(space): 把激光/爆炸合并包拆成 14 个独立特效包 (#5)
|
|
68
|
+
- feat(space): 移植激光束/命中爆闪/受击电弧/陨石拖尾 (#5, 特效1-5)
|
|
69
|
+
- feat(space): 移植陨石爆炸组合 meteor-explosion (#5, 特效6-11)
|
|
70
|
+
- feat(space): 移植 UFO 丝束尾焰 ufo-thread-flame (#5, 特效13)
|
|
71
|
+
- feat(space): 移植脉冲激光齐射 pulse-laser-volley (#5, 特效12)
|
|
72
|
+
- feat(space): 移植战机推进焰 thruster-flame (#5, 特效14) + 验证 harness
|
|
73
|
+
- feat(guardrail): debugParams↔defaultParams key 一致性检查 + 特效制作指南
|
|
74
|
+
- feat(guardrail+release): 新增特效 guardrail (#3) + 版本更新机制 (#4)
|
|
75
|
+
|
|
76
|
+
### 特效
|
|
77
|
+
- 新增:coin-burst, comet-trail-flame, debris-smoke, electric-arc, energy-shield, explosion, hit-flash, laser-beam, monster-hit, pulse-laser-volley, thruster-flame, treasure-attention, ufo-thread-flame, weapon-trail
|
|
78
|
+
|
|
79
|
+
## v0.3.0 — 2026-06-30
|
|
80
|
+
|
|
81
|
+
### 改动
|
|
82
|
+
- feat(treasure-attention): 宝藏注意力光效进 @fps/vfx (#8)
|
|
83
|
+
- fix(release): git() 对 stdio:inherit 的 null 返回兜底
|
|
84
|
+
|
|
85
|
+
### 特效
|
|
86
|
+
- 新增:treasure-attention
|
|
87
|
+
|
|
88
|
+
## v0.2.0 — 2026-06-30
|
|
89
|
+
|
|
90
|
+
### 改动
|
|
91
|
+
- docs: 集成指南版本号指向 v0.2.0 + gitignore 验证产物
|
|
92
|
+
- docs: 新项目集成指南 INTEGRATION.md + 根 README
|
|
93
|
+
- fix(service): 监听 root 销毁清理 activeHandles 防泄漏 (#11 M4, #9-D)
|
|
94
|
+
- fix(upgrade): initialHeight 走 spawnParams.offsetY 存活通道 (#11 M12/M13, #9-F)
|
|
95
|
+
- fix(vfx): 纹理泄漏/重建 + renderingGroupId 全库丢弃 + 默认值 footgun (#11 M1/M2/M5/M6/M7-M10)
|
|
96
|
+
- feat(placement): 契约增量 + laser/pulse 定向武器 (#9-A/B/C, #11 M14)
|
|
97
|
+
- fix(player-upgrade): 金丝粒子 dispose(false) 防共享纹理被销毁 (#11 H1)
|
|
98
|
+
- fix(laser): inputs.aim lookAt 用 Space.WORLD(挂点上的 root)
|
|
99
|
+
- feat(placement): inputs 通道(运行时外部信息)+ laser 迁移到 inputs.aim (#9 P1)
|
|
100
|
+
- feat(placement): 给 16 个 demo 特效标注 placement (#9 P1)
|
|
101
|
+
- feat(placement): 放置系统定稿设计 + 契约加 placement 标记 (#9 P1)
|
|
102
|
+
- fix(spawn): 修复挂载时特效双偏移(契约:root 摆放归 service 独占) (#9 P0)
|
|
103
|
+
- feat(spawn): 完整旋转+缩放+可选挂载 spawn transform (#9 P0)
|
|
104
|
+
- feat(energy-shield): add hemisphere glass dome effect
|
|
105
|
+
- refactor(weapon-trail): 彻底重命名 axe-trail → weapon-trail (#5)
|
|
106
|
+
- chore(axe-trail): 显示名 斧刃拖尾 → 武器拖尾 (#5)
|
|
107
|
+
- fix(monster-hit): 修复重复播放不出现 + 闪光光条发不出 (#5)
|
|
108
|
+
- feat(monster-hit): 移植怪物受击特效(闪光+血)进 @fps/vfx (#5)
|
|
109
|
+
- build: 重建 dist + axe-trail 可视化文档 + 刷新 index (#5)
|
|
110
|
+
- feat(axe-trail): 双层亮芯+光晕 / 能量流动 / 跟随挂载点 / 刃头火花 (#5)
|
|
111
|
+
- feat(axe-trail): 加 bloom 辉光 + HDR 亮度 + 加色混合,转能量光风 (#5)
|
|
112
|
+
- feat(axe-trail): 加「拖尾朝向」水平面/竖直,默认水平面 (#5)
|
|
113
|
+
- feat(weapon): 移植斧刃挥砍拖尾 axe-trail 进 @fps/vfx (#5)
|
|
114
|
+
- chore(lumber): 升级特效改名为通用语义 (#5)
|
|
115
|
+
- feat(space): 持续型特效补「生命周期模式」通用参数(对齐 lumber) (#5)
|
|
116
|
+
- refactor(space): 重组特效 — 删溅射副激光 + 合并爆炸/碎石烟雾 (#5)
|
|
117
|
+
- chore(space): 调整 4 个特效中文名 (#5)
|
|
118
|
+
- refactor(space): 把激光/爆炸合并包拆成 14 个独立特效包 (#5)
|
|
119
|
+
- feat(space): 移植激光束/命中爆闪/受击电弧/陨石拖尾 (#5, 特效1-5)
|
|
120
|
+
- feat(space): 移植陨石爆炸组合 meteor-explosion (#5, 特效6-11)
|
|
121
|
+
- feat(space): 移植 UFO 丝束尾焰 ufo-thread-flame (#5, 特效13)
|
|
122
|
+
- feat(space): 移植脉冲激光齐射 pulse-laser-volley (#5, 特效12)
|
|
123
|
+
- feat(space): 移植战机推进焰 thruster-flame (#5, 特效14) + 验证 harness
|
|
124
|
+
- feat(guardrail): debugParams↔defaultParams key 一致性检查 + 特效制作指南
|
|
125
|
+
- feat(guardrail+release): 新增特效 guardrail (#3) + 版本更新机制 (#4)
|
|
126
|
+
|
|
127
|
+
### 特效
|
|
128
|
+
- 新增:coin-burst, comet-trail-flame, debris-smoke, electric-arc, energy-shield, explosion, hit-flash, laser-beam, monster-hit, pulse-laser-volley, thruster-flame, ufo-thread-flame, weapon-trail
|
|
129
|
+
|
|
15
130
|
## v0.1.1 — 2026-06-27
|
|
16
131
|
|
|
17
132
|
### 改动
|
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @fps/vfx
|
|
2
|
+
|
|
3
|
+
Babylon.js 共享 VFX 特效库。导入即用 + 项目内调试面板。消费模型 = **模型 C**(runtime/类型来自包,本地 `import.meta.glob` 装配注册表);发布 = **做法A**(dist 提交进 git + tag,消费端 clone 即用,无需 build)。
|
|
4
|
+
|
|
5
|
+
## 文档入口
|
|
6
|
+
|
|
7
|
+
| 我要… | 看 |
|
|
8
|
+
|---|---|
|
|
9
|
+
| **新项目集成特效**(消费包、装配、播放、挂载、瞄准) | [`docs/INTEGRATION.md`](./docs/INTEGRATION.md) |
|
|
10
|
+
| **制作一个新特效**(作者侧:create 契约、debugParams、guardrail) | [`docs/AUTHORING_EFFECTS.md`](./docs/AUTHORING_EFFECTS.md) |
|
|
11
|
+
| **放置系统设计**(placement 标记 / inputs 通道 / 挂载 / binding) | [`docs/vfx-placement-design.md`](./docs/vfx-placement-design.md) |
|
|
12
|
+
| 架构 / 计划 | `docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md`、`docs/VFX_LIBRARY_PLAN.md` |
|
|
13
|
+
|
|
14
|
+
## 30 秒上手(消费方)
|
|
15
|
+
|
|
16
|
+
```jsonc
|
|
17
|
+
// package.json —— git tag 即版本锁
|
|
18
|
+
"@fps/vfx": "git+ssh://git@github.com/forge-play-studio/fps-vfx-library.git#v0.3.0"
|
|
19
|
+
```
|
|
20
|
+
```ts
|
|
21
|
+
// src/assets/vfx/index.ts —— 装配注册表(模型 C)
|
|
22
|
+
import { buildRegistry, type VfxRegistry } from '@fps/vfx';
|
|
23
|
+
export * from '@fps/vfx';
|
|
24
|
+
const modules = import.meta.glob('./effects/*/index.ts', { eager: true }) as Record<string, Record<string, unknown>>;
|
|
25
|
+
const params = import.meta.glob('./effects/*/vfx-params.json', { eager: true }) as Record<string, { default: unknown }>;
|
|
26
|
+
export const VFX_REGISTRY: VfxRegistry = buildRegistry({ modules, params });
|
|
27
|
+
```
|
|
28
|
+
```ts
|
|
29
|
+
// 接线 + 播放
|
|
30
|
+
const svc = new EffectPackageService(scene, { registry: VFX_REGISTRY });
|
|
31
|
+
svc.playEffectPackage('thruster-flame', {}, { parent: engineSocket, offsetIsLocal: true });
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> ⚠️ 三个最易漏:① `@fps/vfx` 要加进 vite 构建白名单;② Babylon 要 `resolve.dedupe`;③ glob 只扫本地 `effects/*`,每个要用的特效都得建本地桩(re-export 库内预制即可)。详见 [INTEGRATION.md](./docs/INTEGRATION.md)。
|
|
35
|
+
|
|
36
|
+
## 开发
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm validate # 契约 / 特效 lint / typecheck / build / dist 禁运行时
|
|
40
|
+
pnpm build # 生成 dist(发版前)
|
|
41
|
+
```
|
|
@@ -7,13 +7,15 @@ const DEFAULT_PARAMS = { count: 6 };
|
|
|
7
7
|
const coinBurstEffectPackage = {
|
|
8
8
|
id: "coin-burst",
|
|
9
9
|
nameZh: "金币爆出",
|
|
10
|
+
placement: "world",
|
|
10
11
|
debugAnchor: "playerRight",
|
|
11
12
|
defaultParams: DEFAULT_PARAMS,
|
|
12
13
|
debugParams: [
|
|
13
14
|
{ key: "count", label: "金币数量", kind: "number", min: 0, max: 20, step: 1 }
|
|
14
15
|
],
|
|
15
|
-
create({ scene, root, position, params }) {
|
|
16
|
+
create({ scene, root, position, params, scale, renderingGroupId }) {
|
|
16
17
|
root.position.copyFrom(position);
|
|
18
|
+
const s = scale ? scale.x : 1;
|
|
17
19
|
const pool = new FlyerPool(scene);
|
|
18
20
|
const coinMat = new StandardMaterial("vfx_coin_mat", scene);
|
|
19
21
|
coinMat.diffuseColor = new Color3(1, 0.78, 0.1);
|
|
@@ -21,9 +23,11 @@ const coinBurstEffectPackage = {
|
|
|
21
23
|
coinMat.specularColor = new Color3(1, 0.9, 0.4);
|
|
22
24
|
for (let i = 0; i < Math.round(params.count); i++) {
|
|
23
25
|
const c = CreateCylinder("vfx_coin", { diameter: 0.8, height: 0.12, tessellation: 18 }, scene);
|
|
26
|
+
c.scaling.setAll(s);
|
|
27
|
+
c.renderingGroupId = renderingGroupId ?? 0;
|
|
24
28
|
c.rotation.x = Math.random() * Math.PI;
|
|
25
29
|
c.material = coinMat;
|
|
26
|
-
pool.spawn(c, position, new Vector3((Math.random() - 0.5) * 6, Math.random() * 6 + 4, (Math.random() - 0.5) * 6), 9, 1.9);
|
|
30
|
+
pool.spawn(c, position, new Vector3((Math.random() - 0.5) * 6 * s, (Math.random() * 6 + 4) * s, (Math.random() - 0.5) * 6 * s), 9 * s, 1.9);
|
|
27
31
|
}
|
|
28
32
|
let disposed = false;
|
|
29
33
|
const disposeTimer = window.setTimeout(() => disposeAll(), 2300);
|
|
@@ -38,6 +38,7 @@ function excludeFromGlow(scene, mesh) {
|
|
|
38
38
|
const cometTrailFlameEffectPackage = {
|
|
39
39
|
id: "comet-trail-flame",
|
|
40
40
|
nameZh: "拖尾火焰",
|
|
41
|
+
placement: "socket",
|
|
41
42
|
debugAnchor: "player",
|
|
42
43
|
defaultParams: DEFAULT_PARAMS,
|
|
43
44
|
debugParams: [
|
|
@@ -47,7 +48,7 @@ const cometTrailFlameEffectPackage = {
|
|
|
47
48
|
{ key: "fps", label: "播放帧率", kind: "number", min: 1, max: 60, step: 1 },
|
|
48
49
|
{ key: "tint", label: "火焰色", kind: "color" }
|
|
49
50
|
],
|
|
50
|
-
create({ scene, root, position, rotationY, params }) {
|
|
51
|
+
create({ scene, root, position, rotationY, params, renderingGroupId }) {
|
|
51
52
|
root.position.copyFrom(position);
|
|
52
53
|
root.rotation.y = rotationY;
|
|
53
54
|
const tex = new Texture(trailTextureUrl, scene);
|
|
@@ -71,6 +72,7 @@ const cometTrailFlameEffectPackage = {
|
|
|
71
72
|
m.material = mat;
|
|
72
73
|
m.isPickable = false;
|
|
73
74
|
m.parent = trailRoot;
|
|
75
|
+
m.renderingGroupId = renderingGroupId ?? 0;
|
|
74
76
|
excludeFromGlow(scene, m);
|
|
75
77
|
}
|
|
76
78
|
let t = 0;
|
|
@@ -22,6 +22,7 @@ const DEFAULT_PARAMS = {
|
|
|
22
22
|
const explosionEffectPackage = {
|
|
23
23
|
id: "explosion",
|
|
24
24
|
nameZh: "爆炸",
|
|
25
|
+
placement: "world",
|
|
25
26
|
debugAnchor: "playerRight",
|
|
26
27
|
defaultParams: DEFAULT_PARAMS,
|
|
27
28
|
debugParams: [
|
|
@@ -98,7 +99,7 @@ const explosionEffectPackage = {
|
|
|
98
99
|
f.mat.alpha = 1 - k;
|
|
99
100
|
if (k >= 1) {
|
|
100
101
|
f.mesh.dispose();
|
|
101
|
-
f.mat.dispose();
|
|
102
|
+
f.mat.dispose(false, true);
|
|
102
103
|
flashes.splice(i, 1);
|
|
103
104
|
}
|
|
104
105
|
}
|
|
@@ -112,7 +113,7 @@ const explosionEffectPackage = {
|
|
|
112
113
|
scene.onBeforeRenderObservable.remove(observer);
|
|
113
114
|
for (const f of flashes) {
|
|
114
115
|
f.mesh.dispose();
|
|
115
|
-
f.mat.dispose();
|
|
116
|
+
f.mat.dispose(false, true);
|
|
116
117
|
}
|
|
117
118
|
flashes.length = 0;
|
|
118
119
|
sparksPs.dispose();
|
|
@@ -17,9 +17,19 @@ function makeFlareTexture(scene, inner = "255,255,255", outer = "90,160,255") {
|
|
|
17
17
|
dt.hasAlpha = true;
|
|
18
18
|
return dt;
|
|
19
19
|
}
|
|
20
|
+
const flareByScene = /* @__PURE__ */ new WeakMap();
|
|
21
|
+
function getFlareTexture(scene) {
|
|
22
|
+
let dt = flareByScene.get(scene);
|
|
23
|
+
if (!dt || dt.isDisposed) {
|
|
24
|
+
dt = makeFlareTexture(scene);
|
|
25
|
+
flareByScene.set(scene, dt);
|
|
26
|
+
}
|
|
27
|
+
return dt;
|
|
28
|
+
}
|
|
20
29
|
const hitFlashEffectPackage = {
|
|
21
30
|
id: "hit-flash",
|
|
22
31
|
nameZh: "命中蓝白爆闪",
|
|
32
|
+
placement: "world",
|
|
23
33
|
debugAnchor: "playerRight",
|
|
24
34
|
defaultParams: DEFAULT_PARAMS,
|
|
25
35
|
debugParams: [
|
|
@@ -29,9 +39,8 @@ const hitFlashEffectPackage = {
|
|
|
29
39
|
],
|
|
30
40
|
create({ scene, root, position, params }) {
|
|
31
41
|
root.position.copyFrom(position);
|
|
32
|
-
const flareTex = makeFlareTexture(scene);
|
|
33
42
|
const ps = new ParticleSystem("vfx_hit_ps", 400, scene);
|
|
34
|
-
ps.particleTexture =
|
|
43
|
+
ps.particleTexture = getFlareTexture(scene);
|
|
35
44
|
ps.emitter = position.clone();
|
|
36
45
|
ps.minEmitBox = new Vector3(-0.1, -0.1, -0.1);
|
|
37
46
|
ps.maxEmitBox = new Vector3(0.1, 0.1, 0.1);
|
|
@@ -57,8 +66,7 @@ const hitFlashEffectPackage = {
|
|
|
57
66
|
if (disposed) return;
|
|
58
67
|
disposed = true;
|
|
59
68
|
window.clearTimeout(disposeTimer);
|
|
60
|
-
ps.dispose();
|
|
61
|
-
flareTex.dispose();
|
|
69
|
+
ps.dispose(false);
|
|
62
70
|
root.dispose();
|
|
63
71
|
}
|
|
64
72
|
return { root, dispose() {
|
|
@@ -5,6 +5,8 @@ import { CreatePlane } from "@babylonjs/core/Meshes/Builders/planeBuilder";
|
|
|
5
5
|
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
|
|
6
6
|
import { Texture } from "@babylonjs/core/Materials/Textures/texture";
|
|
7
7
|
import { Color3 } from "@babylonjs/core/Maths/math.color";
|
|
8
|
+
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
|
|
9
|
+
import { Space } from "@babylonjs/core/Maths/math.axis";
|
|
8
10
|
import { Constants } from "@babylonjs/core/Engines/constants";
|
|
9
11
|
import { L as LIFETIME_DEBUG_PARAMS, a as LIFETIME_DEFAULTS, b as makeFlareTexture, s as scheduleLifetime } from "../../chunks/space-shared-BXV1hc0k.js";
|
|
10
12
|
const arcTextureUrl = "data:image/webp;base64,UklGRgBmAABXRUJQVlA4WAoAAAAQAAAA/wEAfwAAQUxQSJBMAAARGbRt2wZEsin/X9z+ENH/CWjYjS3Pjsb/Qi09DORrHoJsm37+2IeIiEQQlGxlX1u2ra22uUJCb7Nt0zZHjnZZ8nfJlPK3Fcq+r+a+L+/7ff9fi/btSOBDe43Pk9se8UdBtCd3vEJ7jcuS9/h/Fg1mwQ5BXqE94YrgwXiDPwqBWRToTQYFmnxCoCdUFg1GQTCLD+2+GYw3KE+uvAajINokIiZgArg8/z9JkmTrZZy77/u+7/v+ki/a933f9/2FuEdWzbyCD6pBfwULGeqiyVyGTsIcZHKWtJkpWOqHZtBfeVHI2TRM5nJ0CtZh5jBhNzIFC/3QHPZXohsyNA2TuZwVrYtMjm7CamQKGH9WDYO5DN2CeZnLWcIaZHJ0kw4zBQsZOgXjIpexosVcxhLWIJfDVERMAJfn/2dLku2gez3PE56hud+/zIzmeZ7neZ69VkRmd1eV+rCHGf3VrBTomjZLBTO8RqFkTc22iqXWG/jqslJAQ7NQsoYHRsGGl201K/2RzX4qlgp24GGpZE2bbSVLaLbUMPdLaBYKdqFhKFnDw7aKFTVbKpba7MBioWRNLwslO/CwrYStiJiA39F/Mfd3elb/w+9xxT3/Lv9cRY/x+r2q2HZs2qHj1G4Mewzr96DiZDc6nOzQYRjoGaB+eJTzaqqPpzhZ+8VA7eeITsZQP/zJ5n7rYyb2y+D3GJuhMoU50NPT/bf7Zb84fv0QJ87t6DLoGUN9jMRuoRi1G9sOJ8twZB8ZQf1QJk531IAeD14fD7Et1Mns6IZxh9GlI2PUx0juZf7bV5xbFDGe46E++mJbqP3Y1rmDnsFUY3K8j8Gcesb1ySh0dNDhYDjdQ08PsV96uhitj7ago3o7nPzdhkJmZiY6mCimwn83UUhf2C9H5KWVzemOZzNen2zChsOZmZupDKeCxT56gh7qIyy2pbZR28wgU2TaLaNlGBQs+/RkQPLyibM7nO7oeAaj9Qkl9kvPiDuWqSA9iNH6yIrTO1EIMmMb25kyWmMIZRhkgLxU4o51x/OK9BxvtD5pxMk6I7Y9FcMRxDCGeTnFHZbdIMjmdE0Kihqk06YyjOPmZRFnd3Pnuw3zQKP1iSH2yziQGdtCNjUp0tFMCtEjg5dzoJtmRkdjKgIdZZTh3EKpiYI2UYR25yUZJ2s4bmNzMDrOQEcHHR3oi/1yRJfjDOsTQGw7SmUKNToady5HRhHDjOSlE6gaRW0ToYg71lSmgkAmBIKaFKXIixana3s7nBx1ZiZkGh02xpeR0aBngPp4i/2iBEVmIOcV1ICgpId4CcfpNaPIDhVoB4qhpC+GFaijCGUq2uGFjzse7phTZ87MzfEzNnoU6mPsZFGb2gaRmZnziprqGMTReans1/5O2CyoEpiZmZMiAhXDKsowjrmXFyb2a5w4NxsdOsroqEEH83kZ1sdR9lqjo4Ladqr7DSYxVaBY6OalGlRHmQqdERVVMrOmwJRCQxCUUKowVUhoh25e2Ng9boODYZk7y1CMNZFpGxOjo0bTU0eZaD06i/1iHzkC9bGzXzo0ChGZsV13aIbRtZ6pCIXSnbwsgmM4u5lRoU1HE5hIh+DP4L9B919AhZpUepXW7kF38gLEbt04oLbLfphixhQzppiI6dzGRE3l6KX39JAx1MdIToyWsSiC2lbQc9LREKpQaBhQL9mhioUGFg4piQ5Xfr9/xEAf/A1Xq+mE5q/61/4jFhUUynbarXohY1s47AbLfmZs464TYtZR44EaGwZyFPWxcXociFMRVHQT/avbTiF/y1/Z/oNdDVSNRY2dzMsgG3XmzlKVqmLhD/sD/rG/4+9Sk7/mbz71H1qv/7Nl/vD3/Pv/zEyiAsV0Zof9PFdx7uF07QaZ2dx9xjZSpjqGPIg8COpjoz0cxjI5TBRZdg/bL/+ov/0/+e39xNXJX/vXv9/9S4+gUcSwZdHRUdu8cEENu0skuGJhpdLoaK6fff36z/AHWf7s33R5918eQmvTu7/fP/wIEhWIokvi3D5HcW4zsykxE1OIbceJGpsaM2ae5kbpUabCRAV1VI9hBj1G6yMue8ZBiaU/7N/3FsvgCMf1vXe++fU/YvmP/6l/c+b97uBv/fW/9XrXQwVFEFrHrdRL89gsInae3nnt9SMrSiy9Pr5+5ZuH7uyylqDJy7/PP3G32KYiSijCWGTTo2rkOYltB8eozCgxQ1FqN9PJwJRpe83BMTOlKxTpmglF9Ix0MoL6KLtjVDx88nR9++b15fHx27fkeP/lp+/f+9WXvlvevP/dv+32fsfN3+Enu5NGVFCDYX8zNnmhQu0viPB0/ewz3n7+SyulVTFd3Rw3KBZifvmn/ulHiyIloUJEVEtWaenzEXSUg4oUISF7ZIOODnecufYyI2OjRyijQRGdpRtmBPWDFuYzGLozU6J6fWtZj+/evtKr40t+/bf8FX/N6Zuv/CvP7tcnB26+fXtIyoKGKMrEwM4LHWcGEjxd/4TH7/Or6+evp+QgRbEuTGbboll0v/zsH72YNRElMR67Q+bEoEOeXWzLAaEIIlCxW+rcUaMDa81X9KCQ46kRQRnPCBlD/cCkwz1n56hh7CxCoqNp8/TDy+WJr6Yf/SnzL/gcN3/j9LtFZ/a3uVtbaKGCSLRrZziOnbwogeEgjCZw/fR7fvyt9fijN2+CJywcCGbEgPKjv/NfeL3WzCxoYu3FfjKGdhQrzyQdyuAYTYdCRzfNDA5GY6ZGB2rUgMmq23HCdnZ+ueqhxHiMF9IbkxrpC52MdGE+b9kYOu6HMqoD1pK9ktaij+hYD3/Sz/6sz+3OZoXm676d/71d1yQlJLGb6mGMzQvdcRAlhOsnf8yHH7zKoce65LMHV968/nhzfbt5ioXb893g8d91v15frxbabOkppUiyqbIKN6ph3kuUUgeWKpmaiobQylyImWAiM3MTlaentZ5rcv7tTw8i1KQNhpnGyjAU6YhhHHc+g9ypGDB03MvAoJVgRqSklSrLar3/tZ/9bd/nnYPDzqJJTYv9fHL6fI5UNCFOTNuOvbwIQe0WQtMfPn342kodyx/3o88s3vz8wzfevnriE156L26frw8bC1+7Pl/Pa+fvd7dzqUAgs0lSNTcOxZB7qW2dLEJDNCiVQkSRCYNab3W5HrfxtgVO5ob0BAoxLIUaEJTxGM0R23lP99vNfWbTUrsDQkoO0bIsR5vv/S//Y71/h8PF2WGWBov55MPrWUQlFedG6W3vRQzKgSIR6sBKV5br+uTh/e3ho/df8/FPfunp/UeffHTynnLx/HLz6nCLMovYn252LYW2FmRiOr13v3XHQBAotBYJRaAynZvFa2jbtp1PZkRNRQJFoJSaaiCoMRlhnLE772G/2C/sF3vLYI+MhXkqth0cQ7EgTgFRxYPiFVQbuPoyPOsmXRYUOFcVFepTPIZgodUMRZw4nLpQUwVtm0q37Jeekfq+iv2OyqgIDQaxTgBv3ery9cmT/+yb78bl/d/w9/8df+Nf/+PL539uTO8uqtlut2dACjXUqkFiHQXW4qjBWbBqe2yPAyzYPk2tYAG1KLZPA9IH7aOACtCABxRUQBEFhBEuBUGcgFUsFpwFHBbUOgdYHGKwasGJs0ZjEkhvg0mFvhjt0UOP8TrWfrFf7Jf9gv2A6IMM7tzeDmNRSIEiDXgUvHpEGpjcdnGWzYMSoXXtTtgDBSNTE97OqgrBeaQWDUUQFEsnUN8/sVs0iN20nFMBrANVBXZn/911Vdus68ohZE0C82gXRSX71ckyCgpSBwbrTW1xBRYMWIwFsDgEg2JxiKO37tNfeUABnHgHKgCKQIMoiEBge1BxkK1fklamqKyxzoIFcE6OAAsOLDgcWJzFghOa0ZKJglBIx2IvEA+xPNypJqNleaAyDmQvChUUBCWwAWiDN5O//K/c03UNAQ209ZydS8FakRqu6m1beRw4b6OCOpYghvX9EWeWGUU1rSUWR38LeEjPqqJO4ziOWf/8b9smsCeKtktKksu7KqagNlggxVmMwYIBa50FsIDxDqsWcD1HWkB5UA8g4DyAIqogqAgqiqAIgtYZi6vdkgIQZ51V+lss7kEsDgtYwDrAQlAxWkUZD+XIEeSBHuI0GPZAPVBRxCZUEVRFRQgIUBpRndwew3CNQhlCa0VcJWlqxVCe6GExqNpYMKg4Cw2hpEcdj/oeTEedW8Q2dhfAPogGsLsrwJJNn/21nzz8m1cQmd0u+d/fbcMSTpZJWtSprR3WupqGtDBgsQ5rHRa0HeUVDsD+xggKCIjQ61FFUBFF8Aoi0ADC4vH2aYz2AA4wgICzgAUcDgsOLKYHsK4nChlQ6ogeNdX3wTTS06OnHyWdZqHGXhkHo9LLIooFqh5FvUekESVjdN8RrufzAoYNkyQrCkNQrKi8puy5HW0BJ2KdxoyGUNCQdKIH9RCmmqDnqI69UbopTSVxKOBAvaiVxgVV4txkc/OX/PT5fD5/WaC2KMZ3TwdfoHSrajymina4PVi6g+4vqj1QABaLY1qm2XVtAXFWMd4cJeA40tNfBAFUQBVREbyCRxDF06vzL3m7A2/p2yO2xwIYwVm1FrCAAUwf5yxUsl8U6VE1leoxFULXTDU2VfSF/UKXQd/WVJOaaqoJpqJMBW3k3I0wFykaNB4RFa8ojSgZwdUV/K3Hczx0BYkY51yztL5y5G01LsvW7RVxAi5REQ1VUxVCGRh0qaOmkYdZxWgUgbCq4NR6sA4BbAQNsHhzOkFVM8A6qrMn28EWIGbMeBdFcczeF4cuDPNP91AFkAsUSpjbhr1gnTjBcbTxWLV6BAhiFSeiVhFUBRVExTsvQoOAqo8zsqtnFxc1jUXAgVV6a8XiAAsWcFAD2B6wRi2ukCJRkJrSaypVU02FtiUoD54eEzXV5PhVhs3o0LFXexVFowqKiqpII4oGBOtb+P0vo+tofnPfWQgbvOOgZzPysJDBYBGKqS2+FiepiiYlikGbxpDQ43te+7VfVNusZbAYiwq9KlFEw3hQT8BypIFq+fYzQmDLgCqJdruqil1NU7K5SGmrgLwqrMWAEZq9ABYe6KMKAgigCKh6qyLaoyKoR1FBGVEOv0z+69zgnUUBrBMnYBWrFmq1OHFYMIDVGiwWZ3FElSgJaioEhUI5ZgZ1rPFJTTUNalKmUgrN8ceJKKQ0Dl7A4xFpRPEQZKdX3H49eMR8fd/R9zBkxWJznVxFcZWezH6apSnOItBSKk0qFJmKUCMEeSj9iOHs7ARNG1n6OvB9KHYRTJnlgLWFtWpRA+Tbt8hzeDp7y45dshvst6YA2ZecnoIhqb7YWyjA0tTOgjvKfRwKCKAgqOJBRH0PHsULqKJNOe+eze8uqtzwsRqw9FosDgvWiasBnLNgFCkVJTGsAfVgoufBJsfs0UNNVQptUoOxUZnRoZkNVseRIniVBgIVVDXAHxxTXVbd6eedBywhtN3bu9N/KHk/96Y4W16eTtaFVZwasqiUpAgpU0mPIrrFg+6XAfYLuldkdtPQaJpaziJSZy/PPbWzVKtUju87T13X9d3Tp+86wNJRs1tNJyQkl08qeB2Nl4MohwLQAArSNE0xDoMFxVpDX28sgBN3lACC0OtBFQ+Kd42C0uBRRFFoJpvDZmN3u3F+SXqEAhlQA3vA9Omt1VoHYKB2iKVhhsWwdQuF9KipptRUR0w1VehEb2N9ZNKJYWmKom0DEzIFRSRYjrEa8QQoBCqqisIkXPOSqLv5+bkHrKJQTub5Xf7J7nLSOl3F6cj6pgiofaXSRCOhIdRURE2tGy66nkGM7h05UIFCB8SRtitx1lkivvo7NxSwvwM2w4wGZhf/wa9+9e9NALO+wbCm15EnwL9ytx0v8yXsgYIaa0xtMcZiMVjAYsLpKsJjeizqsH08fRUBaERBIUBUhEYUlcYqqtBO7jdtG0OcX+ZjwBxx+HFZ1Qnk7JEUjAOLc9SABWcdWMve6WbeGV2gSNSkIIaFdGqq8qCTnr3jJpVSg9Hh3EIoTTVNPIrSiEqDqlcbhiFrZszvhx5A6VV7Ps/vuN3lk9alaQyNaYzzRlKpVKUsaKiiENTIMdOX/eCYRaD2F40jzaTB4Li72/zOhmZP9W653DZAw2x19v33//6vQqBcg+UlI+aTEPIEnjwab3m6zKECU1Bga2NqizEW56wTA5ZNW1VVP0CNx2bE9KoAqogCSoCoII2oIAGoCr5u2mf3LVo3gSXPB1UCkBkIu78QVlR5sd+T1impARwW289gwTp894fPvrlgQRoRVAJFBkcWjqoj9piOVTIXqqYaGHeJiKaVZlGPNoh6RdWrhJOQNbvhfDjHgLf9gPPj2eXpZc6kdSmxFCoG0WbRtJSmEiWKGpAHe/i1zUwQJela0Rs9+qx92VYNxMkl7AFD3+ikhXVKD+uG8PZ8EpLC7MlgOb7OlwQl+21RFbUxqbOmttYZ63BgKaDtmnpVgeuRoEHLYJCjCAEG1CMIAaKINIJHaVA8qBAsrm7A1k1rgTjPc8cIX4K2ITXkMfvKpqu6sMY6LAZwOGpwFrj68anD2+e2J+a2mzWEFCEKOYKaxooaTLU17IP0nrCqKKbWiZiGYlRkKo02zbIQURuI4gX1NC6ce4Wwm0zqi2jOxkIIZNDe7PJszGoyYW801MLUqdayEHOfJZooQillmBpMFKYabGlbpKejRu0XGocl1pLD2aZJv/sm6ILaRNEgfzsOjDFNoM1otNk8PK1JACZtCx7K0TwMmzn46Jff7AYs7ickZ2eXd/YaLoAIKDDgHGAdTTgxMVaAUZOY2FDTq4EYoAE86mEEjHDSiIAnA5icv2wzKjabdAxaVThoDeCBhhqbgo3yPGGGkmMBA1Zw1GBPv70+eXl+drZy9f5kdXGX8/MT88VGM4xATVqPY9ZEekz2yNZop6WWWYxXOpMZphCzNNo0a6UiGiDaiKh4Gt9O5hOAZx0fLiIIGGKnQ3ont8ntFxGEbcUs6IZAerdeEqLNRCpBoxAm5ch0UrZt69iVhRDbxmHp6tIjFMAvx5sAMlgu31bAPq/X3c1f93f/zu/8LZa7RJnMbwALFJBbXs490dP/icXjFqjPztjd3XH3Cy4sOWopwIBzGGhapanB1VVBf1XwWQUNPYHSACMgQMCrNACT8/Mua2HzOdmTwVipaNoMrah8mWXUxa6ArqGK7mYzbshfmAq8AWcBS/3i5M7JybxqnFw9faqsz5x4dgGhN0I6oUxqKsT41CfdMZs5bVEywGQ6GYRGm2atNKIBogGi4ht8OZ+EwLpr+PDLO+ByOMPBqzAAmMwP33WrkDDncsV8TrJbSlv21trcU9VEUAJVqLGHOTlubCtqqUabgoUn4yHQwODuhN7w/Oc3664FbefRyXreQX5Ujn368kcPDGCxTJKJOYeYE06uf3ExjqKkrqHGYQ04wAAOEMCBVcBDVkC7DvMIFCUYAQgqKooSjMLTqwkt5f3nm81s9qtBuqc8EAOThCxemYKgyesmaMHNVqtdeHr5dF9ha4AaC+5qWeagGZ6/aJfrdffF6ZdL01AQNRURRxbRyQnHq1brmoOSgWl3yoxQqWZZ1koaL4xAVAI8ZTmZA91L2P3y3R5wM3BjJ5tDCzBZuA/dKgxJ6zyP09GkSSwe7860qFZREkqgCvUgU001FRNkCgIN2qWuadqqAe7uglE2Uli+q8ARbl59dd9Vj/7N3/4Xstt59GwOF4OKGnAkwPqv/oq+g+uTOyacswY8F796nyTXg9jUYK0D24PBKKafoACCeIXzb8P8ya7wKId5Q5RYPIoSjJrDIZzQlu3i2y9Bxr9639Bu8mtoJ1lpxnGc1jRNUNTNBJTV7CwnpKq2icHYGoyD/bJgpTNDWHfOT1arkYZCUKQTBMpk38VJG5kw55DGYrTZn7ZhzXBYsVbmkUNQRfE+gIDyfNMNCdurMP5QYaiIaVHS99Hk2SIAXhYDYBT6Jq9czmixaNrjr3+2o5mpSgmaQJnSoybFRE3UttRkpGZqUGgXdVXpsnBaWIufTuclgw9LYHH+MCR6cXl2cvL+l/v1el0CZzsSS0hvw3z+D/4AHorq5OykGo9R4Pzx4tFnCXd330ABxIABLIZexwN6ywglm/5RF+y+3wLl7aCmbIkKGgjbdnMQFoTDm/tDBj7//jLYbPaPErhdLLD7eDuLmC+AIKDIoyjZRhGGCSezGZY9BWANiajSUEuPOtm2ZT7dMNOjppoCzfhUQmx7+oTF6c0bzza15BANIYaXGmp0WHBYsZZVRwLUiyfwBDA5v3p2M5zZZ7D8bLnNqnypoWsVx345nn4O8Ny9bupmVCrk5Ewtifbiq5+sdj1aSqqkhBTEkdEdXZPR2m9m1FKJa+roag6HA3CHW1J271YE4QHGy++/32q+3+YDupcXQAXER9Tz4/ntH/yAKBiS5Mn4/Xg23qKLP/n49SOovl8ue9RAAWAgBcI5RAl4PAJNQJstTg/B3YccsyZ3Hl+ul7SE4fRAu19Mub257yxxEJCjJXuTwJAMYL+9u4gMV2E4CtvBHdHriCgjxM9WgyiHOIZlZhHSFxFFE9k7f/nzzz9d0YSSHuMx3nBifO/rH39x+U9+vl3WbS4lNAynZ2y7NFlNkyNFvfjAN0JAeX4V3vzRjjkX6++XW5b7ZaIO8YGSy1bMlz0yKIq6ycpW9gms0sTy4g9/8tycSKVSSgoJRcaO343nFBUVa1zTahoB0wAzJrB6sVoBdjx7Em3Zpnm+jBzrmw8zsIDlyPnxsTz+gx/qQDFAPRi/f/3fjRnD3/FDfvFsvlwuX3/BQAED1DiAxZvJPPrwPpkUXiGgCbKSdtQd5F1VQY0DZQ6LNgxHLbTT6eP7DmwynnZ8xDij9+lng8kpRRG8nFxHyfYOuhaSJ7DKK3JyVut51lRQmggVxfmffuv/eYWBEkrOWSOTgj0mlD/9MH9+fTnnkNai1NTUYOxtq6mkuUqOtAGNDxrfeEaT8iqcDHcruNiRb9nu9pdQiUu1DNoBg9X8S+Ben4qjZlJO5CzPUmny4tc/ea5ZJEqLUqIkUCPRo0dPN9VUk6OrokjquqRN0wIMMEiB3TtyCPIng3HcU+V5VbO+eb2irxzRMj9+tvnhJ10xalwPDC6+2A5mgwEPv74lYYny7z6KAUuR18bQsHl5ANK370OLgSYosyllEDBqq7wq6hpr8IxOR2EYEtAyffN40QLVZXPrP8rR0WoCcV0wZZXPdru1A6rVEwoq4j37wzxfri0NTVEhRrz7/779xW9CQxmoaf8r95dNI4NuqzDd/Hw5mw+t1vOaWUmhmTAYVKUJ1zjarB5FByEHWq6uNsDsfEr65I7dPt4msTGeNIW9SFgO9MPo9Jyp5GmAAc69jS82rHnx61e3q24WkQUaJQWLh9qNViZEacdVtLos40BssJi4sdYtBwGqYT7Yqc7S3Sw3eVrTvXoXmT5qFUHxhN3NYvrmzw7BAgFQjGfj18vLyz1wVQLzlioGD6vPXs9oboazFUEbrMa3jiVwy6GDBii3MANmwGQ9n5cQenj+7M0tChSWQD8urILFgDdxlRdpvKVMB7NZDlSQXrq/XD1nhYYEFqjlR3/6+i8ZSw81xbs/d3P5m9fGm/M9LLx7d9cxL+t5CTMVDIxpP7S4pq1rehwdHRs2h+nV1e2QaBdMEsbvZ7O42u9tCmBMbWuxRsPozF2NOrpuBQ52LKbjzVsb1l98++pWUpqFhWpEFaSQQY/0bmrdcCrLfkhZUm1Tq4+2cR5DDVQ5bQyHhiSK49nexNZaJTt+GnN0AyoQhM+mi+lPXg1ZXazwCzZrIDi7/NlvnZ0w2QwpgfE4xk+7rng9Y8i7DymMlOnPby9ZtsfHuPWOHYR2w7Y74W4HzOdzRoCfdj+8GdLnN96C8Yp1pEU+hmA1Hnxgh2ZxYeP+4rnnsLbYRkNg9+aPzr5hs0U1o+t3333WHv1kt0XbcoVCQZdaLet5iSiYMafdUVStlbQrPY5F1w2zw+nm6vb8ykK0uiT/D1+M2e/ZY3rASGHjxhrH5cno/iHrv45Vt/7AxXgx/fzs8zOL05evVqtuSWmmmWgDKTVVoCYUpu7oaUY2asFKtGlz+PJXr3+DfbwHPLADYsBCstsTV3Vdp5ayG9f9xAMNgA9k8cPi8PtfY6+/+ZCw2Pjh9FUAZ5/+82cnRVm2ekpoYmi66biA3fjdaucBVw5/uIXN7THXCdDAeXcKAXckd9dUa/AhE128eS785rAGb7ABOMACrBiPX89WdRAx4/D8wmG31syzItq29ej+6MVvnG6eWZsy0ra8e3H15Toxem60bTmBZn3WDstSSsOFwdwgXFlJu9LjWI+T7uH9FZvDwyt658CnM3r3gMOBklJoIk0Kkkezh38PdLPdzfpkPF48utycbeazl16ttFKLm+102M1II0pQhYkywDSmRSgWWpZ2Vbz7OeLZrs9wKhApkEdg4gpTG+OEIbsalF4PHhrwIxY/nL76+rnl+ptvBlAyffWS0/Pt2T/3ny73EEwmp0PIbh4y+4zZsObDp6uWICCc/gk23z7m+lrW9O1OD5RAzt3ddhfNr8KQ825Kj7P72tN4afSjWcVirImhMGUQsGI+6gMMVrPXg6TAixemi9vnd2urw7ys15UFGk6/+urF9eXTy083zuZFTaHQHu/WjnyHl3dG33E1Mc/31/ctVAlzzHUZ6NDoEjmW5dHD4+Lqp5+EnHdzIBqPifiQlLcrTliSIFiweHBSpRqsrNu69WTS4ZeX4c+DfLXccube6pvnrClffXX15MlZm0mI5sh3Nw6rcHXCZrYd9IEiSA6j0Uarb398qYnzuDFGinLiyf+t2S7N98muSMFY74t0NLp6l9KjoKACvkE0aNtn5//f+Q9w/YtvtkQt5XS6fvg5q//4T7+HEdPpmyHDYTeEGbunDH/XDkEi5msOp3PgJE/6BPdrMiAgTWC3nncQAA7Gu+Qpvaes5wsgOKKGWQcI+zymYxzvy6w94AEDeIqz7XXEyRVvNpuf3d9z2Ny7vGeF4sVf/MPLzaXTTcfZb+4EC4LChA98eHztEU7nGzdPXV9frq6eXMrcxTRHLxgoqfLE8ujh8eurTz45D7ssABj/Yka7ixjOOn68PJDkFdQNGI9Ba1NIHHeOXTenDZcv/Fcvn97hFu2Pn9tZ617+8tv7J39wpmGlNA3vPtx82Nt9eXf12ZsXnv1uYyu4QvYOrKYcSePVtcmrfA+MgPnV+XfFXRTlFDiDrfeQzq/id1atdwINoKgQoJ6gOT189dXzY/jil99so5gsmM6nm246/OI90wZe3TN7/YHuvhsyq14MX82ZzQJYt0B8wvIiiih7WK+VyXodEjQwD+g3GI9Xg12Ep6RYw2HItCcAstVq9Xze9QyidHTI9nez+LA5aHgURRqd7ZbVilObO2/uvb08W91yxsZ7//+Pnv3kaTm9eYHVxS8uVvdcubTHHsE/gLVHNji9YXV/eX23XtksTUq4mBgzimoty6Pr1Q8/PT89v2oDei/fz7boim74fMjTUy5Z5nkq+0YtiNTOYGtWOolW65e0p9Xlen7/x8/dFur5q/uztfWeX3/35v4futDEYtbgw69PN5vLHe9vePHOP/MEuiMPtytzieIqTdPmSE5yhWkLCOd3I2APWEsNlPPN/t1KwQNqlQARVAgUH8jtz79af3uMPvrlN7tBjKWbTpner2HsmQGzVQTd9L5bVy9esP795/Sa5eXyydn5V6vPIlrAs6ZhfvtqOmL/WfItMzisd6uTwYycHb4M8PQfAXNgNGO2eTm8yfLxIGXUbYbxfrAknLSEcZ5MjucUAcnd5/er1fL0VHOmeJ37e+vTDW/evD+7/H/Z7extDjdf3z1/5bnnG2Z0TtfrsctHd492xlfz7fz6/n6dLJX0XDA2CBZd8vTpe1/x9feubiEAyC9/drFlXO34+fNXMFg++nb5MF8WqY1FLGpRS2pTnQM713EIww8631zc21zeunf2zdv1yoK//MYf3JpltixLrE+/+mxz/WiDm3a1cvX1j199s3Sd6Wpcyvrk8fWHo3AozaJpkrtcp3Q5JPkZJ3UEWtcAzlB267N3KywNVgVFEVXBIxrgR9z+A+2zb4+V5fffjMf7LXQLGLYwGAE7wAO+W8NqPBs+7BYHuH5yuTxbwek/Ob97lBCGfea3355mEP+HX9DM4BR2YyAGPNDAnDGUHVggmIFn+HLEHoXuuCSr8+0yBYqEycs382gOyVsEq7Mv71/g1tl9x+HNezenNod37+zZFa65vdtcuoSnp08v7vjltvX15eufvsaCuPPp59c2VjTmbY+hdHSxjk9+9c3tg8vDww9adF8Dy+0vl0vOTlZ1/X8cn/51f3TP9fXr2eoHzXe+FkeOWoXAO72fXPwsuYVDQMoetXr79sLZ/TfX1wrT48PqDGs7axv59dd3X17O9hb7qdJ93c6aLR7/wv/97Wl99v/ff/Xd9737drCxYD5KB/UupnV1XTdBmGtqCwRb29paH8bp68oigIAqHhGkEcF71YDw6+fn5+fPQs4uBu/HdysIwqBRevckxFlASQaMAMMlQVsNdnFtDN53v/eY/3BFZ6ZM18ePjyHi5CRhP8tTCxigBpQKS8goB99CCbHPQAx+1HbZgoqHVyShVqvZdk+Rw/zNOV2AWfoWLTl8+eh+w8ZzG5zy7enuADf7q9VZP7zcPdtx5XK+xfIZe+xPiMu3m0e3Grbc/aVnn1/XjqUxDJNL7M8VPY6f/vT65tXl4Ue/RXK3fh7mlx+WyyVnJyeFYfwd51/93z85/sn2d1+vDhyYAS+5wAHNzj59jZNgcU7vCKvdvd+9ur67njAb3U0Hh7Wd8++uvznMLEY7k+LDzeHs8cOnX/2/nzhef/fFp1/8xoMPPnzF9KVgefgImFXgAQc4+log3eeXyzimV1BEaIBGEPWIJ7At9z+enp8PX4Xo+P3bp7vxqqYJPFCTp0UKTQnaEs4Bm7/OAQxgPN5t3vxj8W+vmEx5c/74mOg6STScn9j9+DoG72oghraqcgM0wKhksoA4X2cj2Md1tm7PF3B6BQ4wcRLv8yTfn59PrgIl3WhYLWdvX6/uPT/H+dOfn37+O/Ycbt58uL3z8ubZEybjpx8+/PyGi20fUNzCyt3d2f/5+bMnz+3K1uXylslmIjiO45MnF5eHH/wWrK/C/PJuuVxydnKSAAhn/9LLnxwfdw+Hs9WBw3OOuUQuZskk2fP0actw9uHUMoF5e0HnqnH/J7+9vtsf6KwmDlubQzvw3m83azFjimEvth2/+YN3v/ru5sMPvvvi0+/9/INP375+/fgN3uDy0cNHnBEBGZACBrDgMJz8+l1VWbU9CggqiCqNiNLg8bRhd/rXnQ/PT88VfvVnZrtxVRUAqd8T25Te1kEZlGDyvOpRQPAomzf/zO63MdM3bz6fw6NH0ct5/jZJqSCgSKGwabweLfv5HsKwG0KertcZ9X4PWXN6WITnEwRbKGDJz9J2wiGD2NJHRq/PLszPH3vh8vNHGxZM3nz17uLRu/PfOQTnePkXP3y2RXcMExZ3Xl0/+18/fX1YLZy43G4wzAs00cOxWF8+/ODryeRqQhRdLpfb5SBJoIYAuP5njznu6A4cDmvOUI7vnkYJva+B/ffuELGO3jt1c/PtrxqeXP/2em08dlas2s67dn23W5a5VS1G92S/GLz5TW964IPf/vrN5z5/evzk8csvv/DdV++98vDAyV0F7QLo6G8AxadFu8BiegCv4EE86kWkkQYPrdye/tHL8Pz0FvjF69e7PM9rjUkLYixpDw5oAF/RV8GIxytsfu//XO3y6ZvpEK5/Fb2cXL5NCKgINE9tXed5OjodX+b09M5pmWdDUkan3QgXxeAp2+Hk/FWoUMRQEBIAGZBW01Y/RkN4tMKO3bwasP/aXTvcHF4ve++8efHdZwzMER5wgKWozJPLkxfj8WAM+NY2AUVtaotz4NVTW2uLgJHLqun2w3a6vn118c319bNlNmPV2fKLdn717uVPf/E/f/Zrdv/3n/YhP/Hmq49fczw9Pb5/8L5lQrg4/PQckA9/elcAI9b4HbMVkNJW+WVOHlcU7LE9gAAC+BjvgRHLNSFkrPFBN3p1CQrT7ObbbMXdLz6M09m+dtQUFEBZ7lnGmGVOUlW0uWktlUhFam76vi0XizfTxeL2c3C//vUsXSVUQJ1EuApS+hsAb1Rwlr4BPqN79b//33/58Yun4tUrv+/L38zTaXdivrx0tlod1n7uy8MytrXf2p6fnFzZenf6xXekOdCgPttsbqZTBKK4YD4nmnvAAQLoEUdagBpweYgCcQvGaBH5EvGNR+m1rLqPVu/zs/z9ajy+Y0ecjtoYTZ04BMRYRK0WWREUsBicsnt0d/eL3avNoWBFQmdZ3bq6/uwXv/jeF8SP/Xd++e3HX725ubo+unrwoPNwrQe+fD7tEH72YTns8MM17erFOGeMaamWeZ6T5+z3BYYCD3j6NzBy0AowHEIJw4ItlpCyY3X/VQd3v/71bhxXRcrIxADltHsxo7CwtJaktZZSaW2iugp0+LJcDDfT6e3jz3H8+t1FAjAmIWHPnn2fGYBRqwDWG4/SMCpZe/UP/l9/7imee/+tn366c+Ls7MXZ88szl6vDqv/8/Tf3Fkf2rRP9xPnelcePf6zMY5xXPG22eD6dLgBHOpIHWYXBR7KAsUgP9FHA83F+NwMHw3ACAoTAnjy5HI9X212ya9hXGXFgEWcEsYCC1oErA3+5+fG7zd03d79wvTl4f2q+v1ytO2bpBwyf+egL6scmP+8bLofr5dHT4p2bT+ajA4fT4avnQjSzXYcAhhXxeLDatnCR53kdV7nZx3tX01fxKJBB5gkxHcMfXjE4YUZLRRZy+vjV6m3Xrfju5M+cxHEV10xeFo9ilKvb17MYwLRWaa2qqlUUTSqZBgpTluVwseF087d/7pi9e3E3ebngiydxRVJD3EDn+LBKawrFKn09I2iCUTZ6z8XbS6+/xO3by7M42bz2+su6PHuxOqxW737/9lUWFLHF3glXC6f9/ePHp1QEE9iXwHQxvJl0cwdIFAErLB3h4SNYnI2rzMQWaFkefL9W9OP41wEL65Y5JgzDrGkazUkuGY9jdtF+DzvUQiEWaqWvNK60odmou2ebi19c2BzcPDaffHb7WlphUXjEEw4MvMYTR3kMGubz8MCh5fnXrwAHR0AB71/kHN4mOWP2cRWn+zTGCErjQRuaZpTRUqKs72+f8Orr2WxsAcvpj5/A9N2nq3cXV2fEVQxpF72Ogee3s5MYjIVlyZJkSaKlKrQiRQWeshwtFlxNP3/8GH5dR8yv4O3bJGIPFOuHOGY/W9Z7AwbAq+Kbpk/mMW9/evb2LRZnm/X7zfXt5dmZjU+t+PbnP71t6AuFiZN3ZsMTj52HZPcTT7WHEqbtfD05HzFYRhF7oGZ4060nnXhAFKwDqGO1cZFfbgFaoGHzKgTtZ9UYBxTBFk8DSAAjRtNuMgHyfZQWcQ2VK2N0Ve41tUVgrVpnnTSCaaWRbnLyzZP7+0dPdh8e1bJ+dr948+Ll62axMMITq7ab6dH2cWlUI4xG7WKzYH3/ySsI6G/pze/OqFrzgVnCgGS/36dxjEKZASEwZU0IZHT3y4YP8/kPfD8Gw+3zjF2y/A/zD24+SyrjTK0FaUV4/rB6MsNitSaqqpQkdCqtT0qqlVTjpSmb+ehqOH3+9x1Y3u3L6T5riwFP706A9bM5ETx9EUXU9HewrjMyRqNm3978ftt8/vkaq9Xh9um9J5vbeU5FNadf3X++Wr27+tAPt6sFC06/3mubFfbnGV1w6O4pEpASXEi4hqQo2DeAokFQtuF6AkyZTgBLfx1H4/1FggVGzL992YKDmYNVvstpYWItSgMCKFmmtxOKtsKQUgABw523K4mlKmNRLLaxLtAAcZthFLm/v392fbpZlrapxfz66uWHT5cqxz4Wxc2R+fjOt7iKxqJRmrZdnHesn339iqAAr1YtmHiXhpOqYj6LxoPlgN0+3sf7mFEJE0CHwJrOdRBws+MGQuANzC6Hr4a73dMPAI9hEFhjYJQShqe31dszrLO2xlW1oAwXLUpHYQtV9RlhtmH46g9+PGD2ZVYURVwxuf7dHTy7fRZtt231YrdDujV+NSOGuQMaAsqbN1dPXW6dc3iG68v7jVlEs//VL1YrX/zS8yur57fLuhuefv3S4fBodjJiFEyG826eJBPdsC9gDuACisaCa2iKFkpgxHR4P2TabzZUoL6+vnAR89Hxs64F3Oz9OGDHKqdvEBt6takbl8EonNC2bRmkaQElAs6Ls/tI88bgvBMDxgNrJYL72+uN3dwsZV767esPSnfuvGPiady+HYN4PD795Ic+fP2Tr7iKEk9jSzZhd0/26uthENGUGaokkW/DhiNPBp8tT/Z79jEQktVrSIdTatbDxRQcM27Wa7IOCuCah7P3H7r1MuTm8z/cUhhTWFLC0/Nw8PZ/xDprcZCkqiVJVbEQtMrEBBrVhtHIDqfDm7/rx0ND3+LpU16OP8yGp8+ind9XjO9gMYSG2TgBLKDA+g1P377l3Nnq9tG3rq/vZvPSk0wL775Z+eK7m//tws3Vi5PZifnRZj6xf/z1F4eLR7MlKEu6YZMwydqWWU8IdhQQGGt909QOMBAC08lwyjGQDd+9c8PpfE70yHbMg26OgbMPL8akQAokQFHEOIq6CWiUDIF50E43a0hNSxmCrATZq5NYnRhxTqy3c3HzURRxf+9+s9ucNswWK6sntUyc/+j8auz//PL15eN5OUyH6+O7b77+qZ98/vFbR0IFr1Cuh113zqtXt9MASw94HtTw6LO3+z0V4FsIJyHp/ZQMOJ9CzYyMjob+D2e/y44PhJxuBp8NMKkDmH8V5k9en4HBOiy0JFWVKolCUUFVBtoojEYNw+nx5k/9WJZkAPvomhA6z/Wu2JCfRcwn9MBltKMAAije+D/+ws/+l8unWX95ud5xuL7bsFhSFu32i29c/dHLnz6ZsT29OT85OfH809nk5ubb0/kuKiCDDpgILQKEt2FezydrTAEldryHDkiBkCkbYNStPtQw/PLL+XV03BEA5uzu7WAFMWDp/zQjLuqi8AFNEDRktJSEHFrYzNdk0MxtOQsvNrtYJQ4sKi5o6lErPuXkiduL21q9O2+K3kSLpi5+e7I3vL19hQSNa+L69M0nT69+/u1rBwW8pxmVw+ntmsnx4zdkQIDy4Jbis08vdtRACcPnz0/zhG11Pwmz1qvFPUPon8P44nV1/3D/XZUz1e+ulYIaITv3u88ucqyxOAu4FFVVqoqkxHiZtNYoqqriR215NXw1/WGz4BZQ0r3xGlhDHBXlKM0JAgxKg8m3VQzMiZLDV198+rsXVr+7vt7M85vfrXeH3ipVJWvuf/XIn398/bPdYHhie7Vfreatvj0//+KxGe/jmBJtoKFBaWja0zBbQwNkgJ2teLiGLXcE8zkNUEIaG9hM77vkcNpzcjbbjiOg4gEnT8nmusprRowIPE1L1oUwgszfrOdNW7VMXeauTs7WcZqlinWjdJSyuU1T7txePF+vVsgcc2g01dZGL9e3ikVIUw4eP3r3zasP3ubowhM8nmZUDqeT+VoX08fPOsj4qBYGf/w02o1gonQP/2wIxfZRRTgfCSiA7RetnjAb5MCANtqNp08qEoyxMUz2318kFofDgsMWVZgwwSSDCoF6URpV8aN2OBxO54vPN/fMUSwF6jHeWHoDDAQF4NljgYxqdcaT1dXtp9fXd7u8c7ewWKoqteDdm927r+5/dm17RNszGd5MJ5yfTxnf7QECFDwQGMKwpGdONglb9pfV4Ud24+uIeH48nwOO/grrEg4GuGBADQmQJxNomZPAqC2LvB6NAo8pDCVZeBqOADe5oWKxT7l9deouKXIHBrxTO1TnUhJ3f39/a5iZOVVNSynFgl4WFJYQa3pyeff4eP14Pn1ydfIkwpMxKoOXk/kQFq8ePiTQjyF5/80FMZOrW54P50DB0/x8PgLlgZ++2w7GWHgXVLTP4V87I8FYS+8vLxKcxVp6LVNVMyymgtKVMgxVLyKNevwoWwyHk8Vic387nyuAVYvxOEAApb+lvwHe+u2X82F1u9vg3f2yWMyl2lI1vfv65f7u3aMnG8N041ukX23jRli2U/P6EvBIADUZBYTaB7rz87Ddr9IQ+FkFnuPj+WiU9vHeBPTdhsAOqBIqIhKYEKzp33SBDaaUpKtBBZ7J6XzYVgXHN4NqX27Kku7q9OxFmOY1YBgFyeZ2B0l+e/HcasBSVNNaSVnQtq22QdVCUhy8c5Xfe8P7tzloCaP1en3FlGD99f3U64NZoLj45ilNOD/2dD15QPUhj+Y0Hu0xRWLqd7un0QyUCdtNNl2tXv+6SgBDAdfXdwmANYB1gJaUJlEVCoWggQaVBmkIYLrgls3i5W1324UKWAprvNJ8fHdn939wv7LisPP1cr/uy6yqpWnbl7985+2jR3eFBT1d0NFNHXRB9jz+vjDgATzQABYIYP35eUh6siePIAKU9fx+3pDEZEFL7yqE5YD+MTmWHAhv43yHAaxn1AIjhtnsrsAAp1fnBDOGP12AXN+Vt93VYb/KoyyFrMiC8/Z6X/Dk9vKydaspbUlfVSUtoqKI5mQJVZZILVYPmz+Y8/R9gnh8E4TtYh7OS+Y3P/2BvtrPWDzJp5ftlP1wwRTD2WXAeLtYLFjQf3l2yUmSJEVF67ssmae717/1ZJmnk2GWzGbOYPeGAovB0T9hRIeUYkpQqAZRm4GH0mTZaB0u2kU3Xbw6HOit49z4eYAe4ZwgARTAkjHtj3f7Wd9tcnW+Olxpt9tVRRqPv3v89u3FZqcWw2KqqTCh09GwNuvT3WsFRMGqoPQXwq/ecL0b50WKEwSkQUftuiQhHWULoGJfL8IPVdonHrXlfk8ct+WoWuGkUOsNHsA7suF6cEcBZOHVmyB+F58wRaeMedO9Wt+pW7V7qtPL8+HqBD47uM1mtzFs5qpUQ6HFgzfQRmOxUoeZ/Hg8evIeweMb2vnkPCznzDdvHg8fwCpY1JzBBGhg/O7sDAt8vzkw4QBnXC63Z6QJiS3XsHr9yZz3b98tc24X5MPnsxfRcEgN4ACc60OV0cDEJCVQ4EUgC0blfh+QkbXhJuim3ejw08MBSHKTlUGDPcIabKBBz3gJg0/vLtjtHt2eXz3aPf6RR3+ykghf/YXP3v709aRJTSU99BjtTNDZwIfVVfcuBwVQ+ojDI+GrV5Pvn0YGEHoVtRrUo3Lvaxrf0lsFPN5vd5DC6PYGEsYx6a6o1aoDHKCAhWwzeZfOD/skj65+ODB7+pTxsOkYvpfF/fOru8s0Lwkuz6/21/PoM2F62LBCMy/ULJpFRZnUNHOGChyika6oMHl2m7zHCY0Z0dDOTxdr5vX08ZuXGTg8RxvAAGY8fnJ3wnYEtI82ZIA947I5gziCcN3Wd7uHwPjtKs+fd/nJuLkdfntRUVUUqAUcvTbVWpJQauzoClHRxjTT6XkWz6qYeETZLqYdCw6Hn76c4wCBPkUGHjAA42i8/4LB5+7YHZ61ZV7Nz//E5cq6aNNXf97bn74WZqhJmdQ0gk62hkFDV15bUECEvgpkX71K3l1E+D6CKoDHjOhryj4U7fRl+nSXwvzZdLuvBMgrwICtG+qGvgKUi6h4vKm2ryOuvh3u3zJghiycDIbaXV25rVZ4Prm7nkdfwGMudxtYbeZFqlVpS1dRqtIbHZnRUSihiyW64oEpOYF6hBEN7XyyXq9LppubV9OsnrWtt84/QDxbzWbvx7MTSIEM8EAKzOg7H7bV2VPGr5+9//fG+5zLdL/HfHN79apqr+sCLBZwYDGtBXoPpVGqaoSatmK8yqRFu/UwiNNBCrQspms23Bz/2JVQuzhPYsCWwbodNTiqqODFn9kyZv2IzcaurWYOK1lDmx/7xeHinsxQlNGJbKWXyZFe4byqjALeqlVAHaI3X/HpNWABj4pT1NLQK4DS38PinPcpPNuMP4ABLKBADOD79QZlyGPy1+M9w+f3vJsBiVpZvDA38ebqpcT1avwqjXz1flc+TszZnJk3j3ahWqsULZQScTJTBGGhTSXlRWlUUE8jokjgNeiybhrC4XA47FlMz4EAsMxmq8FqNq62l/u0oAD0CEP/8w2Y+Ok2bdfRbz06i5w1A1M7Z/iQLdbbbVqA4ug1oJBRMV6mI5gArx60KdsunISugh0QhFm7XiwWN8Al29UOMHRde3sgZsmeX4yTtGBjnpeWwyJJNYsyFxe/qZYUUWqqcvSkJjUxmWoCUAgO6Q4LKEBTN8DwJ5OLb3IABVEQQAEFaOhrszizIDqiMovH2w8FuCN6DahVEECtIrTdcfjLFw1a3n/Ltqwq4jwWPT/harvZfK7s/DqKGM+G00Tc5YrV+5tvEmmpVFIhHmo3MyocoolHpVHUo6KqqBB4sqyZhEzYHITpm+fHUADx7P04mjHb5XmVmxgw9PY4oGT6jzwbvB3nH7Z5eBpWj84irCmonTMYGz16dkxvQV8LkAE6OiZMY82CB0W9NEBGO5lsaEfk24SyzJgyBSoGmD7MS06JOePleDAQ0rRpDlpjKS2paqS0khQpCscZn2BC6FWCQ8qeljqnb3MT3Xx98YcRiBNAABUUPH0VnOcBbcMofTZ9ktegYHHiempQpB9qFWg25euKBr35saRlUxV34318pm6CG26Y/XWLKHq9lOE0EJdfmmHVZK1SVIwXbWt0zLUnMiUdJQ6xRAWvNAEo0gheoIQQyvj0sPELpi+/PGZ1VjEbz8bNjNm2yjE1BtOj1CAK5Zs3rxYxbJ9UeXg4sDyLEryhcBhroGDy7cUvBmU7K3osUPfkwYZFtoYeRfEQWGjjsGXIhss9rIEJYIAcCICU87M1sB6M6b05cbm3SJilqmmpSiVKihoZpqc/gKMEVx6uPC1ZfnmZVtD+3dxXf3yNAUTpVVAQ0B5PXzs6jZMCsTAKb8eDAnD0WrWAtYoTUKso/WvAeJrw23sObdXuZDxLscu76WCT3a+/s6yG9XA6zV0yWc3Wg6SpJlqIEoomTJbLInMjQqMaVVQ8Ogprg4oq4gVKIBQ4PZ6ymL788rhYXl4mszEJs9Uuh2CEqWNrVbHW2RoOX3/B+19V40fjMWwehssXUQLG1hhrnFgDSdJRXp2v7gZgndUausQwZEEJqqBBA6IoBHhAgYzyMNqP8UAIOMDSvwRijgxGV9N8eb4/uW9pErVoqZImFcMq6dEbk3Lktk01dePqBCgPm6wlC7m8uCg4/lMP+ZefzqOZRa2Kw9NXEEABj1WBw8v0SWoA/G28xQHixIFFLRSAAgLIEc5Z8NAcFoc1QBw6R55IPb9O6uF0tuq6CdOd3QfzOUvNbVlSpVQKBT0efNnfhEbrAVVRr11tTIMqqor4RlCCrDuEYXl++PElDN5tB4N6DDXQAJYZHtATMKdf/cPLn11eDu7GsbSLZ+HuxUUhrsZirANrgYufTlZc7p4vP80NqFWsxJHRe1CdQtBUUI+igFdBEUTRJmx3NQ3CCAsYwAN4UCwO1l3OzXS42NT2MEsiZRYpUVFFIeXhTvSoVQBxgvpMvb465P7dRfkH/w9//Luzz8sPqxiPYhUFBKVXBFDALob5qqCvYnzBRzUPpD3iAGMVwZVhq5N1OIu7MhmlaSxuaBImDVDuqNKJbZdUY3doLVGLjNTUqAeLnqDQaMCLqBBoJjUeD42ieGkEZRSetuHmnIcsTgNmjww/MGXL3QzGzOitP+F0erhl+bOzs3QfQ3nf7h5dJIJzUGCwWFdT82REEb1bHkKAWrE8eIcY7VAQxaMoqKg4D6AuoDF+hBQc2QDSxwIOXwZZyPVmd3ixrC0ilSaaRENR0DzsyajSXwBceEpwyMNXj//4347j7r7dnawUBFCrKL2C0l+E/g4EBwK4I8ThaqyCOEH6KIjD2oa+5Vjsy2fxDHUF7EiGExBIK4nCzB72qoWLlUSjEDRMxXLcHDfH7dihtqHQoIpHwQfaYPEqaAOqeOuhacMwPLApNnC/2QxrNlgYKBAxfrHdsX52zvmc7Xf/73dLLNDet9V/d5HgHMYacM7iahyQOoie/EFPDZa+VXWMDMarWkFBUFR6ALziQQDRUTmkSArTA9oDDYBlNO0YDJYD+7YELU2VJqmgKEFhYR89XfSQPUycgPbDAgqEi5v2ij98K5Adz6PXoFZBHAKg0M8DBvA8oEUtvQoGwPQDUBBBrVrAeUsDmIjdwwwocCmJS+0EYlwAslbKakl/tzPTLEJqqtRUU+tw3OwfhtqmMkWCiiqiIngCBQIUFVQVPGTQhpzSAfVmujAsWG829BnP6FY3N18CyfZf/P8HS4ghW2x4d5FAbbUALDhrcZa+80VICDVNTW9VoY7o+0UQKTrwFo9aVQEVAfBKrwcNz6eMBxFKf8EDIx0u26DeVzQcVqGllUpZVFCC9Da1zsJ+6aFHTx9ZdEDFAd54I0cQLsrp4v+aCaDnnDkQ+isgAEqvWqWvuH4PaNXSW9DfO0EBDyD0FVD1+dlk1FIzKhw+j1wyIksbVwaTuizYbg+u6rZFsxRVpsZUqocOA0XtB90QVFBFVMGr0KCoR0RoBBCvlEETtBArYEtGup7MLfDqEzBwtYKLP/wP34+L2mKh3MTXryMMhsLhrMNanAVcj3Fd1mCVmt4q4zWSZaFDOgpBQBEUBURBARVQgGx9el4tc2APpB6yLBixjTwGLNnPS2lSpUlUUGUqUQjigbtx5UHFCYC/ejzh7NPKA36Rx/Yoob9a76TnQdWC2gc5utZ+gtAbAIJaBcGpc+JsOhICUhoxttiPUhiZQDIzKgIeX/3M+eXZujchiupBEHTMnKBnkSAKAo2giopXQVTBo4KqAOLJPIADHAQjEhwwv7oiu+YYnl6YMdSFY0+7mUd3kTFgncM6qAFLX2fYvbv6JAEsgKvysMuoAgIo4gRVQBRE6KsesunDlhSwcZUyGpEmaVLQ10VpZVZFk1SgSgVHBIXpOPSBWtCjjpz+8Jjo0xf0EMZG6C+A9PQqWAU5wgk4xAngrAIWBZzgQEDpK+IEj1oVxYg6I2wXAW0Wm9xK40gBRrXHuYDuV5/yaE2TNOOhEHpMu+mAomRHAjxWhQaPCCpKI6ABqFUBGhTwRwhQzyOYJAhgAQvs6a2BxSb67DpqLM6BdVjAAbant45OAAvgeMA6Yr8ICgLiFJy3CgLQgAcQECxkxeHQro+IC2KTgj0C1jOREqlAZapAsRiWY0/0QI8CqMVCc1Tz+5O7QVIhKHiDVUAcggCCE3DCAypWrYJFLQ/uxNU0tYJVtdpHAQVQ0cIrzjUp0Iyu0lXsaqG/gniOi0/frlgjaYgSKt0w5cx0QG3LwkYERUA9eMGjeBEaPH1VABoeVOjf8+C1xcjmz+Oz6wjjsGoNYJ04C9YhzjqAJAEL4PiY94iMWEWcOBEEUOvpVbUeUPoqtCPAsccBBpA+xmuVdbRJowkyhYqa1GK8PPg0QC1qlR4aamig+YG3NSA4cXgjgBNFnOBArYIDEaw6xNFfnIATh1gFrDqHOItaFXoVbAN4QBDUoqJSi0Cyq5+tK+MN4ABP7evT+uzTJVW9WTTKFOmNLCPl/GxQlBQCIoAKqoKKgMejKKqC8wACMIEYA9gj5mtIWFH1S0cphEN+eR2DAVdgcVhw4ABnEQfE9VzUguNjXyxd72gCCtBPAAUB9CgBtQqgfJxVLiu0KKGUQoqSnpHykCcPUQE/r/fOKh9ZQMGiPKjQ3wk4EHDieFCxPKgKzoOC4DyCQ7AusKAFSR523oAi1EBjlXQ8zyUsGkXoYdmPlAfOeVIQJwqgII2oVRFFCABUsU0/19SbZ8x2eQ0OaqD98mZNvJu9vutJIYW4zH8rrwGHVQtY+vdYeq0yuwkAx29oNy4A2gMCCIACAqBYRQEEpa/gBMSJAwekIEqgjBSEkfK9lI8CSqB10WBBPwJCf32Q34Q9IkdY1Hp6G8BjFacUCmIIUutBxQIeB87zriualophIYUFykPMRhkHrKFWURBQhAYQRQGv3qIovYqFpsyAlakFhwV/+idf0rKafbpbkY5IR9mOLRUKWEshGAArOAfGg7NqsewBHB93jaAvC9b3A3EoDhQnoFYBob+CWgXBgTcIaryhbzIoo0GhYdkTKN9zeTDQLLaAWlB9IKsg9FWrvwmMBxziQHGAiEOcgCKoIgZFUAN4CgQQwaoVwIy4HHVaSQwzCOmL72VsOxwQJwqgTgRUUBRRwSkeFHwP4tTS34AFLQjm8/XN6L57+HdEXAzSEYYvqACLBcORBkdfI85ilZoiAsdveCwsioqTHkUAByIAwpGKOOFIRa1V1KJYsDjpKvSUoqaKMlwQKN+n8gCEJqa/WrWgYBuwAAqIEyegBm+88QaP+Tg+eswIgca2lXkgKnX6kmkwiJqDGRNz1jMPFEUoREQFh2hIWe4+XJyceXz64rtfn/IIT97ePbdQiGFRjDQUslC73Ul8b2O3dguhTofMbDIFOjoqZpw908/XM5SiECNBDKfnfMJV5iMesKee61Gjo5ttxEyYKUJZta3tdV1Yl8uYkzGnMZ3ZeC5jW9SodDOTYKElipAOJZhjg0EmXCzev3jsCzv3T2YrHTFeRxDDOHo3+54HHbrRzbRGTxD7QZwso2RnZg6uthvDoBDHCEwv5NTbyFTTPez3djwvZkwxrQ0yaSahMqMWJbNTHq6X2+sxJy7GGIFMGdgcO3kWhNp2KAuCormsjnY0SiAmMjPnBhdG3QyXx8SZYY+ayCQmZRiKxT4ydun7MLYdCh2KmNbONhCGZtS5HcWAp4clnUzHqHSLLlBenqGDY+wYOG7H7bjhuKfb5qKQaXcNtY2IGSq6g7DCuKwLhsu8wGB2Mxyez+jgGEZV1CgiAg3VmESNwmCui8wxapiYBpiMF4VydHPMxLA4mL53RIfdogPqZGyLmHROslPT/uy49nUMp9bTNVMj0Afl5Rvo3vnHjePGcTtx3GwHar/2l/1kJihB69xhdJg6mLYdc9YYfT6I3aIIhZCgBNpRZw+G3Wl0GK1JUVNNMpWjgzKaDAzLsL4fkL1tqbsGdW4mmeQEMqVGhEIcMSwf4TmrGJ7l3UR3UKhtnCwD5rANQoPhOc4diO4JocTpjhpq2J251LBfGIyWI9MHZRjdkeX7Pie21btte+IeM1OG6fEQysdjbPtstsXAgUyZi8yYhahtdqbMXOyPgTkM2z5PxLaGw3ZtJoVACTMzM2ZwQQczZgxTeroyLKajQhlddHJE+YHMKTu9HXtDx7Lbexp0pEeRztKJ0fJJssOZh9FMLKO2yYygo7lYF2cO5pxcMLiN50s2+y2ko2NNyRQEOtx1YtpOw6mmig7NtpkoUxE64sjyA5ydjg4ddo/R0dFR28zMzbqsy+KyuITejKZH9oseo+WTaqF2g0IIMsX+3EzTxJjmVG0999mp3SLoKDFDbMMM84SJmelkdIHygMHimOUHPu5a1PMYx4zx8olx7uQMiu7IXmZmmXHu3NmP7fBiZkNPEXQUsRtnzsydTGfHeHnAII4sH505A0Wfo/Sx8sk0exTjYBRRxpqZkSlTmJkz0/n1wkYHyjgYSths48zMzCkz08mxp5gox0166DFaPoqzowNFh96OG8fdlj6SHqPlE2x2lOG4VRHbIlNmZiaMcrHG3g2jLwhBjfZGoZkWQ42OnpBpO2AizJHQ02EZ9NBDTw/lozzQQQ27x809dqHHaPmEm83JonbjZBnowMScjNExhuHFjTN3UIgZ+8WwP901xuMhlo/BuHsxzjpu+USc88axs83m/M1d54tC7gmnzpwyGT0Vx3yQGnxs5g73Xj45Z48aHJuhEMxgMoox1Dj1QmeP7gzHzrCmzDMyDdjcsJex9DiyRj6G82zKJ+zsnTzcjqFDDXTYHbZj2J8vGNlDN44bSgfd5NKBDh29HTfo0jMonxBzDzXySTznoM6t0WF/DC/V7J1d5w73Wn6InfsbTg6n50sCea6mf/+dU7sd3TA6dk7O6eWas/Y71HD+9O/Nc87ZNU7M6WWcu9x5+vfuuYeTc3qZ517m5t/T527Tf/P/fx4eVlA4IEoZAAAQaACdASoAAoAAPlEmkEWjoiGTeSzQOAUEpu3rAvAHGrQkff7T8gPxm+a6y/4b8Tf2vpnS6d32cL/Nf7f+5e4789f8D++fvr9AH6Rf7z+ie1//l/sB7u/63/xPUP/Tf9j+7Pvf+lD/l+oz/l/+t6/fqW+gB5dHs1/2X/k/uV7T///7O3pB4sfJTGPzO/Qv3HkfRIPCvFn9jvFP5WagvsHzsYQHTSgr9L8tScr9msNB4bvc/s+/GJscjdVX5N+TeVBHRMdeEeYWm/J1zSPtRHNaaJHrTjSym9IRZ5DiU70aLzyqGqMxsoYxJMNWlSonzmT5AfAUwfln6h36ljbnDkRHZ7mga/BCduqllQsZSgivwTBuM2gix1eeLdfBIq3NGNDTioRPhf1EbNYRQ9nEOuKIE4D50d8tQZy7XdPbWvby/eme6/SEFQ1NjG7bu7tOVQEjsU38Ag1W0e+qEIWgs6pW6+cgPhFELrhd349g2WCBjuyMh3Jry3A6QbkMsuSloMhZjUMNAZKnmEEgzXFEo3LRCopr5sYGHZbfyUwj985Ekdwcsoe1XHy+9rPT4UNmYgfmqplVKilYTwskA2jEOBAzd2nksmrGknAUxA321UTDQJgDxiwIVGoXo7wSwk8aDRbXu9CrtbK+0ae0jsiQJBxieQYHE65ahGtXBfnwz6Ontxk9hh8bgUxU4tDLvuI+LgpLA024H7KZKy2e4gaAv0xtEFN8HMwlijqs9LcJnfYEo107d7w+K8i7w6dFNDVcE+ypHflFtF3H42w+YGJTP7jpKRnE/aZ7Pqwo7ieQv6W7ooBizazmGDMtaBanExpWwiyKrVVaLCWZEkY7fbrEEVyHIAh+X7/qScPqGLSQoQqhHwBxhhZoVRxu/fqLq39KHvAghbMe+GCappintuA57Ei6b5wg2RLsncvCDTdRJLC/X/hvR6fLzcstrDMOTII4L4fjr2/k1pefNm9vMZT2E5KUpY1da5jXjLxgpIDb4+iUvSuWfxjWmb947sxbvg4csC2PWYN3449AEqHkrU3MbP71kvnYMY1KmDrXOQxgnwpJb0RmMGdp+TJqmDhXe15RdFlFXIvsGv5ejXajdpPNjocLKkNyNDUOlyJgAP72QN//2xDxb9eHhn//9YE0fswjMMNIayzm5PlqXMVPygFHeOIkqWY6COuskcmb/RLjPaNLpAKt+hR3jiI57Cm5WtQIbplOnzZ81wgDjDtl5RBGU9kt/wOH32dJ9M57HKJntZusPuCEvxhdjrO+wtkNHMbb3lXo7Z8oeTC9wUj61mU+ODZHFwuZrA+dwb6whdJYf7V55XKPmw1rkfk2B7XSQ6bieQHsI5bA3cMz4W3g5iJLvYdoN38GHu5VInVi2wRoO3nk8wYUf2cbg7L5NMpjtogSmzzSj2b0w02+tY7RhMywK+6OEoulfnnYnRhEViDhUKv2LJdRHm45fltD3vkrGULcsOb8BYPYKcGMeLGm40tJj95RAEOhazyAaAyFbck/f/DlcsEvrt1wX0W1MwMMeTscQSBLmg5xS89XBowkfQpUlJJJ67LgA+HXAg8aZgm4yxhW5RuJenVaoa+QRzaVq7kXQdxPR/0iBsM1AOvY4zBjXHl2YCvii7WDtEXp59xT+Y7WbOMhgKqnzztjaIK5QICcmfvxCVrdyScmS4BsWu0DABAFvTefx/ya/y+G+eT+1cpnFYmJeq5gPvzHqfm+mBCoNMrYHcDguildT30e19XeJFg6iXp6WcnG3d3f0MfUQdr4j/iqQSWUYCErEpSkW3KJ94T1J2OPc/s3ElRz5gZIKN37nVrIEQ1gHKUg2XnLyXttU1os8alxJj9tWA47GlfpRADYiUcMbMi5ELfRyw4csm4e0teZF1/9Ad24u7Q9O0RfR/dyOeqtazafcTi1pzrE89tsJJjmD/zP63jLVrONStMMR5xma1g6IPXwFLQqkvSUoBEYk61wJM3F7rPELExMUr94ysF4LWWPWfZANkUHVL084ZeZiKeknZv0tb0SUNe6uKdwbx29Po9Da0QWyBbwJdmYs/BWNAr4WND/bU/+Yc3JHnZXZCQQxX5qhb5WEbNG0NXE2pCWD2yj18yOYA+ybhiVgPMTJtZpRTRPdNttezA4LMLZeOXBI781O6Z+999d2FL4rCHGXkxlkK1+ruLlg5u6+dIQk4El/yxGsRoMyHgMtjlmEOsbZ2YiT8FLYXGpzQJlFl/dAfyAfwgpBSO5HqdufPWIGF3KBhZQq9wBdaHBBQg9vVwmDGxGXJ8AEAeYm29eZGJqaMcZZHKy9v/H89IzHlPijkxbCtZCEyf8zVUXc6AEfJTZDoVNYH0+XGuA0sDYThuoAJ3tPJVxPTB9moZhmOOUnq7bm9LOSPJ5LGWHV/vP0HMKU5d2o7Ugt5/c52UoNkB1ZjWa8viilLe220iI5X+2QFAch2C/GNA9PFwhPEltCMGbXoTwguUIX6cSycsLt9HKW0ML4TCFAFpHriylL506q2P1Yn1eRPEpgL6PCdPeNFMnIwX3qTwiAWE1n6Wh5hUVfx0OoqjNQf399b0t+yKofLMetGnFqA9eHNB6ULrGcqyoNK1GTwGl23cZRJn7G6cFEmEI94GLTsC5Kgn1XQTjftOjDUkZWMmwHlEDh9o/9o4Dq5YfNQiMqYknv8H2kaV9uojczj4Ppmzr4WGplIU8MlsmYi/S6d2Q+MEnS8aFloHx+KfG9Ic9XQaFGjWt7uTz7ibhqgrW7pTyC8o+DQiHtVVjF532xceqXdDVp/vyBLDJdUSvUGwcmzAu0IU144vEjFlRSQ5Sc4ptqWFLZfuglhOcllY+1oskI29XNjBpFUYlcJhROUI4fgIdTUW1wnFvN2bdEcrmpNVzd63hL9H6Z98q5kVJlZDMtqujMDQL7wZ4DEr4yRl573NFEOO0SPQvz6qIriBR5UXcKB4em05gJdNfggLB6prK7AGTdP6zDQ3wYrrlT1GuYvPZ9JisBHPoZQZj+9L6KACNbkBvLmjWqRg/+aA1gJESbuN8LGMTqTCrL/90XeANlIeLLRal0MPXdoqD7Hm/f8gsSvPazKtchoBkBAa6sqVW+vLr2UgQmrv0H0R+CrFWm69OdZCVKFc94EcwDL/jwmyAQLlheHmszeRQn5Vs3TvtNJLGP9tmKfhaImtX8YRmjmnkgMO/z54egi4t4PrbO3vJH64ggcSlvwp/rY+huBeF3MtKcruMHkCYgMbH891JLy5ZjPknCp9sdu+4t1hdTqdoN3zUQRFC6w9E3z6gwhmkVct88NFnLAm62Cq3DVLve+7ctW+0WWiqw6tQV7rUtq84YktOwDePZ26Gad6xEe9Ygq7RY083+jtO8poyVPsNCEFg4UzEokt1e2m6zfMYZNsDE45fQVnOKKKYd94ONDIRs559bZw4QfgJb326LvboX7RuFy9mDnUd8YJOuGAtBklvQXssRdQjfMdXBR1SavCOp4f7SU2QO0eOpV+aPdLzrYihPKghVHj4Rc95YFO1EEzn63Sw5fXdkvEn7tUkWpUbazpyFmfj9yl/7LMykfZ0woL0xrjsuu3ltkcWVXGL+NgkoWYGpDyt83/qKhlJU6ZzPz5HEPXU/SjdHgQfi3Qh+CohkHibiySBoaIMjYJ5EosxqRJqGyOta0Hmui6o2aK2JFQe8SfDdMmMlSCrJqQG1gaJ+qgIWUbwS4Ny9//To3mBA0wgjhrrGE1WDEa/+B47KFSEMUFLbA4+S5PPfq9ZYsGKxCjYKYA82cGfYowHaiAcwKH4V+3+/O6iOQElPAnl+VjGxVf92++n7VVOUbwInd93QQUAh9vUbzJdpRTjeXzKtTN4avOkCaIYshhypEzzaIm8X8cVaCSfzzA/4smKgC2xTVzKDB5ogziuvwhRE2LuVV0lGQ1jK5MaKtTA3jxxs+bTSTZhx/vJhNVcPGMJDre78xMSCEhDKrcfVWiiZfkqxVqmNHhyoEq4BxoyIa7CrktgvjnZUtOe0Si7devvR4u4Be64FnMZeymnTRlk10CEXhWZezUrEnxSGOe2W8NWcjSiukVlINFnbgdeZKtfJhElKg0FRWzRrt5sAqM+abuW1OC+ujFIyCOvTOGc5i2FO2iy0syAfLLLXFSKOVtfjGAkcUpo9z7aXMKHUD5aNqO7fMmuXbMZCIVoeytbmDS9J65hHuBa8ifrpdkqp1CHtw+Z46LtpjIf6UPo7eEUAPTXtf/GyLeeVjv23vcNNFs0dcfG2uKTfCQmYzNw+vsrUDq9kEh78pLaz8cpkHXmi/bt4/3mchfJuhkHZEv6HWBPnqdl3haX8rWNGuTmGj/v5a7urwFvcUkfOcr2mYZafBtIpMmO6TxMR2DfDc455s1hz4gJVibhBNXemT9SSuZp4pLQlDbNd9NIQResczTvXUTOf8jOwppJcqmUyqRtSozuurdIEu/EDImgfrd0igj9DiGcq/ttTeiT3kFsANrJFBmkKtORmvrSTgnYvXcIzDRrPlYy+7pdOc3faooN1kUtEo44R2Gm383o4S1v1Eu2u9qjUtzNOtAaZhPIfaLu0IhJvThD6/J+AYwuBbJyB1Gel5NdW7fRrA4wV/Av5yv/y70QmLQmjIUVSDytHP+PhS31g6cB/T59xbRCImoZQP6oEezlEJVTVrkWv+Qg1Nt97A/7lSfkoRKSkpm6dqLgvk/rl0aalmdDUYq1vlMFqbw3ptb0KLp3WhoRWpZ9DKAaPWBtjoaVmeuvI4oLzpm0YdL9GNkEyNP3ce4bp8ZfGI0EMfss+IirrAghXoP7i7kfwCq2QbDBKIudM/S66WqbkeoI2Vaxu8eskttoNHcgIyarL3hBRvIr43vat+GOR1z0oBt7cc42cGvsxAMZVIssulMx7+mY0xeDUcc7xTdilFuFw9TH07AH3nyEbOo9fOeblG03HwK7fAhEU1nxDrQZbL7pq/9We3ctQwvYhG+AOw6mYQBCydYO7+e7iOOVvsozzdoPIM+477Tumok95ZSEVOv308W72k/GH6Qcz2yGzCWJU8vRtsUxqxA6fcAbJNJq4itCs5KnlzVoJdnWkdwCsnctFyivjxTOywUM7Pff44UJOJ8NMoIxoEHrN+e6nT23XfMdVm9H9MFWMPRGFXEQqM7ioWSH/XsMZPoylw23wNur8uO0ZbJRQc3PVI2L6OxM3gs2sAnL6fCFBXEcUEd88r4wCSeENGOPyFEnq3Z1j4V8zXFTfZNXqlwiSBLsxdjWyqTUvbKnOK/a3TFpxiXQ5crIN/gXj1aEsKIZ/RYAXtWmlfbdAIYH/SWjC27BuB9wEFJjz74ovAEffLQ+ZQcpSUGohmaskvpDGPNyXMYlXOlkIOiONWmCjk/pQiTWFC+5l1QsfsfWB9G5KfvQ3Vgx73Z626DMhhrLRxlkae558CWuqljIDGYmPjYN+D9EAMFqg/2t0M672YfDGstg57Qgz+JHfcgUC+EmC1KYHMerbMO9gFwB6evA45kfdgIytOzM2qSd8c9l8J6qyhhLTwpdspUyDgL+YwTCJoJe72nrES2mHJtPy5yrep2LIWOzERKliiw4bUxZEx20U7CFmKpyUk8sBNtTT5wGblTvCypG3t/zxBj2FPIE/fDpXbPSVDvRyLxKAtrVVbDWPbjBap5qu2C4+xETIkYmE02YSYsow62CM+W0rz1Yzg/deKueHLAZQ5xFoHzH1KxtB6LU8VIdvzls5TuW22hoavm1/bULFebTDhFKWBkcjulngugLVjNnZ/lCVBfQzP/xPMncRNFVf0Rmarq3VO0ZXnTXD5YbauA2YoLnktC7oZ643y9mT/1qBYqA9NFvvgTPmO1w1zleicgLDbeONy2tXzruHLayqbt+Kwi/6iBnm6xkVq0+T6F+ZbiaTw97Kw/k6yRbPp+Bp3kGxWgqs+tpBNtOGo12Bja0HZ3oGIU1Ktnxk6WNWTgO7q8t0I57xb5c9xH2uSjjKdpr2pDr2lK43p6ocb4wNPMIQwVN06DUBfexjKDhskAirr/smNZoz+StmDoZPAP8jYJj72vZ43pyk9kZfSrIZZuKWYoWTDiP1Qxh8LkZnhqYcJeoLBONfQ4nwobtXfoO41vhFMJmaj14lV2W95s4hKDubKKIG2XgNLovXrcPmE7eecuYr6egBn8TDbf+qIB2OoKm+syOnpPxsEbeZa6PdeRMg/6m0PqB7FaVwKfvEL2u/J9GoiUaHxhPLhFVAMHcLpm1JWwaOTkboxR6853bVggYZXlsfJm+Al7doOggbRbojUW0VkQ/Q9pr16BxrTFuxfKfKvrznR68P9TBml+E1n2hK+vfRfdeAQyR8wIqUpCSbNwaf0qynHrXhrm4cQ6n0ZVSEzc16LlmoOhNVtxVTtZ2MveNhlD3ozDPCCX1uNb6D7uxS1FFqdlLRveEusFrqO1a7nafCMFLNVI8fmKaa5pNy95A2rTNr4XOhV+Es2aImrpqiSrjK7C5m5M9ig2MA9BO9xs14UDQ7RF52VBn0eqb1MxlTI+j2LAldP2OvzXpSw9l45qA6RlCbHklSTjeYhGgiSRs7Aa77aWys1l0wyEw4usHZiF1zIF9tk7ccTsrxfVIW3E+pewU6luBetMY7CNFbZGgdDjNQ24f9KrzGnmMKaw0GscmUS7Ldodh64efAkK+QRPsQUWQj2EB5apk88Ld0+iDIJmGkTzIt8x88kWFD+z4PmfeZ3JwcjUmXGhsBiIyVb4qUsLnDcSton6HhIpdgkFPqxbYbMn++oomOERg4AYMng54pa5wsZEfNLGxI7spXvqGBvu7QODbFlXxv3t4CvUGWmwlwaGZ0wKQwEUY6aB9s9Tkm3+vUGJHb/o/zE4oJKeCmrADUB4f8WcuqPBM/5JwQY84zWoTl4HpY5Jd08xx9ObefbCUGVovrPRzY4p8zEj8FjfxQfa5+Ii+rhhO1fmvqpxqpgtJZMjvsprollXGrwLbCmr7v9nh56EsCc5xnOnv79XZBx1WHuvJNBUPvJ4mlKVKE0iJk+bcoOMxdDIjnpflgWICS3glUrmfswcNtvfsQT8O3PaIeVvQMtYkRkpDn6xSB/4/Cf3JdMIMvJPoVk629BJ5d4kSDR3pbYmHVrnPKwxDj8oblISUnF6YHUZoFIp0KB6c//dTOTVFvgSmMhf6RkBwal3po7JNsd1RM18OLwAeE7GWjpBskkxWWo38E44b79JgNfDhPC6EWy/luY+YhiKzDjJEx3JlQcco/9OXORDazuV4woSGc1ykLMd3D53LtlaastPg3WXAKAmalm9ss+A86NmykcLlBF7/uh5hUivEodBttIAlVsraSLtT840tvQxFHXjz8xJMfniNZl/S5OdxBoeRGGME9sqU7kCCqyxlU47tCSn8YXYevAVj3rr7w4eUh5/IzmglTW9AM5mnG8O89x0ZvqtNYO7ubtDF2KwMqUvfgOyveuNb2bL4gIWepdGJ7AEJLBC+ke/4h9zMxbBo6GoHvTzY6cSdSNVd1j4lkxOqWXYmv+Lom3QxudwF9GOdadDAa4gDZ+tWDD3HAPPm/shctUFdvZ5ZCZPWhd8MtnZb5OeizIJvNqhjEvGPz18b5NDslBuHTz4uZSz5AmcFqbOSU8VS5duxjIrJkkvHQu30oUl8vfuoXJ2FMXZ2mNlb/AIojQkJ1eONhwF5RPCJTYznMPa+PkyH4nDXLwmhb/W6UEFEb4Is2/twcFSeaMfVthxFUwsJCnOyIiVe1mhKaBmlT8QfaP7c8Q0RC5HLdiUCbvD+ygMR+br5q1chiBI2ixCsFC8D1+WPZO3P6rSU/agG+FKiaF7tOPV7/W9K42Mnhf7mBChTjI1ayAnx0I1ggyaXQYuRmMv8FxivCJUxwDX5UwuLxtcfX/j0AYC2HO/iwiv5R0CrZtU3iwIzFKPpJ3t2AHFJ4Zb2KiuZcHcrqwvybY425lEY0iDTnw/yTGG4vuXDiMsw7t9tTOJMXfVd8V/YHJcIDwNym8bWggboTNs5CZRN60FrRdL89cZl2At0L/xS2HqVL4q6IjobfW34292lbiUUHp0N972FrROwv3hPXxWV6YUiNB4M4XRkXTd+E108jsWMYY1jr19coKhheJLCNRmvzsySapzgX9/2taou5Cnid4pPZ3cJ5wd98vU8UVO+sK+PK8EswV5XtUX7fAz9GK5gv+em5XO3Q7hZZ7W82a7a4zCNIlnBxWcyj/Ce15ghm+ulozSPOdoniga0sGqYI+2l68IAk6CN2aSkHb4/YRYV2xmuTnzFWXdfGfeMm2UHkTD6uCGwmjwDaPvY8OTgZs0Y3/fnCKPYt+vChtp6Rs+gzmQxIVVEN4WKgdhATlwmrM+BJbmzbc8O57FJfg0hg3LFNxrpN9S7jEb4X1WNa7EeSfLryi0iKqD0SCCvbddUtEF+uaHTVOhzteTUHqti+MFBXEt1Wz0jOgnIbLEsd3LC7oogv/NwNoZStBIvLl3PDjJoYPSQG2w6Um7rOaFwAAv0Y1baCmtYaq02jQAAWW/6X9tO6WcEvpEX7wyb4uDhM9Y5oV8LIIgcyM0Gr5FKjKRgACSqp66QZyd18UElWV2fsn87o9+2Nd30bjpXbmWcQgsQNzAL5CbFSQylwAjAgA3OhJ0HqgAAAAAAAAAAAAA=";
|
|
@@ -27,10 +29,15 @@ function createBeamPlaneGeo(name, width, axis, scene) {
|
|
|
27
29
|
function createLaserBeamEffectPackage(id, nameZh, core) {
|
|
28
30
|
const defaultParams = { ...LIFETIME_DEFAULTS, ...core };
|
|
29
31
|
return {
|
|
32
|
+
// 定向武器 = socket 起点(parent 到炮口)+ aim 终点(optionalInputs)。geometrySpace local(束几何挂 root)。
|
|
33
|
+
// 'link' 留给真·多端点特效(本特效只有单 aim 终点,非 link)。见 #9-A。
|
|
30
34
|
id,
|
|
31
35
|
nameZh,
|
|
32
36
|
defaultParams,
|
|
33
37
|
debugAnchor: "playerRight",
|
|
38
|
+
placement: "socket",
|
|
39
|
+
geometrySpace: "local",
|
|
40
|
+
optionalInputs: ["aim"],
|
|
34
41
|
debugParams: [
|
|
35
42
|
...LIFETIME_DEBUG_PARAMS,
|
|
36
43
|
{ key: "width", label: "束宽", kind: "scale", min: 0.05, max: 2, step: 0.01 },
|
|
@@ -38,7 +45,7 @@ function createLaserBeamEffectPackage(id, nameZh, core) {
|
|
|
38
45
|
{ key: "laserSpeed", label: "纹理流速", kind: "number", min: 0, max: 20, step: 0.5 },
|
|
39
46
|
{ key: "targetDistance", label: "目标距离", kind: "number", min: 2, max: 40, step: 0.5 }
|
|
40
47
|
],
|
|
41
|
-
create({ scene, root, position, rotationY, params }) {
|
|
48
|
+
create({ scene, root, position, rotationY, params, inputs }) {
|
|
42
49
|
root.position.copyFrom(position);
|
|
43
50
|
root.rotation.y = rotationY;
|
|
44
51
|
const { width, intensity } = params;
|
|
@@ -99,7 +106,12 @@ function createLaserBeamEffectPackage(id, nameZh, core) {
|
|
|
99
106
|
const observer = scene.onBeforeRenderObservable.add(() => {
|
|
100
107
|
const dt = Math.min(scene.getEngine().getDeltaTime() / 1e3, 0.05);
|
|
101
108
|
time += dt;
|
|
102
|
-
|
|
109
|
+
let dist = params.targetDistance;
|
|
110
|
+
if (inputs?.aim) {
|
|
111
|
+
const tgt = inputs.aim();
|
|
112
|
+
dist = Vector3.Distance(root.getAbsolutePosition(), tgt);
|
|
113
|
+
root.lookAt(tgt, 0, 0, 0, Space.WORLD);
|
|
114
|
+
}
|
|
103
115
|
beamRoot.scaling.z = dist;
|
|
104
116
|
const tiles = Math.max(1, dist / segLen);
|
|
105
117
|
arcMat.emissiveTexture.uScale = tiles;
|
|
@@ -386,6 +386,7 @@ const lumberVehicleUpgradeAuraEffectPackage = createManifestRuntimeEffectPackage
|
|
|
386
386
|
"assets/runtime/upgradeAura.v1.js": runtimeUrl
|
|
387
387
|
}
|
|
388
388
|
});
|
|
389
|
+
lumberVehicleUpgradeAuraEffectPackage.placement = "socket";
|
|
389
390
|
export {
|
|
390
391
|
lumberVehicleUpgradeAuraEffectPackage
|
|
391
392
|
};
|
|
@@ -13,7 +13,7 @@ const DEFAULT_PARAMS = {
|
|
|
13
13
|
bloodSize: 0.3,
|
|
14
14
|
bloodGravity: 6,
|
|
15
15
|
bloodColor: { r: 1, g: 0, b: 0 },
|
|
16
|
-
height:
|
|
16
|
+
height: 0
|
|
17
17
|
};
|
|
18
18
|
const texCache = /* @__PURE__ */ new WeakMap();
|
|
19
19
|
function getTextures(scene) {
|
|
@@ -51,6 +51,7 @@ function c4(c, m = 1, a = 1) {
|
|
|
51
51
|
const monsterHitEffectPackage = {
|
|
52
52
|
id: "monster-hit",
|
|
53
53
|
nameZh: "怪物受击",
|
|
54
|
+
placement: "world",
|
|
54
55
|
debugAnchor: "playerRight",
|
|
55
56
|
defaultParams: DEFAULT_PARAMS,
|
|
56
57
|
debugParams: [
|
|
@@ -46,8 +46,11 @@ const DEFAULT_PARAMS = {
|
|
|
46
46
|
const playerUpgradeEffectPackage = {
|
|
47
47
|
id: "player-upgrade",
|
|
48
48
|
nameZh: "人物&NPC升级",
|
|
49
|
+
placement: "socket",
|
|
49
50
|
manifest,
|
|
50
51
|
defaultParams: DEFAULT_PARAMS,
|
|
52
|
+
// initialHeight 走 service 存活的 Y 偏移通道(不写 root.position,避免被 applySpawnToRoot 覆盖)(G6/M13)。
|
|
53
|
+
spawnParams: { offsetY: "initialHeight" },
|
|
51
54
|
debugAnchor: "player",
|
|
52
55
|
debugParams: [
|
|
53
56
|
{ key: "lifetime", label: "生命周期", kind: "lifetime", min: 0.2, max: 6, step: 0.01 },
|
|
@@ -71,9 +74,8 @@ const playerUpgradeEffectPackage = {
|
|
|
71
74
|
{ key: "glowWidth", label: "光柱宽度", kind: "number", min: 0.1, max: 4, step: 0.01 }
|
|
72
75
|
],
|
|
73
76
|
create({ scene, root, position, params }) {
|
|
74
|
-
const effectScale = Math.max(0.05, params.scale);
|
|
75
77
|
markVfxRoot(root);
|
|
76
|
-
root.position.copyFrom(position)
|
|
78
|
+
root.position.copyFrom(position);
|
|
77
79
|
const strands = createStrandSystem(scene, root, params);
|
|
78
80
|
const strandMeshes = createStrandMeshes(scene, root, params);
|
|
79
81
|
const rings = createExpandingRings(scene, root, params);
|
|
@@ -92,7 +94,7 @@ const playerUpgradeEffectPackage = {
|
|
|
92
94
|
disposed = true;
|
|
93
95
|
window.clearTimeout(disposeTimer);
|
|
94
96
|
strands.stop();
|
|
95
|
-
strands.dispose();
|
|
97
|
+
strands.dispose(false);
|
|
96
98
|
strandMeshes.dispose();
|
|
97
99
|
rings.dispose();
|
|
98
100
|
glowAura.dispose();
|
|
@@ -5,6 +5,8 @@ import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
|
|
|
5
5
|
import { Effect } from "@babylonjs/core/Materials/effect";
|
|
6
6
|
import { Texture } from "@babylonjs/core/Materials/Textures/texture";
|
|
7
7
|
import { Color3 } from "@babylonjs/core/Maths/math.color";
|
|
8
|
+
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
|
|
9
|
+
import { Space } from "@babylonjs/core/Maths/math.axis";
|
|
8
10
|
import { Constants } from "@babylonjs/core/Engines/constants";
|
|
9
11
|
import { L as LIFETIME_DEBUG_PARAMS, a as LIFETIME_DEFAULTS, s as scheduleLifetime } from "../../chunks/space-shared-BXV1hc0k.js";
|
|
10
12
|
const boltTextureUrl = "data:image/webp;base64,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";
|
|
@@ -58,6 +60,8 @@ function excludeFromGlow(scene, mesh) {
|
|
|
58
60
|
const pulseLaserVolleyEffectPackage = {
|
|
59
61
|
id: "pulse-laser-volley",
|
|
60
62
|
nameZh: "脉冲激光",
|
|
63
|
+
placement: "socket",
|
|
64
|
+
optionalInputs: ["aim"],
|
|
61
65
|
defaultParams: DEFAULT_PARAMS,
|
|
62
66
|
debugAnchor: "playerRight",
|
|
63
67
|
debugParams: [
|
|
@@ -73,10 +77,16 @@ const pulseLaserVolleyEffectPackage = {
|
|
|
73
77
|
{ key: "boltCorePow", label: "白芯收窄", kind: "number", min: 0.5, max: 8, step: 0.1 },
|
|
74
78
|
{ key: "boltBright", label: "光弹亮度", kind: "number", min: 0.2, max: 3, step: 0.05 }
|
|
75
79
|
],
|
|
76
|
-
create({ scene, root, position, rotationY, params }) {
|
|
80
|
+
create({ scene, root, position, rotationY, params, inputs }) {
|
|
77
81
|
root.position.copyFrom(position);
|
|
78
82
|
root.rotation.y = rotationY;
|
|
79
83
|
registerShaders();
|
|
84
|
+
const effectiveDist = () => {
|
|
85
|
+
if (!inputs?.aim) return params.targetDistance;
|
|
86
|
+
const tgt = inputs.aim();
|
|
87
|
+
root.lookAt(tgt, 0, 0, 0, Space.WORLD);
|
|
88
|
+
return Vector3.Distance(root.getAbsolutePosition(), tgt);
|
|
89
|
+
};
|
|
80
90
|
const boltTex = new Texture(boltTextureUrl, scene);
|
|
81
91
|
boltTex.hasAlpha = true;
|
|
82
92
|
const boltMat = new ShaderMaterial("vfx_pulse_boltMat", scene, { vertex: "vfxPulseBolt", fragment: "vfxPulseBolt" }, {
|
|
@@ -115,9 +125,8 @@ const pulseLaserVolleyEffectPackage = {
|
|
|
115
125
|
muzzle.parent = root;
|
|
116
126
|
muzzle.position.set(0, 0, 0);
|
|
117
127
|
let muzzleFlash = 0;
|
|
118
|
-
function fireVolley() {
|
|
128
|
+
function fireVolley(dist) {
|
|
119
129
|
const n = Math.max(1, Math.round(params.boltsPerVolley));
|
|
120
|
-
const dist = params.targetDistance;
|
|
121
130
|
let fired = 0;
|
|
122
131
|
for (const b of pool) {
|
|
123
132
|
if (fired >= n) break;
|
|
@@ -134,10 +143,11 @@ const pulseLaserVolleyEffectPackage = {
|
|
|
134
143
|
let volleyTimer = 0;
|
|
135
144
|
const observer = scene.onBeforeRenderObservable.add(() => {
|
|
136
145
|
const dt = Math.min(scene.getEngine().getDeltaTime() / 1e3, 0.05);
|
|
146
|
+
const dist = effectiveDist();
|
|
137
147
|
volleyTimer += dt;
|
|
138
148
|
if (volleyTimer >= params.volleyInterval) {
|
|
139
149
|
volleyTimer = 0;
|
|
140
|
-
fireVolley();
|
|
150
|
+
fireVolley(dist);
|
|
141
151
|
}
|
|
142
152
|
muzzleFlash = Math.max(0, muzzleFlash - dt * 6);
|
|
143
153
|
const s = 1 + muzzleFlash * 0.6;
|
|
@@ -202,6 +202,7 @@ function excludeFromGlow(scene, mesh) {
|
|
|
202
202
|
const thrusterFlameEffectPackage = {
|
|
203
203
|
id: "thruster-flame",
|
|
204
204
|
nameZh: "战机尾焰",
|
|
205
|
+
placement: "socket",
|
|
205
206
|
defaultParams: DEFAULT_PARAMS,
|
|
206
207
|
debugAnchor: "playerRight",
|
|
207
208
|
debugParams: [
|
|
@@ -218,7 +219,7 @@ const thrusterFlameEffectPackage = {
|
|
|
218
219
|
{ key: "tipColor", label: "尾端色", kind: "color" },
|
|
219
220
|
{ key: "coreColor", label: "内核色", kind: "color" }
|
|
220
221
|
],
|
|
221
|
-
create({ scene, root, position, params }) {
|
|
222
|
+
create({ scene, root, position, params, renderingGroupId }) {
|
|
222
223
|
root.position.copyFrom(position);
|
|
223
224
|
registerShaders();
|
|
224
225
|
const preset = PRESETS[params.mode] ?? PRESETS.ion;
|
|
@@ -228,6 +229,7 @@ const thrusterFlameEffectPackage = {
|
|
|
228
229
|
const swirl = buildFlameMesh("vfx_thruster_swirl", scene, FLAME_LEN * 0.42, FLAME_R * 1.3, 14, 24);
|
|
229
230
|
for (const m of [outer, core, swirl]) {
|
|
230
231
|
m.parent = root;
|
|
232
|
+
m.renderingGroupId = renderingGroupId ?? 0;
|
|
231
233
|
excludeFromGlow(scene, m);
|
|
232
234
|
}
|
|
233
235
|
const outerMat = makeFXMaterial("vfx_thruster_outerMat", scene, "vfxThrusterFlame", noiseTex);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { GlowLayer } from '@babylonjs/core/Layers/glowLayer';
|
|
2
|
+
import { Vector3 } from '@babylonjs/core/Maths/math.vector';
|
|
3
|
+
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
|
|
4
|
+
import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
5
|
+
import { ParticleSystem } from '@babylonjs/core/Particles/particleSystem';
|
|
6
|
+
import { Scene } from '@babylonjs/core/scene';
|
|
7
|
+
import { VfxColorValue, VfxEffectHandle, VfxEffectPackage, VfxParamValues } from '../../types';
|
|
8
|
+
export interface TreasureAttentionParams extends VfxParamValues {
|
|
9
|
+
radius: number;
|
|
10
|
+
yOffset: number;
|
|
11
|
+
height: number;
|
|
12
|
+
rays: number;
|
|
13
|
+
rayWidth: number;
|
|
14
|
+
spinSpeed: number;
|
|
15
|
+
color: VfxColorValue;
|
|
16
|
+
auraColor: VfxColorValue;
|
|
17
|
+
ringColor: VfxColorValue;
|
|
18
|
+
rayColor: VfxColorValue;
|
|
19
|
+
beamColor: VfxColorValue;
|
|
20
|
+
opacity: number;
|
|
21
|
+
auraOpacity: number;
|
|
22
|
+
ringOpacity: number;
|
|
23
|
+
beamOpacity: number;
|
|
24
|
+
particleRate: number;
|
|
25
|
+
particleLife: number;
|
|
26
|
+
sparkleMinSize: number;
|
|
27
|
+
sparkleMaxSize: number;
|
|
28
|
+
riseSpeed: number;
|
|
29
|
+
drift: number;
|
|
30
|
+
bloomIntensity: number;
|
|
31
|
+
bloomKernelSize: number;
|
|
32
|
+
}
|
|
33
|
+
export interface CreateTreasureAttentionOptions {
|
|
34
|
+
root?: TransformNode;
|
|
35
|
+
target?: TransformNode | AbstractMesh | null;
|
|
36
|
+
position?: Vector3;
|
|
37
|
+
params?: Partial<TreasureAttentionParams>;
|
|
38
|
+
renderingGroupId?: number;
|
|
39
|
+
glowLayer?: GlowLayer | null;
|
|
40
|
+
disposeRoot?: boolean;
|
|
41
|
+
/** 经 @fps/vfx 包播放时为 true:root 的 transform 归 service 独占(placement/挂载由 service 管),特效不写 root.position(G6)。 */
|
|
42
|
+
serviceOwnedRoot?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface TreasureAttentionHandle extends VfxEffectHandle {
|
|
45
|
+
root: TransformNode;
|
|
46
|
+
particleSystem: ParticleSystem;
|
|
47
|
+
update(params: Partial<TreasureAttentionParams>): void;
|
|
48
|
+
setEnabled(enabled: boolean): void;
|
|
49
|
+
setTarget(target: TransformNode | AbstractMesh | null): void;
|
|
50
|
+
setPosition(position: Vector3): void;
|
|
51
|
+
}
|
|
52
|
+
export declare const treasureAttentionEffectPackage: VfxEffectPackage<TreasureAttentionParams>;
|
|
53
|
+
export default treasureAttentionEffectPackage;
|
|
54
|
+
export declare function createTreasureAttentionFx(scene: Scene, options?: CreateTreasureAttentionOptions): TreasureAttentionHandle;
|