From 2c3d1290f7489d4240547b1857b35322a72c4979 Mon Sep 17 00:00:00 2001 From: "svoboda200786@gmail.com" Date: Wed, 21 May 2025 12:31:49 +0300 Subject: [PATCH] Initial commit --- .env.example | 9 + .gitignore | 4 + package-lock.json | 1265 +++++++++++++++++++++++ package.json | 10 + public/images/balard_avatar.jpg | Bin 0 -> 3231 bytes public/images/elena_avatar.gif | Bin 0 -> 41175 bytes public/images/elena_avatar.jpg | Bin 0 -> 2161 bytes public/images/elena_avatar.webp | Bin 0 -> 283554 bytes public/index.html | 239 +++++ public/js/client.js | 583 +++++++++++ public/js/ui.js | 534 ++++++++++ public/style_alt.css | 1399 ++++++++++++++++++++++++++ server/auth/authService.js | 133 +++ server/bc.js | 179 ++++ server/core/config.js | 111 ++ server/core/db.js | 94 ++ server/core/logger.js | 93 ++ server/data/characterAbilities.js | 178 ++++ server/data/characterStats.js | 47 + server/data/dataUtils.js | 72 ++ server/data/index.js | 75 ++ server/data/taunts.js | 118 +++ server/game/GameManager.js | 343 +++++++ server/game/instance/GameInstance.js | 474 +++++++++ server/game/instance/Player.js | 0 server/game/instance/TurnTimer.js | 120 +++ server/game/logic/aiLogic.js | 133 +++ server/game/logic/combatLogic.js | 313 ++++++ server/game/logic/cooldownLogic.js | 154 +++ server/game/logic/effectsLogic.js | 153 +++ server/game/logic/gameStateLogic.js | 133 +++ server/game/logic/index.js | 66 ++ server/game/logic/tauntLogic.js | 90 ++ server/services/SocketService.js | 0 udo systemctl status gitea | 5 + 35 files changed, 7127 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/images/balard_avatar.jpg create mode 100644 public/images/elena_avatar.gif create mode 100644 public/images/elena_avatar.jpg create mode 100644 public/images/elena_avatar.webp create mode 100644 public/index.html create mode 100644 public/js/client.js create mode 100644 public/js/ui.js create mode 100644 public/style_alt.css create mode 100644 server/auth/authService.js create mode 100644 server/bc.js create mode 100644 server/core/config.js create mode 100644 server/core/db.js create mode 100644 server/core/logger.js create mode 100644 server/data/characterAbilities.js create mode 100644 server/data/characterStats.js create mode 100644 server/data/dataUtils.js create mode 100644 server/data/index.js create mode 100644 server/data/taunts.js create mode 100644 server/game/GameManager.js create mode 100644 server/game/instance/GameInstance.js create mode 100644 server/game/instance/Player.js create mode 100644 server/game/instance/TurnTimer.js create mode 100644 server/game/logic/aiLogic.js create mode 100644 server/game/logic/combatLogic.js create mode 100644 server/game/logic/cooldownLogic.js create mode 100644 server/game/logic/effectsLogic.js create mode 100644 server/game/logic/gameStateLogic.js create mode 100644 server/game/logic/index.js create mode 100644 server/game/logic/tauntLogic.js create mode 100644 server/services/SocketService.js create mode 100644 udo systemctl status gitea diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a8cf526 --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +DB_HOST=localhost +DB_USER=your_mysql_user +DB_PASSWORD=your_mysql_password +DB_NAME=your_game_db +DB_PORT=3306 + +BC_APP_PORT=3200 +BC_APP_HOSTNAME=127.0.0.1 +NODE_ENV=development \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1aed9c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +*.log +.idea/ +node_modules/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b0e68f7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1265 @@ +{ + "name": "battle_club_git", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "bcryptjs": "^3.0.2", + "dotenv": "^16.5.0", + "express": "^5.1.0", + "mysql2": "^3.14.1", + "socket.io": "^4.8.1", + "uuid": "^11.1.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.18", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.18.tgz", + "integrity": "sha512-nX3d0sxJW41CqQvfOzVG1NCTXfFDrDWIghCZncpHeWlVFd81zxB/DLhg7avFg6eHLCRX7ckBmoIIcqa++upvJA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.15.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", + "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/bcryptjs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", + "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mysql2": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.1.tgz", + "integrity": "sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1e86957 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "dependencies": { + "bcryptjs": "^3.0.2", + "dotenv": "^16.5.0", + "express": "^5.1.0", + "mysql2": "^3.14.1", + "socket.io": "^4.8.1", + "uuid": "^11.1.0" + } +} diff --git a/public/images/balard_avatar.jpg b/public/images/balard_avatar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4afb06d7b0a4c3ff911a4b1b14376a075e79a89b GIT binary patch literal 3231 zcmaKs2T+sQ7RNsbgkB;_6BCu*TR@N!MEZg#MG#z&Dn&s+L28zwv|TV%1r()v)p^YZfX^70Gw|J2{^ui&4Sjf;y*fL}md zSXlg^prGKvA4c#X?B_DrUylE8_s^|=#o${2#tj$(h7dLwzz$=Bz}Uc9fCJzLyV%P1 zF}c`&=m+o*ad1M|p=Z%w{aF+S*x4Wub`EwZ7i8bCN3gL& zU>pGFu>+c?&Ot@Ij)x^4)WSUCI`qO+y3=8FUR%_AQzkrVOZGIhBDIUKwC#7}R(@quH*uNfA9=f=>Q&D; z`P&Igm&;M9h1Jcy6D!~KtXu(j5CvDtY|b5@|QRTMf@T)C9k6KzmYA@ z{~P*$GXEcBa2nu;{KSL-C}93%e16KfE=8x&yhvs#UT5YOd%M9{wU!Ue?l)<}CcjFL zHCxFcQCCruH6PQq) z+cJPlt!HKvvZ)s7m7SN5bcyP2(L?k%(?!e5>!2>>{Wssr&2-F+cxOQrOocUBS9}hZ z&3&}H^V0k(M7%;tSyI?$gkh4E|KU%d%}!el?f?R!EDTe`hbFW+F=1Ru3us5wG9&P; zjcj-vHfpG(Wl13{PAnJ=j?B}&7OULh?_}j!(AIpoU82Zt0va9Zd(1y7=Gf$yca6^i zo4$E8PbI^;RKgJ3!xj|sOS$y=KzdtPx?7Lm-LA-`a(}m-eEBRdpHhy*^%$eb;$ECe zT+6#u#?nTyx*@S<^~CENghIHJyPLUxYBDbVJ-zfo3ggu3D*fsi!O|J*#}9Lcm^+gl zmmiP$;nXC0&Z!$vrV zh!a$|M~Em;4?^Q5$(k=U0Fe>N5r4rF0&eVEiC!ZWZtMR|IrfOxSJmP5J55a{NOd7x zfAwzK6_U67@5z1C!FO;eZsRAkZUI^!`b>;ew$Nib>Py1Q&v=%Q3QIlt4NeptWU%7m@|?i<<}Vy$h{|OrV?@G5V2@Cl#)F(UD-CMdI?{lG4ObLnlz}o z?%(XPQwbBE%hwm$khmss)j^72+cjxWMJj{`k6fugjyJ?VWQI>Wm`Og*xZvvm^9}L) zMP{oa@&PYG6 zvu~4htGrxtF}rP~;Zk~_rjXH8g3E3=G5OPNs-tEjW^xMI`A(rG=d#5j$1jWxR;Rm4 zZ+6-?+kvnAi+L20johA%xVjYq_fu%3TEP_tR%I>)X02#0Xwm;-sAqmHQt0STF{5Ny z6PI-2%(3w`9ZQ@xipWCjP^BiW+d10fkGBz@28B5AeUS66vZ^nziI~@7@F;QBVyw5~ zYx@gMUvjD59cpuir=WQ)KUeQ06Pcw?DAmo=eV$MM>;%V_J#$%&b5|)9VHP=kv?b%b ziWD#IKyDd`{jEsUhk{{we$@ueg~=Q7^1H;PScKkv)OJwjK&H4~(w^O`9ApQ*?Waby@Diy+IyLb%FMDU=Z1~UJf*)+SyoIYxa^6V zWMImA8l4X-ky`}r=T-U%$=xa^%}KApQd3^HrA1lz&hph;ESm3mZas6azVmxmCO-w! zNI5` zbGNsr%@31$2=zP*1isX7wJ52E^(tO0JxtGuN9RXww@fe|lui}W_8&f;j3@}2Mvz_d zEA#WO#`DFFU#>GU$b=1MKw7*fwUaKLj+Ax~s-+F91)Ti)+C5lQn1mAvPxtpQKin-I zXM-l_KRqCw+mOdbuCH1>=}{Q;>a|~m?)ib13=Y@v4jF}+y%MO|x|5lpZR{ZCGYBlA zR4$r_;6Lg1tUj~V(Ndkcb9X+_a&P`@zdjo4zSze*Z?tyYwl_lgy{rN;u6+3qk1*rP z%8#>WbhWfb@1m9S7uV!>)%(IteQ)@vTq>?8q*|UE+eQz}I?P!=!vFcYsi=SYk%NYZ z*fE&yAwP%5PxmgUnB0TvTfySu>4Kq8n3uD=>4?wUARxV+{m64iYUccULn%mDI5~Xx zVH;&A*COZ@G6(!FN6eCw&rBmocL-fSr11qpZG!^uujXAt-8rDv8TGq zk5_`g5mx3lb|&-_cGO5{Zva050vPP7#C;rg7hCzZZWSa_uqOS&S)xmDz3RM}XFdzY`bOqE9YX;(^q&v8WTy0B6K4Z8t-8_?XIT*juc#q+ zwr;JfcdO?Ae=V=I|A#II_|)7Mo$o@u^Q{&1dAOK;l{PaGoz z8Y?1Q;yrW)1eD`i1&aX;khG?`HiFN=I4E!AEfH*FCw=BMABy+xxSN7NJ_yi4?Gox( zqI9A`OlgJDNf3Cu|5cP0rgl$!x`tLWbfYYw!Es8%c-j3}->um%NyVQKRVX{xgzV~x zj+a8j=!nHNdNRHa)jx<|813*}X?lR@Hoi+uWz^cwGNTwgpEq#Zo~s>B&uDaP=YH2k z7fTDUo~h_DI(DICFhPBDO9t+?Ptru&MVo;@s$%CZ2(-@?3jSh)Us&#M(`QllhCBtm zgt*csj-a1|fOaBX20?AX&dElhDJ{nB@~fy(B8fnsm|*fwd<)(Lf%n*C?oD)N z_7({61Pv_Cfk5{5r#@5<_R)6`;HDclUTkF*(DXxBH)#oQ!y0Dq*Ra9&Hx1=+^9!uh z@1*S)U2u7^r_91pR`&@imQhdIT+jW?x=p|3GRl;oksPx_T?oulnpVqB>?{b}wh($ literal 0 HcmV?d00001 diff --git a/public/images/elena_avatar.gif b/public/images/elena_avatar.gif new file mode 100644 index 0000000000000000000000000000000000000000..d61cdeac1490ba15e9e98596a947e7a28f222804 GIT binary patch literal 41175 zcmZtNXE2=a8#nxISj*bgJG**Itlpv%T^31{5UWIp67mxyMD$)(uZh)bSc&LFZ$b1R z$ReUfOM)Ql&i|fg=9y>ad2!7(bG^OhJkR4iKHo#n;HILILna^-a7qTKGO!pju-#=~ zb717~Vc>Ydz@g91=fxoq%psu9CwLbvWFjebM@rt2U(829;w7I1PFC@;v{Imy(!HBE z|I^a*Ho*p)T6o!5zp%BBW8lbO;3{O`dc(lgO3xL`C6vJ>T*x6<#3k{DOT2|cu!ZYd zoDe2MKsr|XMy9lCp`i2|0huBZrCO02E&S3g!YYMQs&Ay!`x&^W7`T@icy}4N2RN@y zafmK)h)(j#F7wFjunX_-${use4G72%2;W#1Qkf7@*%MZYQP(R{GkBwB&?=?bqGmiL zthFMd-H*}gmoXktHJQZRT*2r~%bG9Cnop@>msQR8M09@(>g`|*k1}-&~neY z!}d{<)5BPg$Jw4fDZ#-du3m4QJUX1cihTUu`S^GE1Pp3hPwU;8(y?99u^TY6-`2ES z(sS##c`#(_Ic@H=Z00m)?zwH^^wY%Ym!{{sxyPZE*O96Bh3<=uyDv|%FMeCSn8Q6D ze&9Xh?ltOZ3M`iA`SkNNpB?uTpIg80LYAh^zUtQhuwtgfuejqM+CMIDn z1phfMqc0Jj*BcRaQ-xlr5K4PgT9?t$*9w);d}LZu#x|+1B>$(&}Hu6~D{tPTzEpT00Jj z4L@5ye9!N{$nE``H?>9S?xV}IY`z~J=w*xcmA!sofo;g2VS zBRgZ0ryplFKYu=*oj;ymxfs~EnA=}jC2g**Zf$S>{JwSY>lgq4p#MN1u>ZZUXiRl) zTWD)yb)~Okz`%c>B>p}B7D*-m03`$d{#h5slQcr1jAFJ!#KPWaBrAu`ylD)cNnSdM zZL-LTK?HmJja_%jqRo%qqRQBnxG|E2aT3cHA+Gf&$jkeA97%l}N)>(p<2G!lzy@D> zzc|B&M@=URc#}fjIvW(Im2k-IM9x%vuEWLefAJU6iQ&ExV5}Jii9u;b?#+wd*-7Wu zo9@|J{`h%y{_Vr*TF(pl{iRm_pC@*sY&G(p+WL5`ieNmC>PuUav8>7WL6cvK`L}oSJD3iyX!Ht%T8X%-&MZ z(?@l~fL=bsf<}1Fo8mlAT;rl&&sZdf3GO^~j1zA>b4-w#n@0;O?a!~L>V|@oIE1oM zx*^d_=0n^S1g&ua+qu&r{x0ACZ`sn<-i;!!H)Hx)?g^~9hrY8Le`in2 zajhCI%Zu$26@KNH6ej#qV5f3Qt**VNhw)ZT3x&{|1_rp=Sw5qoNYR8YOq3)dCZ_Xq zsb4ETnk!X5fA?G3Cna)G%oCVh7ohlCMYGuLD*kepdflq()ajk-`f(e>H%-c)GqPK5 zmLw(^5}!eZvblgvtOl$flHWFmUqQsHc#A^st@3Wm#4a9aEa!r+qOAklOXK(X+ zHGj*EuWj9HrKxNGwOEbP99^kK-Z%^hT}AJ>6vc^6;95;(CymXGr3Us`ZpTt^CLmSz z^s#!}+sc>T%0J59Rf;V8h8urWKAEo{B!@^y5AUeez(#f}M%_M2`ZLOe$374GculzL zuC#>l=1(tKB0GPg?57CE@mQ|u(TPa|&bPkPxAat>&RU-G{~V?Kh#o|CReh9|*h#lA z74v#N{Z{6DH~~qXvk6?KU?;+-h%R3-%dl5dLD30mMy7oVW5Hmr7}5@dm*QHSjHzt5 zBa7-Ib(UqH1(YqFW1e_M0*{%v-=wKpy4sGaANKvHAo4ygZa$ldHifQ$m z%7H(cHak^zItL9cMSJqzoNCT+<|nc_vN>Mxm@1<6OF#UlWPld9V-fV^e-7bYM})#{;2SYj%0o;};r|h^S$TGc@Q8Zu!}ZiAt%Ve3BFqD1(OG z8Pa0y!63=fB@XLeLp9LUSR}I{#G{5-c{gaCbF=xA03Us<`dJU_MTnhdqrPhRWwP{P@^zw;7q2TWb z0~4>%v^R&&vKl#+W>1FG(Kk5h^c3~(pKKvsPJ~BL(u-TpM{#W`Qc1}2!%hpkx%ab6 z#LAV%G;c;E-OL%hu@oK6^$mrzErK*DY{H*c3CN|qH7vxv6IuLLrqHcK?GsnT z_KjQ;q4Y6i{6;+^+K0BnnCv!|5u?o&Zxz8)5=N!3QG~o*0?C3p2$wH~nxq z>5-OT4SeZ=^N!}n7uJD3=wol1rs7xwe8-rW%i&Bp)md(JzS=P45vEO&$Aye-hUX{&4Vp>)2e-58O3J>*p+fqs;{XC2+M zpc{}$O<=y}#u>Oc6ZX4;pSG4h{@>*6KB}YVKh^BX(wR zH5D^n zJVwcHIx?L)iI4Nwxlhjq1E6osqVjRsYzsqw#UJ?xGCELy`JHU%6+>3t3ym>xqr_&$ z4Jc!D=^nB7Ac7TVurTEb=8y8Dn3E;75w4XT>J6!^m<-QcPMFoun}>%*X?$EG8aeui zGMwgdL1oTJ6zyXFg&gmat!GP~LQvUtl=@6cjwvB+m@n zcMfx$46sxdDT_1F)e=u#{?BE)Ce^C^Y@t10KSz`Mk+3*~*xr~Z_DyK-$qib@wJ#>& zm}|}tFRWh`o(wkRj7}Ef0xcH5pR~2VfqDODu(UD$)X?M2vZOz2G=0>d)i3n@7cwQh z1hc-fY?i%pU2W#dMNpQFk72g$V&}TclNy?pZ()$7g@&YIRkC6?wJtVZWfFf+HanAA z{sC~s{jR)+y{$7ePi2|geRxp8RFzNL`J14+lC1gtq>SF8je7ylM6&7sqxh7+w9wHq zWB1WCVs?Fwx_dFUbGIs^0i5=0hL%`dSbMF(1?Ht3**``(bbj5MSM4RW&*t^rM1N!6 z38v;?nR^Hi+&W9^MC8|v5E|Q=3G^&JLV&57-OYDuV!J>r z8bDY8@cofu$}#s8P4s?Y{a5z$kp-)0z_i9u>dA_<>Hflco zarWXc+WvBV8vSQP*!?H^DCn9_fg_R7`MbXE1#FP~W5wTY#>K@;I1uc^cZm0=FSGxP zrFoA6!UzCr5`YeS-4G+~hP4~M>zGVZi$J1$Qt6&p(4CjFBvR0dqE)k^ELSaUT~ERm z{oUmig2_=#2c&0#3W)zORKgPgI2NRS2v#P5RVJ(g@z`0mwRXa7sFPcEeKq5q|K_&&jZuO$4&R=9>pgpO;3eno`T z`-s1!hzLNWH7c^!JkrHK@;N?otTEDIz?6Jn-`lnLsneOdw7#d2&{I1pmVIvqOJRo} z$lGW*1^~Y14OYNJC?ip&g3zpbDoaiHO0*n$imJr| z3QU!L;sl-f%oh^^-6AT?3)nt%ah{I4ZM20H=1U-ber)fOa2}HI1B)23LX2)DOo$}T z%v;S>P|eR%EoSR3u_0F2prmYyH7mMru72NRl6FMsb<$HP&XSy%li^ayfg6r|?5Wdt zLnI3M_d>!iFz{(4r5FM9DuxWcamaFN2IUiiuKG%KBKfWzu|4x6es@S*hGwl9kI4EU4XaPU8rgdw{Ox(zf0AzJ*WRo?r{9?174`&4rgF}YFVL4PK zq8X9_+4h;)_Xh1%H&X-z5}Kki)&@;m1~c5*99nQd79`N^I93G({XNOvPr{lZU!nc7 z4hv~3h7gG$D4sk82^bdCum(>lMyMmXPcbgPXOk3Iz1yhnJ!J83|C7yd5^3Lv=yr=m zg#(cQATI!-frN0}q-On3YRFqE9j`J?O%X6h-7<*ya7g115LLqq-7cX*O@gG5U}Yk` z@x+}n*VnVYUX5k1uZ!fxQ|ll6Ns?)HrpUJc-I9EoQ*g0RaK%pO#}N9lgivn+bt?gW zPB0fNOwlOB;tJWS3b{tYwkV6R+Zk;NG7@rmkNt}dwmpP>;HYUjFA^mUDg3&3`ePs1 zt4ZnW;k2bV!RL6DFcS7PN#N9*HKHGK4KGBetHJJFC{3g{16ZF&zRnVU`XCB}oAa}% z^dg&|$9!)@Ea2hOMEDmh4I>6fjSN*qqO7{u@A4zEAIT{gQl}iUm;e}(57~ZtGd~ss z1ylxL0U(qHfO`TcrNQmjMbqA8o+}i)8-;;hkw^O#^|&*s+Z@bmKT!lfxzd7r)E z-5T&23>8y0Ky3^B1`Yi>3D2$y%6?S*zy_+`Rq%|+NkTE0hGd~8RO7NBJS1SvPL=!y z)d+#sRwR~j;4v}^(`=Z4am~)3uI^aK3wj8j!_vGJqlAV4V3_PqwWFJOHdIz#tlw2g~to>yBO zQ*&H3(6S;0`sff@pBX~wN(5Djf%Xb1(s$4+6TW1k%jyI>{(?%r6y$`Ve2uxIr@_rJ zkq|HkWa$!jh;>_Jo`wg)7YjH z1xEN>Jb)Glwo#cFbF>9;*5fo(___-2c4a9lQ43zuQvFsunUEg9_RwWOxVnJ6hb`cL zD;5LrmH>c|fG0NqRNesL5VQnRPzWQPg5@QD3ZdKb_UVl6`(D+z*VSioBj*s%AFxSh zA@TlB4XtGY$eI>%FM-1DvftrS-DwH_z?S!6{(yTv(DQP@=f>dEpuuOCpg=CcVA`QD z<00RyXFW0X6AgXwqeE;o{dIZW9US%dvDdSVDem&YZ24fvVVr+jBd9jm-xUS;VXb&j zgB4r=0Df%213-xy1bG8!+CXM&z#E*<(@AaXziqJyD$2_ z$9VM8Z8S1uG>tG?hNB&oK!}}<{x*Y68IR4;4$jw%0lLOkXvcX_<3bwahPd&vknuIb z__geqgYEG{i5#DJjC)0=5PY=Vnv4kDq{y!T?M*_6-8UIF&_GYxyNvBG8|tV`7*bGH zQ2ba$&Dc`S7_e*1k9OQC7O)XURSz0i!y%4N-FU9mw&_iwz@H3v*pL);ZUd?v|g(v=x`NDoZ?J9`i^b>3CsiOEEv*p0o} z(?hKCJOzCnXu~`Z*=z8xWHT6#Iqm#3z-wvjNkT7^B7k0Gk^(RY01STf1l=x~{1sA( zmPWKFAks{xTK4>dqch(B$!mq1w2gf^NSvSeMk#g-tX>5@@u3K+91g=TMUt@Thuohq zaswz`JM$JL2>>Cm@Xa~UKLKEb?%==xbnXEZ69Ckab=l9ccZ=bs417m~ukEisT6WO1 z0i+{bqT1MUn#-fke$chseX{TH`uq1QP>n=EW`Wj{X!l9T{eFg|UZ&T;S`IMl>s8L{ z9XzHDXtlNBM{BDgYb3&&MC}(;FWo#~U0N-5^1Ef`nPFk^u*@3*2j7BRe# z30~zk6E!vSUUZ}t57Vnq6axy77_SD74c?l%2M{&_2;Tz~R09j{0p_RS&6DspAKgwA zZ4VCqn#}fke|rJ2-s&yO<}uVxozb$l09^mZ(a-j!RPlDv%L%Pzj!6*i&kStXoMdSl2wp3uODR}JlK-%>Vh{lRn`yvDn z)Y%1U>cRx1VD`tq7!GRZ@D;Zo!zGm9ToN=GwbU(+(p{GCD9PiqzkRCv8I3ra?s5PR zj)fP;!iOK=MzIQXQ?VU`B&k8wDIo3?5RLdY=R_4Z5y<8B;}dyxE}>W79>9x#mw;_~ zMqsv|qP9Q&(L{R>V5zn)6co`ro{WyhZB1fJ>LYKSj*mxTC}jQW$T@h9`3Hs`~iSLCZd1oiht z>N~ivJ7rfZyN`BZgkE>`4UP$*{OlcVjRsLHv%z6%sqw$_52*QxJay5-3HbG<73d?; zfyvp`!CxIpQEEoo41T2stX5k}I)L4*-^xvb*!a}@+jd*=OHA>B?``|JR@Ni)@2Hju)+Wr9m^=;oQhnki<;Y0Y*H?= zvnUOXPyvHI3F|2kWdf^q6jJkFqvx5g=-LxO@7S$Xo67NH<&6rxz+iIV zLSjON^n;O%YxV>A)<3^T<*QKtx@7ltH7ha567xKly*X2Pr_P1Ey!hLFzW%`{8^gB7 zA77gP`?(USdL*ak5v;yKnSbFtV#g#xD}wFPb}ZK6K41zV2;b$^i)FbZrmZ|(rtCYB zEv2n<2p7BU5xL5z3uAQbOF(N0JdAB}TPhc|_^xV;rXS6AVC9r=Z)^Dbu6nEv?;BXI zk-p>;UJ9f{p_)OD76D}>X{c!kh3cVcL@sQ;5>TX!H;vkICUm!QKhc<^&w49rKjUX^6n9 zy|)G(fIgwgE01;ektLXfcb1*R(375A*^pR~l2Hj91*Hy7nk@zs(D! z(D^3ZQx=@ARm5)o05H@qK zdw+N2!_vZ>S;|W6P$rabLKYfmVdS7Q?LM@v>MH;bz2I0t%20t z8uDhEHSU%ibYBWG#m}P3drJ`w0@DHrVK84!H>|6|cQ!!}^`t3zuZ!ohOFkb}S%sdK zIyHg)S2MEiSMK#&+6Q|cvf@JTTstF0WRRV*t@wD70@mUr$Ilj$vUq5n%b90SBp=f_ z>(ME3d5>xqJ>u51zxz03r+HmbgMP0pchMmlMad4Ku@bH zkoN<`X=AvpF#jL{*Tr%V@7(HBYV>?@yP|YOp0Dc7Kx;3lv37QMZ*)$>V{mA^d+*)! z{k9)X>#y7P+kURJ9ee=uwjcH~+S{%VCB(~7Z-pQrosu}x`ABY8gxm1 zn;+af1eW`+Kxb`u3WP#Ne2g?X0u_N76Hw%B?&Yoli1SQ%!fL00HO>=E?6wFQDPpLI zPDEPVt@rLAr{S-}W?iB)QgfEZ@e!br)4Razr#FXtLI+MDEn$T`to0Zb%< z1#pM17F{Fnd-ZK6xLRR{wZhGZl&7h8;FsUUv|0_77Yc!AHVPCl^HA8!E+E})XYf5A z+%uif3CfsM;D=H)=K?O*OGlI;E#X^YInx?)qdBXSSj@tJ56NU`Oha%{M9AZ&9c}LD z-+vn$b_1$~R3-yzdV>IIwu||cidb>F1_FhoRg7F!Lp{jMExko)gVTatucAKn9@n!6 z65PH*pqW%~)93>ezXc;LSVCAgR~c2@vr(wve>sg$gk)DyCi3Qtw#nz4a=Jw{VTOjR z3%*WVzy^^IOGk<&=C7rT*Q@AdBVV(pNXIniIX&=6Q8=}MA|CP%MsGtRiJ6V6U zV36Dx5-*jU_Q?Q>$s|)4S>^0!HnAt!>ecjHDRsRKE?!(R_LG);_xiVYLFcroh))2E-Bwtv|8XhBt6}fxc!`8& zY^>p)mSOEOO*(_P=bh_JW}oBU*F4tTwJATHOPc#V9bm#~556}KLCJf||J`;b^Wql9 zg5{HnRrM;&@`ipj-fetmx2r4DI_XO3{=U9@)qSRj#SNMoJ{1mPGgtuKt)_6Qo87r1 z4h#59x6miWbN7XR(3hqC#wPZ}-3W%9UQa6$YbS|4_VjY&H1% z?-4%l**Af~Khr}m;OR7)8xravA5Gx@S=Mo^*Eaq+2ut#O705N`tXB7Fmd2Ov%P*VN z#?0#sv&q3T+CGfLIr$LIr$KtlZr})&DBg~LTkjBDx!~!_ACuMK3r-SnrmUnE9Z^;DJ#yTksD27N+P71g#wV4BlL3j5zh&Rh|6H^5;`H+nFsRCy#Sf^pN2a zA)s7paKP0Ar0j~bPBK+s*=jXZt^-wZd^#2iIe#88E%&Ims zje+alWHDMa4yCQ8V|7TfRChCxh3iPfwjpM< z^Mz8HnB60bNZ9MEFj7}H1i_;fe3`&;XK#|<-MBZ7`^#~qR^{zCjty0jjn9k^!}?O9m{LVxQfinjXIH<4Blfvo!0;Z{jx$65_u25n6 zy!qascoAALfmnen;^u0CNMs?Fg@)ZRMrcBV5guO%y}6DTU0}0LQaL#*7SZ(j%&nzU zHIu9*i3-*E$IQpKf~#uYnJP)-GHhg{-eA_W3UwGwls=@SebwrLYJbm%=NJ@exz#Rw z&x7@E7bN%^{+d&_UST&>-X;}eWgnlgZslh-KaAAM!G#%!d{`|`Qm%#GP0*U7J>SBo z6k)d2Gw$bZm%Vy@ysb_yd@}f2b33th!i=R05T2Rt0y5GrbUwZ1M+*7*@J*w%?;v;9 zuUrEqSjM!Wc>0Z>qv}iEU^HP^zQN;Nh0C!=!}Q%i&&I2qnK#fi9+4BHzw0ZHxa=pcSUtv#^!=|jh7)NZ2F1erOX)z5}(vn%Qib&JJE5%duvvieMBY-@4imBt^Dq9xuI z*yue$fnV7X5QZT#kD*8*w7tC9P9J^MW#Y61sciP{2^IG+!p%!1-ezO> zIrTy|Op>hrP4ap0c}h)ky#ch||9tsMDqV|dl0T|z%~Dn?29*2|vl8ifvs9#C3qwyI zGx<@QX2}xVeWHQT^$ufp9#FsT+7cm!b&3I_+=_lCpH+CEr$qFWbQIHzh>uY}cE4^Y zZxz&Bu2YfKC&H(R-(}6PJ!+oWUtUUE(Q|VoMDHA-k&zX0d{O%`&-D42X4gN^HXaCx z=?s5@{X+1bk=mkQ~6|$)#!+0uC-lvB?i5NYcVurTF#*pb`gc!|_on>1}tYn7` zZjDQ(U$F_M7!RrbpzlM8IG`9ajtz!I`;g({I-5*S5f--oHT7@o zOwSaJ=e4XEit}Y>3X%QcI#-%z@s78PpG3ruc0C(jZ;|Bvb5@}8dMrzg>k!SqM3RO zwA}gbu$wX4LsnVtK7Ew-=DV>Tj#=D#EN-r(Mo##^z3AFQxl(5_9bWoB?%Lm_5a$k{ z=QK*FqNz{~%}dRfPj198^sWttGx(y-Glm|TxurR7Z}LtnLw?QlA&q>7tmN3qqNd6; zQz;R?e8r`^hQ`(SncBG^#=bsxrz*CbAIHhz%P5`OIcY}3t&nU*lDf0v@{^J<(e(6d zIJbCPSHGiBP$-q>sv>?&3{pk=r(aS_*lq`qh5iMjz=VXZ#AY`&Fa35h`2)Nk9CI4M z-v0OnbJ6`1@<<0=>_HF}SIUKB6XB)pb;Dnp4!u*qP_zz*TC#=IFB}~`4c=Ur=z9Q- ziWO}X^l|rk*k;T8ilFBO4R0B zGqu3YW4JuN2E9t^arA={eoNV4WTulN)ns*;#$5FZviMT2FPF?ZyBWLZPBYYtrA>n$ z{U{R{W=u&@S@Nk43-SZf;zG1KG0{r5`8A$~i=i+36TmKfk`~!vD2QeP->u4GsUlBT z<7*6sz`%(sHoy7{pFL;EwuMifdJV$Tp7-wr_RY`9H+8_rY*xg^a(=H@Tmc_Z%Rta} zDvCNx06rFC7j=l6j??1m1G?+ZjP2AqnRArzI71h-Inhv>{40N?`TU$%aw4K#WW3K_ z(89xXUte`ggNjkH3n-*$gbV{rXaT;n_QT+Bw+Q$tJ0WiA+mX%zUB5)Fbnk7fJUxCC zl>Q($>(l5r9D%v>iF)00UdiMZEtmU0^EPjYsT}+(NNVpDT^#f3-8!GjBm1V7D-rDq z>StAAo<8SLX_BJ)IdeHg=QkRFUKG)0Hz(jZwzz1%utbKDyP!8ioZdOC_ey@!Lj(m- zhguKGXHo+1MK^!?wtVMV`9;vmV}&%{d%}v92j4$POG0><=n@6*ABHjgyXq+DxADLx z|D!VF3;S?pz2;H&_Uk;@EDLYERZgH_F@I(#da|!5MQ%^k829utih40(7JO@(x9`Ms zyhF?H>%~{9g3VsZV1Xq2xNqQV_STQo)Ls`PZ5auDnWcgvj$YjDxAlJS$h7)m-qH#> z|l35c|Aj)YG}+OtJDxiXO-Slw&WHexD_VxhDt^--v&GNqtmXtV?} z9|CB)eXo$3yaH_L-qxWQ#o#WgAPt&E1^Z=)GEE@`7C^yGWW1W-lJ>Wyva)b5wU_zN z8K6M_IQw4ld)KRfwv7j=SW;MpP+Y7E^cHbTg0RmIsY1GGMmCYUSP0FL5)0bS?TC0>dInh4Hz#-y68y% zU43u3Lpp6}x?v^Ucsm^{n!%u%QHsm3s?4x`2Dx7e!HH(VH8XMit{qm4|Je5twR%gR z#^x4s0clXEMTMIBAK+Yd`Q9P14x#AQ2uS4<#4-U{PGYs%M%r0ZJtf}cJp|Gp#kBo= zUM1{T9u+l25sgYQPJ<$*G4Lx~q&5M`O9EZTQ4F6UUW+1*NyyKWu?#x&FSMEH9ke+p zqs>m7<;i~c5|z@M1(6+p^19uqd;cK%pXCp?NKmF01s)Wo?)6eA?bp`}J6nd~U zob4`3pEB%RLK#SM%ku_MiTu;GLqwnh6sD!S+?7w4$vr^4bEQG)JV9-MhdeAn(tSp9 zAi;{2NH?TdUuO1w)}%kuu~#@(MCjAjp&0tYSA1rOZX_*D3WyH{Qo{+_Acevv;lDHx zbSGkyEl6h!Jv$zFrIAewq=IR}Jp#N-h4kH1vD`E8@n1cWyq6$-(s@i_@v7 zjT}GHERCkco07)i=53$!|au|Y7sf3^aJz_QVQV{K~$}8_s+DSN} zC8oH{s71F*(V@!Jue83U>T;s0j-y%)Qyqq_7IvX+R?elZukN3y{tyS~R;~eIYX+;3 zBdy3$j$Ep<8o?3dR4Y@Z&6{UC$XXaBd=kn!qs6h9~SXsr^d`1~e7PAU!i@5fTZ98!;o)}JO`m!hZ(igT`TfWAinN(?!y^Ro0;?+)? zdHwU6u82*vlT9xw0GmXz{n0BqnK&{u_oc$ef*zt1H%zwHsLzruE0k+iewkqN((r61lZ zdR|9WiR{gMM=MoCvr4y7T~1>bpnrz2zCcJ2Aw~cSOX3HZBaFU**%Vax2A~>l0dx8Z z)H4M9vt63{E)G%?0$-MH2*{2D2;wQ-4s(8uAl0=E5e}~WhHWmQ!Ec)!x4$6w;yVul zla5s?IpRK?fBcX}`cMk!I?d(=kD@4ox~SW`;O#)f4FG*Y38QT{Ykv3GB9fbn9wiZh ztT2*_E<#EbJ#=wB!1wG`Ae)HDx=PT1HKZ*l9s>Y-eP7C*9F|u~iTZ z2;p_33IVcZ3_|1a%vryw{BX=Ky_*CuU>OvcM{Mw)I&*rL>=<@KkRRbkfB{38fHkI| z8=1yVAMXdh!h|-92fJ()AXe#;Y3Wn*8PYEinM;uD{NcQ%;o!sJ!UU>fV@PSxNabE( z_0kCY;m8&8Bhx6d;Rd8}3EsSvN>l$4Ir;HJ4NZ4K(ZpgM3OI~%0OSlKO@Zwinq{R# z=)cs!6-HhMBY)Ng(ft=*It0qIgXLR*@|8&1O9TZzXZM_#Y6E%^G*EH~rR}DHp<@t* zv?GV(4AelDh;78q6>yytbWbCFDUQMekPpfm!P#VrGC(O{~0}aP!OyRTasM!Y^v$_7W)*Z9` zj8h*EXB|3_|14O?;1gF~9Xw@-_9*uXN8tTO1sKqWhc$v4l~-N4*h3|;!(0x)B0qA{ z$99GbImk^wvZHFi-mKRN;-VP&tA7~@4M^O>IJf>;7;x%}l07M8N{0EO=h`buH3q|bWCoyts3|^56d29xyacLB!CU%ndP4B|b z*%Mu=lrLzP9KB~FC#k&@sAM;htsxBD7oCj2<=tJn=?>)Vi{*um<>kN2BFGgp%nB!V zh0}XQ4!`@+pgd_JU1?&v)N$@H>&%Em>y z6UmNSR8M#hlLEjTKm(#6wV)CX45;oeQf7infy5|4>Z)@C)#ZQ;tv~}dl)x8F`=KCL zB9iSA0e((t+eLBpFQ-gjaD=r{(7>}zGKa>$O=w(z$XIp* z4Ab0trw^7TfPHU(2#H0hD_8F3w;qtuo7bTm;? z9}IHKA#jfO?5Ogm;q@vemzO`{vYklb(aKd!DZ74s;xu$oV5~4;iWVBZ`YY z;1U5JON4t6ejDuu@6X6ha8-=1C=wZ`qWvt1|CtlXtf{!^XWna+RTG$r!3PAS?oR~W z1hUQBYMf^WVYcog5;1jury+1?j*O&NKa$5Dad;n{)*f+vT9G(2Ik8gV) zC*qH(JB~#sjvWt=uaG}wJ|X39c`G13JT_p29~?rH+jc&Fr}ZiE?d%f3FzbE|km~{l zc8pNq2t4}}KE@O<%@+^vP~5@rx|ZvedRv?QFL73}PydrjBLT6xM^#r2r=xdE?z9S8 z-}`bx2RvO;J5|J3FQ)D+;owXJ@F!Jr^Px$) zsSry}^1A5A_|+lN*NY&4o7+VD`?;iQ4!T<^x>|QyYbtpb zKl<`urQ5y5omDC%xoNCIs64cj(Dr5$s6S5bzqEd?xmvrH%o0O-0llseO~}?>ZnOJ(hRN#^ z&#cyG7nI}XXXEYPy)L1gyEXXl(zL^v+Z!e=$2~My!LSW z?}_Qk4uY$bo=AGaxt?Td7@WUGMn%GxpZMpvL% zDO`SwFqX;26jtJq5JVOg$_W9PRto$9#Tf_}(3)1L8Wc^9a2kwAhhH<;F|Q5ll64)G z{uF^8qn-137p`Cs_p?CB9W-KEcAOxorg5N`eEli5#hXK$SwRc6lv6H1D_S_E00tdEhm+!NvN-m;?@D?>6CJ$@7*^`CcF zQVp<$)2GIWBnZenNjX_GOXEskP>JQ+Hg#QpC9gxjk;XdVA-R4R{CFM2D)wYd*K+yg zyh*oRfZi*b=I417M=LD^lc%hw->X=@6m+-&XF3m zxvRC!TgW4_33mAR$OVhFvqR-UOCRM!Di!R7 z#Rnc08FeW#Nqv7Dg;sI8Wm`002Z8+mGtK`U+#`H=3H^iqf0?EkyV^(3ZzEA-LA5IK zKcBpn|G!K_qZ)9g7d`-w5C{Rpf+%3)#rkPtf14b2=^`vx z$bZ1pXi@X>%ty;3za6H_uZsBcZGL~9t+AV~QauN)(?yu0Sj100rIo)A_!+e~8^4+^ zYMrlGIbAg?V3__r^LS(T^~xZg{rESH6~>`AhFQjst3~Gi_|@}YxPZ;^ipO7VbEG3_ z7b+fnym9qTB+oq?bA?C!X?hTk)`}pM8l8U1u3w~e!SuP{RzY7Q?@>zE3=Xu0^~ufZ9fE5L0FhBVUXJ#6+YphHp)M zCR?P~M5Jn{D83BEw4QL|=D$fl@f$~d>d{=sh`0~`rpRPf<=In(a*5tw)w6NO$>Oz` zKgpT!h4pV)P)*TN=G!*0rJ1(U-%1}+gtDiyIZ8MSXm6k(V**zh<}eSh6So-jU2@up z)@J_nNc@#+u}Og!nSF}@Wq$6O1QoTp=LeSF!Lvevj~K3&6Xk(!7MdD-!a;mO*R-CXO zX#X_)!rM8pWwfgCH+TWTYK!q57L3s6hf7$MO%)`t2-b*3UjZHtwMQ1x5T1`W$J-8c zO83SgaFd2y1)4{NtN6!gHkWLePNGNOipdJwppvox(yx!7k;;5Px(FY} zSjlHf`745-Yt=Fg*{8KP!M~T0A}H3Ke&Owyd{edvo$LhNMHW%1k{sy%L$2$;-sp*m zIrc1(6%^xqJMyyOIHSIV2k|8vZ*%kett%wM1)MgQi%KsUyG z^~KrilV6NRWE=kZc37qN?wb`PJjgaiEx9N5d1Tmso8~+c=>pOwb3D3(Ax1O$vwY-~^V1q9v&^Ex zk|zz0|MA{{t=9ZBV6HvlwdCH6wStHxTo>RrH1Gc|6xx+HKb`%katBeDJ1esSp|>@O zw4?{kJ&BQ{cg`sOKXje@Ka-FDxOZYF?4I)}HsmbFBE-!3P>pm@6hcTf6rwWcGjmAJ z=QC+kPRS|fl!=58rJR|LNZWU>_YdFSK7YXd%l3HOkL`Iquj_g8?TkPm@`=>T@3nr3 z74LG`7GSM^_G>}c0-MOudm*MuC(96>W`ak~c#nA7iey*&Nkt0OEqwiVtWDJU<)=7%*nsEPyl?77uT0;BKX@6+w%W@4jHApwjCm8C z$rUt=$Z7ns^TO;;_fWvuK0}QR4*iDk=h$lHyRF18M`te7>X^kQ|58eoV72t2u^OqD zT-;wcHtNPTWlFMOp> z1C0s-c2P2IL1gZ76=MBGdX83tX;i4_v3b&n_1LH_|op%_masv$td9C?J?91;VfFg0zV>k%{gW zzQJ!_iD}vk-|^h-g4(ekM|?#E^F;+S z*|?`Sf-=2oT*-zMUt3Gm;;7;Ly;4yG6M-q|#tmY0jahUX{NvZYWv!1Va|6HR;J;Ti zzhs6{wuvixO8=ODRv-q9@H1SCsB0~+DlKxWttL+Je~9T*fn#~Av+=r^`8Q}gQov8u zr^atMMd{9v$V5ss>cgv%WxsfHeWEbu65}J+6g+tjesrnjic}4WLn7-wN?dftxT|io z$5&`#d1Burcy7{l#t#3`1FrYYW%>AMH%wOE2xX_1vmy}AD<#`|Hr^%gnwzwki2W-f zjlyyJLI)`xNW0M3g?~7jb{KKr&16>FCJYf6e(VZjZou+6^6UNW`=JA$plrQ(oqLB$ z(pM0^#Wi5uflo6>d{s%|8M!Wj&-)ayd=Z@+S`GtaLw zUCv7Wo$5Ox{yoBG4XF|YGz2%=C3#Pjp3n3PQu+lXlQZN*qji{u&p>$&f{!<~;ojH# z9VmBz3T+*E7i_H@mNSwY@P$PD!LWt93q{(&5PZi~cmN0)Ohg&F-r)So^_B*lChw8dMM9aMZ$}1axxC&$zo?8?rSeMH}gVJ%<*?a_>^GI$;7A5JJLwqJv@O z3t{b`aQ51VjA!^`N_bRJxbjN4+6r7_S6S-*;`KLLCgCu&6Cum=$_t|)(7u3#xwDh^p;kG?qw==oc+)FhbEG%aZwz~>p;1iRcD z>06`i_2RcN;SL~iOxLiIG81zav*XJG{WtqZyFiG(F$fp&$F_&caW_Iq`~f&*n%XhM zjx-A=OrQWEKYYX~fabzEaAw52NJSsdOQ74ls5TaO zER1}D;AqfEK)WQgR3)UmO6Wi&g5?v%27o?<{eT4^BX#u?{Pqh@XC+gyk2G%9f(rJ3 zS|hO=7-euF#<&r-T(OJfup+qQ5*lHP$)zE~7ulv3%o69y0-1ARq3U!aKXc6{>0pMi z)d>?l%VJE5Hg}3Sw}&(5!4yboA>4?+F2L9! zqY!Ryu?koX0XD4;WMuGGn%Aw$;5Rz)!yqw#9GI6q4;z5F3BhKgf!{P)7KQ7tpaK67 zAnu!L06?sYVDG*`F&>w`!YtN36aV(Q@CwC$GfBDdJ`Nb;K!eRMGUP4*25R;EGfwm!G?Q#4?Op%oP6IY~)%`W{m&v6e#yO^_z`0zzyq7eLA7#9~-b)G~;@1(E? zqRApyw(`Yd11)l4$^gLdu+E$U-UP)#-HeWE+H@ZG^2ze@xqFsT#5GKc#{-ZfU7p{x zg;?~5J5oH=IzkyVAmUmfOD#iY0bm2Z<~@x29xzGbQ6eDnzl> zZ8eYMfhTLulhU=4l~*WkwU%RE$3@Gz@x^)Jc^Q^ut}wYufZFSk^*0&_a<;@^L0}rd z=?r2m)GxR40gDBNhfVPD6X^J_~y+mutqm!+~(A{!*hI zP7y9IlIS1fR+GV~_0rI`D8wS@Cp#0vsONHNK#YNST<|8&sa0d?Xj=?6HQUab53KlH zRt#(F{hUR4u3i1yas7EesOdAV=`*2eDyWGKXt)=fSfFNZeDm$U+yNh{cmGzg0gWcM z2>kkqe{>Z=`&y)x7n>{GaK<|g!$h+zb4=>j)P=a#xf|@bYOy6O(tk+k_dW(j0NuV= z!^WLF)WYGMIvBN3o(jLq!2DWv-T^Q-nL3#5g=o>}W(1Z7jAi0nu;m|Laxe1uXSx*B zx_`aicK%~2i%=Q=Zvn9=0=_9Y&HN18dqGWIZ3jjgY zx*+a8{cf{hae60G87tpa`7ItO{|wBKQ+QIj(Z1+rYWB3bSoOyN40f;(J?N!1h#?Ke zaGA#^CM6vVrs@wptP9Jk8#-tO=h^fZ)`5$~v84x?MA9&eJj|UsygLQdB?9$aud|Ll z3b1_5r;n8o=S9)#D9aUy?x%#Juyc>#o$K&)ANa3qSXMh^pl(E*Hi~W-JybMutETYv z3=p!0z;sgq7w*X-uF{t1RW$bDxn7_+u&K^v1Koz+p>yLQDs+g8V`0WV#9B=th~aaD zP#s;zgcuZ!=5Gvv+DAn}bko;OqD6d4Ah~<=%JdO<+XONn$Hg;tmBlvl326DnXBeZg zbMb)Cjn^V4u;QCz2=KcM-FKm+ch=;0s?>K%FTqzw3YlEv8Y&>I8{;~U$J1y3BQo7; z6Q=hkj)e^b*>n^pjG=wGKhWAas10nwj-ywgjjH9H@v@A*K!C%R3KPb@H&Dwsgo9G& zh9>tg>Y!n$)8M*ep&Cfe_dCW~2#(kp@1fA|gR%2}pHcL%K|Zy0Z#qOPdT(z6a9j-+ zn2s3pIb6iY0}4_mq{^_d4g)M;1rauyo)qcgE*_DfmT-`Yv$&vPFU#GTY=%f6S}$L_ zO-CY(;It*6b)m>7o3}Qp>hZq`x!T8}mnxsV1e!J*BOZTj-~8C6GK(k7CS>IfI(-zQ z%_^+TW{J-ktIfSvG5WB1ZD#J;+#K*JX=1jmZG+av)6Ih}p5St6%nvAE9-4*SfGAK4 zVMTn3^a?Th2M%hXNYO}w=d3ztT7$i*LH~RVfF2|Qt()MZTj0A^Ff6~3E3Mpxy{JF~ z$j;_1VStjtqV?heFb`~yt4?lBpuryziz;Z_cJx@MQd>#LCfJnp^;`#FzVP{IQR)Hm zi?&)=r+oiW67&eYLXuhnS8Inm1ovIdOY2zb!}CRWWQ{dZAumE*07C8GY~shc0> z#Ax=(z!fIuLNai%p3mv+N)i*}aua%00_wi?`C2{H>n-^D{5L7aHx6JKrnY>Tv>aHl zjA&Tq8e6X3jH3pZ8ab{6pI_liTH@(`^7#+O^e<-pBvwEa5@H3aumasrf>Ix^YWYBw zEMyToP(xCXAx$fQ(PzrW-D>TEt}wk0;I0Ii#&i2qEhYt3AkuX8*;~Nka`zN@oo}SW z-)WIAwO^DrDoxGZcnKE8)i6(i{(S_C4s9GMfF6mB*JD^)^Krb}f+x@rNeqTHee6aJ z$efbBmcIxML-Nu9K&rM%n`~! zq%!KCF}PU_Y$t;66GR|%N90PY*ry%!nlVfDZJW)3z`8PSbx7U=s5VGYiqKE(0NnI5 z-aU|XBeH)0oLbm{X-z=&^*BfSlLES)nh>hxD8P%2bp#k}^QQJq?Bcg1ej8TnsnG8k z62Hz|`GxTPHTbgR?@*y53woJ_aaR2uLHMoX{M(fB+q&qt&-QO}3SjmZbDPx~uuZ?K zg#7~Nl~(N)R(ZZzL@(b5RGedYtDt&CVyuPFC#WL_Ty*No@Mw_4Spa54;TSwG)0F~T zH-JPRfGHn*Wa&P-*cxso9&YU<_CU2~X_MzP6Z4NR31}#>qlFtWgmf1^OR0UycvGmZ z7ItC>>NY?|U|~FeV8k(4?8`xq1I+#VU%NYiX`dOZ_HGNVK@b=-Zy30R5IuNAS|moJ zr;plPVBqxPHfK&oRFRB{3dj9m*&3!X++tEXWRh^Haf~oqeJk22wR)%^&c})5UqJ%6 zlO-mWX=LLbjHH&wAf0@vKXNb>Nu9e;&mufA+|B9c=gaax{)oS&aK`$giJ;}uC>lZmG=ng5!v zE4^~MtS-lNTRyr$9BvU7lvl$o_DA~Ok=XzqttN{2y3bOjz9NUPI6{_u@GfH&BF{qJ zET&#O^ZE>j;=LN`clTAq^P7`vd;XrAWsh?&$6owpILUH+m6@N~bYu2EOv5w^+Ka48 zl0N_TV9szE`s@6kBZPXIbu6Kx=QB@tG`ZU}cGRs%C3Gk)c(&~8PohjFp z@)W##{v0mCBWeb8(X;Yj zFoPeGS`F^E-pxE`$%m%v|I@m#kjT%BagTCcdVU^?|88~KR3MyRN?u56;VRgGd3nVa z^b8;@f+)*Jp~Qdqd`IT#f~`6B#m1FH9E>wAD-{Lr%RoW zb$7U#E$Z*D{zc$cl$La6ZO(}^w=)}c?N>HJAqWBC{c{Y7Tp!UTW<+=C| zsI^fmEab>m%SZgAW^KzexO~MX87rPn2&0nz><8J6hu+d>UmT%`5YN zZhlo`>?H@me&$`{{AFs?+Ra_>7yAO2$^C@Z;CgBzzKtH(CE}!>p!87+{6gpGm2R&4 z*{fV4n;vFb9ac$GzNKP|^76;i;*weamt4a_E)9%I8a;sUDcV?&|H~~(TRBdVOHAcS z%HFe8sYTuKyheWF$L(Z>B85-aI1V+ITPMld9OSBhSxh^u>c2KSr0Mc9nEhIzr_wCb zWgrUw_Qs!9k5}@Aavr+))AL<9>8bafwi_P-tQ4=Ey69K7{F^3&RBn3y_)?~lebqt( z*|Lg+OcD`0>NC~ROo5sw>NRi8wTJv=3XJHeiR&B|T(d2Kk!sHsTRl`fXOpJpl4Sfs z0Ip8Y*Xu2_yxy0ssc>`F#t{bH|)8i(cwheDdR3x;5gK+9oUDdSYBd};Ig_ZOQw zOV`^2x39$U8u0RnMtZ-m@gFN;W^2^$edy?WH(prAwrm2n=DQQ*t_g94bor&%K8VdneCSt)O%V7e2orYukW@lC%B8#O90@sjVbM^?qovq$ z@9xw))y{*f_QW_uM&j{r)O4ysMO9U)S+Y)J54)Qf85hVZOMh#aE)y%Q)&+Y0B^~{1 z_bL^_-D;I;F1t6HcJgh7WQMYP+K}O5KHH6<$(W!NoSPH?71ef1j4!Lobvb(-kzqyj zaJZV3OX~SAw^$QiS#Yo5bES_0q-_j$HeSj~YrU>CnNW<27%{xx$CpxiT6Ym9TgH_B zX1KfXW7J)g88M>Cj&GpKtIAwm*RrbXXRO%hCg*ZQBQpYhgtgIN{+J~k__lG(V$F)U z7+ESoYvj4K(NM1Vy6!36O3W!fDO@o|+!*0zK@1#v^v$s2SZUI+Zr$B->}^!ppgYQC zrB-t%Sm)MD*6g|?aUa#Y{ea{Cs=Of)v6DifG9wUL^t$M2;=s=rUjpJmpM8n-EmFft z753w&4Mjr+8wM!aTnoaQy-vn#)=xqL)UVVoHE<@9d|>8H;}6;^ntAwX{SG&-Gz*U zLWU{tIiq;+tjKsdVLlQ^#=c{q_T;p-;^HKb>(fx-}<0&)lMt+VgTLOxdAV zs$c;s$*{qfTO=EM7E5ec(s2AXu^dj}-48vf5fY9T2x;{mugxv|ja=Mwtr8mlTA3(Q ze1Cv1m>wbT$&kF@8!I}&kU8{VDem*r!MnlQU3D;5BGTcb;ZGb;aUf0B^(zq$tV&hr z8I8ZdA1l}zYZ68oyQ15R7f~s}+}BT)Tk?nSWjW^(WR@cSs^d-iEuKEqDvOd%b-*2D z0jBa&M(mZ<^^Jum2p-<3EafsQ9!rCV@@c8RgO`Xh)f`r97F2Nu7n35Lfrn+ONkj42 zB9i6tp0lZ$x9wd7{Oyvo%(&86lzb(gSGod=b}rX?znc6ke!bCJ_9zlWB~TP>TzR%L zYRU$=%l+f5N8aFRWH;hxiiL#7_+i^T$8E$rOT}ZbFqZtnadSv~OcfC~#&qUUUU`;r zs>@&c?TDrB7j~i26@zDFRGwN}rG}(g79Jc#7%qFIQhvL|a5sAD4+T-9XpQ+Y4KXK} zzcH2yMBI9#ZT?cURz9ON?+Eh4qwmmBGyJ$@0yZDz#QWG9(R(VMc1CO1Xo8WrDjCHP zYrptiFTTd4qN#4x=1%sRSe>&{C;n9{hS$Uw3mz3UKdC5_Ih#GDp!{od105-z zQ)H=3VO?{#+Ki~RnH$bKhhjC>2j`MUd}+^>9er8Fn4qo*Jh~cwjFw8h?pVq@sXHAi zUBcao55Hb9IBqzJEigv(q9CH2mhplgrz#k@$I3%dn(H%Ca&E5hv|d z@kqe$d{*hZfYVBjPGW^!HD1w4m0xf8_h5D`Z>5xWYE^~_#j&$Q6eF(u71+`FUmvIH;V+sdg zo?(a?DNw<+`*Ps~Ca1$A9r>sbKH;32=t?HoG3wB+3j~#| z`x9BFY~zA*AiMQHxU?7%8yFLI7GcBDGl@$0VA*aq&GyQ}*Y6RISXvPI4$Km7-u@!u z`#jn{c{b&^vdk?yZG5(dyrEcTI|#a|M6*dEp)b-th*uvoeez~2)aS*=!@EtWo)b4M zGXBo4UT!9w`gSv_>(V@=_z(Zap_6H(lb?`_+5*hEZve#*HWoX%K~-4c37{F{@7S9O z_b@=1AOeR;8AJfqz;D5(g0x^xjrm`n9^?&R{!H2{$&^P)9^mle@Bu|*l|`rIr9{QI zqw(nZN!{-ek8eG9H=LR=ILvzW>M1+t$P0nxf83Om@-dN4cG)7gUzap}eMLf63vptl zTWp@v=g!#>afq?NID28q_*t^?-ch|lRPOH&qg=nwB;;>wsp-wUe;d$@hHPnN=*_)w z3B0jz$US)Z#V4@p?m1wd5qrM<r9L@XR!rz;TBJziZ4@ML`8opgK4~_Mpo=;6lQ3 z$OIAigV%km<*m@{1~fd_fBygp4*hN(`db^hTN!$=9QqnW5&s+r^Q4SWD5yaS4> zwEPN|f|fo+^9RLT%Zx!c#B9-HJ}_d!bYi3A5izk`af2cW7Z6Fiv5*E#>Y$bZAuioH z&LS1aZi&m+F_RE57vRLQ4xeVekA|9>86&Wp{|&>yX!V3j=bMm4?y*XdC}-^;&;wM1 zIXVn}DhtpcL64G*GEKZUv_edS95at5Et{+SrMo#5|EIZx?Sd?UD7Lm!$pHGOp(8@b z#0kWxd#uEe3r>XhO^_weqFgtdLg3((U5wujtS*AX`wZ8Z z6vlq~5>nlm7t7ubh-Q%zF*~7ueuTKdGk9DA{4I_O${0u>{3WZg?OkZ(4#*^fBj5}o z2aEOarLq7HWH%y_tj3}ni8;f}80wHzFgx4FBA6u51u}m{$O)b0GK&BC1Gp~Ycf$*N zOXQKi!=peX_O1eOPXTy<%nG;6ir&M-R%a!yX3cT>n+T)6m(6-p^uN7+NhqW^#n8xkO0{83{ZPNV`9mS+NdnIPyFa3Qm)x=75{ ze8C~o*2;Wj$s37>m`uRxq*0bqh&{Q7^JzC&3rX09QWyh(fUrt9K$e2_RWQ4 z+-_%Qp4Hi2aM^aQ%+dwA!b`4Dfw}Y4bc(dy1r$=f`kkCgUa?5IGyKpJ80`WA@F1_6 zG-LyWEsj;s=nou?%=}R1r<*3&P|cuMqZ=L+de@a$)v#P@s`j&4T{V+GYI5+k283Fa zORZ;4ErwS6a=#YSfa%f2_F7fIPc zEK)7{2;u^2@&2!vk8}Yw4b$K_;OI4f;4^&hgZ_q5rg(8(c3i$h4X{nGmiT6{l2ecJ zJtReWg0y{fy;vxRm&tP$8MIwZy|o7B5(6Fki)le&p$ovVYcNqat~#s8seq!pHJD+= zMx)jeWjg%JWRR$QU z2J%t_5DTU3UFdWrAS2-^L=ALI8+M-#?*|1@ewYzjnrzoY@Ip^aVzaAdfkE9i>A#pw zIuFVyb)`k~qO*1?HS|CZAjWV7iy$9JBi}xPyxYUDC*)$(b~b6zZ>!Q5Z8fHubm4#w z4xUNoUrd423pwnQba*bT29WyL_NJ{)Qt-u7F~R2v@Mb0U)&%m!>mxbeUv%rW_l2|% zy#T-d*p6u^Wr1G4vwn$kd1?3PrDFHX_gN44itDyTpOlO7vDE5?$6)T7IeXfu`(KeA z3+NQ8;KiTtW8)CEjIvt`7B2#hD*}Yz_I;&%RRHcwN2(|Rv{b;{^TET)vMV0|dwzyrIOL)7;4 z5RhI?C#$Q|lfrqME`%HtD%!z-_AwtB)FbCz@3l2WMgajjnRS0(oveLeoCso?jzG*0 zFqZATq4eI<;(ccv`_AX}LAv{LR{EUpfm}yAkuLq7b^Va;e#lP0FPEynGVp=Ku2$EG zJrCnqe6V)D4Venc*Z}_#fgrIrqZ74Ui&`jZc*?>(YkI92nNJM|9HPd>ulB`@^ss2{ zx)(tQ3V=I_^VlHP;yAa(9!5U6ZC0tM3XMID?tsTf*3Gu`{{^EKBMD?aHg@~^9dp%L zIc(vcsgFTE$9q|4@*@HSUI(fegrb0iQ~*iJNQ4f|m;h5}uVx^ktrwt-82=Mg*kC*0 zw9%J&fPs<6{&>M4MzHt<&b(a;d7%gW9T0c~G|~nAH~eapfh|VE_Wm0b7EL?}8cys< zu_;{Tf+|k8<8eZAvl5b&(k$U2Ic~v!BZbE*A%Bj?c8klM8iD6!aYDdwp2eQbq2VJt zlf}wow>QI@;y$2?K8P|(@1KB#9-4xvPbre7=A)i+)qU467@C=+Kz|=Bw<$Y<3oqhdDMfR0saE2fixKX9)_$Iajb5CkKxIC^c&- ze{{ftPlKFA1VcY?Hz+zlG|x|t-s8NcKg%lOO!4JJj&a@~y;9z(?oXOUf@f9~fz1WZ zn^b_62PC}&-qb;W5(r^`j)PEmjGDEtKaw*@(Zc~+ghEyq#Rj-(N-k6&AHo2w@)QW& zXPQs_`bJwF|BVtBu;6=QK~-+S-D`oJSVWI4pmr9p28#9{+g;J+J z%O-zTeEYfc-{%K)K+Yc*X#A{nz?auAXE$i$hdf;$jdZ>a`GyP|`FporlG>@41ICLF zv2aigLc|Yk4m%u&E}4&M&_y{(NyZ4Tn33ls8IV<*f~C8h*XSJ5xo(!3fGE0rec)V{ zl`Ew93u@6%Md;heA?&bn{?Mh7OX5AwSjeA{tQ4F1tyq`^0~#a8!QsH~F8y9*0TO8m z^G0xJSAm^X5n#>h)S8s@nsZ%FbwO6` z{Mr+VbqK!OmayJ*6V_4yZ|i`y8^m^8KGJQl0oZOx+qMlRt-4-zs}!!@Bwzr87c$x;DFa@Z|48BewJSVXKVnU z*QZ0SV}H~$H-0_%!bUbmZ?~B|49Jx^j>2|$Ar#rku%k0DoxW_LMqm^yR0zRScVyr0 zNQAah=j&w}-XOy^`W_E&Q@N*|IX@qKQQntA`bHmHNX8e9RGxYlbw21Eb=imt{AxOc z#BzjWz!LAbe+Yy4+CfrlI~5&}`|ciSb=MbE;05&$*5!*$=PI_C3*ypLGd;3&fUz%v z>f$(lG(&H9fU~paFXzQ+k@jvsfL@d5fOJFa3ZRvXuv3qK0K%vWVG4)igEV|V8J)}j z=W>TTvW)xfVEmup(LccApM``~2>w6artf+?By~68({767UXRUQrYbb65Sqing$7+c z{Z7k>-~aNSraJVt1e7m@qt2KElqzkWB4rx;%+tuVH&zfw@L?=0elp<}m1REFQNPrb za7eBLy4p~x7bv9{tu^iY;fRmI^c|nQ?XM8V{NfY7y(-9MWBr$rRp2X~K4 zo2Sd3Coja}iM{a&Jlc*U)T73uq0-J>EuKj-`i-o?o*x#COTCQ>b8Su{XO`ZCs^6M) zGY@oqo1n+?2{9%087*CuK(4c=eZ1U5_>w%+F42(jJl8@VO7#(ei2{qx8u z>)d6T8{@J~?d7dyBZ}|(ZAS?ar~{$HR%sdH`hE;2hRRUj(KDi~=5tRyzDk`A$y`sn zxt?(M;lqB37UP%_zLZJl9=D+w%mr;njxF21-XjIMs;ocB!M`&`KbdJCzjEhJ09*9= z=k;QXxp$;}XE;|r;s?|6N$D@9*Dam&$+Xi?SVtm}Vt{(p0sV!^IdF?KBLa*)XQ0VRI zC#Rc#ey8SpRfN$JFii)WQVaLiYe8zNX@jLup51E{@R1%4p#!Ug@pr7mU$OQnr})lV z@x;7joKLs;Sp@>agcoMmz{2^`O*viyF`HVO+}S$i!aXqZz(w(!AWdWh+M zfD}d3=K6T}!xu_#?XO(ilWkQiw-!vn73^vzJo;BR9rx`-^W61%rq_GT4oHWmGd|$= z)4AYk>kqFmMkXK1<=r{&d`pnObL3iq?T@0e)^AIkI#bUHTV z6~cNd=6X0g^3mxtg)hwfS1zuNLC{?wV+34$aCafJDp5BeTWw3!fk*pv^^XLD%8-eC zZJD{Be@VA2@34W*ziB|OLL9tWS-dW< zk`$eYd9jV(EW^Z90-o)vm-VC`zVW|1Rb!>y2KOv1N=tYTo0nSUTRmLqxbF;X5#%}W z>4Y#GR!Ym;zQ_ISc#V_ZPw&alORPKp{v6$4pQoB;->Ldjc zW<^T~X7qt7ieHZA+>y zL*XjZOkA0{>DP=Orz=Wi$}R+MTIwp8K4khUm}cL+c&ZX{ziqyXneL$>LtMp`Sv+8g z``hSkKD$OI-0q2|9}0Bz8^qVh1f3#Q71_mW&u8skH|bF{scRVgA2>Nz#e3@g<=Ymr z*#|#79wSa_AT<&AALpyOzQ1y8K_y+=x>zq=C$^_5AzeB;;E`$bu=)IBAe(YO{AhkiA_dvmd* z6md9>1IR&+ez8d2`y&cRIuCrws{@ES-qa zlt_#4l6w_fGOUd9q+V^ovZHk&Hk+b+HnAm|rlIoZ%9tN{l0*ZELG&AeiUMCxT8(oG zOr(4~ZCD&5QJkGd!aFA^Y9?LKWSyCW-_av;cg zt?|I{4eQ5&;n*3Cmoy*4<9a{Fs1XBJIZ#f83E8}EzvuEw3@A?k~rD(5`f@=B6PixD^n zW8Y*l{x~rxzVEepLy#XOC})hIk!cn@Pt^H zyFQVX_D67*`W=opaW(j^ZlZWYv$GHHd)eziTG~7txbIw+Km;#-@+q%hfL$}VNDIHk zxngysN5gmUpj1xX^)+t=O};H7mn^$zZ3;S*<=!@bGTsv_eu@Jd8~YJQEh=5GhoP@o z^4JPCKOfRhm%1oEFQ?Rqi5pa++A)1Obp{|!@m1@RJtNR31wT(oANwx(%--U}ddz6m zil+C!45=B2E@|yeWU6rXpg#89$jczeLqxYMbO@Yeun5K^Qsc$S{W|&EGAKH>SH+L53N0eE?T8Na#gMHd3MdL+;2Ru(YsRYhXScm&OSWg%?-Zp zuiWJtDR&)vB3KsL6_K~Ah>SDk&@pfO!Cv5@^PMzx7liq84L~{jUS5DE0NBJNVn1D}IiC9}<`DJjq(Qp3aP?b`8hSqVo^Ci~AY6 zA4_}2;gl@;p(Nf7FLNfxkBOBH=ZmWW?x(xE+20!9J+6Q; zJP^*I;tA$It`%ocPrRCOdm<_2(Bh={yQde2&q#h~F*6OvCI+)t5{|s(L?G1*Rn_Bd zk9?jttROeMLVj&cG?iz5@K%_v7SFEunxvq|8gpx_5gE3tKsR_ zM6SX}8GwWn1otHb3h zBjV&HZJwi;UT;yZn(sySx%fUmbF>mGwWgjvGf{O@j(D=9FX>sE)XT4B(N1;L_=_e8 z9x?%TiVTqg!njtxM(gzMG}>2fLS0;8=5gDe;ej13l|uJxYJV zq}0PXTkCKf3RRvc2kJG``F|s6Luu2(9(50LgN-R?mpXnn4Gz|S$6Q;#N_-YHdWuXL zlvo6#NW!K%T@ool>}u;nmig>6T)A+RtHJq}Zf;&8$Agl9T2UjHUs8G0sNes#dad_A zu`VyRRhZx+we>#>qyYU_-|bKz>9cj0y+NOj9~zLCE0(wRlV%0wYT&BtdX695g3tUO zG|(95V1D`&Pc^|$%hz>VG9n}ozM#%CeDM=!V~_8%CBdrxCTXcp!&oJ=l9t}XKXJhl zyM?HLvfCS4M#Aq&@H-(#W$yC^@O~6>dNS!DyLbhGCCe8<6iL`#EK>9WN<0X4Igo3v zn49lAW^hq7$eDvWAY4m6Y)b){1JIKIrRFe}Xn#Uw2gAUHHJv}N>0y)K>%p7q6A;a- zYY`?G8i* zQf*OHZU<^sB~lI|6&C|5kJgxBQv$9~ns3W?D>2wtd>9z^)e{~%M1}m|JW2c@oU5NC zXeo>gW#^o_V0WqTHGWwZ?l7`AECghV1DUgMiQ0(MRC~9-Ak7^upGtxIBW~7t7eW9F z?0?>iXM9yveBGe%6CD)WczGU_&?+z69t*IImmZ5mSI@*j*iHG=MBb*v?EsSmW7&`9 zD%L_tyIH{Gpwfafx`}}qYl3`jhO*g7IZkYmT^u8$MCB3Az6MBNOb+g*9r3^U^$9q96a?m4b@efAO84NGtHz!j?ex6{d z-Tka~ckgr1vn=2T6(Gs@i?+ao#*9-hq)4ARqv&vZL?`fQL!6q|6^-gREr$!*+3AwB z^c5L|0WyP_eZ<5IZWfneUX+pAl_9v8G4?2pXykGf>zaMxky0Fbg1~)?0R-ha!j5)83&oosn078*p5f zJ(a@&hrqn!wl2HPqA2|<5d%ZPfq=C=H2?wR4~-T4ImOhZQfG1^vl> zBA8R^ki$dHiKx!0bKt61z|a(OE185`73bWR>0DMC(>8COA(8A_7X$PH&ru_i+m8zr7?TWL;I~=`X?pj zs3IDV0RM&9XEB!|Od{Q~?hE5fWV}n{Yf6-JfFto(b=^{YG5A<~DStz0^M_LJRLc3| z{3+4w${6Q%YN^3*%n8@5x|JJ$7?yi3T$O|b4XgkB9DXAZ!WMKZ7$WNo*$#sk(=^{K zsGeO-!-7i?JNKDccljk|G+hL_swrIoLAqnyQ)__CcvBWbL4At&%x^w^W!>P1^Y2?YVfso}m-YI6Uow zn!KBj8dK3qGYq9B@nb1OF;|q9nEc zNj$KzCb@-rg2F$+T0i-{2HsuMc)A=XdYv!ao9_e^^E4j2I1|i{CbDo@$Q_uQLPfna zQ?#GKHA*8RyQ{UrfdmHsVK#!nHSp#Fx_>cj(FNsl2iX8m@WKiN^DgQ(CPwpa6L@jC zkBMu={Oa8Hft7eVl*>DrxO|*Tfrp)v`%WW+%?#jxhP zSm}h?f#bCsT|7oLK5{$Hwt(l7-q!ZDz`4Je3$@Q*PCZBLJa^G+`b28-x+>^d+vKy} z#0hE!a3n;Y&I8@|yPx#gE?Qprk zRniiGG#XI?cm^eddaXfz`xq#phKJpga>n*r!w~9RguRyU&Ub$?)iALaTefL!?CwlX zmHnUCkT8=f6qf&=?DO^NsvXM?)DC|rLx&+INTr9pp>xfWJ9}waq6nlCKcqhaG=eT? zH><=bpnh~F3slqL08%684yfH-ECNbnf%Znf+SgUWsM-y^Dui!`RBZ=?)}3Dq+eUW= zo6Ts4P4X}BTq{$2$c1;J6lh*a>% zbqKqg)j{s$n&y7nrNuwXGv|Swq7Ou@@h}6BIqG}fyhcH+E*{H++fw&R2R_UuKMTl|R3K#$eR%iC zq-T=Qm|K1j@Mek9q3?tU8x+Lyi(pnOs71!`+wEL zDA#=*5`*6wbeBaRD$RSt0_DN-z_(D(h_!WyMKF7dA#vdji`0z3bBX2ylk~Bekd}TV zMoYbLf2yq~@3p~G=HO}bXrsRv&Pt$a3VKy-{HimIZO@q%)dUh=bgn^bW@1@tQ1;b4 zR57^!bt{VoW-D&Z7dR0{V~GpHjgH~=NrI&cBJ~N_lNIl-Rskyi-XXE$TK}t$S<}W< zrcw>qCw^2W5Q|0pE)y`y1bdEd^LX_18-(p4_?aZZv;Q#X9}79$cqf8woV2Qe({e>Y z<0|9?SB8WvE=!RTuSjVW6o$a^=<0+Co*8g{JGkf~$UF&a5LIDfcQxJ#!WZto*Hbt?%wvqtcWYqnc=|_$Yv3)32O20CFHhTwLv=0I^ z^QQAhz&tZU`TvG^Q01^9{@CjfB?@p?7uM1}V|St)0ftvLeC(h7DEIGUbUyIPsirT# zYv2ud=;iFKnOSAV?AVDpE2p^+H|EH5)3cj%fui#-Ybw7Uf`>R^?Q7J*@$ZVssq)uQvG660;CsftDXl@}pTEa zVSL5mcOyC3;Y(jvHXm@MN4t^Yim&eTyx=VI|q#)QQXe~ zsu876cg4|JxL`ZR=VQ1msiNnnD1bF})`Z0APJjgl*Qnx_gKnCg`iEf>3D~l$Sl%hy zL)KWEt);K8yZN!7BCNh5T)y&$eJy^|?#RSk=K1DY@Y(I=H_!TSK3m_&N0x8jgs?MY ze)Y?Uv1QcGvH*64aDgu*VF|U^7LmUaH~$jp(mrr$BH_mDqR0oNQ5TGfVFL`Z%zxq7 z|M?d%yKrd6=AiaTnC6>FQ(TrTTdgFIhv>>jHa4MYpqdv2;+Y**m-&}M76o}`|H-0F z3#M*}e-^ME&I*f{p%1aT+F?Lfz9aO8xv(91L%+yZzevgsclZQ-2v<~im|Uq!0Yu_9 zRmn3U@c`R$8Eks@%y%ntPIG;J3+eKiAHOX{*#4c&+}?i+J~+bhA2m76;7nnlMj1R; zV1Vil(soDa)BEc#E7m;gv{Q>B#l3vPi*G`<6}N_#w4f#C8%OEj>|$`uNV^Dq=s0-; z5e9R31ci&hnC9N+0GPu7Ov<5Df?PRi2AeY284Clvl?1*#;MKpiVmQiU%UqIC|LLFe zha>5yMbS^~uAiq=A^)$gGYyC8f8779W~?*AShLKKowAm#%*ehDDWqi-k)?=K6lUzQ zhlDbAW#6}GY*}LnmB?DQvM0pMZ$97uUWm5(X2q zg~1Eza;leZGo@zImaDtVTNM{z+??ms^h0K6A~*$9LjXxEQl^hKr?$dY*1d)@Rm|q| zO{%4)V#SmuD|2cM_!PnpLG4g)SHZhs%l@inELHWHG4bu{yFl=I$X7t2$1 zbF7@ItL>9p9+vr>_ojMB@}mY zK^55^i`GxTa|vg$>ZL2BtckHJXAQM-I!zW_K&+Ikbp9yLG33$|ax(_}K!01;1-w9B zh^-LXZifjA^{P)t)`dEjdJDnsAql7p5Y{)6wxTACdVndJL+pvN0{tXaMoZn0F=y*xqfZ<$hG|L^UzaG z_f%~jPR0IIJdsfg`y_4pS#n8v6eaDd(lM@S0YiYgAye*VGB?(|^ zb={VygF#d{0I>NZ&B^5RlT&TWUz%LzBb=j$Z~FAK@|4HL(X;MMrYKL|3rW(6Uz=Y} zf97E1oISyME7e_!)pg&mJl(Y|NYr9E;__V^K%T1J_qs!xo1!r$*zBzSgr$zfC>u#)muwdS(mDQ`h zamUjtS{APyANh~Di*cXNd{E)QKh^&9;(E*0`7^yUfubvgf$J|_on|vX1{Pwfwt`MN zIQuLdColFy(Ua$QuO1(4n(AtL(p)UhRFhr+zvtgq=`{JL7%RzBST+1~v*w1OxB&i_ zi{GuxlASFIah?4{Wbi=Ua&t+Sj;GPi}K`%i_rZto`z^Sj?x!x7P;F6xoFlyOY(Q z)t61Tcr|zhD&K_YoeMY;P-Q8zl_hs_FI*cG@Tj;BEkX{GVF>K~MGCL{MADmrY%-$wUvavMf#f60LxKu_EZ)zr7uQt1Km~LH~RG=&VF+&FbI$j}b;WGc$Zp zHZcgN9!(_6o7+OZ%v{ADzcbi56ME$UHmeJwU!aiSa&8=-0!^Ftxwmyu;S+etn6WK)VM~ZKJ%=4`KY{AO?kI->v=F_9%|JRl*D>|RN zT=9+!;`qx-Fk2f+|876NV$|qM#s`@9F&;7M05DMO+*)8yjZN^CDt}Y&{ z6c}*T1CRPu)x#Vz`82PaQMhwLa@2SSadv96hlU%q{AX47`8M+-&O~V6ZCWyLT&!)F zvLT2=+LKM?_Dk8~#A5^&;?53*@o)v~y>+y1`?vS+9(9G|>SjCZY0py^Y_yK7BvYw? zY;0EqcfW4>8ykXaZ-@-7NtE9<(vNrB45>O;yIs-95P;?BtypT;VAc7ZDrd?e0zviG4?@#9EHl;1;{oUp!}#^$p=$? zMTYrZ;~y_FOAm_UwK-z_0+IP$B5lV%`)ARD;n#I@v@D)iWHCo!bVseR)4Fn|Ji_yg zqq%3NMZUk**5m9}ytVNjru$^8hbLcWC)aSM80RWsYk*&-T%?^$!f;K7?)h7O!ZpsI zm(v(W+Hf*$@p;0z+7n;Tn{R!aK7u&w9<3w<7iFfIL&VSRrn|9siW_V0M#l3+lets& zBRjr)t_{u_cq+P>m9f8@fWE^iA>|l0ptavlRx7_{D4xTrn%u#={W0|PK#sn~GEDaf zY(@`VMh_u#bD2}sNEPy2lh8hf$(vxT_uCP4H<1<6Q*A5Wc%_- zhKTk-TfK&pX4yX@3>+V2x~sXtTOO&ELpKZNAJ|buVyx&Pu86$jTP{Z(6a4F zdHQ~rG+T96u)vTw8L5vK=x#?AmP@ib=StPxM3@-oYFeguZJx3*WpFNrkI}E1XpK1Z zJdrDSwQ*#r!(>SUEM=3b_3dDG9mRZF`tn96dwDWw5QB7cG(#>&ygCG`u!t3CC&c{y zqG!fG3dvrYVl~{w;try>qdN24WU33q?TumVc8wjnqO7^eraSprW_KhQ?W2*+DY8?{ zsa!htVn%aEVFdM!#cp zJMa^;%hR+8Ud}6K!OLErf1yZDhu>q!M3$THCG;2D%0^GIU9FJ&=rnpM#J^i!I<5)b zb~6_foH|Riy8R`m`efDBDwBVssY6frcqc;b^X$xXf@(?_=Jq2;Vm#LCdYGByx80I-zBM&T>u~oZjk8Wt} zyLQ*tw?)9i)Gp&yV4vQR;Pbj=V}rL*5uUO)-{_m?;vAZ!OdOKSPmJo9Nj;sBs@i^b z!>s{xPV1Dx+s4$0;>+^CE;$<;=6x7%~6K% zVxHAXCijNo>d2EzBcFfDsf9gCS__xXDjaR&5Z!ECyGB~oxbt;Obid9~o`Svcj2bCO z`wjDdk$VeqeKF$vWq4xv)v-R&iDG%Yiu9GbMJc<*KI_-jbQ?n8IDCQTvnC1GQBar- zg>1I-C&BP&tjG~Qw69pa7oD$qGJij>Op>?v{QBFOe5H(Z19!2d33rz;g3yUCiJ>Yf zTZpN-11&V3rn-0{&cp?OR)@LQa~W$BMM#%VXj|~(jj=La+8KTM-LJ^Q47HQ^cx+o; z{q3cXtNHmW<252GcmD`VJf%IBbXwMzUL2tWIyZ`k5!CUcBYA%2Z!tE2K_#801zw)6 zum2K92huwbF4-^Me+=?B%suzp{YIwv*NKk$mSZn+m?L6gs89M8%LTs@$8Imp#s|kM zTn}xX#Tu#Ix_5Bv%9Dq5W zct!YJ(iw@h2Og%Z$=b3r9-F_f!!yBIH#0b1V*WI)<^CCA7f0fu4Sm2xN!@K}9W0^) z$OM$PLRlg#;+ z;C*`;`quOJM?cLD4o3cd*j2WEx^_!fVlwhU?S#$uCGJ^%xp{t9sO$Ageu=ei1mZU5 z1d>IL0#Zx_Yf`}?E*fNon{QP|GYl zQiX)*#ejE`k9Z8P!`r!V>4AqKzLLx^od$xCpb^-UDMXDVqDG1mTON(|%a%eSk7kJ< z)D|TiGplXk4L(ai+R_aE%|1!35BOBet#>;luJa+0kEqk@XmkmQ>*dlj4l|4jV^G2h zs9~1ya2wfFyQ}VHih&4v;&~8Yx z*l~6`;`|>#)14yu?s9DqkbeMSUd)AU0!Xg`?BpZn5yqjn9Ttv(Yr4Q7 zKtYH#(USPYh$z9_UI_+h$RfeY)Mu^xNRS4qTuM(d2k{-|n!+3OEI(q{{_YVbZp@9j z<`Rda#^Gn_=+Aw`@;+j`Owe`SVr~o~xdk65a9S;Dnc_fb z6bQ@AX-D`O7_VN9kQ))#dD4O`HN9z}HgScQ>>sDYt5UzwQh7I1oix*2OpvZ$Iqzu3 z(>|v$2@?iU(=Qt7AN>N7^Ww>hy$D(@jxUn0%067sR>?0`BPmt?0MTE`F@S+Vk^syQ z-o`7Ih*9erg+@b!Yj>!l`4=?nX{udg=*vlErF|2)n<(B%6UjgCCXhNCD!nv^(} z%wrSOEmu}Z6-bPh%WSc1DJSePC(nO%B#x=Tb3l+e*3$tHku3z%ey9BYCGUp> zM{8Cd{xmDNiIGH;je<{lj05sK5qg;dp9_NujB)M}5xgW0MTx=!0=F;$$uxqgQ-Q9g zNLwt3iL3|_Uuc;knK7dGEO#E(E`%Fb^Ds3;E8qTY0i8@ONSQ4lnxbwVBAng-2bo+! z-V-YF6#_jtL_EwW`czjGr;hr#f0@1uI?a3w$_AeLtPxurCtM?9)n<)pvVkp(oC^Ak;Z<`|~-<7W& z^4B3-00n^h0AQO4W=t!wjb?&t$(05rkMH^wW<*8$9z6)+ohuD?r;G%kJ_!kr2bA&W zl!;Kw_~*(X!18(1aw{j$QbxH~ZMpt<`S7Io8R-h>Rz4i%e5mB_Zj2-nP;+XmxBq0q z7W9W}@MtI4X?GN}4v|j+LAL0?G#+@nQyE{*WwmdivG^*eQrw9r5mdA8~_OZ9h5 zH56YBcdF*mL&>&OEB>oi4n(QR-%G6*7%&ahQ0BbjgUY26-NyvkZXp_<7xMwNMVbA` zCW6U|xIRMyXUlGD10Zw2x|kA3`xzf2+dnZBr=o_`x?$^IM^yd6L3x!}o z((D2$RN^IDwoIp(8}5jJusjA)Kw>IFfdpjA>)UGp53;5zx^XZzhuDQTE)i2})`TqBJq6O|LUiIp3P{9g#&I zs5hB`C7Cr)rW}U>XaPx|TL(DTYe(~EqOeH6RN&1yTll3~K@C$xz%GZL4cmI{b68?M zOJY4Fu^wFVA9Ju9jodc_Kq^7&VH`7?oF|ODxJEG>G#@^XRy64jPQ$-2HKXQ4u9cR* zwwdi-vkR}&3kHFkk$CaBw58TdeCQk}bdaNcg!#{7WhVN*>{Li4FiER&?g|QUgaCMg zz@xauR5D4u_3c8CjGK6mb5dEO!Ksd0*x(igpG07c0$6pkS2ZLXf`1cp74>dXm2H9ZnJJPR&K2OK9cYHcHL~Xp za)sFy>`W?i!YX+2U0zvr3}T646!Nw;eER?qR0)3uV7;WqIt$--{F9})kT-aYWoG|`isqwl?nq2j>x(v15NJ+ zT8{=Gq?nFhsO~3&eeVW8J}IFod>kv~nTY+US@iL7OEgyB;+Hq-mbAaTbsc1`-JlO5 z;KY||%U!w)!{QRuHhZBy*(d(DG39fm8CaPmehryGK;9PjdgV>`;|0o35RlC7a4#de zSjQPQ>HUlN3DDf%)B9og5Y70{tA%)~4Du(!PG`Z|{t7G6KnAoii&qef-#|~(K@}R% zv%gT}_ej%ZJrp}Cwb}7Ri3v&}ygJ~qA;`W+Waee|S{yrrHgVAHz3%83<53623#sj#J4`-rgOzAvmBm|+m-dc`#2&j zQUt2Z!qRf(aY$wdWJqASZ2|LRj?EB8m(nL7^qe=$HAr02Z>w|^wjuD=3b0GMH-mFgiTgaJQkUU`uvISa*VR73; z3|qh;co?(GAGCno!J!Us@_%ccX3S1+vVEUF=f5cO;pbD-O6&LE$KRXSs5f+G{wVe_ zLT0o|oBmDDFs>m%=OK{0vk4)y49YCW&=evbi7MxVTA(yuq3Y%11Oj83hSOe0l$THu z6w}$#2TEH22`i4C@Xk(-o!Q+-B$$IC?ZA}7Tmp{QfIvDu8@PCgXc1%?hM3<`S`d;~ zS6OijeP)-whrmT~J`8Go@DxS=;)ufJ0kp84q6E?@O0R>(jBv;xgD`&iJXm2Lp|A|D zc~vM+ASq$q<{ct#%?2u70WHYAW?)!UMVYfWlcRxmrA&Jev7G)xT#wdQ1LzBUBTMM5 zB@!1kG^i)yI@0s)a%@m`{CBbzX2sZeB`tpC!^ah|*_8``)!gf|`FB@$Vpa{x(Rt_A zzFe>3MHRt)h8e_VA0f~kAy7;faPQmTE<9p&h1@D0+`E$8;7^<9ByF|R0%7PWbn3Z#R+-F`YFVY3WBQ60G-X= z(Vp4KX8)wgzpI665tv(jRL(QH-+DIoogj4t;smw{+g7Ee%(P{xD0DJrjkEy~0s;|? zO7NT-bSoa3)B|n?vfi$O@D>9HGzUNxqVTHPJ*_Uq9GVgEul_K%FQcqU=<6c}B7gyY zvhnv>7Lb(P8Fp`9Gh{#JG(7e_IQ|ASQ5TxbfTdJ`(z1c+r(b8>KxPf^d3vHK!DuA? z>e79LJk+|mbJ)TN!wW%eK)1R@s3Xy$;HRg-yY^svacI)nfJ;dDTt2iJ3l~7Mmh6HX ztVzuAfX%q~`DLqmuWJr(py)??fcEtnDrp_g?JVs*TOG=YxH6KnFq0yyv61eXinJj&=-19)?=U~QJ-}PSy zs>`(88!s(I1TM*qg@#nz)Q*i4In({D?(n$iAu$SR>Hn*d1Yu1ORHcv8i?MmCairIb zKBS^SKZSJhCQEy$YXU+{8JB>>ig&J0>oE={Hs>y6m4v*Y^XQB#-EU8X-TYR^UbRzx zWm>jWJ&|gS6xKLuLDBO#Hws09Aes{J?qFqx^&SA!rF+15=ds`91dc*U(Cgfs;JI|4 z9?Xw;-(HapHk~M?!i;}xS}R%qikd^&x-7NV-+`@kgnAdK?;>bxo~^Qz?DMV2`OJ#8 zf7{fKMdWlE^8z!U#ck*{}0@v!VY>9Xbz6HpF z!BtEb3oTSpzkiL<#ERUf#^F*pf7s@Ic{r<9GA>jgww7W#Y<|(Al*RobHf%QK`B3PuBgNt1z5KVs zc>vyBF+G?Hnpu>zR#K?m92ae_$)~E>Sum0|<=924KV+Q!9);-(hJT5d(><~M8v|%L z@*2o=cav!d4kM;0FFkTm%31reHJ)?ze$$UBN1BdY?+uh*Z&h%0--onQ%^JqwoZaJf ziq!Zh!OE6l!qgv&;;3*5zPZo@{TmvO;_eipc6#qC@7C&(6n?H6KNxrUY+myHhuZg* zZ2NRGt-dzs=x03a%3^r~`xd{t~@*ypB3L`J4u%d^rJ<5t%BZyO~bF*k3U z1Qp?}yxv!STWdl^hkjOK?^n|v7|>nsHfOg@V+pMyXZ(QiH_!OZ!AwVYgCR%?@XQx# zk{jEJ%<_vp8-;`w<%9E48%Y&D<;rAtJTSaBw)=F0AD}ER!(KCfz z3-5aETiCGCrnTAq+3a8lhMq_1JAm(qIj<1U7KU}*BaLH|eb>(>vIZ60uk#S{D~{y- zfZGuItkkbN>m8w*GK@8uXKL_i&x)(!F!2lMKNy+!f3F$vRTn#5x*ypfel0YBB5qs- z2eByGhhUiL%EG*D1Ao9hU!yl-m8<;S!d#0HnuB8K!2M21F8wGlHWvjW>r2j;MLRrZ z@=R_YEP8RQIQ@d2)V7SzuJc}Lb#IaSc2d&S1)NN@fZ3@0bBn3POCS(H G`~LuIo(`q} literal 0 HcmV?d00001 diff --git a/public/images/elena_avatar.jpg b/public/images/elena_avatar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7163c0e89280d74da2a5ed4672bb12610dd50420 GIT binary patch literal 2161 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

=NkXy80EkM%2N;4w>z&t1O+z6dce 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�h5C!Q{TGa%%pfu<@MXLaUsv&l; zn%WLVB?LfG8UaEJNtDJ~MGApevkNQcA+ZVORT>DYa|2TZ3idP~!|CR^fnbwC0)2tr-AySv^w-W%}V?k7U!LBG2; zK_u9N1JbW=!@$60S1VY&4vI0DU9TGyIqwE)^YrGIZ~H*^oEu#PO#rMh7?Xf(I6M~J z=Gm!&09u2>QEdkTwFC-Hi}Stv4v5;OE+QEa#2jRs9R&0Z!iYfR0fK>Ka0)tT;jx?^ zo*36hJ-h8_blTOZSQ;eyNECb9EsCI;%^Zlqpvg-LU~7^ba6mwVMO~X$F;UW?QKID< zH1^BL5O0loHRKxLxtxQha!^HNbg@`P4lebWeDHenUUfy-lb)_lAb^Q3Ocu>%cOq)H z?{;DmSkqa?M?>|fc@1&fvz?ae};uDC-dsE3IJdMk}JKa|ofBU}geCYJ;ny>7P^A`_s;j*`T-Ente zYMvLDW9(FknTZ)IQ70(?6O?pjdS^_=#MSH6HVq}RwzYK4TBBr+r%_C*O^b|^ER$Ha zDp}{+_0>9^T^#GFyTcr=E5g>iyLfUjvpiI(b~{|zoK7u|x14>^VUK3rmZ@u>`r_Ar z^N0Ta4_7zz?uX9W^Wn9Zy}0AOZ{+?u|JeDfulf4oJ#X}{J}^fMODICa4Ff3LxE6=T z*8m_eDp49eEL{Rqs)++pX@hga#JIVgKqvyjt<)s}#Utc_xV;n5n1a{U9C~!kbP79W zulQ5e?eUVr^S%4bzCM;eB>|r$b9d(^=scBxVP#f8KvEJoMY2QyJk@xD0q+EuicQwt zuY`oV164I0;+OzL+zCQl%tv+iVW;l%kq`$s8dL)Wy6vgN^fYz#>zkh&b`f#+o$tLA zmF*r0S^(tbi&ayIfb-m&M;}=hLV`TTnfr5vxQZ169Fpq3`vpo=Mqd*F33GtHx;B{r zlWI=~W2Dak6RHbP4GQ*k@4io~7|+3QF>+!cvYAe>3OF$w5#XYh8BkpWwW?XEajw_E z7}cC3Qqg2^kYlPEgIL7TQFHAWGXgX_YMLDE#MGW54Im<-*eR1mUCH+DeP`4Z84!T! z*K8nW&pCM9Qp{A$DWSu0eHalZ=GrsSnod2PM2pRWd!|Ej@#MZyUt}>WMUS^I=8#Cf4KQ@W4|tSYi25%wpvNe^(nJi9?k1uig7u-sBhk^ z!;H67uP*ZHjr+~po{R4EqnI1ZW!q*=L@^}Rkcy2Jb^D#xBv@gpQ>2nAyIivx+JtIM zG$-41_qy)g8W>GfY^qMGX2wH_!@2s4>7=)qo7-(UxVk-?8*VH&YPWrHx$Dde&7?Y7 zb!(l37hm{`ue|2*ogaBHe>`rz`=_3G{oi^2|M_Pfd(WpQIu3@+J$!evi@J8e6o{yX zSf!|d64eb{Dm8=z1kg}<5F%OtN~3_FFlOzjz>pGxc2Jaw0PHk|loi1?0i0A$3@5rx z2hR&3*+y5io!CpFO(7VaC{l^Z=DGLn9l~%Q7vzt_=a=r;m;YS>Xl#o6?n#A2pPlPj zvV@Qh)p4h*qM@Pg^XuDo+FRNyqJh`=Tr_iEHO#i_e&Ip{D_q4iG8n!h)P38IsOf{^*@5x@|W-x0u3(nO%-dA2`gM zg8S~_(<(CU+T7?c5t;z6zBY;v*UHnh97E{NL*M({Aljpk!CD?X2%#be z6@=2#Qq`=WW^<{cW^jaP*3`NtX6*oKG#Oykbx~*3tO5$43H2H%Caaf}m<62CY$Nqf zrKP8AP9JZ-wIc%3LHfCyPAaYRdI%w#EgVZtQ<;mqMoVoaZd%u=R+TcAqyT1D>kQK% z)?WQbkQ9%UbUwD)p*SPyJ>5J5#=TIXW!M>8Mg@#?$}Yw7@`+JQpq?1P+(Jm&gg zdG7W&>Ns}Z-`8_9&u&i7E;x0|eDbc#(X?8osnGzCHlbn%6{^|E@nY%pwT@NWbjnoM zVl<{2$_Q9$&^_aA+T*dRjJ*uDqd(43Uy7^??fOF9VqVLk6#9#GbKdrcb$z=nhInFT zytb+um!Pw!<3l+(eeLuA{RjWbPyX9~&K>W*|Ipih@aMn#fA+@bfAHb|{iUbA^c%6YJWL9Qf*Udx3`I`L?_G6wRdsjoWevIr8rC{lY)W5K zl*M4)*Y^gds_xEw6&$B(KVM(sKB7l(Bn5y~Yzl{<7#B?eA&xx@0m&qsMlKS;k7A(b zA>9sk4L|s_&zJDL1{Hj~3F0vyJTa?333N|bbH}+xpp#6^*K%!)(2-!Qnm50A#6-lb zHfz&DoOZqGx$X`aan>yQP=lyO08~NMU_ce?3ryMV6{$u8o_A3E;xg?VNRySKqS+>M z%&1vsC?Eo=Q8st)GyoO31TElBt5u*v5G6R8b#)uNQ@1ihpoMm`oulr6JG1F*%NBRK zj@B#f+@!I0&GtjGOW46n-N(!W=NfyAPup%(vlDw)In3T0K3UI8)I%y$5YbpXH}-De zyvGy6W}()-vg@jqna0#kAC~WYB;Wq_dvAW_H%tgtj7ic(6hv>pxHNILpQl)3QRL{X!eSHs`S3`ZUj;W*AAkY4)ZTBjzffb%cUx&~iL-olT z2Yu~{%5`Xd#U^kdFb|s|#9k4)DtjQK;kM@1VDWZFy zZ~&}!-;ZKl8=W`_CYt~eA;dg{EQ&d!iDb|qh*;5K^Fxba$O$;9fOx$Y26q>c9|q%h zvPNB|nnuF0T|FpBfvV}$f`h&`hgWO#%?e~cC)Ko~qT6B1zQC&~`C3fhu3J?AW72S4 z1XW*-ZSz6_P!S+Po`KI_jLJfQx_}yjIzxj+yinF15nu%nSP&G+j4xQEAOKr>hMK6l z_4Y-_s1~yujM)Wk^4MxOh+?u;@R)Uub~>G@761|3=k#POvyqu+bIqn%1u@r|^=Mc_ zfn9c%C?oFR)!v6a_dIpDSZ6VNfpk?wAvc!ao7Y$6Y35ejrOeRe0CkOEKWxYl z&po&P)eDzBhx?WdtGTm}@uu42tix7c`t2BFaz0MC)#+Y6o}JdCkG}Cs-|`pV`_@1Dp8xYh|K~6NZ(sPLpP7H? zWU>=&8;eANC@nxiDuIAP5(1(bKPV6ZLBfQf7NSA#$TS5sHJ#mnhA03ku8Z#*>VHGs zX)&z8euPEehW25QOu!U8YDS=|0IjBXSBcFU60g3#63{V)^+@698{7nrX>8h6Fp;YO z%eVVN@DAgj{on^h^y(N4U;Jr1LV|ePfwxDEuORb?7C_Pk5ZODxfUGuzj7^76fWTox z2qVz7T?cl)ZH%jayLP72N}0eg2EV$=e?(M+qTYSiIk;rnt~(1cn|+nx?<_p}`q5ETluCB)>K=WF)3*A&GfT0l@;Q(6h7n^d6#Y3c#q z+O^mg_bhmanRI`&-mFhiT^atai`;{g`r`)cI|%NePfg(D9rK zkG1~!iQcc*lVqOj>euhvPAnkm>nQ?!!*{8`cIS7G@b7#N6qC())ij=P;h3X(jVw;eL`%vSvl^bh=zY` zj04@O%|Q$jPhrOxIJa@Cui`!tAQsAKZnJa)lgFG{%Z87Brg{cXB>?G06eLKS7oBf+ zRsc-90E|3dY7G#u-u6Y+)X^8E)KN1}Y~@oBGeD{wb=4T0!*KReJ6(c$hYTU2v{2WM z2NY#0=70)BhW>cwzFx;ui9(ZLN34>l2^*ZsDgrR2ax%#oU}Wt{brWh9$8QL3XQT@FN*CaI*7 zMu`F-q2X$+Wtuo$t7>8~9#cErZ2r_IKKCbIl><**MKcu=RZsddJhr!M7FB0Fg;2Zt z($sT6G*3HQP>`CNqzTVL znuHQ7O+qFPky2<4fPlzWZlwq|s&0^y3cptqa&FV7>j->y@%N9t`??BH19ulhu4It9 zx{L@S!FT{Mve`ASHt*@$p8)6HCE)!myU4FD4xGg^o>rtQ3$}fZ3=`Y zYzn5uP>Olv;%Q4%5jl;z$@{+ZJKzZd`sPZ}b$`Bk&n>OQRK#!)OsfmWOb1}9WFU;H z&2IA>BvW3JQwuti;eS@Q2}4Dv#&kCPlcW5rr0C`yaHI~Hl4!SPYY_#=0)uO zeErk~er266Ynw$ePf!veIPcshu!<(^+|1x02xQZS3XrT2Vt^of)G+lnCqn>~rbaC9 z6yJTuyJ<&g017X(#|&U}q#7M#uNgH!3S^FUnpL;1Gd%jd-UI-J1l@p$7Vy+VH`wY7 z&0B{O@CP1i&k2#Jc+8u zVRJrR+w2yW*PX?2$MVJ1_4&k0PRul^ry`_PZ>y!%trkKXNFTG}Vu7&dlrTTrbCLZPU%W1>=}6#xPV zMWc8qb)7;8P0>{6y5ZPtt*TgBMsF*2#3Wd;OQ1}FlhTtDAYAlN_2yC|KSGj!lxDrO z%z5WF+^oHTLI)6ZunFJp%;uHaeMdkSsZFcxCIKeI_4*CO>zHPD_x1Zb@7%lZldRh& z6w`D2`u*LU2S^tfAi;pEMkn~5D3(_>`p4bkkPNCHZ*2Q{wthkLrt7u(PEXPbLSaoU84?U>^wk9810Ced_h9A*NtpX>KGX64X+ z>O_v$x0bAku^HW;&&^5BxBI?oIzi)Jclndd*aTgGa%qk@;DnWEDB$FL@9qK=2{^fa zY8itJ6b1+vv{D2wyeA=wnwpBbt&vQNX5+r{rZjuVnrZcJywVPexmFc`nmJ;tahOM7!9hgoG53?S0!G&raj zT_cpq5S>kXqJ{)7MX57(vS(-05j=M8>AZI%D{+t-M)1dUM zIaa>VMhaJ#&^g}JB&tapsCINDQf=Xuq?a~%^`><+TQG4dC01w5Yq}PCE{w;S^(zu} z!$y}eglOftx3;4*&hBE?olxq6Rz=qJ`SjZ6Y;osxx$2!~o?qLXZ?GH8OWSL!^|0?8 zTgy2;9(rxhm@DJiQ~RIlc)x3Uy65_s{~an=?RcsBWrEVF7`N zz6pjixZ0(76L9Vgt!!8mknA~;Q=p74Lb?aQGxE?+;pq1UziO|Mwh?S!JT<=vBCM}$ zO4Rup&F|dXh$iRW=42BPupM~iW$wGG5?~(QJi>6@01xN@+O98BJYuVPjK&;*bcu+x zyMcmv({m%6dxB`#_cpsteM#$Ct1na{hCiwZe?7V8=KZ0r@iId;pReOsf4DImpaeh{5R2zPHiOe6e#6fJxUT!0ykFINzi{fu1|Mz&X%; zo^qno_J{_t?W%L#$GMT|1lFeWkZ25~CRzv4)M!LC zbychlZv$9C0E&Hc4Zvg-sTu8P(AoB-KVQcj(oydkL9r)3aF))oPBmbb6&rJB9~veb zolApDZSF-NHLu}(d7W;uv+a&h*eVO6LdYWLK3i$(E*;I5(y{Z@>AsznF+82QUvAK* zEX5jN$YFL$S3*scqqfV+bFU>#rZ#?|7KRW&Bo$)y}<3tg_ zp;Q1sI}|4AA~YJB$xZ51(}*&l3IvqW@N8oQ(R(U@w$-s+1?Gui0pMl<`vR;QGRGu3 z_aM}MF$r!bh+u$Ba?v%0$qSfA+=yu@ZA zkzHSaz|DU9`rFvWPG1+Npt`e4HirO2Y#M<(BOwaGiy#p?M8cpLo<oCj+6ow|Zk@_My$LckE-edpGNSkH-W9#Zm6F%Z-L;QZtbHmp`w1B(!vP zTV`f5s4Q@Vv$o=Rw_sRjY)PYszF(4@3qv7Nq?(l@Q4qU=i42J@hy_&d2oH1iXD5<5 zmKrdD+HO3D%h}^|zSfs$3o|adQZIpoEH)vm#VA#XmQyRCc4bDHvj;R!)}Upkj}c;O zr&bVkDKl#A_{KJ!T$AfSGp4irC@A~}9zWL}6 z|0fnc^RK$|M}MkSyLxd{M}leyL1~lXr0t+!h^j`3-Y81jQ5u183X>kHMvz#7qyW%_ zAQ~{3AOnCd*s+HojP6lbuUN-MF>e465<^lAwo_;d;1;h2bV^>o*dr*>Iw+WQ4fc>^ z`2D`~4c$q$i**|au{6eY0l=d3y}MKM&UJsT8@qpe`@65dp}#($$oMi9)|T7p!C4#t zWP>1sxU&qyq_3bS}`k`%Yv1Sr8)8T;T3LwRdQvhDqy0B)MYzA`Eq4dp+HK^8feUDqAVf0;pi+;PA z+gppqj@@V1=3C><(rbt8X}QzY^#L4gw~wwreb?~qcisQ$avnbM{I8sIWIr_pI zpYwaa^_TvSANpti^44Zsw|(#EwoWyfAima-;e(17eD&$vL-bKyY2ZhFkUeqaBjmAkXKcz8=XcS1(Orn9KI&^(dCRo^X@w}jP@7w z5B~1||CiUdz0pOcaYwS$)Ve*)@7+)L`uKr3JWNr{x4Q%9+oY>wbK13M-};)zs_0pX zAMnT10S5iH!K+Oi)%6meJE~#Uk6lr8cQFwl)R7Fqu;a}#V^8F`UbQifD(u|5-u0^m z6u=rJU9f^uBw}0z5(~n4HL%fbejA`bw0O+gFyq^6@$mlCDcFFh znj%_2q(I*ud)$buS+}{&?R1?VoUcr!tu6v?0ANQA1gHotaJK(Fun^?JNMFLbPnW(SI5QK?vFQB?~m0HTzH8g?+>%Cg|G*sb~^ zSvNq_hE%e}>8d5llqu)7tX_OI@N9Y6)_Yf&QHY`C{MoD4`c{ESg$+Sw9U|RcGM+2u z7Ic?0Cq{j2ZAW*Q#h`;klF3w~I=OQFq`y4O?76gjwYokXZ#Gjq`#Phj36*7Um^=FA z+1&Z!%Wl5;ZI6E9fBw18z5czIpZy2RPrSLWy{_8D$Nu_%{C)rKKfm{V4~G}`>g7cr z{iPpRKlkd{Z~I>y`_+H_zF+&&_=b*I06;K8;gmI$sl+Z7b_fIkimEXR&AQ)bD+Ma6c=N6 zhwi)cO)^1Z^J?b#Hje=^PY9*dbdL^ny%(BQp z0o()-O%)(wvId|2vE`FZ3tBbA4zoj3gIXQyhKhhRi&88aAm?Ca=%^teWTwUKAm|w* zaXa8@qBi-3&Qa4!PriQl{^P~mJI7HI07e>JFmy+s?j`_Cd&8D9m8~AAi!#Dgml{ih zY%zgi0bxvCYyOOQu&O)2SgNK91VyLW)UA`Voinz*%q!>Xi}>2!T1Fc>pdIyE1%|Aa z#8d%FgT^|Uqo?SO?58GErK^@HtU_8=Hib$H5od<7b6&N|!q!dKY^}ReGnrfI^B3OQ z|IlSvNs@BtQ7ww&s_w#8>v}_-q*JaMFqIP_5b|-zo>qHiDlu^pZxN7 z{Eu$^m4D6MKl%N`n_n|$yqo^X`MdwtfAWie`#*603*KA);JfuMQH}Hv7OSR0MxO`( zw1QANH2~27Nt%eJ0EAVbKxjp!9RLvAPMS45(6Y!Enn@Q~|?E;>c> z<>|RLWApow-1I9U>|E1zXbLKMRY(w%Ld<);(0u;D&&)0nd z3Z`IE$squD*LA&2asjmjBlFPRRojWFkmwu+b!@js0VZT!2Ele8U*a!sGCRF_z@R7- zkW?F8U|RHzo;HOj+=StDQ|LSk+p*HS2I?b_X_W*b%4BK9*f(?g9)cB`0Q`+3IqNb?UUU=GE!8 z-fB6FHehtsUZ`f(q&lfgVdG>Q^Mp5LX1&>10aI8K5{wpzDAP$QmHq3Sxncip_naPF zAFnKz?lc!_oQBz>m#&g5+sx2vN)qb1>Y1MAU>Y^$sZ$6#JF}XR3aASy6ewHU(W-T3 zZ?2whx~#K%ht;FqrQ_3`2ba_1RW*%4ZCgs+*xH`@vA3Ul{vSN{hkyT}AN@mr`S1Lz ze&HLx@zM*v{PMx+{G}g!?G1nX^FR1=@BR-zIX-)!x>}6ZbbkCN|3j~SpfJwX|2Rz?EZY`dq_;G2?n*nq^Mm#AcS%C zTuf4e5sIT5qDEv_v^n8bQ^bbF@IKE4!JU20JKy#&fjD22io(3@1P%eRj9PG>ppiBl zo*Uf64G~~$-n{zd4W{DlM(Et`Zdl!UO8AW3cU|!|0sKsjqr$DM0t#MG&c+4KwyLg^X1Q%&#B8? zQ(6IMhRGsPNs+8z!=C54?0e_gp_60w_bw(e2CdPx#LR%MkziVtTs22^4S>}s)Fg_` z*>189W~bvCW41#k$tJU$?J~72GIj3l%(ddyp%qZ0sToociIQMKa3lLP_MN$ON3C@s z(@ANtAf2&xTBQw&Fw;w0-n#j6v-K<-nC6;DGCg_pYR-3^l&yz-!71%z8^~mvsU13t zIp`Z4&y)+YRXH-5N6~FJYpUu|WVX_B^>91ATzk9o>d|i7YJcVQ?Apn0dH?my;(FA# zYx9m?In>JbqB?o|<(L2TYhU#7(RaQ32maF+|KB%#=lL)A*_SWW?N6;Mcl`3_zx!9- z{a^mrWbewos}(!m@x>?q?Ei4(mET=_?-v+W5+YLpO$w|x?b{#p^=(*r6@Kb=uix?u5sG+ zt5gliYJ)MR1Kp#XBBTbQflv{Vcb?l404ORj@7%l34uTj`42kU=heZg3+_@&CSoV#8 zapKh?_|&ibU92#4ZN^(v)0kF}<=6LyfMJi|V@vhL5Fp0p6xKk^L88qnz#(*QclY(p zPwN!GO{pECvjOfjLLCvA_au_lO9w`DITD*pI;Phn_2yT>;T~P+ zv%?{aTL6GIvlb8$Qvg=1;V9LGN{OwL0ZlM5Ws$0Ms7XkoXq98DocI31!oyRwGq=H3 z(xVPMb^3*yUnB`z($#FQ_4?_EJ{f8}*;{mPn4?|+o@R_gqC2ePX^dyOt?eo~VRuWn8jc3)pSxxKzU*|vB+v%KEy+m-oNSw}oB&9|$=&wS-u|5vN$_wz?5 z-}8Sz_m6($%W>i5>lf!O4;@!;`{^gX_2(Y=A3rrd`#MUuP50Wnll|9<7B&!tMu#>b zQMDC%ECdK8N<+XXtwJA3RUw1Fq(9jO>v-o+Q2;bE*G13dUq#(%>U>7CqUNb zktwKZgA|f+pexofil(Zs@AsWsgf)V@1sy5Q zsB7?yiZ&zzV5Lz6fDHgp5Yk>kt4M1H5IqxN)-XH{wE4Mv^%x2zj~V6`EVv-e3Qew= zDP^boh)HJZhVEZqk1}hb!S)$2yK~14Fc<)5C=|M`1c(NRC{UJ5>>`R?Tu#lm*&xu*9HO@=gh(fSTLYYpA8lg+k*=}kQHPdl3i7|r# zKoS*!^s;ZwiSz3>Keo-T#xX5-$ai1f{JGEf$n_gLMRVMTV`t&kv?QQW;HyP(Wt6dTleix2|o@cI~`4bN$XF+wnXWhs!J5^Htq$ zP5Z1|tNU^J$3Oii{!icj7yq60`RhOMAAahu|DC`2;Nkk_9Xa-?-KD4hy%)d!XO92E zAFIyqy*Z3;eAd11K@OlO+CeBH2`Gxq5dfmMU6K@l!Ihv@(=J64TY}OiqMPVT-Ai7DAZHqPzt=EcA~0*c{q+7(O+K*Uxj0P_Sk zE`r@Q`4Db8Q4hWRv}0)^nbZn#pSXC_SKUfX9D&la6GRD#ez4%b)T<4yW`Gt8?tl~qa!WsHe?LlBw*l5fDud-ctY2JK(WQNUOXhKsY`n_Cb5a2 z*U#$So?Xbpr$B^)nzaIDuf=QBRj(1jG)riBdvzTVCISJZKnM<{X%fY(nrMhwPz`Yf zKpK&vlVtlRuXUMQK}#Q_&@jNu6XR+cNWbc88f}S15{B*JvG~WNxEQvpq z?_ORzIoZ4aY*Mwfe061edn3ETtZ#N}x;-2=U;3lJ`l0{)le^ZN+u!zsqv!wDIH%|C zygKx$A3pVpzx(DFef{{aeD0M$_%q|>i*28LH}_WrMrl-vc0fl#MAQw)gbGNAzFNZy zQE97`h9Ch&h@zFIqb#-gI)Wgm2F^XPeL(>uD-+w*rgp=Q4Mv7fqeiz0iZ`sfslgDy zzQQ|T)I}zeh>IYj))a2ONOfO(?%4-;(W+@xkHT6tb-pfV7Ud?yVDLll_Xq;s{`Zq) z{@bs|x`vxEUu5&#uGdZ6-KGF)=r)Z6*s~zMjf;18YL!ITrdZN=VA?f~X)nEXTGrML z=YdJQ7a0Z1n$fidnXmgiS278raRC^e+hFzbOlp!GhT!xxb@wUC&ezJ19e3K~xd0Nh ziryQN+ue2nhpSYQ34l<)JH*a!^Ds3Kb4HUhnoEm)TOyks_1a?rB+f9&Kxb$ScH|mx z)Y1tSKmZhQ1k_EBx~<*TAae&znAJtPC+F@?8)Zp$G?^v>B^Aa@KbvR} zESz!QIWz6;Gj!8&l%iOVOBO9CBGMWYdwTM1ZTlNW0+NoAG{Hc0geVQkwou)zba(e> zmVKaCFVwMi4xje6Z^@XX5`7lVI5nPYYG(4#^#{cnKul(lOr+xQb|L~v9PCx=dpfq45 zsw3@I6!gM^qTRs-T9^WeNK?>6qJo8@TdYuG5sFo{IYhvc2*A4ogkRw!H z?-V%fr=6S^g0<;50>`2Dfs2rsm0HwPtbkQ4 z7_$}?1L6)06qADl7m!3Rwe(urYqYgLr-oQ46abB`MX1^G($_0uo0wpyN)TwGBJNsq zG)O$H!lTS<-Q*ePt6m;;n6w5Oki!^GodfIZv>Uaba_>b6sHD=(&QZlE5<59AwHSJR zvYRt1bB!31Eu7pNT8C^Cj+A3D!4S(Z>K5$Dt`!#!Or5U8cF9on+?l9VtuCu39Yk73 z2b*khv;a_4rU4-PHvP(le&q4Ztn7-6-KvKkeezmP>mFRuy2MEqPtNWf)=xK?FhyTsHj<%O~9uB9oqLz!dJZ^g&dC$M(*Z$Q1^^bh- ze}ug_F;N5n3y4ZZ14W~1tCSQ?P!xbdh@zcr15^O)CQYydA}l^7S>8zxJ+ydL0~_aC zFx{N?JKnS#s_yR1c?jUJI|0~fR2>yTK?F9sK{#<#)6MGi3twr+zJw}Fb-C*e{PYEEfLO?6#MNzS80Gy+}zUkn}=LJlypraZ^0sv@@XFN`} znZZ+WSsueQn^e|B?`}F zyO~oa;6m3xlLA`DB|@#1x%E?boNg8@G-m4FzAP^%=GCfHq-45Mjarqlc53=urE;|< zCRH{mvsF5&P^OY9Qv!s_+G!WtH@tDv^|;(HGo6l`wAY4_vwFtvX$0b22+( z(;p8rt94_$8TX~0npSmLjHA=$xP~iLjh1J~vYE3Q&=GmPO8DxW0RP1wWF%tT`|9EQZ+`kX;eWV;NZ||$X)ox2|G8v8CL2m9g zgwqSh=_W<&dJ90r_1^3lq;}sIVQLsmyYBmH)piBi1mo0who)+~Xs~_7JXB2tx-*(Z zVu!Ayz@X+Jq?K7?=BSryp}6A+i44`lD04N)QdAJNbfB252H?3~k#?Yl993v4n-^b~ zhjl@_;)ocmbxn#)AjrTJfJU|F1Zx>kxJBlk3uIw#bdKXw&gQ(!Cjikhl2k=Bq|&Q&cCRlk*Q2$H zXbnt?(KPytwZAwHw_`AmshuwROcF)MsY>@*cUsc2G%+1nY|*Ia?k(0)E%aF~E4TB? z=5){A^VNQlLQ2pJygD~Oa52el$TENJ-LHMwx4!LP{K@6(pNbn^*YnyMHgDhXE3N*< z?f>hIU;CH;!_od#(KjGY(5_e!;1F?$5Gz2#FRfrly;HHGYC@_M0t{9}01*pBbB9+r zB&OM`12fE~76j8tG5G%D9I@fJjrYcryRQ}$;)x~mCKY&!vhS|zJ(hQN z;Yu)#G6#XA0Kjlei-B;cJ-bGG;;!((0?Z?ogkU1})J0R$?S;4Af0E}8aN~i5K=k9| zer`eIRlJDZ^x^AvcjDYuZ~h3=s>C`DkxJZIC;;eCgO!Q0atbwOv;qMK{fjg}5(v?JCrCObx|40HseS?#DbVH3{KqH9$%yL|q~ zLp;j_qS}kN#H3<^Dec4u#mKC>uuD42jBOG7z300cr|yU=Lp%3D9%s{|JUpeUB>;1% zMUn0)(@D;v)o}q0^HjBT*K*EWI$w1?)~uRliwGE8gXU_Tr(Qa=lX(C7nsSEjh$>0d zwKJM~q?3-(SWpKy>uYD{J1>t}WfVGO$GYe+ zcNP*BGNs94IFH^iyWZ*vLq}_!5w*rZ zwH;&aCZ~*Zn$;~;C;KjkLEn)Y_0gZ}l~??uab2-t!m0|_l{(b+kfg`dg688 zzVp|=SdVQg(n633QJ`=O(9m!StsoQ;N}@y|00=8qsoHe5B2gOA6zeDDEE2!`?GHLU zLG!wY;Pnn~UL=+X)VBM&H!?fvcGaXX_U=sssB&QE8aI!z&FS+@!f9oqb1eWE0wMOS z#O6x}i((|1Y(iEK4nHMFo)a1&Qoc;Lt^3D!e&?t*``X+T)qP*>P5{M&@qJu_zu<%b z0``7ghmm7|z+qL9uiAi+b4W{oDTWIO*}HD%nA}ODDVUB9R9V+WUM(_el80(`@Z5t# z5FIzKVnyqGiFr>_Xi}KxHu(u8|MFv!O|W;O8V*ReLpm~*>luH#gpk?J7Rm@OTrbn-vxA)R@>JY)f z;DVDoqh~a)x=wVX*=j3J)m88Dnu(ZQjc2maBAedB9FKj6z0-O;*xjqwSg*sr3+9~W zFgeu`glr+(3`W}QBu%qJMEB48FsJlpC$tuogsIG+DQLp6)7c{SbLCuG^Lb}v#?X>w zsC$n+=xgO5&cPxO(yY$Zb6KQFhaeh38e#!Tsyc@lNOsIQd(+iWHSFoSGrjv18U<@T z2Pn{{Y)&RJHJnFp(VLr5)iOBE%yR8CMl-02dCj(DpB^e2E@+ElL67ETYS<6vt-D%` z(U$e&WoCKYn6}ZVdTXqX53;Q;Y-1oceg4@WteknWIKA!RLFgq9d~u)7P+_pA(U_S?iU6kv0TO@t#GA3#4S*7wDK zoZp|R?)>ulO9ZQ%Q3DLmy_e>@-N-Q*DPA@_+=!kpFv5MmBZ5qa=Qg}^alL9_VB=1^ z>WI4EyKi1`VOS7!-#Nr~;QQ(LD4+g?cOHtt6hwEYAc#6PvFAa9cAyxX+ucoqao_K& zko)#P75G4J8)or&P^!TxgS8WLtyVRQnl()Yp`jEr3;^T+W5B$8ZrDN7nG65|6%b@% z>fZPD>UPplK-{q-RGS%~u49idG7sxnt+A)n1ywxmT)nU}dmm5PBRF%|YYJIL_ceR& z%-orA!Rgz8fV+vZic{={Y3O6hG^MEI6pNiu@njHP)FrvhX4nk#nwZK|ORm`tnF)y5 z)B5rHFjvPu7*IjuQ3txU$0d!a^dv!}nwPp^;gCwL(XMGK6@wE43e{<#`?IrqSEqNl z?Ks1(RZDZRM8k+at2v#;tZEp_MXC*^CXDITTtD5`wQSDY`SH+gj=Oc+d7g!$iYZlx zQb#FSilwXlsCH47FqslN z>gd>>tMk;S)-PU7U%Q~%0)?nF5YY)JL8)jL5`r*ESEx;y0wH#yIzoCVof?3GlvF7q zo{(d(jHZEIY*N*xwfPOMqK(r&O8Q^X5(w1Yf!g(o$$j=U4$*EnfwEoI0L41`+Ryc^ z-1d@&%^vGJ>y_j!YJgVpBX&adA=P>~7_!0>v@E=M$h({2X6?_yQg-6<1_p#y+T zRGoXl_xq|QueLdr5DCd1aP_HVseb)d*BPi|mNWJaY!nKRL*sa8LMHQS_noLcoeX!L zTQ!dyqLrW(jixBr*schs2gp>As<=Dh@Cu;Z>~?DKT*=!3IPJLew8``rOGP$L{q9!{ zPVBljF4_aUF9tyEF;ufsM3lDs&K#hvrl6|e1u_x{ph9Yvbl5Oy@RIgSC}Z0<;~GV3 z+Xvow@HIIz;JTz*)Us8N*$N4o-NX*Em?^=cwrKZm9`DX;v1ZgW*dC9vIa{+w;HnE# zmA$v~c4>co^#ED4E@x_X(I_5f9Bm672t3W@9JLW%+4s}qac#eL)+}ek3C`5pH=kA2 zYRlF%*aR7K4glAWA7Q&Y=1*i_x!n#cTj>@-WSIVZaX zsw;xhvQ;TEn&}URazVWXQ4hOuXXMkW_b8|J+_fu$RfUaSo$dDQO~+YvOC1A+PrLJR z+kKnupMA4#wRr!-!I|sr4?T2)04Ub0Beji9fe@00f!AfI719Mn&2-sO*}#c!lpb%= zt?g`fW77`oc?}toQ?Pw*wDz=D$Z4*&-On#P$l!LQnn1%401|6m)SOn2(93P3n0Icj zJs}C^ofZw{HaU-p>A6VPo`2%osjKIjqKz&K4ohRRAmIksQ<8YHnpeQ${Q2(tG7}ik z>>CB=2HsoGivT15YB)fEGAi^cCdUXc_i%O;U;-zw%bY4Y4|D_1Af6;ZqJSVUQ574aUaz z(ojE*89n#arO_MN>_h{fwPe@Yxw3+(>N-*pb?pc=Yq6qqqhRfSK$A80W`<%FK%iJO zL%jO+da0ZMhSUmGXU&3chb*;}(6vDlCkUs|sLOWy@_6(5b;$;#bG**czQ@i>^J4Dg z=>XOa-G_O6JiHaLow;W)XVh>6nw8G8y(5xZFX90VbC+xOcK4W*Y2z$PNQZMPT45|z zg|(c+1$xSQm3xBaJY!GRLP;f1kYx;GrF)@pz|nSd;F%xQL)A|0m;kk_U5ZJxutP}2 z=`vZ`&h?v~ZwDyo8xfalxYoCQ*{<|G;cQQY@({!IF0G3UCVIZp|v$qc+b4HPB%I_ z-*NZ-dNHEXIzF8&X>MP|LCstpTi13-l>mT4K!iXADp0B0h_r%TDoxB#X@kI$7@}sj zQX1?y5EvAkyh6IR+e5%hrWi-axshQ62NhDZy1Q5x9Y6q=Q9zNIMFUF9>1L5!yi=PR z06}tko1JJ9Qh1JO&6rms1XcrKs+J(x2J{q#+5{b}0@JR;02tD9dvyh)F8clk+x5H6 zKodm4P0$VPS-7@KR*qmh3ZO9%f;grKhXhIhOlRHZ{U`%_kmpbk1}Zv$B7oDt$7$D_ zg3WWQ$aYG;T>-auwcs_p7Bh-d4ZwkDB2mq7OsgsBwgD3W&_@Q*%Oa`4Dd`p)PJ;>R zB7mCF8nb{V9E4&(Gsa|Lu&CK&L{YF7L`@ArG)V{!ARw?ulN#nIPy->9hEVDqFF*N{ zdFI@@PIg^Ju%=o;?VWM1vmXAiE=4kVS+Nhx+bTSLr}b2==S zt47?D`8?Zm)E-)KtWqeDKKrr|LoZf}br~&cLup|`NnwRx5?!~Xs9M@ZC+=qb{H@ph zH8aQk4JX_C?|(1HM0LolK~bu!3J|XB?H)VrHmi(6R>`DB%U#IM;BZqE0u;?F%Q0Mo zaM8{zWT7q^<1FprIG9g0>q=c%gUpuL()PsZ-Q8zjRv}!)Ol$w;d;asE*!_uz3XpMA zQ>*|45M^Q}=dSwq-tiDC5G0C90){|hM=_!mrA;6~LlG5m0IU!P;+FHRCwN6X??Y|3 zcT$(t!2F^qfb+;<>b~n3#=^%qc4*)!gjdIK11QBV!t-qkl5-&Q3=Iwm0a0ZH793Rb zPWuIO2h+{%^vF11?s>WM4dO@u*q)IDnS2Cr#98h6N}Cib3J93eY7?N~S%s>aADf1VPhO2STzy6gmKV72-ctp0V&;6?%zM(y?K$Nz39Y@ z1Q@L}Yr=@LgLJlt4?8mxS}|a!d;d5m)DOcz=A=7c&gJXZ8ztv1yM42ioN03A$vPkL zcqdeL5H!wppoulqOc{baxsAHPr7XI;)@8D#wW@kvf zxSgabnq@lL@YZajoq2V-)nDwZY*K?z$cp7DKYQ^0i`QQH&2H!L!x^ov@BQ^3-SeUQ z)tLo#l$#eWd&ei)ATdEv8JltSrt5w0dK-a)5U7L*4Fp8PtYep|O;SUcErh7HV@Dk; ziXcSA@OkuUF|Ud~!Jro)n%rIOmDyn61mWy;(KfVKDpw!~L=$h837C zBJw^qfi}8bFWtL~NwlHv4iP##Yuc5YR#UKt6HK-{fzECFv~khZ)2M_DPY7P^Cw(n4a+&;V3T#Sws19ITj%qnd@43Pd$}2{tV&29h?L zFMao0&wG2lbeVF5snM#F%5mo5%-NJd5_W=g3nS<3x)plQqCM0;EN6GmPH!Kp;X!6Q zuUvAwQ~xGTIhQB&zKY0*Sq6Q?5)Ne zmWvbg>=+$2jLa!MK0cIjhAi~?^5XQ~`7o89Y2$2aN9Iga*-O?GZ8t=_tXMj2FtIe6 z#X`ag!F1Xp&UW|oObvD0$(pyle)O4{>BgAsk*!m0Rw*Wq9?BG*GOaQ0T{I>vbt*%o zTV5pO!o5gz7Pk)SKTsfW4-Ut|z7g17O3uBPb~5-Jb)QxbM0^U(9E}cvO?U%gA(( z;4EKcv$*eI*F5{!_dc!KiPZTvorkXXPVU2?Hm@tXLN`Edd|iNp+jVbTZ8*Og5Yt() z*raz?)TY(TEr}jW5glHnWH<{Ro#5@*}%}lZRMXuLE z?v#*hSiSCICJ_M#{seD0{Qf!DA+SeCUXt9a!6vCaFpGMEU zX?6FV+oZ4i$5j#TFdh)i0)caHyKd6}mOyF0VGTqbWQrpKHRT#LHN>L`#eoulAjk?( zL9wFD#9lQ=5OP!jt?EU}UW+5eQL}XD61+yuputYPoW=b-{_0Qu^fEuVhC)$lv{PE8 z=o;_jxwBu>diZ>8@>ndM-ZN%|24{9>?@P>ADNh zes%2EdEVZ>%&zFXTSYZiAd;jjHm$Ko(^8I+JC!;`<=W}_=5YMR_nyCcJ8bFB1l;`_pPPQ{MA8cC z@y^ZSQx994sum3Zkye_r8r!Bpa+5$qNg-_^qM8;t6k|ZmYL-M%nNWZ|RILax(CCg3 zExR@aIp6NP?c6BIrqHT39bIBi;uvOKj@P{0U`Z@A={nc_gS|tnW5bU!q+Q$XQ9Iw8 zLX;e#hmu1}L&0+yF3I*3a2Q1p(?N(Rt;SWf==LP>g@xxSwA}<$Z|(#t0A10z0cKK_ zQP}U2?{{b_!AFktI|ObQoo~Ym!0VkJ&qQQT09E8QE^?@O-6o(SKz&M(ynn#-MGfF^ z6P(|Yjosxs?+(cXYR}&3nAWHmMv%}7b%E_F0b|v4%0y-mk7}r<^WZcPtMIi@blUg( zDF`?*ISmBBfGANQAQ}K@E{q5(?Ch_P^StKPoVmN~t(keSNGz+Ig`V%7%H{6* ze$Q`DiT5sV@9tOMjj5-thg9Y@09x=99`DZU%BuHln#r?SL(~|uF#sO7`|iOab4eEE ze9gmi57)Vm=RK=5w;f#$XKSWe10L;H9FM4(Ai1Z{*R;IGa*R0@&yzh~+Vg%KU-d!_ zXW$^y$+oE43W+sLr=n_Qwd14~mQwBA9LK{ls-Es+c{?xHNjR;Aqp8|EMa^i6Mu}?5 zY(v#8W`dyzb)o9IsvIq1bV39O1?sBGDhSmmtvYPZXN$Y9CK?eY8sm9)agp!)nV+2G zWcR(ht(~(=i`m+_Do#$^N;FW+eepT3j(qUV?&(Q%?En_(AzrWS-&}{=HOf6IMGzNh z5I|uGtHy8|ouZ1WNuk(L8e>Hzgbw+A-Mg~gxkN=a3EH~?JXrO1OX4~=xcFs}%gWrK zZ__8n9l)b+^m-it0YW$(9d;o|h#&g8UNB#P(aA#EC29(`0a20UHI9_#X`}Jn?*3UT~)DM z3+|P6-E0;+YMD74KV;|gwanbQe);g>N1ylf@SSJxSEfZ=w#bchS*Nf=+ene$A8+59 zue&&qRRcm7hBdJ-8IY7Z47*D$kEdVrVahz3d*5?~4tv+3?@sp6qd0FW<~s9i=@ES` zJc`WS>XWG}$&s3$98cebQ*Ar&kVB-F2!wQ5rYmI{V-f(D5d)@EtIF7}<=9DIw%y{^ z6F;}wb^B7b$f;)J0t(}97#EtR6=8{!p3!w7m9NTiZfMKuv5*rjR$M6?3ndshY1NdYAKY4PV* z)5V)5Wrao;B;de&8m5|VRWCxW)Qp2K4#C!x?T6E7+<_%&|w9r z28zW9Hm?MmUOr7$s~t55s&*DbLq(C%teVjx0Gs`SOavTF7Etqov13u2Z=P@b-P?oj zeER3hzj*HXMNv#Oqs%l3_q}&`JZQV%>njvdjb`)g5O>a<)}yfd-J8$eA0H3F{pEZ2 zuchu}7AI2(qYiy72?zLf-hMdm3+61=ZkBXZw67TvsGT!&*iH;1K!34>u%$F`Kv6cq?dcMOnJ2t*46 zkpf9s8wXg9ZTj;ExH!CJW7uzBpDDJRgSk>AW{eP2ZOXwxP>4DOpkbA=W*{n}M5a{4 zZ1LPqirx|0tgdBl@j8{9tMb^ImK;Vg%vPr^HxKxxpT7In)9#ks|Qf80Z_ z-oD?uVZZwmcdNySzN$n;kxFQO>DAx(@c;S4^&-(50gxa7MJuAMP@t$}LYt|ICY)A4 zpa51BtN{&Lg$fWMxMyDmTkIX{m&K`R2YgLILAuzx?!~|+G(gqic&T~>oUc8E@S{6$ zcYQ;#am8d>6--Tmns!m(U@v+94e1EiZDMSvu8T2DrA%SKI-OyDtXL z{qjsiVWi9jiJ-X8)4s0lzTeOJw%K)Hv>_t}V&<%v1;gkiI7OedltURuTjV zNu%R@0Y;zLo-oL!LxF8yu5+yFd-q*e18)clqL#;C#^}Z@KmidEXrO_R)q*vv2M`rR zUC^q+1XkedE)=cR4?vd$NwBOJ+hQSZf(( zJJei{<3Z$LF$Y$e-OJk+pe}Q%uf2UYaJC-r-G8}^OW6%ih4X4n2gAOjbEdWaS^Yk{ zGkY$zRsg#$4UAcfW|4?>>g5gZzsDn+G8gjYctiJkx1P?f_TJ-cP_twxYgwtv*r}dM z7uBzMd${jE?~_i=2^l*@K~JD5gH7##PN$QO6;<0&P^Mclq$_$ap3XAM>cGzO-1!H0 zJ#wAHC>x>FllBN&XqC_!?xw4dN}Q;X9h^)NBSxAAGF?Iv_gGQ7)of`kJSz=CRW;OT zw;9!gRYTXVorlXG`mbO7^w<7_OSe3J`g?zN&pSWYf9%1I`|jkTig`^ZTL~@7$zT7L zuiX0KcaGkDKSdL1XtcJ}P#Pdu6&pAzh`K0>1i(sE115kS;Sg2g5pk2quHgB>*Ct@O z;S(A&uW!|~b1(5EX7`04T1O)>J--d7e%Ta#tH`(t4bQhtG-UML)MyG~p2PFK?P|}~ zFNZJy1+~bXeVuwp@6E0a@+l4e%Ulr9+6ewEY8)T5wl8X+;Y z-hHehGjwr878EmLDn_vsJ8GDwdYsq`0*=}%aBMfC2ucHp)@ITQVD%D!oKd$Pz4P~v z+i!mSW86L8kMls(EtIj+f%hw)k93dN@3GEyhdMCLHJFPH7_tL9`(7N+i}qkW-rKJt zJ7s~+$-)$+XLq{ip3Qn5?XTaYe%!i(3_7=y4vN$wVRG5X8ajNB{UwjSD;;yb@Z6X^ zn%C+$y>pH3mkUj>aWa953a8WOuJkKyd;fg1Bd&R|jdF15zZ~O-bKKetu ze)sL(BX?>I35Vj8;qKvQ|H#iD{>-DJpT1WcL30@&=~A!)X(Dk%E1Dx|p|ZhdgNUF_ zDMA9sBmrIXy0%jj9cf@)X<+kqzzR}P2gdw0jclx+45#4v!WNmOkIcy3*jN(UoE@B3y&Y+iJCNJf*c z<*KjhYOUkGlhfx@tHC|Ho!b(nTTy&`=eF0kA_6A{I4AYY@r?LPR19<;9B*Vr$G?K%qd68ktmDQEW9e zM|EYUclt*c8aY0-PS z=G)J{?8hPO>eFE&w;*XRD_YEboX2PSYU_pxw9O2#m__NPCVHG@tbMvfx4K{ILfJR- zyj^e5A$K<}*=uw!bH=#ZZJ~OiZWkSAin%@Ot8KHt_D>JHR;?k5gzX3r7%YuWPn=4m zNI_{8)u<>8^iHLi**EnkZ&}`d`R093ygt)U+P0uWI5;WXg;lkP%A_J7L#C^hqg%Ph zr3et|P-j*4QXr|5mJvj8b#pT6OF2#}k=ff_+wfUj6hB{Plg0@YwUe_VGXd=jSfHyYGWPxcteB@z5{7=*4p{_>~X+!jCOq+1A5Q zDKt^UAQTWn2w<0JMZk&FM3qf+SJhSks|Z#UP;8125H-3zTai!Z2;DKQlQ*=p$_;3=}XGhPaFP6Vr8bLzo(MfaxBMm?(3X5YxueG*OO1Ypi+ z2)ZEJth&fNNhbTD%Q>i~=)O}yH3j27^6(z1QWrt}zF`6)alY=)&8F(~u3ynXRdIL7 zcms2Sdyq{atKpvD{#;+b^{VVjfI8pEcB2C{M#KwAiYQFZG|oK&#SWqdx{%aVKnx%n zV&^5rOHIIY#`Ol7167dV8X>DdlbYDfppC~+z0=M4*5CW#cR#)V!uM~~r<|D+lgGJU z&g(1Q#?HovsnzH}FLpl01V!9i^mB*fjC&s(&o|~3x{@`WXeCo=RR^oXru*sf;r(Yn zUbBdbg|o$LFxANJe@T!DwXjaJNSdMG_I zh3JMowLUj)x8fAkszM8DI!Y9P+Sq}BwXt+e6tMtITuG&d9I7{WF*9uAzV-1Jb}!~j zL1dN7rD_&KTnT~7X<8JEhUlOLlVAqM!j3Rgt^lt3of60&2JC(f9ROJ<`3pdby5MOL8Lo)H9|wG;I2 zib)|F&M^wFRyb^EJzmCX6+OK%pIFYzBVsa`$vdO2`@Wikpm9Brfb(Kea>$wv00;zB02+X50t`+nF`LJzK{W?K z@)!+jMi|XbH;-@s-SfBK`}UiUKi|xpR8xtj#c-cU3bTpWfPLhEbGM7$%uMdt7KRSP0WKN4pnXS5&9g?_cp^`F}8l3rd z@4ub%hV-)4lX`#6emP?I**L!}DRN*AI>ADj-ajoV$tn;r!nm3mHVn&V-q zg=GpOF`-hjrA$<9VgS-rrfZw(R1Tcub414fWMtm^&L2&ABm;z3TbQ`EO4i{NnQEO-yUm zM(Fs+J1_p|-~E$={T{Cl#c6d<7KYyQ)UMh^)ewXQ0%;WzVTE8+P)xx>&DfZL2nb+7 zp@={z-NYirbg$0WxCWqsf;9<8!)|wiZg)Qj^gR>FO~dIr5fMncz#ZUxyF1c(<=or8 zfC8kZg*6#~yQ{iQYvT%V+BnQz>>yQSyAl`8`x#5&3+~-_zcMeomaHH|?n(LrR)^^} zuUj@fZZd2@fV1VSdE0@z>xzlZzTPqPnFw?^ygQK;kqlsMyKYlopn|Y#Tt^K!hZT)Z z)NI9~vZ$IBID?8*5G0ps4)(;n=G*g~Z#;haf&1Cn&2Zs}?O?>+c|FKm zvgXiKP=jV`J?JE!7r4xPe8j@@iG_iR0D*^|fRPu9nuou4yv zQE|ela+b3`-&sf9XbHX660lypX^-HR?9SclV?1^4m!opPE@?4d^;p9a>o{@RN>RNm z=IHgTAVg-wzy%O1C82f|p;Z&bX(9>=XktjGR%%g%-JAL3g*tH5t=gA4k}8{{iS)42 z%4&#^(lwN(B|M?+ih0_DCDN;k2bPFnVIIgHp`pq z7oT(e`b$4J^1gTKE1%x}=taFedLC73v_nkk9BvVl6~QD%pAuFfKy}@QWJDXK5q6Lu zhJ*kjg%C-E1B2=SN+YbKkpM8k%QwYsqkY>fVqh@q9(fA|E}rGnngJH|8)Z|1fZSBj zxIZVR-A>*2b1KrK#hn5ys}nE+0?ZTW4XEMpro$-P>{?F9qi%xdMjw;~Oi8sVLp$& z-iS4zt{1@T*S9D4Xmj)2I_3>KIXS52iQ#q;t2b|}++3V5FzA*{2ZM*% zz0OeD5^4|_d`zQYfYd-MCeq{pfmDj7a(=akR&So)`R4P37w@Yt{rE~fTU#Jz16Q-h zjvzCRHbG8xI!IY^>KDqa=(6K^g)C<$WRR@Fpcm-g3eFtaqq8#iVD^#c*FJy5*Ylil zm$6fyd$!K1b(+CbmD-Av?bY00&0{mWmsxfX+GCtE58K{4SLVH~%rn8e$$x-~J?*&OT@yG{of zo^_2tqg}4mZPXBSYPe=gT_@ovDgpuuRJ1a<=*@?9Ej3yw7A7&3&P-?IjXqTdmB!s9>DW%+l7I;1f2oSjMJKtk7d{37#tZhnGZ{E*N z^b09CsqVXakg(H=8LS-Vnj(PEpqg$}n*)e041%ix9hlP%zNmiDR$9e-!^Hy`|gvR^AJFo=>U_0oZFp%bxak!8f8-h zI6y#3ak;|?0N~=%1wh{r0AIgv3c&g8VIE-Z?%o^==2w-44ECH<`w>oLFqK{4&TiT? z5Cxb*g}@}@03fa|`T-ie$=^XB-rz2V=iUf$dz>%8jCL3xE~u^@854j24l0^3$d0uRr>S?|$;m zY!?9{MaY9DX`KtFW=ikEO`1Hzs!W|;;r$7x%-T7dJrHT3y}3_eUNghEVej>Do9XC~ z9@O7^-s7HoHnWbgsC!SGH7zI|Hy2od_#Ur6=W%^r{W>c;qLXf{=ke^lo+aB=&$F&+ zTm_TLj9e|~ihc8OI4rKURMjWDJD>g$l!_#Ei%rVFlvdTItd72B8wvs!#9Eat5oYJg zmhIiu5F*qnsSwJ(0;&dFq7<6rK8l9s>0$9CD$-IxQL!Sj|H<8AhtqY{7M)>E`m*<=zxK*QO*yMrj{w+Vq$V8LNECFcTjc5gLzqti_R_YO#W=i7ZIi#;nG z>i~BbnbjTBqynV6yN(|l`h}We@`WwQcB(q}CeidwXzSeW4%SBM zz7ucYU;&*10jJcE?0j!im|ap%2!_tJ2{!h=egoCUhL{pX=2a3*K?P8y27&Lma1)^V z7zl!5a#b@rYNX^s0|aZ0*_a#w0D=HDJ58ZH4W&>sC-zGO0x=+oREvrob&Yl~h}Jst ziCG-&oi9_~|Mg$}(!bsOo1g#vuYTKEmcH9L?A02bSB_`Xy_8+9nP#2M*-x2`=RNNM zdsZ*-d_Atr`v8}F27^E!3*#JSoR!{^6`VeV!2i+sF4?9ejjqRUWXPS!K%MUzVm ztnpsf-016Z<|=Bfi!hAY?X=zUbQmxj>xGL#vHY)2SidI0S z>o$VbDwTFrLWlrD!JrZaP+=7cL7-?6F&$G%yTyrkYMMbkxgF@hhQ%AEqVshq;CM}e zMnfX1581$rOi#P+KIOsex}19#k&VqR_$R*I*#)n5jhjTEYiP1L*gcBCso}XcW82m1 z8rBLngyP(`JH7k9)0T_P8WE&x&%uM0Y@ToT9Yw>;vG;Y0eh|oS=@{PKGZDszP47Dq zZFb9@vZjwM?A+!Zc6ak?gW>6Y?+ed2a(9ObFa?H3x1pE@#U_MwPdIgTfbBz**MDbi z+O;RB-Cb=Eflh4iFoO0&7ZSv65}Uq>Bi^5H2%y+gR0N*uz9U`t=U%DN+;FoS74*)A ztLVBr0c1zb6H&8{WnT&vaE3;x$B9{jm(&)Bg?WyUZNX7cY&w``K`IDCu|*f4s_R29 zHBg*U7g3k!2tbg7VZLRZ4-Y@}!++x!e)o6&PI;US zf86t3UV|*8mQ&BAt^{4s8qZ*klRV7x*8ckPynpFgv6s%Tzd647>G^pT_V5f;Ba=Gz zJlb87!K$NKWxPAGk9@mm>)^8LJ?61tHEj;#vT8nV!#=$eFN90OcD?4%+Y)E{dZBF! z$nCDm2wKD>odi2&bxl&HNSDx*(triDL#3pc-MTfP5)q|n36rX4e#-tM-`uyYQF5$$ zBwNkVTykY63~CZr04c5J<(ppK@zlvZ_n%%mJGZR>iWd3}2Pb#jbNa{s#}ECBKlFRM z9^YNvUc4~BJv-bi=4!cJF7%sY^VKCSpZ(JBeBwL)!98#N+>U$R$!RwlH^tno&1h{; ziz$e@MVSC7u%k#t9V_Z045>&c0zwpkh@w7G6fI1kOq3|9Vq4qPNK9Pzy*q~^0`tJo z=ok_dD|U^?+#pTqtDq!R|mU(!2p2!4gmVV0#kPV zVpDi`dlcJsZ+hxQQ?~=#$vtY-0BnjoiKq;K9+RXwEYNn{=GFF;kCbexHWmEUR=_Z} zX}ZXEkKTZ0V?oPf)Xf|wd(=wPgBcf)cI6`!(YF>h_A_{7u zY8C`g&05p|2Mvi`HqO`LxJS=@`G5SQf4TYDzxW$pe6Hp$?QGxa_@IVovNxA1pdmM& z-L{B-)W?s!Lm6o^oH0voDN$Cif^(X3YVTto*XDJ8oG-_N#d5#?@LN;mRxkr;P&b=JyEsN8*QgDW!stS>zOu+ z&1SPNb8R1KV(nBzdfQGVWlBe*6Tpy4^fia2=$cu}ZW*MavVazebk(Zvj`#V{M>qfQ z>#p`cwV-4&uF>-EaDp9Y`*ZOkALTT z|Ms!>|DpZw`}EYM`^gryoz|MmPk%XeZD*GNP>gh{lnF6Wuq43%s(&MPHt8DfedK(<(}_T!Yja9a*EqN2 z`oVMCm{JE4*AyhxcBkJ+jdRVfR$Y;1S4Lt}5<#Bf0O2@P4DG}wQD7iorM5%Bx?F91 z4mYCe5v*&w8eJ84U%lx00T9NkP0sE5k11@7)~4s+p}x01xW6$ zo2g!VIg(X$RF$md1Q|RHAviCb#)gh;9`ZqqS3$I662Oq2xZZsi>k2*4o1o@JP|GzyT>4v~^e z6-`Ehx+sF=8a0|&98j!Bb?s;ZXhy0X3Ju70m&fb1cfE7lle@P|YaV^-xBsnw`uo55 zM?d@ao9FbHGVc|QQ5RvDJLWm{=5cnq+jY7r=-=G$FK!>hZ1o)DZgz`tx#<(SzSA2 z&DvGY_PZm+1&NRr1;}W5Jo?o3?|$v$I-}FdaWIb^;h&pNzTo}sJ}m^>@!DO#f92v~`gFVJ$&Ru>-}lM!TXt&(}I;LDDq~0DybBoi^A! zOF0lDpke_u0aiswLV;Msxm5)S(%2w~PB^Xk1!MEGHoAb<(3~L{y#&<6sxh=XD0RJr z=s*x)oi6O2_v-zA=k?cJ>LFk_!&E-?KHu?Ie)l$d-wjww{$0-&d#3P_c&~sCA~fGpZd5o z-HTuBlv8Z3XF%(joVxCrb9aWBprQpX<$f|VQKtj#?t7ARt-CwDBrYdg#p#wE6=^KY z>mB`Y#dXP5SFaCCw)NUm=l4J3yMaL&Qc1;;Sh;FQ1z{l!u)B65)QLk8in&)8W^gNY z3ye$+)j8zix%H3z_Jbe)Uhn(VD?2&6QOi8Xi_?ke5QREx&~Q*?Qp{$2@^#<3vhVl5 z|K8XC^_L&|#K*A{4bEQu%oo1&!|(d3cg8LC-g4h3PX78o|DG40d-H2w{NCHQe|yK} zH}M#4PhQ%*xiq}oKfLHI*5Q0O^xn^RpL}z0+p{-c``g<;^SK?Le4~4`sh3eTNJO9} z5m*T2vQa@zyAV3Jm};4!z&(~zOS6wQksGyac{>Q@ceI}T#tG`RV4(;nf6>;~Ry zuZF|S5sjmVNim6DyRie`f&x}*U>3hz8yirA!+DR*KzUljVsiQuA2@A_;hw~B3SvQN z0$9z?p}GfO)KJLlW#=|E1q;A|7!}oKZL@NPuiY9uP`en{EC4VG1VA36y(mQqvlFw3 zi!&-{OpcHYuo4KkRHHK(Aodvqr_q@T2-*Px4VZ{#REyd44geeR@VwuB{mb#qeEMj< zzI)q$-`D^9FaPuY*S`D77k_f~-E%HqtZ#1TaczI?*5ka6b8q!^cjk79a=QJu_2<1E z2|xF-efRYz`E-51|FpY@r!cI^?g?FnQr_%-3ZM7+=j-h!^JV8aA-mzcx^>!RqK(dH zK5FS8GQ-@5oUc8vbW_9G+q)u$$-!D**0KeIp3_;E$+W9g>qO&NUssi#nSIUSwX;DV zz1AVDBwgEjT#7y?Emf=TtV!)eR8$c)Ymcf?8!%yYw0C7&^p-j>peS0kY@NBe`|*np zeCWlMC9TDht=8rtF*DA!?UJD!qSmg|sidlny&ZQPo&DD5U473xF8=#JJ%9AYw?FiW zXXC}j>+Lh2_YW?f+btFbq_s^btGIes^S#foJ`{nIh z2ipg;@vWEs`n|vSFL>Y|{giH{YPL4pdW-`K!X(mwT^JMtsFG3%1}im0LUcK=*+RoD z>O?h3M1T<0R$w)qPEWt{+x-6dItbp_bJc!rsNp}luU;kR9!CDg7Q>ySTQN40HCN2n*vmu z-zr;N6ap><1lI#K^7@|KL=preS&%{v=Q%buUK^n}1{?y+gOMWh1j5no$-Aq_uy$@& z>9!6~+;`o;-NnYeI!6>3(WV%n08AYK-1nWDc8tbRHJ#2o-*0X5{UjZ{c(Qx^n3@LD zV(+_t=Q_98JOS8L!~|5s!PVNi-|%i81K+36-1dLMQjsP&|mc3XyhJ?pVOZB-$R1v=SR^@7Hw!_&hDm#yAKk*-Oj z+Y5%VlC`NtQ5^|X0ZP)Dm}HjKm&CRL+NGeSIb@;F^)qw(mF;tPJ^SOo`0jW9nalg{ zxvVux=9PJ~->*+|r|RU~doHD!F{Ki-O#$WiaKY7;!r+?)q?)<+u{K~DH z`=iC!eeL`I=MVmsKk>K!xos9wRH~uevj5SOKl>j(_r+IS{>)eZ%KYVbrWWgPyL)4P z=kW65*~7P=|Jpyl`m(E;r%v5k5)#oC5)opRGTo{x*i zSOOakz-iYnB2lbs8k;OnO}e4scBfrG0H#>=Nk)1x`r79FYCFihZidYPfWG4HY8$#A zAn{Xu)(w*D$6#&n{qv)yGMo1fhmT~oo;fX94v1>_W_TwVsHUKrRn-)1Utg1fNE|=} z#YR~5i!02;0EWx<#2ohZo1mt{d%wxvh z32wUGlgLDz||Xqsw=~t?-xwxAAsAgD$Z?w?#>N76)G2* ztgSP;PystfbeRa&~j7fk=^`0&d4 z^?U3rQ|27UIwbI|?i;7CO&4mzj!lp2s9=pY(juxy9}r^ZMLBtmj+v zZWIZJ-dwAmX|-Ei?7cN?H>)prS+2Do+M!D}lKEljX1)0EgGXNQo$yj7lcu4jQ)U~r zQbA(vDnfgPBvh)ltrQRAr8Dg9hgVO}Tq$fhD7S~K?X3M1E}migF@Nya#-o?nc7h>d1$vVo|_t?XK`UigL$A9+6zx(a?zW=>{=o7E{@{a3Y z-0E!0+yCh={p=t9$A4_*^j4*nnyRiHllQ*=-kHEi zepEjB?qlEh(;xnq|FqmWA`li(ibRBHJ6M&bh$IjIDG7lnp-?Bs5SjzS0!RYEB*sVu z6&S{V=v$1;gWcd%E{3EA2NgjB5I}$gLC*cLz^7z#Q=Gf$-GQk)vY}!GRojUu`X$p> zO~LZxqT2_=6fs@$&Y}`smOS@6B*9@! zqKMSR zMuR#(R!9-S4WC(pd#<$HeT-8Y_f|L%`oc9uq;S2-Ef=2(C6TD#+Q zXSd}!$`Y9sV=|4(W8c+BKl9Z;eb>ET`pECx{o*UHe)EfdwS9QMo$mGRPygA!`OQD} zrw`x#a5x-XF)PoSEd05q*( zxG5pUbl$mNFeGSvF}i)5RhRJsK~pgZB$$523jVs+WUnLTUp!SlYQ+%c8r2Co08uyc zx+0mUU~QuVZcYU?7>24Pcw?6pgFtgjRKJtr=> zoH%*+&;EGrLdlMI=hff;kMVl9opYbJfBlPZ%*W5o;dJ-m;Q6jT_{`;bcb((+Ub{I* z-R-HoxOeU<3GQ7+uS}E_)F$zeH9bDdjI*)FW;)?)%`v&)jKhDIe9+z zd1dx;`(4iCxTaJhOeOAfs4ATu)vLqyM4#8aJL#YV8Y)fGPoAImf8gyefAMc^@5ia5vs1TT_iED`QWve0v$Gdx zakxG7;N8{ztLbYGzV_ma`#$z^W;Ju`8J{2SukP4Bd3m@uo~SD=Ta}KL#$evewEgyz zPoKSb{fGb7Kl;i4@x$+Y+xI^Dc~9Rsx_kSN0-timXGg(Rp3Dn7f zOBX|}fBRqlWB=e!{+)eKy>YGXo8b*>H_x5-+^&bFf&{HdO{oCdMWGS^6=+JPOTh*r zBykPLKqL{NYAd9WG~s47j4>5)B_S|?F?eq9?!+fh{NZK$aoj$44;b*K5=d5R^C-k# z7;cJc5{L+>&5F#cO}i!(z(*$eFy`dVf@6Z_)!_JQ1(;Wx!bexR0NC`owI7F9HJpr` z2*55;ysvtSgy^_h-FN5fWj($8(@jOVa|0udckbOCs3sT+$eT6LMh%AmjSv>qVAp_( zZj(;1C*ZlX5`bd50%%xR+_ai@$?ahEDqug$XdVGW@MG`1k7-({+7y%kjRW1_0u1|; z^Z)okxcQ-K-b6?P)I}hG0oH&(jR6H^Gyo#Rh}of`4w|T50xBRAVpMaCE~P2?v{GOj z0M_!n7pazquHo#3*U&L)qQp>_YF!tnpB=j0w9N>J!#aQR>QCPJ=AT#D#buU@<67Q)?RS3V&0l`skN)x5qoo>DnR4;L?K5xq);oUdQ-A!w{mK9K?SE_MwJ)yb zmTEWJJ0ASd{bu+h(|~|bs5c1#p@48JP-v?jMJtLbK_LOeA^`*xikMD;plah+3xaoF z^^(9(lbawmoRVqRr)#ONAUh7Ov>iDghRk;e6Q)u=?}c%eO0g4 zAuNh{2u`YMSiHKI9dCN^54154onuSEa}Uk4-~be8sx2rI5NN;vcgoYFS6haIf$3tH zK^MdCUHE;2K{@w^^N5mMH)GT-+{B^<_G$_#L)4g3P-O(0hpD~WA)eSb zhEh8ztdX>Qa__wG+}u1n>pDIwyoRY0#jf;i@RppO*4MA)a@-gD4zhE&bF}pSl|TFY zbIyG}e(5)-58k%_)9-he!voINK7IZ}Pw(7uaD3gpd9KIrJ$?UVJ%9D|`PR+rD7%yq z$2K!h_O8}>=jr85OBwHvV9*DKy`(5LOa z@A%@2U;aDWx4T}sIaY03l?;`rTuBuuFd;@-ZFyyncf9k>@$UYXPki6-+|!fKzW&~y zf8)>m@Yna>cOFi*?d6?^PrUHzkx$$?e*fu9PrteMvHho?$0uL$$^7AN*Knh)Z?uc~ ze!TPO#f8Q8^zQj+9%`to02$P7W78CzAAZXp``XLCy-{bCAlBo6AOUC-P^E=R#Enb)YXpUf zg3<<+768Ob^iGGu4g>-?6-h8Dg}{kBi`o=Ss|kYR7^uywHi~mkzTxl?HHl#N0Ko3P zPC}t#(+a@hrXXFLLW!;ksE2b6fh2>XHjju0@Fmr-0@Kgm?$>{w-kn7?DGD$x_O7C! znDu3o(X<+EcNn_kG=sF}L9h`CGA&LUZdW@@bWM4R)UG$I2t<=E*LDalYUh3x9Tx$| z=?y{GY(j^C0bwB>!1e>G0oK9Ao|;)u`I;7@Ym;Kz#pZ!oIaL7hep2I~bzH0Feej2` zSa!F~sW=z7ql{=K(NC)dZ?P0PDhM`(E5?|$I-`PG)kPskO&~=rGaw?+)cyd?2tYK0 zBY;p4G$tU|KolHD3aAaDt{IAEy@q0T`|=Wd)@o?UEiD^{m=h$$SZEy-n>2hZ~pD<;oHUajt`$F&wqPz^XQLOk6S%{EFL}v zb~s$+o~(22-d7vWV=dXVA zFTVJ?_wRV;o1?wXahW>Yj23geKfeC$_y55U{p@I~+4ZlUEf-kV4l#i#(=R~rWlFcfU_ z)ozKz5{rfBHt(gSR?u{t%_sm9ZhCG245t&rn3Rt1U2)o-;{I6tQWSoU=~Pvl#=NH; zZL-m^F4$0lJZ-p$$ruBoFJqh#oKTF@00iTF*67`JAoK>c1Ew~B0EU5SE&3TF&{HQx zGzOP3(N}Gr)uBNuUr|*tDI^CW703!uEP&M!!T^DzW>Gb$;bCZi1+>zrR0x427OJLJ zXq4h$ZKBDnjw&=&hvdZM48`&6k;Cm*VLB8tXsWy!SVyH;DP6Z}TK}zYo__n&d~yHj zkK^&=X3maoU%a^e(1pdjU-tLzpLcI|wV3OM55*R9m$Y-?;BJQAlzQE>r>Ax}JbJo6 zel&eKmAhAde}3FP`}&68zj|eLhi)Im`osVH>;INgx?4Jp7{OZ0W|(*5c6IC;~0#{=89ZhCd+XC6NBqW;i(?%aRR z=U=?*@znOz8Sj4Ilh^M${F8tE>EoAn`;O1{UAcFDaryKM55MuMug@L6bz<-I==JLl zJn-`JuzAzBI*WC=U)5wh)F7LYhRa$R)UMDh-Nl%aeXC8FTZ@y^y1P>^mmeLcU;p!G z@48;RbZXO@w)JWsovz2b-uK1lU-4~C%eF_(vXQQ+1{9j(GuCZH1W5pbjpiW1=CId7 zl}HjR0@MhrKtMnRqy?fcGAM=-V8roq&jxq^J+7z;h$j^Z6v-_qnUUD}cAt(1Ll_kV zLA7~zPg6*=2_8}1`F1_f4IqMvdXu-N3jxJI4Tg9u9@Nv(8LkGlpgO5G&^$DKjZv?Q zXKvrQrsL~I=5-MieBB0AE~aeWRQ=)obgz^Hf%EFmjE&RYvQOS4Sdnv0V`M;ygKRpg zajbf(sx?s|#z^$4CC-b!et+kISnS**Q{1B}L0uE%NP&!=BYXpNyrF*Yk=+@CBsWba zAm{jaJRj7kuc1murFnb>V%8>P)J-g+-d3I+1{R@M1)0G_G=N5RG16#2t8YV5qj)Kb z$r3vN$XL!TTq(0gDVxi&jhqA9r?zYc3e&bFj z<;I)$`}h09yyl*-57yh}4R1brzj0AF)pc@~H7K^byLJDizrU2f`N#iU%}y4}sAodu zSXE;(ETgfml4~9tXHK7c$7Qv*9iOXv&g4ChFYV17HZ&h6OTYP--u;zt{OYluPGvB3 z0$AbLPFm_=w0bM6(v!jE2XDuAQK(kyd)@%IKuEv&6K7uVjbHk*U-;+$_)q_VUw-hf zzxiud57MfWf_e7?!KKrfjxY)5ixn+I(iu&|T*Z<=yzcv_-ubw&Yr1xGv z9D8zZ?9A0=c`p-YryW9Am1R~ENOEFaiw9a`w>9fA7u&6KcO3UWvU~j7{{A=rlYh2X zs!z3qO|$sa$tSaMYLX+4i zO$Bg6pb4pV4zo8A^V6mkh=u_HG}yaB5;W96FmChQ)(JA1Aknu?fN|&kgyRE|`1*Y$ z8qR~~8d$*uP8_8W1M@yr-}X)cRU4Oqjk@#wdG|$NGCd^Xo!fAu>%lmRd=Aio?i?#| zpH?;ALJBen4r#)4po!5Tv7-0r(cy?ND~Biv9Bt2Y51 zlW6$4N+3v3ALA|#J0Rsxae zOzaDY5E_8O8YxB#H9gUbspT3-Ma`kRKX*U%!_DXZoxznM>YPDPily{I(g3-I`Lp+P zeEsfkz53g4pP%pEynTK9;j^1Z!OPEHH=q7^^Zu?@!$Hi>;exqf&dbx|!*-+l*Iw^c z?zP5GmVKAT%Xb~#|LNuIg%1xr^3Jo*yt@1Di$D86 zzVNUAz&FlcI7}a()gHG!cKelQJ^2fN;ur4t)R*4*yLa}TEc+kX{=I+o6VE>X`R}~u zqgKJ0@$&7Lqt#Wrj&-{lFFIZHZKppfw_DbMMHRba94*c9=~4`{zc|wBaxSjixG(E) z<~i5_s#*VT1rK&R|9#8tF1JjC5a)iRrRdhx`R zJdu&7cX#L8^)~x`o3-in;+=lM5X68{6H@^bG*72ZV?Y_F$=Fn|j&~pd$N_L0^-fN= zY6DOK5`*U&P$*DY1|HzPQ`PoqB|sz@N3Wi;y{0CpKVHxK$NBp9;|I_GeCOHg`u_6L`DORPqhG9+1-e#>>a=FW z?e6Q#r+Dwn{)P2&A!T1*9=dUO{LsE|eC~X{#*?Z?_x=3I<}qJAyB~kouxq#Mt@bbU z8pC`xmAXoqhQiPmp%s(CayL3XRNKR7vGqWIb(1NojV4Y(kiB((@aMkxyI=p!BUc%Y zq=``5L1Q$~?G#ST{l<;8{o$>!JuUt9SUM#Dp=YReU z&p-9LuP$ABbJa`B?z{T->aCZ*^ro+^?!FjmGqrhr{8L{zeE-Sn!PPCFdTYnE>^?rr z7FTI9$HV$wpS|Vy*3)0ScFPA_XFfp}-RloKho|G^rkaj1Y;x#5drw=7{kie>!Q+qC zp7HX|pL=JxKYNEKI86QQ%YNoN{`bH9z(aRaZATFh4JFzDG~FI5ZK9eeHj1XAw~ZYq z0;ClsR!LKZ03l66q{%>F7N4iy%40C`ePc0&QMtc^ysRfNaVQpSFmJRQ_ zu1Rg(rU;6HZJyiR)uzWc1gA|-`)URPU%lm?z+s}s`MMMGPN-?X6FjQ`^T6QMJ&cJd zk*FS+R&O^i1{ETvD$eb`ul9;YITmgSged{S$zs#PRKyS4t~T7PZEDG+0-MCJ6AEso zad5kuAtH5mq$+qrS4k$*J;WZ)NDa;Vu4`Hu_kEbYVDidRQb3~91TlDEqr;f?VeAPY zBOQ0%pR4A9Jya#=N@B&iXD&rBW%BFyckgTEbD&Xh!StbCl!ju;8Ud=8AOb=NT`B^2 zNyjTS6v{)J3J8e2)Q(_B4f1hlbPOyhc@fd%3Pp2Nu|#?J%GZP5bSO-2XE!qjpr52L zxy+C%1nHSXeRcQyZyx{b(tBUO@!t0@U;FlcS36mAJLk^br+HlWb?fPm*L>~Wo<*v{ zI{mWDmU(r@lr{6Tx$kTyPt@(L+)Pg;#ASxNhn58I&B@cNcjxbXpU3j?+xy#ho|kX# ztz%a=$H#*+8A4HJGMbKqVY4>(dNHgjVzIj`*ZT?1-!>L8DHOmI(p2T_d@4uZ`_{D= zeKV?{@23vh3oH%>$1)2zN;}ELJ~#8o3V3muvviGCO{K19bGRfW8(&j4#A^1fsaApb(q~XFIwIC zhWqsa@%5p|{wIKrx; zfzr_RDyoSTqY6b66`*F&)}*H4_7D7d@B99pjy!0M4pYd?@_IYQDJ>m~N&9xszAoPU zaP#4Iy7SK0(fRkD{N&)>ukSzm^74KiTwmWlzvgnBd9wNX=Ho?O_I0gauPzQQaK88A z`b{2=_?yc*sWwYr|zy0XYzNjC6yYcGP zhwR+Hk6ojT3-R21Qax%lJ6fJB-qqW47%z8g+jTr{R!{SFFFEzN)TqMHbh|lRZSMTY z`892ho;qB1XhddfLdnGiCodiz`ql5f^`pDVvVvL8vl=&de(dgv=f1k*zUAYu{rL4e zKKaGhKl_f~xwmy@Ir8}KFa4JfRku9A3mSVD5 zPP!8G%G*u5ZOj8mY&gZjbX_gq+$inHc8HEbfxxiaW?!3#w|Tyyc>@G+%ZyHcky4y* zT?P3Lq@oCGEo;HB+Xf*35^4?vSe4k&wRvSAV>Tl!02{qlRahE9qh>)ht7h#O zjA|B&5ptm0O3eZgkSeqot?7Z(wNfY|R9&hHAl3kYUTT6=9)9?@?>#e0zs;4`Ky;F7 zG^?T}vShTAIgkFopZ?yL{`ImS9&+x|U%Pp^NR z_VuT09d6oV9G@`)C0fq&ZEEQ>$g0yOqN+4YOvtiShs%o&AZ-v+FsXWMNumPSM(mQ za_m#LHqOrXe&nrR|B~PT+OwYi>#zCvhkx@?uWzfJI(YTu``&VIIIzk7;>|ao`~1iI zo;shuc6REvSI@lQx5e%L_4}>O+kgEBfBxV8%|H3$|Lc#etmetvE`Q+!<$ zP36?4^N^scWpzCzC}4o>0Xd#FZ@UdZ_HNoGuXorUaymeC(y^zKU>OaK+`4K{84YH4 zKT+4@G#OU&Ds+HM3sjK-RP*|>b!-;J9At2C;~Y|F1pOqo)>bn>taNh#86I7qvMkn zga|-($v8`mt|M-AEv-Rzcf=w_M4$j>jT>=F@#Dk6DeP1!dy zYSFb5WH@>Jd3f-(oOPVADK}>2^u6<SS|aD z<-(Eo{g=)1gt>dqS5Kc$?;o|V74HW(UgNcYu%tL19sclg|ATjb{;HQzPH&s`vjfBB zX!V(6y@%Joe|Y@lQ`^IbH`R^jAGrLwTpt6VY)ljzLp{l^C${hY`7fS$&a2MGxvM9O zkMFi!-l(ic>SZC_3ZVipj#qTn%YC0Z|3g3h1ONN$e=g7N|J)D!^tb)mPuzR|cIM<{ zB-i$iFFyO~rRRR|fsehj@0z`n<&XUKJ9W)ho__r^Z}`3JX9w!;s5ge!=3$!=fD2s8~@)A{m#Giz3;l0BT0s)ulXnc(+~dp@BDh6c~vjj*bYeT zb><}17}1r+%=V-`Z6`xv)W{Z<*-+3VRS>%f1PKZY2?^46DWW11f&yYdNsCkzBrFs` zNVbAs_pWvTRLvu3k_Z%S)*d`J19x}6w;f1GM>OxS3=S=Ww|Tz7^R-!`1HmMPA{hj6 z8jWpV)tB9=*`D*xHH}aj8#cA^v0}%(E0I7Wx<9P4yA#Dm^(e(CG7BYGw{gQgtL2?J z2qLlS3)f9h!3O6x4<)p!=0!kWX zA-=vCxbuy}u#B$4Uo&g7e7*Wgs;FJBIxZsFSKF-7!G5fQZ~R(<^mIhi;|mN0b(^Z; zeQdjBZ5o{hTcWu0)Mmo=kd3Z^FKXwg29Oa^CD!I=k^?KFnpP%}NW~@)Iip!hbPZ6D z#ZI`!Xs8;+m`D&YsAWdopx8GvVgYN}Uo&rU?%j|6Ie+N4_5>+<;iai?hLdg-xZ%d6 z7L(?d_rCp4{qO$s;@b%R(1UFR_XXEK$z#dP^&b^;oZp zgyUAvdsF3*?i?%oZ{ z;Sta0Cw}MBy+3@*Cp#{lY;B2?{pPUTxmuTR@cG&u+-GC4d+Fi*_q^=3gyMBaS5x;^ z>B*v&AK$$H*YDQDLOM-0g+jYCx84O|K#gGy63^SUwiSl{`0qg zd9ZrjzxwF-z2AH2(e3Uh&T7r^$?n{KdGTs`Iy?Hv+28taKl&fv_`&%bF0bsaU%TV# zjz=!iHQBZF_Rde-Z*TM3vtIr3*Zm*9>wo{7_C0bk*g;d*yYt8X%V+<=pZ{BN`02rN zS8oyPXl=?h$bH|rZP#tO5v@ZN$%@LT4Oj&*14Y|y znDZ8gt?e0Xr00s}rO{x|ZskOmbnSgw^^Ct16vgIO8;Y-*ywU@wBpI|C&f1~Ixi%9Z z6gjs$!;0(Js6jL68&V1s_kCh;E+vToU>f@wBJ1P7uTa+>G&mJ)4go)xhX>VK%)T{+j(X5S*O3WI>QeC_z zx`vo6R?sRlqggb%MnJ_r)Vg!;e(cY0t}lgE#9OG)&~?P|zB3ri;j zm$~KU?Zze_^7YR>dAfC)jj4BTZybf9Q$-+wfFy`W8^=2Z0fY_U*IfrRgeF1@s|XI$ zn|%8(ymjsDadUSqGNzA5Z?h>T_C0pnqCIaHu5Ke9yyuPW51%s0Z9;wCRT_9Xwum#N*5>s zMMQvw9hD+hsGtFu-1j|lFdBgAX_H~LG*QPCqV2AoFNr$HMB7dkmqUJYHZgSH*D)OwfOG*d4|Hvc>e0$=l3LVd8sL0; zHxDt5z$b3|s+UpN5fOl!7Y{Ci^E*P-uzIXg+o^*5REm2-s3F}uf-3Os71^)@ZQOl; zfO+Sh1I`LkfG1Uh3^w6m!$#LU=9q_^hYk0~r-cZDdDY#4+HA86)*NrTDU;JW%nZO` z!D>^0dTc7S*)_csVE0`|jM3{?E2b`zAQ-F#Oh7T-K^osQ;ksCSRomzTSXJ{^wHVq4D*5{L${Ay=~s0E9#ofeK=+ z!X+9COof1mXs8+WqGnHa3Iu~qHAK~@t-%0;Id*jAQEcgTzW>+%vETT@ub+O#A8ahT zoh9g0*??6tUe53Qzu*6}-}?LSd&@i9A3sGk8J0tjY##pL55MvB7b}bX-1V)Eg-Pw) z^O1L6e9;GYe)!YXFx5p6G>3=)kfI4dls7^`&E3T*S-n+M+De`!o?c5S|*c_7+uC;}L!Ogy44G5r$LKNz=s93XMAlFeBh<#B=q=HyA>!=3V080#3l_+BEII0<+i@0fl za(bC{MiXL!Q7@pDF$(bb<b|FJFc3_IDhAH^Z&R@ckDUd zZI4I2JcjcgoS$!AUsvD$(@y%U@BDVN+#HYT+QsF=ROgcII`IKH(xe)YC{t!W(+D}WiQwqk)GL5j92s!afhO1lV!ngk(9PyyQ2 zv_pr;p{QG+-+tPQ@xk%vM;=vUS%4r? zUJTT~=A57HWu^E4VQC=V1< zQi0$!K%R_z;;5)1)AMyF0*WH3U9SS*^&=D^-eF?XG1$}bjF6Dr9SDF4D40S>yY8++ z(`tC}+#muU2+(Upyf%+P*72)ELIhBaz^(~&?9q{QfQ>+#K9+K`9$7{N!l)p{YaCc^ zb`1rrP0^;9SF6-KML5CUL@NpDB5YXf>4Ge3D6ERXfT|&y7%U>HRfHO~1Jo!g7^oqI zuB#V}ngttY0Sz^1!iBL??N~Iai!eE=xM=qeCwwa&96PMd9=$7$Hk%){p9Q5oF%m`eTX@U}Ac;PdNf=VY|)T#W6r zXRdqf=4JPEeP(-SeX6S)hdTS(ygt0ho$KqTuie~!;a1&S)vB2uN-J0a6d;NwP5U1z zO-KL=D@YOmFm?UTs2aNrf(ja>0-+!gA}s7U&aAi7C+|*A8Lb-W#KMXpRUUAvx6QUE zzqEPw*x8(5lq_G{%pIN%_K%y3zUw3RNE9uqVeEh4&W;CfU%U17;v=`a$4e?VBJVfm zQWJcKkO=LNwhD=w0HlIZumad2+5t_PJLXnmTqP7yqV}{Xq1g4)g3T_qT~{>~v2{5B z+&mc|FC2VoI0_&_4h&S&&0;VvHb4c~>^i>#A|rOMXa5N{aVP@CaP!T`s+df{ODFxX zdVYFPeThb36HE`eCW26;=%%VLXGSIf1#B`*?u^cL-xsS*=e?NLFeaASWOFioc!xJd zi5E#Yzi&HI8&*>whcf|~)b4Jx{2?fOnPh#Sa~uBBJv2m_=my5Y39mqs3RM3{D|;V*@K0~%OUx}N7M18t2@hnl%&}&wxWH_` zdB6An`t!G5dM~#J&UC-s5F;XPE~c9^=8Wa6=l6Hto?VV_&(8IIb^ltOcX7UkV-|Z$ zD{P~sTVQF7^WN>*sa|xQS)C~d#FfoBbIhTA zmwdVI-Rk4lUpseyZoO{R-0|eAtJ@{|BmfN#BBCXv!NZU!rV^#mm@XBPfQ`DUwxhx> z2PfC*`I|^0;FOp^(?5WPs-~}Mt8d)qr}zjwd0Wo=k9ub z^3JQ6m<%x>B3-$rj!yP|;sIi`v$nPyq7?-Yhd=-!fYMM_lqQI_lqBUqLtO+!G!a5! zqkzhIy|UQj8jP+tBm~x;q?RgHP32_z^apN$OiZ?b18j^_kwjNf#Zv&;={-48Tl2(h zv&&f4Hsrum4+k_PY#2;v4UGdJ+8j*Dpaa0+^=c6n%LrVdpf+!KEm&-)$fTQqz%`wU zJ-SwCMOPD|-9p1VFMW5>#nnXufLPXIi`Y;Qp~OT~0vNIyTu+fc+{CT+QUUL4(8y5NsSQ z#Bj6Q-kDW-79Vfm8N|TorJ9kVu5*m7zg0OhFe(&erD`_UsE7^Lm{CLpY#Jzv0BS<|O3^oGDXdKqg= z2?){Py>+bD=977Q<~(ukt>>N2(|zXS)MF&OIqXTAFYY5dwjwtD+&fc<3V==%<~y z#|1o&bg;Xx+v4hJrsJ^Ty!xGA*M?D{bMF^fFaysDB2o2Fa+w4WMD$KEg{LKuWRtgE z^+nRu-kcE1ECEy3imGdeMz8`J9Mv>H&Oj6p0aRc?KoHHUE@*&=Gzej#)_|3EAfxc& zmf_~z8YnTMs0a&Dov6xeOrE@E|E-_@C*Jq+@&r$KJAL+&kCW^B`8ne|%x_)2%i`^? z```PrH?Mr>M})Qp@ZlKC5Ks$x$j)-ShsU$9Jc44 zc}|A&@N59lj{%V{xZ8s;~FpRo?N+bYkivY&}aP46PqJvhc! z+Rn+PmLeJyAvOTP#0r!G60jnSY6}z$1V<&rlp>0%ZPc}f(kOPR1e=5^Nd=X5!03$< z>9y7B#p^wvd8e2%YDotq5UdD_?wO|_?tA|`bJus0fLm2#4m^JHyI=GN8{B*P^&dU* zo9|2(Ekpozh}i;yBmfAIZYsuM>>?tFc0>q(s;#n6wH;+zI72qNc8H27Cd6afH33Ei znNWo1sqRitIhk&D+gwz^p0&f9r>`Cx%IGV;zQMb~;C$R4-x&6{g99Ts?(Jj%19t~c z8=@lF=<{5gP;|ZK!?zzaJ-7QTQB<63DmKA()x0Sb*CVcM9(^>SUEl9wU|uyD2ojy! zyv!U;g+QVUK;9s#?(XY$cccb`s(SPjo?W0al5W#^r$t~>#pqdRtBXW^+fJ(+BSVBv zJ=WSySZ!u~M1W0WcJ;n)o2QN2?5YQA9}D|6*f#68TJG2egC;5fs6a3{*PYe$ z?rP^YZc@z?2rvq0hQSd~Ng+}M0ujNHqQ)SA#Y7G&G_|(1Wg&XrHDSY1mVJ?Dp}MvY zf*jQ7G^QKInB|fzI}f9Ew5O&JD%>V(i^_7y>=eV zL+4HT&d+?hxbAu9>GeE)`_#8@yn9#geO;bkSI=In^Xty_wLEvR9J1YEZ=sUd#lE1U zoy6=;oV+%c?bVHrH;-;!aX59(@9#y<$$C54kGUrP}+(Uzyc|x?9G|Kva1(M zXet=$c<{m7i${ImV|Rbyi+}#~bD!+{%>Cich-u9hMS>*&xS)PMkhc>qj-V_MCtO{-r?kOR2U0XEJx z!MU!7D5C6DBmi9m7HHk62TK@qf$6lPoYf#9!|D;~Gch^Wecxu^Uzxl=M>-}Om z)z#(s>25wAyM4cLR6Jjtk2}K-6fzL^ySuCHaC)87(@Q`3&C~nm_*T=?&2H?MW<}A0 z;{Z)JPi4Jk%E6|~)3Sxlaq@iiWmwxUhBkC{^XSgIymfu!<$Pr|<=GwPTleng-g&uW zy1sw=`pd(espGNIDt@)y7UZ~x%` z{ih#)@J>AfZ6ewUXa(T~-Q$6g+CT)IbWd~NJHG$r*L>~x1E2e!-}r0a^wuA|_jeyk zL}93hfCxhL&Q<^wMF67G#zqB+qKTpDoD5I|gpH-80M+DQPX(yJJRnE~?5Ykno!4!H zRKsqY)1TM>MFk0{4V*^x2%mM};mNkERj6Y)>DcTd3_W#c06y7T**GwXz`S!4s#Z;5 zg>}Ed6uxG?HZyl>`ld@8`+k=9LB#Sd+@|Orj=GUo<6jJjFox4E1_Zo=kWCSRX$?MW z61tgS$Tq8YCo3Br=k|_{;f8ZWlUIm%df%C@YPtz3AtML*lmmGD*N}11R17Lf(8zY3o z8HQ+JXmS(OGUKR$(ll9^G{juc60_~VtFAK+h(<`gNK7a1`?A!mrt>v( zm~3`(+P?MS7jGU8&%i0pi%fG_k3A>4mwgYa-5shq+IQ&P>mnU%6=bPp%Uo`$dnc)lew} ztpXGgsi6n}M5S>oR0>5;Ps1eACIl1|fvI$&w`SgW&L@ix-!TQl&^;Z$ z__ZG&`Rw@}58fV)k+c(~K`2B)*owkL8Uk9e9WR#s_uaYg*M8uY7rnTA^GBb1?(!O zx%Xz=x59NzUz&SYHLn6wP&2{H`W`-lh;COy41i(0@ztZhrWQei$w2I#>IR=PfDEJQ z`|DnE3!s|1&vO%9;7Mo40f6Xha1*?bnAo{Zehs?tD%#KXME$M(*}PMzT1@Uf^q{r| z8bA~=If?*7t^?2nR={Yns@04pgB76CqHD$2Pyr!80bndK3s6MqQ4_0HGeK5bs?eG9 zmwxDX5{t<|WuRtfT#xf(My)N|dFO|J@w3C@eZ!ou*&XWnip=SGXQy;mddt0h?4uKW zsCAi&iH2>bW1WdoU=D;EyeF1|i<`>VH~{bK*!4@E-|0)(PlscWkuok#=ep-5r^TF|x26o6D| zqG(4Y!9t!G`QbJ-q(3F)I6RuBMXL?-g}#?C}u=?XM&9Ki5N z0w;??0C9I<6BRWmQB7p93&YJb))7y#_X%`=PJ#11ZCQb+YkF?g=DcnM&a0h5@Ei}c zUJJTy0@eY*W-vlvbDo-Zkd4?nc)oRQOc#pWebpnbW#`_w&1gET_C%nYsJHhyO!1)X z?>1)8TQMtR)a~wQ1ponL3K5yg+Mc2s))UtcOuIHJTr~mYv6zn|Z#Jw%5rKiOz3T#y zEB3Sf9cblk?8zvGc=fp6H|)Rog1eplbYC~> zaQpW9#bNW-neFyRUq1c7%Lku%XY2xm5TepI#6#mSh-#o|k3$h-MG&Pa3lbn|ny75V zUw2y%z5RQC_K*C=wukQz4*JrK@4xf??{2@m9WRE`CeSA7ifR)A7@4gNlf8o~6c%Yz zs>EQ4<5d*qubv*c`=JWt;#1pH0KgI&P#U#@O{7f_1}g-mt%&vzf=VkYttbMD01-s3 z2xSGLFzvYWP0|8B$rbZZ1@>bF27V+yqCLCFhUnO=L~UxKuFaphFMNiu_#_#iabsjE z3IYUpm8u??1Xl1f?DwZFsfel#q6k5T!*CYcp|0)D4NQu2Z`cJU^B&;=0MQ=z=eoK9 zA|}&lN{EP}%?@prM~Aewr%=O8{yHgaPP;eUd@lEge+FcpLIqN91F8KOfp0lFb(PTj z4t&0b7auRcofWNFeK7DEV`5@jfVs|UI~N562E%8BvdscbrnSEIsMW{-5~jlF=gav; zO9h@k!VCscK(MhX(1oagCJ;en1V^2g{)Rl` zHHPR4474I9p{UCwTPffbTUe;ovv+xXazC4P<#KxBbkUwL8hZ|?5*R;adU06+yBU`$tPak{mS#(FXria_f3AiZ9U&u>~h$&KK=BC zCtu%j^Tk+Yr@8VED=Gm+D+)w}0?;T&l#)#t0@+mH5rP)uMIF2E*0=rC53iiNX>DrT z<Vki)R z5~4ynfWZPIgeZWZBA_TVApi)02Qc(f2ek713)6S$FjG(|z(gh48Rxd@Bvk-N0Xl>+ z5f(d9ehcE43ytFud_sOf34?NrZ`SG_$XqX8gR5X#aqM*`> zWE0E5ny5qp)0T6#KY40Xt!(bs;6|*b>F=8eq(}RBGjru^yjc5}cPI!Ofe@7nD>u&2 z(tzm^z>Uh&P41LI5$gpMnnO@QAV3fV6e|P(An^cWX_%~&eTkX?0FNO|Y*!ORkcdE0 zO?O6pIFOu7D<%<9lyvM#sVIRVQ0{u?8wCN}uA20XWFq=(0uR9FFv+~yyeW#Z?VOwr zc1?3#um=$b5Rb%&jt z&F-G7H*Y-U*WJE8c)_o5`+NK2+qbV@TitqXaeC|K>b3Vgz5BHf7cX2?U8RAh$w$ei z{0Eh0fha;CC{`!|48cyLMgxin8wAkQCQ^V98b#fXN&vJd4;NL^idHqy_-s^d#R@4~ zMJs@Ukctxo!3t_AZ7mvd2?7Z+ta}AYLdVw?)1GIljtKS^bJV~#3nL|mZFM&@V(C@6oi1!RUJLYaM8tZ9(~vW zG_Cj$f^_J3${b#XP}kje?m_Uz|8F2kh$YVLKCStkdy;K3Kesq-o~X93uiAY-=X;xd zQKC55wb=!DZJT}UacZZ7?nmN|Au+G33!HEBa2=cTLtTJSo=^Z38^wm`E16o6b~V$E zQ4SdqHJX=zGnz$Rz0l5w+MzA=1czW%(=1{Y2vqG7jST}Ya8d)U5Tx<+#n*5S&~l>F z<94wgTbYlU^*oR7{pEA#j@m{XOILS#<+zmDl8L$*4VEnGD)a9C@=2Z#+wt~t?l`SM zQZ;9d5r-*~hm+xCX4B0aO=r`~w|r~ef||`Wt{=@&sl$U!InH0}k2B)}So)TAf-^#d zx}0U!WSdiLEa$$Ebf)9oAAR2a(p%g1S$x%Un&r0bVXn_NcF!I074LQM+9Pm!io?u zmc`33&HPNBn^~LJF|FoRF)%JM7vl8p&pC&n=9)JST+WFA-MPuw63*rohy&*u$RASK zcM7j3S+(7)-!=#2d>ifsrt?&R2*@PJ02`+eK;8BZ+<9(>)yI_3X%r2}a0I9-PE8nz z$lU?p5aUfMDnwskS}UdjH$G6Kj69)xyzS8RgCBaF!jl?NL`@4Y?CUDcU<}?J5a-^B zyYJlQ)wcWWlfTw8vIjl2UF8Ab#&(KDdy4Muk0aT+Ek!X8%wtqzH8cq<5pQ~KQvsyo zd01-`uDDtS#`R8X{|1EW_(G{d3jk2n92{e>noclw2n1RXN5B}fROgsLnkHWcWU^Xm zDu|UrHQ>Uishqssl_oqP0vbIm=2GPlp^K~A05CTH@orR>JAfN5{)k1nrkf<|91XpR%kv{NozJrQ@B%!)L%o)0X)nt?1JFi}y+T`JbdG(r4PPe{Yj~_mI zf%Eji?e`u&+qb%T`d!Z+_<{%hvuEyY1z|-IXn=2u00`QNbUG?Qx3W;$uItW#rZ!&_ zM^bEzL8VE6hOMqQyo;LT@VFmQzllFwhIEP+AhGL2sHEx0?`Bng(MMT zsi1VW5?IwV1Ee&;ij}U2k*V43zB{-3ZeER46R@cSoRD*yo|^E=fJEJ{U+moPl$^HQ zGjiNHj==$V-k50I#OjMq5b2udAe)1UCx8e|U-s<{_->o!<;|>Nxjm<2vS{ zyMujMo84v*jH{;6R1qQ48CW{V2-0b#q0+1>;Z#%N*eK_pS_Xr@SB6QgW zAnb#%4S252K~+qnZWN6pT?E~CstX{|ZFBGXZI9y4B0?Jl(%yi%D52Yt!gJ)q4+uat z9e^6TGuH2b$E0uW2sk%#ZU7EQbqB1Wj`?*vSG3|K_vaug3TW0!5m}=PfrxgXnp*6o zfnqT#1`se!2C-&TOg0)pU856|8lYJsl#YNFY-VC-MS-A`=YHr$G{}xeFTB|vvvn&} zJ1gJ+?Avh1nAxK1?5vAK(ovyqoBNJfwZa5mYH#SIznSO#`WWv_uPl^D)lIDBcs<#h z`#m*aF6a8v>PA+|pt&sRemzr}m>%yodHhYkEHia5ZcFN_yU1*q&7LuqNt>jH1$5bR z!28#iTgF^1j<1t$LHSe z9o))3A&OR-;;T_@#X%s_umxJBG!aEZg!EFN?tH( z0sxvo4+K%yY*<8_$F33sq5XjC0%AZb$N6T{k#hr^cBfqr6_e948kqo%176dPdUsbu z=XPh;eOG~CyDB)%tXamiGJh}wFF&CmzHnQ`xvowCIA8bY6oFB7l|ad=jy=(@!YFi5 z{gS5hGcTVvim3Z@l_?B3DQaLI5tCF66_7&FdA|1H5%~4qVG7PF0O;wxF^Kk_U;V+3 zY7awnEC`t#m`u-w&J8fq@EBG`0!@X;f{KXsQWHsy1sJT=qDFN9G(ngpCOc{fQTwK& zM2#J$q6m<&&;(X-K`JU>Nl_D<*^4J%VvOdfF66O>O&oEKbNBk+{Q6dpbla^}%5H}Z z)YNNfTG;8@#_7AWB;w$7@?F0EZsvBLr=2h6T*%#3U8xMmOMLR%9f*n9; zgCb0qK-E?tpn|YDZ5^Vyf;$8#A#?)?NNo+JQ5w6<4v4s^Er%QiU`xQ@2(2Ih0HvT) zWtE~L5+Fo02^yeG7eQT>c2sSbqKQF5g{>131OaI$W`UZj=LHagoZH=z(}u$hDA35f zS`DX;;e-mHhVve^`*1{K(8Y#Mzbid{Wn4d-`mXtF?{ZCOnrs>k7!s3!3A zJHCk37_ua5U8-mHKvkyI+ZsyFh$2mXu%ib}ySmrVnWjd$tCY;qLn{HRQ z+UhiNGC=K6TJy16eRa-AchFbPoPQu6EpF$r_096p%U2I>z4P*u{W}YI^PwQ5*Lw5^-VsY8$A!e$jo0U*+2e06tX>_yHlmiee;fw0ajYWC zbKC9|9Cm^G4guxc-gmBSLd6J%1ZEpBnqL>4)_O>t8>!tXt{17zb8kB_@Qpa`edp_) z-WJuKNOb_LOJaN9orG=HqH%~ueL+`{ipXeyRdGgLCv2C2^8201KcX)D@ZnNpKV03aP>h2BI|Jph%USsGf-Z*thG=i#hk07Sm1P zB63<0P$TBJzE_}R9^xTNz^fK_-*s&o-DF=@fI85271%5)I1$lMtKei@<%5C&GN(0w zK{dbWM6z*{bKOyGz;0qQbaZb!h^jlg&i6=x-t-9|7}i{y0HVpsY5@#TUF~~zMYQ9> z^xW=_IA@LwY@%@h5}~$Z$c(yOKh@Q52Sx=>o7C=(h|{hPm%-jSrbPvSTM%u?^Jn*Q z&&KlH(DrH7=4Len)_zoA@2eqfF9A9TaqV5~?&tIdQ9=6J?AQoYIRK|!8kd_YFnVgV zPzRkFzzQG`K%|LqTvQ-HT|@YOH2d-0>NzW zUj2|!3(Vj;#Ze~B%^A;cef2}R9rsSotc%HIMV-(D%TRJ^H)X;yJ5zMSNvDv(4nea^ z=k>hmP~BjZQB_y2*I_WhqyWx5g|niPdrywXH+cgNQ+?$_FCKo$t?q?8*(ZvS3c;;}C_-A)+e9c~un;0qB>?bsP);p~>#U0ZoJkApj`dBnv4*6ih@@u`nPMtyqPk z0I?J4R6$#ab_#(&TDgPP?z>iB%RN#Nh1HaTbAA6@w`x0Euw=|w2HlMAO~j;g^d|I5 znE=_h7rk7+7Z~2Y+Mo-veDgNPfmqanmAbnr!f`{1i>4q3YeQ{w9wPVM`Cc(1k(yMl zuCDFG2AqPvstJ(O>iJZ@F5G_Xn#agh&%RDEY7D_<01t0C1)J4XQve4s8ITh(1SSYp zq&4iwyBZs?&U5XNTp&frf?#lfK^@|XsNdarvF7)8eqXoytaYIK^DUFjqINm(bvto& zT1{%(O&;t7m?HhUrU1Mycih6%ovTWw;Iz9N7265Yy&)>rj)bDQnE~15nwYFKRFfkU zf;!SrGn8hrnwkYsb7&iT1OcFoF$?4p)6ixe0|9EA$FIfpLB&u_^_cl!yO=U_yY$uX zZp?Pr!NA%E7%?S-s!0zJsi6~|N04xx{A@92EZoONE~p3aoeQR*>*WVj7A_YU*y38lC+Q_bvb zPCj*s`GptdFMfPza8z^3^~j~iU%mM7*wN)9T-~u(Cr;MKKk($fmpz)kcq^*` zfd_~VidF~&rBg#jg}Z7&Ap#P16d6P+NJ5h!>_pd&q+P03G;y#aK|>JgR24PT1w^Ka zDG;CqC8`mU5PLyWM8N`t0!;~p3eg(@SSqH95)fcS6M~7zVNh5orc2R@TX57Mpq8+c zL@-bdG68HOk`=6NpnwYJY1glUq6)mv&8#k#6B3v=gW6Pzt3mb4b$F_HdS>AD_B>bD zeV>ZpI0)Xbno;de0q(oQ&W#Yq71NAj0+73}B51pW3_;iCGPX2oeQdgI_x$-JICNG= zH)0_-YJ$D5#^iPp9QTQaJypY^0M$Ekqv51WPy*iNT)=1#s2+DqCMq~$-X=2EzL;(>3LPxzwdc?vIwQmqn z4WUxhM1Yzg8JOvISJy)T>^MznT+UX@Cg+{6zAbK{)sj7BUtteDVFgtB?g#Tc*;A&A zo+$P#SZE9LASqScaA=^_C9A*%p#bVi5W&fK-h(sFp5^i0d|j-eQB0P(%W;NzEso>! zmCjT@o^y5Hulc$&52`J*88YVvQtZ*1EzuZFY@Rx+`OtkY&VJ3q!wVid*RKq>zP_H< z%BXr--f{Bk{ z5}_b~^k|M1LIZ~(5-C(|RZ$axBH}`bk}4-wHLfvefrk<*rUF<|P))n6fCyYvqY-X8 z00=-s6s$->Aps3h=?YzinA-{hgM=@sD@M1cHR1-Q|rww`|J1ZeX0VN z?|h?!VYFxc&Z|v{vE#sWM-qpyf2)wCEgc*ka6*9)lVSkluLB>`YD1cMsg1PMA# z5U>;{YD*KmI`Kj0R_BHbY^OxLFdOJPj}LF=nmKoz-&<52`ZFyRd;tiQfyamo|?j8Zc{Kd5tmcl%)1+Qk+rUZ)8Hn8Z(i2L;A$BuG)i2XA)H;jyRYkxqME4F8~44hI&B>6+DJ^t6^a#vteUJc zDx|sTNU4-u2H0^X8Z{=sfnt@Y+Mt*Ptid`%iLP3%qh5jvhya=#)ne9+h!Sk<-Q4Z! zL?F=I>X>Gmv_g6P@#-@JvQ8*hqFKd)XlVw;T^*>>rYoRxk zYs{hsM~Z>=xSh?EX(OjT-uCvFr%A@-n!Ci{7Rf1bs!vbuc7E*pF04zDA(Jf;GFAdu z_DtcHqH%GmBP)*|y*~4(AHIu)!CSuiWcsH+Tw0B>Ys=&K=K7iC$(eC`qI1how=Bo| z7KfKV`pJRk-#>8i_GmGpTXeG(5G4|#G$}xZ1fW6)Z6Q!%Vh37+fMSP;K@km6Iw=&B|G6ds1p&CbpepS8zz8_a*yH>*+`9@#H(`}zo2u~?{JQq+>L{iV zkYajn0199n2Aq3zy>aAKbQlwP18}bGAbA+=$tE}khU73ff#iV4xq-Xuni9#t`F7t` z^C~+Yax}KD8=CcHx7U4FgVmsL5QZf2xbOP-;l}wDE{HBx9?#pRrgsMrnVjGD4i1Yc z9N%ktV|Z>;G>HyfuQu;xM$QWm>@^&BD%&Hd1_tT4LeC|(QB1{6yFoDS6hiaF=)feV zs2~;Ha-DDKtlRcouV&6u-Al$**CSwF?M{GdG5~5KQ99}htRiddwF*<26&aHL{Z2mvhWs#!4*1PE#XfB{wkvVC~_Jr=NPl{quY%uMz5uYFprXbEF6NYHNT zS`4ZjmSN2u4s#eOsRUDK1!E`0nI&6|+j0h5C#D(Zb(ob?n=SDnvuOo3wq4Kn`d-$< z5_1{onC6VMU`(?!cSp{Lc^tX7vK_6n#0cY^9Wi5eQ`KKBtq}9Gu{ry|{^d|hmEIicp=tkxGq=vQ-AO#sdvUq>`qmdd@p$@q_iw%BZnk8L zq5-LBmpV4~AQBW2idJL@HY%gqhKdXVL9|P00EA#9qy{1lqS&ED)ut5f;IdI5BCJ9J zk^xpk2!NHKZBzw~02S>ZP1He3-~y~7(ji(AjZtZrf{BIFsE|YwAq4dYm;BnaSQL!h zedl|}nLhUD+D`&>O@n^f6kuaq1%NSe{JL?wd2Y#T8|BC8{6baZ5STI`=Ix0d#UF&z zzBXe6Y24>2SeQc;92*A^FLS-30ol;Gedv^VV8)d^@4EJLr^zW+B$IlC@bV^m_J*X| z1g2vO$&`$YPoa=^rWqO@fJv-)ZdW)D8BYUhZ61JAAi;DB!meMwxA{GM=mgAftTpY{ z7`fQl&QL^zD7e*Qc)LzMGsMd*?hfO_&BwMr{prW|KJ#ww?KIB_!fXX#Jw_cW78X`10N0xcyg|)-6O#Z@ zX#jwL%@;GvmPAMZF$hFMnxtR_Xu@(B009UQD!`lK!vsZTBFsRLs6>(&gb+n500>H= z0!^S05CVx20XX##5klg8LD=uMPfi~wT@H<|P4FYT|3d)u&Hyz8K=ipOn^yu1PPXVGK9u%WUdU zL~bxJqQkdG%>$DTxB;u_eP48F&q`D@f(BGLLzap$n#_~`MyuPavL-m|_qZ{NE-v|<%Dwk$9F z=IP~cjJI!}uij7{?e>SfYgsSP{gn@W^1zGkwGPfQC`y`CyCa%xVH*eu3E)I(XebB( zHnrmHQUp|50jv;I+94sBAPgozs+6XHsBHXW5DNtmaubyU2tcfWigYk`5v(W;A)%na zv5JTwfChvWrBP%kAs~{V;6^l=2ml~dOwGe)#nl=mwk~|1MhxB}i{QI(3j!<|A<%{B zV1UqK(=|P8imu$HH*ZDIl; zx+eX$IVp&mkOEEmvpvkd(Wh-lK_7kFgZGq&e8T8Ic%t{0R~t+RAgX9(?$sx_yDn5c z{>j>Q2&j2TYzSt#(1_gK`S!4j+aWLsAbC5)XBvq=$%((DQ9Pdk8xV;m)2c>?5Of)^ z8w5z4@4+dEPQUYJJXDhm0dx(nfV$RJ%h-xg07sEkE!V^>MS}_fa0D=_i;zZWl?f35 zZRwz?xsIAUc^vJ9QA-)`zLcV7hrKB?jdHfmJ6}C4qfO4lSRDmGh@wQZP}FFHbZtfB zMr0?bpT;u9p^)h$G({CRu|#9YIGr-7v>YbSYq_zG_7xa_>}EkHP*JD_s5z%+|JqMK z<@3&AG_->bliuE4(j&WBFwLZvao~mOcDR+R_n zCt|xA2;O81YQtnmvIDD4&_$8DTAiY(+Enu@1R%`ztRX}rK@b$Nt9N&9!+F)+vBUN> zHt0qP_RepY+pH{bwW`g7+aXr=;m&~|bxo=%?RYhzK1p;9vs0$fJ`Ln0z#)N_)AeXibCl`FDrruBfvs|1`uKvXo1pPQUS~kML?rC zM6fhdRmV?o3N~WYVI?S_T@uWsddNryuftFS76D(6e}< zoxQ7Hwuc(^UP;!sERH;|zJ6(EcgyMZnXt2^r|jO}ynoog_5Kg%U+{4Mowvu!9W^zB zp+i(af+`v!F(fI1MRS|zDuJ7@k|+q#&0b?AdP8gggbBimN-K6XE&tuvmm5mg#%tCv z{#j8P3WSJBz)`dcuuBn#sM3U3dla==Ay^^j8`#bfuX-S!2jC`8lrOA7HAp9H`j+0! zY63qm<}Y?eY?^ihBxp{m3RJK<5MsG;Fah(3s_wg~lG8wKfIW)sBJ&M(W7>_^b?994 zSQjAZPT_iXOx4vo&Evt0H~XFd%AiPOWSAg>?C1J=G7%qk@Z+HV5YN8~uVfw|U~iz0 zjgEO8Y%(O%;eA&KM1EbtPz9AWzAomrcOAniAoLeg)ZJBNN`_!MKxEpr=M<1lVD4#j z-Jd(R`*Y`V1$(fW5<_gyJqzWy115uD3LqxTNdcg+UIJbMj(K@B^Yqk=G_=WI&UK(? z?tQeCSma32M36XAf<+;eg$HY{M|frFFyUyh+G+}~CTFUX+&PYPw$AHc`?LU4&6(EJ zLEVt(hIZCtClLx~3CG-IPGC*om;I>rJ77hcOuDt^t2;s)fFZLRIVz0tnp*9|0*SM}} zw>_)r-GO;s`|(X5Wcsq6h#;tou+6S~pn#OPZC^0i2;iiecvORA*!6SMLLX4cFLF2d z44K$;vj7NcdhB8q^xPYutT*={+Phz89% zVh(C-Vm6l)1bi0qf4*~T-eu50E24%tYH|We5QjcfF=(d^s#!+_oG!NrCc^5`xOR+Z z%eTJ$A=jwaWu8)uWr`ZJ1;HW~EHjX#j}!H<gF`Wank4)mmzEwVZ*&;gRk2r`yT3 zaXHO6oV7cQ%_;3M_n+OIUiY)V`_b($yc;*(tB5_+1pzw%#sbAjq)7lI1V8`;Ow+@U zXlQ!lSV1bHfJnO<9#90<4N8ipPzVBvFu~G<5baXh1O!nDi2xwAm3k~HqMJ?t2n0z2 zk%FLT=O%|T3y85NsK;}vhhcdmbwot4?K;Tx!}kB$^O{%NeB8{@WLg+|5CN5(P9s6K zb4F|?Ff3mA_Pey1`8# z_vpaXDCq>Lr2}X-*DygKmC7R5(Y)w*0gF)p>T}V&?RjhPuV+a-Z9l>;ioqor2=E#T zfQ&bkK7{s|>S}X&jO^Zbzqx#`E@Q3TES54GMG_FINU-7QdYLNItu`}*JFA7gC2Bpk zb5lJjlMd5a+YCcD0E^iqg&U3IxEZaRV)fpKK6XQ=?4?ku)=Zv#;p6i8Iy~G_^#Ur& z)ZL|2wCbogDMe4yCg*8oyXJ6F(YoaxE2CCvW=GqfeSPrNciPlss9j%k+Wz$?OFN^V z-W+OewVW1*T0Wqg-)twt@|5bZY!5Y>EgO4xHt*8m)O$Xff5C&LJMWIiUG$Y;0U|sE zATXFCnoDWB06@_af@48q865z_PzFdtwr3B3=mt{6ik0X>K#Occg%Cx65UK!(5|$QN z1d09c*_Vuk;ycUl-_JeX(j)8JX#CYAvcG#~FC72&q zY{+N>Oo0!_Mjy0#+v#@GjlxBE0e~Oj(w}hHOCx%NQ``i`{kdwQyyiuD)sm1qPY*e! zaAULEw3?Di^zHufo!>FB9TQLO>h&V90pYX;?hf6ZHQbzc?%iDkCnBpr$xO_}h!Bko zTtu!jnr!wU00cs6xg=UCjx;E9G@BW90aT4fvC_a?#)L;CqJe;;%5?kc8$nRxxU79$ zta;-b&p3?PN>d&)8imAYA*CycmJoAQ=s<}bw=l`6keQ;Cz6d7mMVQOulYT0}Xvk3F z?PllDGP&#Jw;Nsz@K5%qP zU9p%RElXNrAjYh>&GuofhV5;~*RNge4z97>a?||ue_tP1ykgjPsz+^HS#2+vUG^KY z|D;bAro$=c5Vo5&Zp~|ZXZx-j#>tcQ)CWI2@VtA~nOjaM3pMAy~jJ&25DMQN(~IQ8QhQ@5rGc1y(^I77~k0;lNyoCx+%(~1!SsO5xLDwp2&DKReE7sv+d2>70-##OV)Rlv}=>Kw?Q zB>SOj7mor584N-p9IM41t%dt4@H|v%`fQYHvoACisYHW7Sa7>@d(UJ&Ahzq;1ZZC0 z+wT0fA&91U$brod3~Kg`+&rfsHoZ>~ZIY|gQJnH10<#8dHE6Fv0IR`EP_t$PBq$XI z&{U(KMGIzoojetDU2%!;YgCI_p-<9gCL zvfk0Y+f1>mWgP1|*?d3<*ho(X<13kkb^MC?HRpf=!@kA4|Iq1Ps@9%J+T^F8lf&|J-8+WfuW* zJJ1Cx@McQW*Zgd+iphQ8s?p{O7|}Qa-f4C!H_5w}0}A0FDvS!?7R0*<5crNKKm{FG zK_oDk2vkcnqh8uGOJ>?8%>jgP0ID;XCPo!e%;p*mF>A*f4KQLB&DM<0Z7uIQ5*R>{ zg*x7DKM0{@$8~1z&KsW&?g104&KPZi0D(bEBnAMCy+~RPOwmRrW~XB798bg8Db3MN~dXY+vQi2W2ysIve|!e`>|g?JoMz#{%Y63 zp+?>6?QuD#b=%PQ`tb6+xwhpLbwIXSVqQn(+M4dTeRx0PPd~o!nJ3H7xLYqzG*1M8 z03AS#+?N@;2tqS`W;$BF@n zp>b1SP&)()!iq|K{l1Gvzzl-pfPmKikn>#!qK>LcfVm+U-HckK1`0J)+X)G#jxMN( zcouL`2_@l%thNXO=NlP9@Zw7Z&6`)j3J9|x-P`T~06|3_sDhFpU~(u365FSBZFX%3 z5TQk6r((qH>e}6YESwt%P(^Fe)EEkKy~-~4h9r5thHDt1kfH5f0>yUS1E<~ya|pyB zRp#d2Rf3QQ;hh%)eXyXa#(|tLrW7@blm056ls#`miDp*j724b0wSwT!m2(ww{ z%518-vuB!3G&n}dHY<%rhG9kZ=Ird<6_G_WO*Mx$x6{s(Eza78kWAf_knnhX==0q3 z3bzXdw1$Zobb9UwJiVr0JysPp+vhW?Ap*$O)x>RfTtq;WWV2WtNV2UTXCHih^~D!k zy~jJ-{hxEX{f#!O75EckUy9ic+3N|EGiK)^QDpSxnfgp)wlvY`#7}Y^r z5gM%$N};p^1Z@%}h$R6`-6+=4=w#}R0GlR$R07c_#6r=I1OjkNv{`GM+H~#1Q;@F7 ztJIk+?!HsCy%W_qLe(Zu9lE<>GVPkos}vPeaBh40f{zph6Hs+;BVTKOo{De~&hK2) zp{uh$+pae6@||1Ph+iL*AuxQs^U7(L8~*r#Ufq@t1JO?_SRXIu&r|g|1U(9ku9+|Z zJ#C!gAViy`+ow^YAEJuqMfuqJZ?W2pCee1)P-xYi#XK?y6rB)(Ln<=5%=4$fs`2J^ zkJh@uXc>*`Ydg5%FmZldBmgJ~EjSeQz!aW)8}D57@fST0UH7UxhtW$y6c9x~S)_uS zLV%x4nCI!Fw6iWTmud(~K~R7ISv6rO4WOYGs+ziv1T@!(0II~5N}>=Tiip7s1On)E z_hr}!Gp0Ms*17fVCu^8nm$Q>mT|fmbBns=JMJ*Q(yJ^OV`n%vI%b5?p{nmdDJwKI$&IN1<2&8d1AZ9;}bswkW$ZFVu3G z>W{ry!|8Mt>8e$_rWe$Nh%9icm%EX!UAI2^dQha?O6G4G3pbyhYwSstvXh>;uC~pZ z9HwsB>1Wq9p0hUjl24BJ>sV|Y300rh=9H`L!`$dYAFMs|;o=>)>S3ea5G09(5)=qR zGzn-*T_l7=)d(|0TTn_O1a?$f2yt)=NeCpSP?QNV5O3IH4P{I7@l+Zyb}3B|VgOVq zAOs~{G!!XG;lfyvG$;XthkmKj%7*t{Bp#1IaYCD)oSu7D_4)%jw>!IriXfwJWcV(S z<-QY}XfR}(g&<@AeC}`coe--)s-ft@x+nT`R7LborBK&)l>ozT^Qyrf)elR?N6X67 z1TYe>w$bqAw+w3oyow0&j6yPJK_feB<2VOSL(8sVgh^B&|fG9f!qMxnk zomU@RbNk%eyK`)aicH_&nVKXQM{6_A6v4pOjSzrAR?vzzA66W5otT9(ie=~HL8C!N zYD)uij&`USu?R9gyq)H<=8Wr1lIQc%{is_#iCJlYiY6wZz)LS3U1_4=G^-W=ZL*aIKF zvbZ(>ynE$^(@a!BKmZa1#tb3?VG+Hp61tU8Xrc&d)M_gPtE^xN43elCVX*4PO*%Js zD-1|d02n)pG^p5t#zDbY049nSkSHze&?LA@pacL=n)+XlV$=?sN22%h*&IxYy^~7Z z{eW}7cLxEuyD8|Z2$g#mb!FR0BoH+Q;J%AC)pqCG=yLn6n5yeHJ@;nQbCDXN4o{IA zU-#xg44UV<)98y#vB`E|vo_pxqN@aA^CB_6dY}U0-0n_I&%N(`r-tt_=(;9t60ES9=hZqlu`*X-$4vePR zM7?>qN0G62^_*y#ZTDIO!?pvzY?~F$BqT(@PrZ%jLy2O^Db%(3R$c&5+6xDGAof}; z5I{p9FR5x41W?z?Ai}I_YLG=Fb;ps}M#v`fGI~wSb(roxm#RsXO4cUA!>RtUnaQluDH`7!g^5rmA(CwaZYQL8r3Sx^IDqD1~lSHSawg&dYXZ zPwwrGbJ?$o+g5UF`>3bav$E)2AM}nOyav-#9*>79bA$H1f;JsB)#mWVlXpDwU3%kK z)?F*xJU8b|H;_?mb>`Kf*1)|Vdh+I*-kmPn&RbXNgE!y$@&_N^xueHThn^%=&lztH zFvreA+CRQsj%vNw>jj-1$<6GLdZBX8T(K!%MX2U?l{fG31y_MAW;ZF zwu)+k61%Jji4Kwo5+X5b+5z06YJ$}6NKG4}?I5DJg$II33Z>b(P22)eB&d@uXxb_^ z$3I9XdINSDEL5G2jEJ!53q|qwenmtz6uaD%+&{ka+h!%^P5J@^%=-+8@Fl*T8`!9D zqgpi_Y}YlQ3?fm0NY?M(2aqnH(+bt#c2VO!6JoN=;iBy$uKRu>;NoypJU`SEju7Og zfvAvC(;>r8#GlanW3f*$KQbi5LFnY&SceeX#as?k5VeN@$Xz|B@iU_S_Xi3(5!i6v zx%IUBMv!R+DNP8-mSYR1JsKJV-0>RI|U3ny%r6EJrG26=~;a04QpxniVptfixV5 z66ehf3=KeJAY`y=RvTt%RuYtqea&}Yy1_8V%1&_QjkgHu&6=pGE>LGsVN_uhO=wIR z)xyLYvsergbavcLRTD&;oEb$~Dphf#JGZmWS6I6(ENa6Ur?Fp+Q}cL|qFEi7E(!N)NHpTteH= zt(g9C!KgtdDiDdy+|U{aU^N}U&2zi+-RjhERcrtzT>}${KnNx~)Vg)u?ykc_q0O!d zjHY6oB^1f#LD!TCBih9J+`ifjz^C_xT|LL7OOnMA&>=Va*zB+Vc6|MfPBgOM3oI-7%L%dfrl>3}1B)Z0qdIu2)NyYvtTyXBx(wT{~n0K|rF9>piWrwb_!T z72zagYO3jwL=jRaRjOkV#WK4;_HwfA-k2F32IKa~t+&4XnddtneAYfVZ9$|>&TD%p z$NpmRxVhKIr>EtPrFSmo>zjI`Hv8HdV`?RO3&+)$=grjM-K(4BPrdi{{+-!p-z(1S zG6MvN5DU8$8&_Y@R#YmD6KYeY1SqITt1Bm}8-Zd{^l1Z88WaFT(N;jEogff^J7{Ex z*hH2f76=6jI|OwQnC3tki01nrzfrwqR6TV^q|gy4&|!{EF)Y|3WZQLZTrCO&NEaE5 zf(M;n!9qti7xYc;$zxO znW1<0I6ALdfsKp;rrcrryIEyRu!rSX$=)$!CXOoIM$EHnHPD!CGceoO*AJ@??)N=o zJ3lNumRyX7o_Y5R@A~kzzK_+mZN?#c&doW{)VLHw-MO4!JKfy9yWX*zbL;uqcG`5=~UwSoKDTTz0KYIRq_7!R&TvO`;1#GcP<481tJt`5CKu?7Ar!FbVY9y6Ga2U z4nhE01t=ZWcHMv@-{i?h5E~_h5J0tw6#<~MRY_q83QDJ=5}OXHCQ54s_*^UI^IkcuHSYXv)HUUIUVj6q%J zFt5zaXBXqNOHa;*j9BDQq*KZA2OfQ|){N8PVt=))!5Z%INL>!a_Sa}`M_RKTYEg=5 zbz#ay*7vrqCIS_VyOfLWGjkR5OedUCpt?{=)ob)VdA0DBcb4dtorqcNj{83E%@^PH z!7V;|ch~NY$%ZzUCU*=q(&##OkB8?^m%A48zz&0HzBX@SS-aR5!@xxpj@r1k&eVbf zXSns!$D8l{VEnv?i|2Rgp)`0s`==2Zc2QA8SWy9hgmj{$#UVO^ISr^EOV(;^fkm=-nZ&-N>(ziVU zkaDA#etXl2D?AKNlEdZ!MJ7NBu-Qc&>#WXc!Jv6D0^iRn1n-D}D8l_puj|@Q2sjz{ zuGKW4<6^^TI*z;;#-!SANV>$duRE`HiJ#kac*`V$;~wpl+JqHA)7TV5!YRn6m}1qq zT3x>Z=`7?BAJ0S zIigkoX+^Xl3Mz`d4J7+4+R@Mqpn%%KXAt%L!Ta(g8dRy`>aEA}tR7h@2{C0xrF10` zq2JMY497?6^T55;7712a z*p^PNuGiWmu3ygB*_}JZ<$ew8K*dvMw=L3;Zn{v^GP!Va|HB^=i*6>PZn|d2cDF8z zp<-Ths7yK1%B~~FudiO6U3D&772~#pvHJ_&Tz}VlyH}%dS|O&s!gg!6x2Q3ryQsUz z_4(~`*SPPU+1|T2oS(!&v`g?{vdZB4|Bs@LxB6$*)_bao$QZP+(3y>$Qr1fd3iBbrx> z22^c2G&%*9%hlK3O}iY0m_`EtB)ikiDgoCu@U_3y(YeR@29TIH5!1dlx;74tZxtZd z#^hX|_}Ful)SyQDw)aidMu!sTw$btRjdTp6iU9FeDg>)}sD`+{?)!-#eFC3f$EZK$ z3ZLaPtzK_f2o>Z&IQg|zPH0U$cJ67Hc$b7$16}v$&Xqle!T|uWd2&;@;JO$NkqXr2 zZC4vyO=)5XU&Zc=&bLM#(g-Gt=Q)BQt7t=4dj+EEAn*(M1sz z(4dK!Wj-$yla-)eqMExiPIUzm&8E6cG3OPT?aX{`mUKMV>(qV8*$F{e&iDOg-&PaR z-2?YZT26KE7d*PS*PF}Txy$IlptF>#uKp*tDt?5VLA?c6a9n#8x@&npH@6D%`mR zr>Ut>fSO_TexKVVpQ|r%|vDkk9Ba z-}RAvW`y#7qKr=>dcJqPJu9+j$@YBpzuR1 z`q@0+r4!Y!yj}vP+ns*L{ru~N5`&rqdw1>^P8I4#V^+s@N^}hb5I~$Z4-9MUDPN1d z`)-J)(^@SE3T_Cixg8)ZIC2jn%3-YMaZ+a}`s&3o51qc8`p|o4&z#LZ zmCT|Ewm=-OCOCaF{aL_^DDP`Ux{no^)}Ly0mK2oMFV9d$VwRHCHsf-;AqY@F}V0Rhm&EOmeE&v0mZZj*;3DyHX~!vGyx_4=^dysvrZ z+T-f^npctGxz4vcr$m8=JXQ3K_=f-^Qk#)ff@}vWg#(szX#nj$i)vB{()DhCbLcj2 z+P%jg*R>&r2y}qK_q(Dm^FBJTd8imH*rNfno1EWQ05Exl{DE+MYjxVZ_?0FTnmIP@ zw%r8Q<_)ITuiotR#sSj3%N6j~>{$ScbxkLx)(1iN2_fjV9YDhBiwq}sUj@20UuYQ# z)NM~PL1a5P0@}I_D6#K9+#h&3)_wiW?z8A?2F2djrG?YQ)vT%@cR+HDs)$9CbLiG3 zsHVX&*BC_3sGE#HRofibUj~AQtrCg^C4LuX;C+z7AI@)hcY`&U+uK zXs^+Rs(@$+YgkIv9ipA!f5Vm#*a=$;I{F@o|`zG8mnm?_^D*>NqKDCXQS6 zu&17uS86QIht}RE3xZwQDq7};+^#0f0^8VrD%huwg zt+cXU>dT?w*d0g9T@n`KzTNq??PjXwfN#oSwB>id4Fg5F!S`T*aDn zVnPv6#Uh7NNl~|-}NQC-Cb}e z*giJ`QMYNgJMf)*1BK?js;#GiMPKhpt9?I;K{sbaZSDwy>qR))y=mxq?v3F1YCshgjkz)aI9&V!D!1t*P#W2;-Udqu z*{Jref^fFc4J!bh3;1!R)nd~LUHXj1QO}91+ovg7?p+;U>dOoLWdAUyOt&Gk zedJDG52!7a+CHDLL?FmZjn-K+axm42(=Xw6r3pS00e@R2C7IP2o(XLiKHnrB5f74 z0%Evf4JRH;71<^F6zOhdS(BgiS4Hy(z9nwiU|u zx?qeqFH^CCFog3yTXOgF^=-EK?!2$tcJpc#;DdJ;J*R<|FnVV z2&@C36$An>B8rs)sHj--MDXsLR`r2YwL1`j94xbsi3GNShl8P23ND~dRoWF`|@;+^)a?h?{+7uL#Z8QR*seNax+L+Br{B`fI?*SwhM$HgsG$pLJ*?1N)fam$;9f^=?zC>Dui3}e4Dw! z0qvR&s;U@vzvDK$3088KZ+Ljui*-%GY3rgp&o$kshHpZA4zd}+00y<|okll0V$QqE zfJlz-;@zDByxRN*QB@64aE{{oRi_D2P_7d4tGD=mUo`{6yQ7#6(q-4))lP4iIi~Z@ zjcgK!vC&}@9@BJBfE5WiH6#((`>HPJ+}MQJ)1yjoj?HfKrr`Q@tFCvbV@kSY@zwSW zRwxb_QyQD1$Xs7-vo`Gn6q>^>DnLQgY8r2R;!4RQ-eJ#r&{im zo*xwwYgeTv1cc%i<0KhOz0ouKp^sLzY%MmqOze-=a)Xm4TiH^q58b2dnal;-ALuWu zHBgpTnkL^6%_+)V-umDtqq)~p>%BJHsL!9YF6_GdC!L)QtHn;L?15SVv`CfG5TH4I zivfc0Q0IH6uI}gSd)w9PAa9o42A~BtwKx#!h?vUG6 zcHp%(R7G7cJa)cq(qdlMM7*05?AntYLgT|EDd2FE*bt+^r0_&)N+x{)`WOXe01%Rg zNK4UUZoF~zV1J9hk)1ztoZt6&Pr&(hch&QXx(I*;aUgY7TO)ps0vc+tXn3a5tsx*^ z0JNxUN6ijR)za{+>RPo!03%H;GXS8?+2hylbB_qI}e7`y6ZCnwRx-#AcL|@(}G>Csk={o*4=73G7H-G#^HXd znJq1tm0K_-5CnitBu2N3T?thRLQ7eijxWqvTP`c(Mt#LqhFUdDPeWu)s&I@VDkhz( zQV7Z*+oqei|1fv(Xxmg|BCJF?_Bimg^ZI#jw(tIEX_bj$@2Ynel~T{0l&U4dY0)hu zgoi;)mr9$-um}kR6cB++v{i~Z3`l=>fkbb>Q*V~Dzx%C~saE$+xaGWa)A{tyw!Nry zrHbAJMJoa$RD&^7VKEO7F~8`AdaG#{+$g9LOk6z;VzAmX5CTWPzRfF=bDQESa(A_j zVqRCmE8di-Jw?m`&^%O~i;#2g?g&C(_wIhQjfXnFKUYAj5~NxtAetIoQBIIkV44lh zDta00n+$9gK#+4=3X0ZvL?QA5kFrCGcdL^F_&WPDCn(^t`$Vv76HF19OvuI*aKmb2 zFr|ZGg&THn`v#k;x*nVfvm2=2y;C6au9_DSrlNVZp@_t^-gXt8L};_$S%Z18?Fu<~ z#CksV=Vh-8cQUO8_Z=kaol?b~fCdN_aim%(pqkYYRe@}31}j8?u?GSiAUiY$%_6QC zgsLHp?U@ETst^Q@&RjftUtb5zm}_c`^AQ6>GIcUULugb9AOcbsENY~nYBDBah{=Eo zOrXlfS@ouqvn6hai6GE}ZFC%l{-bnr*F?a-PMsZVA8+LW*5XYocAFPPbXKK*~iQ`oQHd zQ_&Jd(Mm8U<8sS$&-3|jl=pnHFi>mIz1Xv38Jp-NA=aIie8h(rr{!d2Utc?mvDT|i zw+DK$Y#oYQnbaPURuobq5L(jzUehE65Omu4))P(i%v+<`rZUh|LH>4r*RQ zL8ug&MGec08mx0#yvq>+2-=HTRnpO9M%20_YAJBqAqZ+kG^t?f#&GuB=j9$m%`HRh z69Bl^gT<(#qz=er)uf$O-H1Ygx`HyzYiNp^C*F6T>@2gI=d?SntL|c3)^s9FVJhf57l$+R@>1U}*E-)Uh7>KO>@d6Pa;y*vRxF5+ z)Bs3RXr2(F6-7&{R1yuiBO(PDw?Gt|d*!)a{HZC~=tR+AMWw0@RM#oD63*{#f{s2P zzW4nLn|^ceZs(bIwx8dyvr83yT0o+j0I0NTIt5on-wcS|NDO&Mj(Y$W= z37rH@&$S6A#bR2UN3pv*w;IwSx>;@P-GgzxoP2O@13+@y-RTZVNJPBtXAlFM#aHig z;Czo_z&I97GhX#MRsQ1j{v~jd#I}O_DDRF=wTCLk&6C1eJer9K*t-`LddaZCWU@o< z;Q*VkFQenlfexVBc0w)&MZGBiOGK&48xAYLBGPdOt>E6*JqH8@n7|si;Y#jN3_uR!g?CJywH5$_z6MHe~NJn_Y)v2f^A;a9ZjTV#J+TDL7L755gjVkO72t+=-p zi|Epp%vyDao&FdTmBnHl`q+nCU-Y@&&W@;v5uF}UJ@#_s*%#Fd-z+})@#3`i2#HsDs!kP?E+PZwxYrWnM#ya?5ISDfOZt4w`hXJ#G7)t>^ZmnKwjSd z>F>U^T~_Y8w{*v8`^+w@8~}uc6*~}f*|74G49Uj z^zMGZ`F<;~In6qzL2-9g5lI*!pv0yFR8z&|!;(&ec{N}TRj?h3L0YJ31WZVVXtI6P zh7c9{xt`e%#^(c;f2E`*VxSuoebwGYMKTxQlguOP-V_Hw7b~U!&8x_!K>y0KrlK z)P!3KXjM~Fk%|HUQLRjsk(BtfbD6D-T8^o1)^tV`EX(Y0 zWYk0=$eO!(ozgn&S(uvRvk0xmFKUx5t(L0K4#qC$Zh!EuCmy4;VvsE{uaTrKWwMoN zDQ(xa%8rq?pa^GQ9j|@z>Ash~St~*zilP;m6XnRWZ^iQ-@rjQXcZN02XxW`Epl(Z) zk)*7vC!)8=yO#S`=5dx~&T~e#M3|CUK;ddz3ba=U%Ws6 ztn=Q%X`Lbh*#ZG*?})Td)vgA_rN{f8=hy((Q`N=m7mxY|zJCtZdkF=vj_9xLX4ixY@n~g?JqxiZu2#d@sUdu$a0SG3Q2@2wxoS%%`El_-M90t6 zieKml%>x1ya>5+1F$guK+IwuouzCWBicJAvg%o9@bGse@Q=}*n@A2#!zV?a9yl%tj z+$1^-1>4WLcfRfvo71jAY}hdsYKl}gFZO(rQO9szS8W;qIJ2Q$%U!@Jg-Xl~A|p{q8D@7MXZFSf!=@OC zG$zE(G)f(>wxnyT0${y*?91(T_kQ%bE6W~FEr3cPHW47L5Nsh(@({yapqcI>%#=`o zk+x6H)!kAfM@jm?Md7o;n5!Ud7*E*AIoi2jsR4v3|Q~uk+-yy(HTGT@fW}UkIT!%F+H6Zk)qesDHKQDMPKKf%{}X*bkxet z^Sv+s=c8}`aQMOR7#fWW9qpa!YoGjye&8?iv;T4V#xHul&F9te(D~AOsN1y}r<^j& zg>OB0zkmPu4Ig^j<1Q8*g_LekCnXh>)9o9f-?uEIsO37EO;JV`gcxbeO~<8971Bwt z7-)E6mdcuLZfS%-S&&d9U;$)Q*N$cpMq=y>puRn?Jet4!#&56v_TPQ-($DvQ(x+oJ<^R%7d5$w`dt-7AI%%T*rYdOn_Uvl>#FXK zO}2Gx+;^vZ?GeqZ$n>SRZWhyGIz@v;@YWch!1G@TK;9c<2?8g^s~~W$4HMjVqHDU0 zIW%##nv!#yCgdl7Hg8&+Y(`g4tBre! z#1lE3OuEK(yB_!zTpV<5vIHFf@8%%5P@|NBgc~lf=gcmMGz7&SgJnS5J>v#IB*D@+ zyWS9AgFwP)U;x46c|ZB~mzPh?+3hi#Cv$`lti!e%tjWNlfYtag+$?rAg_x5t-k+U1 z(|uWW*8$=vd*mm}7m3yYVv{#>nmOpS?>}BU&dv5C^PH&<#zSTsL2tV{Rw^vJ<87N6 z$I@NkQGU73uj0|(!Sg*|t9}3Y_@*D%FVBN{yz-#P4(yp{no8{0s+vN>ac8f4e|)vK zzZoEobNK=DpZ&|_fA+tOHGl}vfY{OOmU-m|e#)nQ6@T$>>qpN&@5k@0uTxInLW~78 z%+8iw=#{(ozqr2t4WAyZxAnaGw4h?$6re?sezt%LAnl}dI}o85supZ|OdhIardG2$ zmq3oh4$Xy$bFos4gW`4FuJ6QF{d3Q{nwRRq}PiRyA-&uo8j z{mFid$OM3DvIjgp!N?|hA(Lr^#ILI15Yh*P`Z$(f%>3LQ3+P7ioZ~;#7!O_XzLAOQ zm51x^Y$x9EN~>|za5nDI6hy1XT3vgT5!0?gk-5CN2h%~V< zEHQL<1vP|pNnv5W|J|?O`qpn{k|_n4EkS_>C?yoe0BEp|Gf3)Ytpiz-qvXb%MHK+O z9Wn6Pb`IXXHF(`Y*RQv~^t(4c@r%h(Hyu0by4nFo&#OQ1^Z5sVGyL_xar^eU=ao>g#L_GsO;CND zGYhvn`oa4Ct9ko9pSsNA`n5k?&MRV>#VKq@9TmzpO^U>zvbT^Vb0GHS8cnF$z{ZeE zH$;FnJ&ewuE`<@3PF{!>zWqsfIDORP>=hOij|L6^0wEBj09Rpbv!faeyU3j#FaVuMhIfE-8=f`~U8Q6| zDx+(#KEPgliu?fAUrY*|c6YTmYY34H$+Wuft3gnK>)8?l~yfyl!ghAYGf|fKi>; zP7HXpUy;PH8HjQ2CJ3T|KtqsYCYB1=q7X*NH5)p*0TvUG13;uo85hUx?6Tay@xw>& zk7lkTEi95OL0|+7RUn%Xl)FI+QBWY@?DG=L&`Q`C>UQY3&3u|<80?w7ohm!}$;dD; z^YsfzwA8s$q|K|K`bC`)YpIb z7oYy^9~OW7PbbfPJe~H}Bq0)Mr_rtPg41`G>E-qQi+ukzxw*~4JoRO=|GUZ76xb=~PUp?be;1+dO9M=(b(wlfw^Zr!A+B>z%ql#ZMKs_ZKWr1Dg+jp{jW` zry-(=nkRn*qQ9h4MTf3=*~@3XkK)aazH8q7`CQR7>n?hWaF9(v}F)L zPR@h5QR_^bk`l^s${2du!fCN{M|`+_cFt#!P-XI@5zJjaA2-%Ml7~LNpnzaKjL4`Y z1a42~Jl@rj7|7%%zf=FiGhdTmw0FBxtT~R?IG_!t4l^X&@{{`7ym_49wdz5Bopfwf0-jRL08dHqvA%b)vi z+dul(kDv9O2d8?v*^ZT@^hHC4ZRT`b&#t^bUVpxK|NQ;DjeV#^>f@q!m=$h3XFN)# zqvFP-SZt;eMNx#&2u3>skqZzL2y}Dn@hHGj#b6Cq1P}qQ0VpC(C;_BtLLBH)Age(Q z*xr5f#-lISe)pgLw}1P8_!Iy5zYGz*@6@>siFka>Z)|}NiL?ox5jQM0Xh}dCfXX!} z1OpWDwyzu6+>ie1;qrkBlFi74aBd*ojNYz5vtgs~oZ0F73p~$wYgd?_2Fe2XgA=$FqM6#g#`X1m> zQ6%ZP6rgDJQ*Pv!n$)YsY4Zjzso?%xH&0iz7*?oH78BW+{rH{H*CV-B#xM$N*rp(W zF%lRFq1IKGP=;buF@+=h>g~s?Hy5=E<=Bo(V%c6jI12zUCBh&*hA<1+Mgc;6S`arK z*-=vWiF3?wxifbUyHL&viS$|Aci-s4-WStX)UFP(y83)Oj=~=2?&&-}KRP2A6mpBd zar_^=%uh4_qSucBXJHm{k7bY6VKXi+e(|wi{`D{a)9j;YH+zyf8jQ;&~nbSUu@YwXW@I8(o{M+xQF&Tn}wmbwQwH zy?IkI=&$t`^C#yYOKlf`H+1(YB@;DuAizT)BGazXa1uM0oMJdKzv$dH8SR2U)}*vH;Q(cnNZk5z;YyKQa~Qy>It?2dDc;m&nu*KwEY*dwsp@)wG( z0ZYK)%_9KO!-n8od-ioD=fcgf3OAje+ql9UCEF$Y3;Vk7PVWQ~0_+`|gd@cgpfRO2 zk~p?TzzL>Yj{>5*v{?q@KcONv@jGEO5 zLgqwYqjUJtpTGT?zxeI{{2xDi<@@!+Z^~?(X-O+=x9_wjrYh&_(a&A)277(n^AxY3cJq1eK3=}m61E|8nD$C|IYWj4C>zZVxrwZIgERws%2avSysU#XJIdpqOl6U=#3Wo70MA9$ipSv?@6nl`-jNBg~#&E-EV$`OnLa-nO3pSF-4@t1;52js15`EYQUwZz#;s847nii^y zq||dyB!|Vc}l`&&x2ATFUgsl|;?xNB@fUKMz zdVztBVNuf1nug=@yRIeOZ=t)lno~Dyt=@;U~^aZk=dYVCA+(VX!G^> z`urnb+yC)z{Pc(RFTeBl?Z>cEkrqtg0y~@9A#}-e_n({JJ^tBWeEav?PPa7Ye0IHt z<$_&zPvhQRF7B+G^`URKE?e{HuP-l^$2a|Y`)l97@`;~KW7EPcmO>XK*0TJ-PrmuD z{pIKX(?9&=#VyAhi`tDs8P3Ky9=hc0xXy8AIlFFa`r-QF>*M3+=b7_doq3fKLk*ie z%G{vNO1CUSgQ7dE7E~~%K#@zv7-E*l)@xWtEb}_O_i-L4FMg|6K6Ovt`nxk^xC~3R z%+Qj_^4>4~=H6fZuKwZgxNE%qMn+@W^uw3);cU(YkDk}fyZd&&{B7U+>-bN9!+-a` z{9pgg|Ni?Q{_FGSw)1s(cOMV}J_kVc>ia(NIqx?yA||Gbm1Yp&(9>RC-DkGkyBN;2 zUB9j_yUNoACe`p=-#U`09`C-(4%l!JJlCEZ)kYwv!5oy6ifqB)km)dR3W&Q8auAu5 z0;=uMbi*^NuBxZ*>eG4NYZmWUnHjAReIr9sZMX;#l+15Ysd-(n%ZV;1h?@lB%IQ_t z52$)-_-INn0*FA<%NI@1c3Y%l+J($YkSTpaH-XhrZGu@e55=aS8{H06BU7o3%em1> zF+~ar^z2`^RGJL{VS}q@vMEYSyL0B=(<4if90`JqAuJ3o4iidR5HxLL3MGJT4z-f* zKF+bbPQ&qfpY7hZHxF{k^W{`i3=EY3G)}qg%mbUF5yAnY1~~KjS$ftiJM-qbD>+lf zk#FGjpnQ)4u55KG_sb4O>oJbb>#dY)Y#{r&EBhwL@J{xF~K_Ix!@j_1JI5=t*vb@a#r zg37F<*kA(;lNSA%LPT0sl{hAPr+2NsHPQ|p0EAT*@fQSpoz`%4S@3v0YJM=3uAI_Q&Axt4g#@M z5Q1vh$DZeFa`o%t4)6O}L?8kVM5eVVz{G`wNH^xTK(#X$SUqdh_|uQ`ehtm4gV!NTno```MLU!H#Uhkwma@57Vk z^)p}Hzw`@V9xoRbZa3^wCt}AsdS06QD`$dBulhm0`|PaT;O^(s?t^WtK+f1 z-?@3L?f&uDaB^RJwAYyUeqOi#_789T_^&1pLIX+{Gy-6>TvOZS_kVNx6aVGSKmCWp z=Rdj-Xf?*v!_e*SU*mD6ZYksKd3?;G9Lz0?TbK8F_~QInCKP*DprT!2vpN<@^OP0DkrI3R4u>;fM7FN z80h?NvjCGCu0jA6@U**&x>Y$)P0!clT$_M+^^?yjsqW)^9V44xJ;zNRRUqn`N7$7( zINj_v53=G(j8O!d=w`bDf@)gbr%P>sZkXH<6#!GnCNkPPG+B_328&;paH=*y*Ux=_ zz3G-96eW@FzWch3n}P`(Mim%V+lkRRHih6`;vIa{A>GDhU@FD58pfuuYY-Vg$WQ;| zAEfCO2noT0L{NhR0)Wru2$Z&0a-X-m+v`Y*T`YSkC<`NbOPldt6eN(Cr&=f#7!{@exke3z&tz2)HAyuSAAa{`qeVR#ZUJjt1pAVl`Xq+aLb)!H0hJ^MClQ9(>cC?}-UB zBepAj>CZbI&)HRQ-}{?C`@j94|G$61KNYUG*X-H7uey>9 zyTG}4QyT3ds@Z9BdTLS?(?g|Tj?;M4R|RHTlhYa$li;yX&ckE#y2NH8k`7=Oph9Xss$oo_vWb6TRt-H|BWL?pH;0tkRma2jfq zo%zm?)8Weut~NL4oo~O?kpVWW=1mctbbPL+?8h_t5(G(Y9#I67$fny=>Ux;Eezh8W zoNRYTR0OiC=bPK~L;%1>MP07#Bs7N(s_srqz|d)Ig6VU6h;HE3w0~zNdCSxG9)b=^ zTnrZA-KTyhG~vBP$x3Yr2u!CNgEZzOR5R`-Zlvw&m;3zbIVTfYMxI^Qxg%nGaoUI* zP#6v+C6TTaL0Ik#b-%l3TBv54J!7&eO=G{;K>Z9NL*T3-QAM2x!aeYy$K&sI#Lw~-ij_XXFa0fa) z9?ChoeqPFI-S1vLKVg1%|2E$|_p-fP$1EtMbEBC7@!$#6^`1SucbwtW^_*Vr0q;07A~FJ9F0W|E~P=|JlFxkJq36@yUa4H%c>7S!yzZ`}&)C z|JPsjKmE7=$3OXx{rN|K`7^i*tF8&|4&4w<;QhG{#JhTUk9>*SJPl$1&O7}MU4#-; zd^l--yf7~~G^}Zzw>AB|N5`~_TT(tK6;%zx4U{K zWUQ|}`TM^Am;d^oej5jKF#sy)b_!a<73cnb#Utk-C@5b&y+_{-`lTweEG24@B4E*_F(nXhhJ{bS&o< zYR9|u)K1siNe{ZePtU#g{+}KG&@V4P_w>SUOad9v(zt?&fQ1Ga98pIERS}kXrDyqp zZ}rdq+wiM@cbOkg*2jyOOx4(HsM4Un;IH_H{_+?8e}DUz{-6J;fA&9gb-fu@UnJYQ zbk=>6Ldn=rgW%#D7sGT~P4BJ(Vx#;dlRd7NR}J<5&JgM<^=Mu^EfdI5w+__ob+A`#De>c7RDusJrU|^J9HD2<>%u=VAi@ z978c3Y*&ksK?TSZplU#$HhoZS*9D1ufkDFVt^(lBavqt-=1I_{wyT|MGLB8F!6*W3 zoXdd_1XDKbR!jtVrK@V=HnyuhhXxxKQ~7I@CBv@jkNqC%ZVm*#XYHh}a)>W0rUe27 zV7oIRW;A%083}-hEAHGt3FdtMwXZK^>?0{n3xRFav3fXA z>m0l5QU^AKksA#3gkA7R0f3I&cbI=fv-H`H=9`;_TgVKc|Yg`mBuhF5QU`d5E=>0kJZ$pO`j zh`@kYKme$+e)ROu{@0`5`}^w;Pp(tST)ecp#888oGIe{aY&|_Y?7KOhkJedtS6#`= zeEG%AkDp#2cjj$xy*eCu_$UUf_HF#o&o6!KyTd0>2rKi>f(}BkLIwb$*?|mA z4xXCTsxCoD3D#xj{h!8v@?Up<=kE^t`S|AJ#^J0+waMb^>udKt|I}ahfBj$n=CAv= z{ENxmMJi#fK}Ff-VS{mxhYk>S-;u69u3rvJ05+Khazr+Myozs8m~`7dWx9Cmg`M6H zct*RbyGz~=&iOu?sW70L+!+v22}s_?et*7hV2F(Bda5g#oZGyq8gveFmH6dD3hcns zcLP9#wwne85CA+sQD0UK_}J8@K{wYs0tu24I03au#Ks{~(8ZgrDJkl0-|LO$;l$2u zN&-SFFqv1|fb`H0hsE1b-ZPas$)KVdjNtj+=5_@j6(pIM7Dx=MO{~CC7yuz)4uD8R zsHL)8kw$=l0ItBuRDkv|ge=e~+c_XCaAidX2n1+EUw{1Ze3yb|ciX2obKvkSSDQKu zq^1bqz9b;+G7qz17MUmPH~}CX8P9FDyR75APxHz#)WzM7=Be)E=kaX%8Z>8}!Gn|Ms8$?aqS_abcJDGagfSV2C2Yes8wSV{!dLLmTqGyZGuq{Qm5X z&x@D7VR8Tgkr9LeKm@GA^;bXK{gJFvR9J~FN8Q+) zYbe&(Yq0}pq6-4;3xWkm);5xYGMDB4Yu`Qjx&Lr8ZxKmB+9 z_>2FKzxDez!{O#>^u$+ni z6otcXdzKiE4)2a>avCWdC?=BOYg>MvgX+`nz_KAw6e&1rry zhu{7LJLAv%&42o%e{5fW_b;ehH4TQ@RoO{`)< zh#-W4F0eTdGzEeJ#Yiu(>=p z01g9yOa)?kBYeO$cyd7$+!Pfs(Bg{GCHoyyo5IaR9pvt!>gwh>ra%n#jP342U-Zd7 z{W%Q0IC88W7^0 zYBIFXJbsdi-RWS6v2h;s&7-e;7#@QOI4rKj35z)ZvP}~1IqoG^ccg+O1yC0(Xih2J z#bGr8PyNh(W}j^*R0p>7aT&F%-rDa>|M9=^`#IUR%^d}rSBi-;fFK->`%Y_>;L%n-OgpTcC(#zm_@W6<^H`M51rRe z?a)OXV{S%DTpq7}`;TXDeO*5H27#a*Oe|!gSpXGHbIz?F{psD`|M%~{@yGe$hq!-D zo;V{&$#lbbIExX9Rx{a7*RxNneb3e`_AX~j9FIr1@7=way*uae|M*X*AN;1i^2uXf zw|nEu;_V;y-}zzB*W%Xh^3XbER;FsO+W^NR++H60c%}3Wd zb$5<>Z0MSYx-Q?&+5}v`8nmGE?z%;DotvlMZ+;elJ|$q$ko}^R85cvbdAQidSNnsK z=2D267@%r6ytCUpAvcb_Qy&6_Bq}zQ)oFKk8emQa6@-xTBaob$BCy%FN!Prg0#js8 z?(STGyg6Vj$bELG=~dB}0pMse7-u*>jQ{U&K%!bKT1Jt0Yv{a}+^&it8AikpYFrNX z1-fnLjAVxb^=Q3difGPh2MJpKs)R9(rh*9y0!Ji*>7-*i4;2(#Jr@CyskM$kHBykb z6aWD-$6dF}Ac-IXWDD3{wKWikJ`}=EF>NC(n7X}sx6h{Aa-IciXEmgz&tIAil1yPq zQGh2p_KsN0Y^2@|skURt^l)!ZVYTfH3TwMFrwZ5^OHb{y_&_M~5CNFsabWjgp56V+ zAMJnPZ~g9H{!_kqV>vcqNZfhNE>e&U5O&V%n{VGgeg6K3zWXoOcG!|Ij~r`g)gXJIO#PGXnOK8SC+STkhA= z7Q3PKvvC+i=f=^GUi`Yf=dC~g=O=H!3hm#z_xyAH{99R;^Toal0E70jWfo9-_iz5u zoAW%5AANfI+E3;Wek_mPbc{Vl_2p$WO+|n0D`S5mI;8mjNcQYZ_J9eV=9y$hmFXj0J)qbJ!3dApj`HwgI@f z_*l|QxDNx!;4I^GbAZIzAAkMp$9JS@l8_m7&FLpEH#C1FaLFf2^8u)to;raW&^ZZZ#>hJ&6pYY;B z&W1CAqt2#)2d~=uF-=amb@u=L#OJ9-FA<`4Js=c~9x4!Y8+wXnto_vDLI8q?B)>Ne<#g1io_#A%z zulSvRe#@`Z<=Jy*rJ!_5>4t9HE(?g1Nw8-3?D8nGbKdp&8rKNa`n_5cp}Tv$`r;$& z`EfSaJ-hbB>+d_XyttDV$xfVsnAg!{PrZ9H#o5hwzyIc6|MT1b^WWWj{?p?R|D5mp zW_bL>K_G&oHD>ANQmwyn`iZ~3^hbZc%RRliI7drW!Jim^^k+OCh`?mZ_Lttj{QM)0 zGpAAngX+FRv2oRMtA>jQuug2^kM^v*d`NKG-KPkk`>~@o5OCvqv~b~uwX1^>s?NRb z+q(PC4cvFYj^20GR2cW?s}1Sp(9H~}?e^$r-S+3v`vla^r7|f_Tt0yfBl91JAdOh|H_~8{MoTPzKK&4vkeTH>1pm-r^e>posQr4!tEJyRV}2$bS5h8T1QMBt%zr->0I@uHhy;5X9g-MJOmn*NZkMF$4#L zZaj%ix;<&=#Q;f&1zT;L3On$yO~Ob!eNPG8U%xBL|#v2L02Z{{Zd7G zXEi?*+jB~ycm!uOws^QB$T>!=1O+mn8(&O1ON=t zO>q$r$gy?Z5zRr}Y(D$WUw-fW8F+Pk{kR`L-%p-KXzJ6C?wLY@mI9eG^(fuEoK%0Ip8m;cwFKZ35ipV|p}Pa^=sn8eOsufMO7(e(q> z-t|$tyYu~0=(D!jF+-r*M5yqKKSgq$G3=T=px>!C1J?bB;39%=z}tf)n?^^9g< z!^t4}`Zg&lULVjd+D?I_kKMO)L}B&v`#VGzMW@E`65b?Y3!ho7R}w^yF>fBWIjQ=_XK zO=t`S(&=aEI!E!AcUO<6>mTU<`^mrgkAC)_yPNU-D=rTgbt@-CU< z>+#qBboG-zojFL;UxMOLoTC=SB%+O%w_D%$>Gz-d%kTX4zjO~il!oK3K%!WBYtHJt z=f`7;{j76h%9s?>hz~Arm!5sz-{n$Zuw)d5QJA&kHM*|feLXn4y*_?DkJCMKNn_2Z zaAqiT<@9yGUk;CZg?qX8(%c*0`}4Pc|NndceZT(thkl(ey~PQjy$;3%ZXWvduP*)G z-~XAv``7>PaYnb(whu@U(SWDsoufnZ&OB6DQHw?;iFS~4O#?voh|R0Xu0CF|^QOt( z@DLFxjNDXoqjI~NF)mW3I6Qh2ux?;p5$71-z#yOjVkHUAZC(VE^EnxqkJiOJrAXI2 z;5=Z8)&(T0Sy&dyRvzN6-~Ho$^`^P!b``mrVgx{n+WMFpZV_bICYt1y4nN*qH&De4 zKc@z~%8vlUf&JXx)#!dy#OVosB&hzc->0^_=!8vamJJzwi__s5Pc@zJL73%!bh zY~VCS(Dk}j=o(;U5(S_e{o<_ype+kQp%6okEI`tt9IaAnX70uyR4RLT^SBF*U71^O z1mrrj5TPxBcILB}uP=RW*E@J#eE9gAZ|C=ae%yI@-kaWEzCKMyS*aU`*^4um#;2wL zwll-V0<}!Tu?}+_63obXbC>XzAQc5<_jon0FW--6cGNg*6kT^b)c+qpd+(W@BpOEc zJTp>O2o-0OnXJsSva(9DN63sLdmdk_va+``;><&bbI#r8cfbGdpS#EF-mmdo-jZuw zg=)vZAr9k%PlR6Mi_#KgFr+^Zy>=3|C>^th4htPAhh*y(|8cj7^;d^mHRR|!mBCvv&sN?bv!9D)LLW30C>9PAWPv;NBo^g%J z)zBSG5bRGS@s3et9PJSoM6NjqP@$I%(RU2!Pop& z0oU;G0?2r!8tW_tpWNTW{$(PT627>nTz*$$f9m$FqX_}kRm4>n9u?mgXsI%q;?&^s z%i(cyu(;W+^Oe>#^w@>sazG%Ouyl@A@c}ko!cPIh85MAKodGr$AXkyCR1SrO<7RQt zb0z=`#KX^+2nBHF&e7>)7!Ln%x;^Okbi?bTG$vxqBHoGv6K-?*SG7*Ea&TGQ+9!^B zp(c$cwI2sbKi6*>F84 zlDJGq<^msL@V)$B5*|%vO$}p13Qp3tVeIM&<7UYDQCvblO(ZKh2?;&>z>Co$Kc9#B zTnve&7ZP{;OVenL#A-FTn{i@SlybvE$bCnQVjyoRLp{2i3pT z^==K=gJEAnH;*H>=~5h(0xRM&O@^u#&Cl8v19fPVMt8G2dw_Oi>x-WBzRxi7OOAnuG)aH(L(JE@kjnkf?cA)T_N6&RBAmSXmPdME> zC}LUzGRoLVs$Iy0I-1JMKLsqn_AdGlz$eoagnt+?b_j0Fq;9m!FMQWr&AGb12U@V! z<;B?_c`NBx<)=m>(>VpxQ>j$;#YN8>yox{@!vz2Bz4{%#_fE-Czd+_`l z$rP7Emy!Fde@FS%@rc6f)+fj!;)!9NkOs{IUyU4{YTD?PjTYpT3gQ+8@fAQL951*( zfz1MUjL+jgz=BhKI5PUoE01kvkLBSAj(wwI1eO6d9I%GzpEpe)68|F9DgG9kz3DOh z6>qv(rJvJlAJ8uGCaBp|(TI*a@@zZmbitF!R^dN}7kR~b?YDcK*aDXwJ$%=j4lFp@ zkFNqT7L7zJTc#I5O3}?eYV}{gnZW<`NWN)KUJb7++}4HOq2pV6lfS%kt2VD%%;lZ( z!ntztkqQQ9i}#ChdLd3l%6vd6t9V2yrBgpfY7se>LDA8e6#o4zNcwqbf-o;*0_e&1 z8lO|@Sd!7*Xrn}TbkD8BO;w@ERSq_~>=;I`8w$7WUx+@YQ8?atBxkN z^@u264JO1TYbO9QAjG$f-1*6BVP0Fp(6Ym;$0@qNBH@*+-7;;!ZI8=99vIlE;qIZg; zpCdd&+hpY1#heurZ8T}Ai{lj+28n@OpP=qRHZP=CyB3{$?IFF4W7V9Ei!^m5l?S@~ z54E@Ke0%C3P$9x38h+QO2Ygiu=EDz6$TNZ6pR34ySH+%W5#)e>$CrZFu1G-)hVY+H zVI~zrO|Uu1g8oG+d3l<~x=O1Q6}{?eQ&_%+MExQ~xgG5QTG-efHr%NK{YDm7Ex;1v zU`|fttO+5I+4jB9Zpgvj(=(@v<|C+69FN4{2t3(7Q0bk^@;@KdLj7K zD38FLscL!h_c0aBOWnzTz854%a>ruc#q)XhUq{d_De~svlay*EE+%8*cc z@|PNt<4(~nqOZ}tQwSKDqB(BOsmc%`(x#muhHxa-_ zUC%-ZfAfIg)04aqiP_Zh?1x+3Xx}+wMC-_O{6qxn&4je7-=f4k;CK=)3=BZ}!GpKV zz~aW3zIh1R5?Da*dj|}extPy*k_W|VPG#R_zOB(f9xzolFx$MX4JUL8ssoM35JKW{ zORJA*Zf98jP<&Q|;Q4qFl&}hXfnH}_N1E8(wGDKeCx5yS z7#`|c=&O;2k+=CLj>X)&#e4mmt%^L>o@Ai|t2+c+^bgAZ&J%WQU`^tsGJ!tF^C|S6 zOLJJT$B^eE6uwP!FSi{>tC1hY>5~uJn-Y)ktNp)H#v(DagNNfJ8uR5s$t3m)(zD?Z zIff2=nK(X`FLFPOEYC(P(cQljHI}AHrt0&xSDrsKLjPZ(%6|LjBvOmFoeN!p$$gT^ z;$O4wfiA0hvDrWP`yW$GbSrdlw`rE_z1H|NHZ814{VGR-;-`G{=Lfx7W$AQ6j}rqD zQy#MHwJDISjKxHy=#Xg)6!S+G+Sdp8w{p?qL*aG_uosLV%Hr;zK5l!lqi4Bb`wF+m<^E;o^Fx6Q>H0D=?uJ z0#q4~dks5tI!l;+Cb1qibV&!jajWH^zROdt0<&LI!!*xh5rkbq!ZMuDNF*fD{tQqi zzdLGFrExg_SdeNuvGMJNj}q_=u1aJ@oFaq3-abX&EM|FVZgf<}xjlMiFe>V|^>#uX zz|R(4>81|J4?-H!Cp8}g?lH?&i8W>tel0f(5@ut-1VM1vv2ot{^B{NqriU+O`hySN zwyT68@qagDa!I58L&K@D*@JlEHjzebfj#)B)-I*|!If6R7($gWH&2xLwTEGb2l<2f z4C)fC-AAXo%S`13EnQ>NhdKl!f)sZToJ$m z6NnSz>lzsR8ihd3KrV5|P~y#_0!}C*f{Y2Uz>g)MKxyh|WZ;h>t~^VM4Aq^;bWl@} z#0ed|)AZNfdv#p8nUmif6h$@y%P6T?(F02B_8@uVF&Vf)&nOGFE^e>q;89X4OfrrP zq9FA(sRG2k^xK_XoG0%~8RcVH{!3-*ZiaL*k!9J z6ju-@(sp;;i!_L0J=&ww+pMJJKv>_ymH95iGhtEcC|Tw=kA}n+4CU1@ z5#vFnExt^DIM9>Rnt7Jrj=VR)i`3q+hbjJBB8&hF(5tPYE^mI1zm<-8QfYZEtmK`W zv6D}=$2&|vF(bD`K&z-Lb?Dh8;omR9S!lm>#>HJRyEh+lbwq=1e}A1_qpIVh=6_{L zsQ8D#<0pLJMS|UfIP7BA3K?#jp^N8c?QzF%G7U9k84v)C3A)?!|Bci|$=rsi24*dltGDtB%E%fr3FbF+6S}nRl`w&`y zMJNsd$N+;7ydeU&QdreE}&Xlbg7X@;%J*3S*OU;8k zqj@T+myGgxgt(&qjwcv>@aAnxia1vB`uX#`w~UU;jd^3GCqfW31o|^p_j=BP*ZaAf z8=@w!{h79$GiwPoYPe_eF*!3xpE{OgCIFGxnGtVT&YmECv^L;X&(AZb{OK;Ch;`|<@EmJ+m&M( zHr%7L`nV?v5(c+HCS}v|q^Kc~{mv({D51|G7`z>4bSG<+l}r5c!FI-R#MxW;Wti)H z@alvIWDSmKFqZVeC=s*knV^?}oFYM7Qs{SUmPz}6dLC07d^2lRWz=}a%Uo`Vk(=1u z%a@t0@;PbDfvu#`{9U{v{+?jM%Ea}auTtRqWzqdgGOh9ZF(KXx_U8*FZ)spKR$|~L;4zyE$^6vV%iMaGh18~qQkApN-Y-RU8 zPX^7r!?#K93heS&B!Ek>XVU+Fsy)NSSfKzx>AK1l}zuPP;O(j$v_y%nAPE5?{eYnztEx+q}06rP( z1g?=xJ?IFal~50y4Trta;Ab0P&k;JJ2RRWV2Irr}p~EjU8(qTpGI6KJ1gr|MB#GP_ zU|*G73nDN8pP-j;JQApZo}|IC8PM}^fG9U1%x+_fuE7K@;1`6DLDWcIXTYzEF8wfB z^Go`9L;~?gOE`M>b~Sw&DHa(J|6LtFg7Z(J!Cpd2H`j(K?U~ z51KK)@7VdO>EErHPV-~+ER3dj%q%jsgk*iTqfDp2<|NGPyrE#rv^OYnY%lNqC`@x+ zHETaKiSmOTA6b9h!da6crX{*z=d?J4O)APfQig<73sNlepgyW>ykwnsPEvUXsO`Phw|Y z#&}7NFJsItV{Mw8Kb>ytJBTE_4^im0^%0`pp=ZGiW4EE}%ZRH3IH9|!t2Satk5#n* zy>nZ1k3~Ir(E{^uOJecep^=ICR<}})BWr2tMSkhabd#K^!ZebfyJZ;-Z^yfP^N;QLglLgTImO1Xz|5=X7x#WIo^=-CAYFu&WF=qQd)bA%&k`O64Q&OV^cedP zc;W4MJ@_(k2t}!ShoZmd3%;Q%@X#+O28!PrJ17Ti-9 z1WB?|Xs=2PrZCa*@@|vBl+|c>k_iRq7F(12>9ST_IBcOneqA>k+~1}&Mybv_y^_x5 zfj}DFLlE<|R?+S{8=ro?P>`*9#FL4b(5H{t4T%=3@9Rfdc{A4CbDxI^qYpwct5ezY zUK0|UzSPu_c3hPt(>IZmeO(gIW2g&4^MposEa2|zeG(607b2(`qP=)Wxs!iTCWwtY z%8j<1JyF4@#VnMH8cy|!bWllt<9bu+jua(mDV8l@yJ~o;(Sy|nE)luz!l4-#NYC=5 zqnKgpkQ8*w?n9)6IOv5e1Erl^BZ>IzM+?w*7dsaAMWwmmodXDd)~9=`Y!E_b}(%Y<|GFbg7!FIy8Lz z)E1HuzlwNAlzwGFF?#TQ7G&`I#Yd^RM=kuzhw_fr8rd=&@5GvD`PCnJ22OU;W)PJ8N#>{`A!;8VBrh0SMp-j^AY~fHTd!ROE3)dyWh5A|9xYhWU&|Ma+vEpnQZSVh|~ zqUL%Nd`SfWz_K8|0FHCrlnjKP`d|TI6Gr$4N8>YqA^;_ble`84t@v30D|tDLEW!l= zf5F$55NK3AZ=p}jYt5N(#A}_IFa8mg@;*b}q*`-=6Q(m;7!;)Di<@XdiWw{E`PBh%+jIy;-g>3{Y3=P(Uf32;x ze&XOFl{wtX+^${&)<3%Uc_j#fI1VoY-S%S_bXJyh@8TB@C9S{19A4vvN&d~F2m$Aw z3w$bDTT{|0aW_-sps4-RwAN;?333X8Fz|j75`D+5O0t99+1p_5!{(B2uJkg+qI~bu zIdrD_hcEX1U8=YJyi>(1^6EX%sey{K7y4dG$2#p*E|bs;Vm6=JJ*ogc0+;WLu<=EN zGkod9u_Xeows*t|zm%`V7LJ5O>u3!ISL_BkJ{EZI> z=Po|;40WjFx*Yi1d*DNV173mfj|uwkh-uiXMNL3uoNvNgT6!wq+Qsq_ANL%C|BL%} zpVYkC3gY>KYLlm)%!gcNflsy@nXQE$63^+w&=A(o5ae}s%s@{26L7|MUdaPBE#Ra+PP z&7;vTB6ua_XloDyI2?a~;~&9^G7AYwdI`pP81&zI1q}bQbyn|eC&VO| zQb%asZHoL8TVv*M=6p{#QB!$d0mtBiAVAu`lhY<2vva$|^4GR{GWrtc4xAr#_CqW> zpAhnn5qJ!Abtk?CKS}tfaD9((T7;7X6rjjZCCQ!7IZj|hhI6aAqZ#%;4grJ zUxfm6#4m;H1TaM>LBMX&1reZ6Bv$fe7OV(&wS~Oih?-QmPyq>lJjniYw8k(w_ioG6 z0sK3+B58-pfxf=V@y5|21-;I?;{NRc0rVDajDIO*Y<7E*nL|beh22^ilDcA`5SD6) zFpz&8_3Mo2LF)x^{;&AvQ{CSQ*GtMn?`7=HqjoIFXyxgegUV`(cQ$)qR`1>1#Dpz$ z%aO}Y2B6u zGg7rs=x91N>fOJhM(h!_6VQXLwC0x)8{zOv0@lwb;-WxFvWF0YCKz`I<4;Oqup!B* z$u(Zulfc-&woXfp)@>F>0slRWf6(0dq9HVE^Ssb+XRj;rJiq?r@4-iPqSB)qL5yK1 zJP!S9z^2{!L8jMkye?hl1A{-v$Q<;>u^W3xc$4P&S@*BLN*Lt ziXFC~f;~}D){hdJVv`~@$!;52dxTlFYate{jAk?w{#=mo4Y>BX61?VuZXW@iMc@HJ zLKxZzc7RLsfreEy6QaOC5EPFD6W0)caQl+%Sn|3QPoyAk1NdS5D#02Gz_3UH9!*?x zTa4Mly}Q1kt#u8Xnip^1^_5Ptf|Pji9crZelPKO~PEnCl6GW`}Drpw~{d!crSEgk~ z-J#hK#Zz_@&xmT6wXWCmvwix(51xlyqmYSB<}P9zJ9u9fZ5Ft2{z0AmSa1r1;X2&l zDI_gp<-WPbpqyNF*NR-eGfwwU$|p^0pUq+n^3NJW^K{EHb#Hi_9m~^qlOijM%U4?e zCDZ2ya!oV`4ct~0`|rq_ERS3o_jw5=t0?v1CgqNOkT;@zl|MT=@)ziJ$z!6efU=s1 zw*HUh%0MS^_A094K5L#Qk4>20{*XXXEIwNNux`5`Gh<13qkfA2Rj8)2yS=^FVB8~s z4Cv`9Xsu9KVH&V=^rvwem2TYMd-lC@Zf@`CgT7dgg%;z2ikm?juhqnNdcsfAbTNUacYr($2?6RQuN*7^h`$-Tc zo!r`-Jbm)r!MlPm+%P^3$hZ6U)7`#)K8b%wBQQE4>TH=)tz~Txi4%Y!;*hm zicgq1oK?7;CFFZtQBSuCDZg2_VD z5e0zo>-UJT0~zF6S}yS&yi_?>55JlPgQ3@l%ed{WqZHgUOzhKx`Vjw*xY7PPgC6^F zUjq^OFk7cqzccLpf$x6fE3WA!OS~sD5s5fU+d^1jsEXf88p})nKvxz%hKz^#zKb%g zHusRSpX-`6cty=Kt81QH9J=$o(EIR@2>~sw+WD?U#VO*)iMyT0r&7o%{3D+7wdFkD z^~^*Z0^8aa9(wQ#hj>aYL=ZSEh)b5Hnl<+X>AvO0@!p*b&4Lo)=L2U_wgD+Wzn( z!+f zpKq?^&!7o%zPpNILQTgew8C-!)pCK)Flbd(f%*p;=cdyr!n#@QC?G6UB9hzF$loBJ z*q|wxS{X;?V0-%xNsa?bsAT`nr)$}9vGa8BYD-Nujp`A~J^E?O!G8kA3w9A@A=`92 zH}@4=#p#SDY3Yv&A4S!TxvNO!BY1ajrPp<<%m?#&D=;%h=$jTEn?CXe#h2ITWd|Dm zN^`{G7Zy(zL2+u`ptN- ztxBtt>Zx(Z@Y%GZ0mCauCsd`g0>~01;2jr6q@K{k1|4=_6Y=4)Wr>wQxF zvzlgrM9L6E>*hU+BA$ndw2n65d;$X;rm5ftSLp>W>ww;FOE(ts%lkpX^c?pS?dNtptGry(-7mEDR3|Oh0zt3Jp9e za8&6Geq`ZUQMqYYQ@0s{jM#8m>2fDC?_ZD7iT+hMTgnreuM#~p=rU;jv^uHRJ1F|j z$y?fT_bE-WCXve1ataEwcR^3{9m%7#P8dWjjYJpIY3&)KxC0x4 z&rGwx!_H#zXkVg_IofiLHOa|3?dz-Da$Ya^$zWGVxNlxCKTJ??oM>k8r@ebT%TxXA z*H$Wzh5;itllk|*IpKd4^0QoJqGGXDJKn7VaK8GNR^jl&hZ!pA34>l6TuE_1UBInn~ixW8%)&+ zoq3FbZd#`1=HGgzO%gw}vaU~-Z+>H-g=5((;^g%AorsGg=$>SSO}kppjhK7gDc)=( z^kN(=>GpH`?x`IYfB34n*XbK&Trq=bSzt65CISs!TZF2D)sr2cPh4?RDw0(9o%4L< z><`8jOr~ZVae1RDiF?4&G5#3X0p0*Opda!N13#dNj0Pq;IUQO6Wc1o`0hU zhn_MuqYZqzlXV4S7R4f}Q1AYa%{nBWv>7|qYb_5o|DMYk&G>r0EoHaja(6iDto-&< zSfEuizY(30{6uY@Z;yhNUIm{B+9B1jk(aqSSo-#^3&Wbtw=(QC5*CTorB1)+!9zka zW0p1Vl~3&;aVErx#9nNXFUZd3=#V>^l(bB0_aJXC$jNtqWYJ z4JK^)U|wP_r^Y={{3Q23E8F#Uzfp*{ z8Yb<+BH}GbBq2q0=TMc{${3Qwmg<^4DFNLlS8+J1yfNc1;T(3UX`?Y__f47Xo>VoR zyAY_>!%j}cT`1w%eIX7`^O4u@P+KX`e|e!mKfLP*!PS2i7K z-=LjxDK^+W@4ouOz|%JpLpq)7y|Qc&NY(Mt zx5P2 zYzuJ;YhmbLL{U7dXK7?BWoNV`{jG~4a%GA6_ISa3k3jdm`!+#J$I2P~pE`45uK(tW z1m*UpAOCcB3dOMNy+b!pD~M2Aw@tmhyv@JKsFV4AvCQyyZ9i#VjqC#ADE}q0?;PYN{Rk{H}iEv*gq< z@KgN^we)$=gXGxa{ZG5avL?kYpsF8k604<5mSFUJ5cCMKrUEntpS-TKewL&oqWfs* zt4_&0Tt8}KwSq>eY8->pgPsn9uYHI&wKeD#^h%EaA(BBc@zBc}FyX8ljKQ@6Yv4Tu z-V2C>|Np~gyEDBFm;mVba*walhKm+Rfs+64D>(I6TH9u8N9LKK#9;o8-PMQG~7(+A^P(JDCgp=^2(3K~|F~-e?`q6_=EM ztS^&AskGLxW{9f}Sc1X9RNjx=RSQ2U&T?~(%D3xh0$m#6G2SRTz zIALN7SU8_^9zVpa709d$bOyDp5+iA^93~z(J@b=)%%5$;;!=>f*xW75SD_Xk5a_P3 zA~Q=$qVS_!>&7!W2^R$!`z%B#0ik}8yF*`h7N)0=!_Eb>Zt)USyB(w4QSCAO?hRV8 z`}pdXjdDv?;v~<>swK+O=K>y#KO$O`W}zl!CRP@qO;{}W_<9p2qunfAU%UQ82E>*W z!;_dBev|KzJCuJ`EV5&6G_kJ0)$g-6vsZ7D{sK{i97!n@Nk?*1d%kj$_&D`9L$55; zfKKQ;^griN%%quDJL6as_ib6bj$jmYqZJ81K|GfXyQI|pQx+rJf!`3kJb?Z~5GIH~ z@F{RHPmlyU;cNJI_%#+z2nIHN@RrxB$3?J0T$zM-n0tgX`OwX~ptQ;Wl6!x;vJUzx zxjEOb3V0U}$=K051-xwn+*H)$%{h{e+MuSVg=|!X@~f4aHYj2k2PS;mMWKo2xK6ml z#VF^UyL5-V#$-Uyf(iM_b~yag4W^gE4HcIl@sn7)B>nrsN$y)aUM3{!290adx};VO zrz8Q$Jxx%3NO3N^#HBlBbsu>_MAl2Tp<=R+G#`ZCsEJ2V?k+iLCHNh97=zs3{<9sA zw^=oN{xE|q@z;y5oyX7q^e{QbX?jLIIU*;i9j|3!1;OY+ktQaO%3JdF%VxwtC+$C{ zM~K&QqJJO)%|*a&EJHC=5m(rk$6zSt^xP8Oj3_FK0FXsM)iHkB6`}&c3lqJlg{D&D zBh6zvlRfVse=QCzzE@y8`uQw)Jd9^twRtR>9(H6M1{6THx8^!JZbskzDthySfZI|s ziTGf9LCvU67GLEn7Vwa8?S(pABKgv*J^7r|$BU*aMf{KBUr= zN6%8+d$0P49aCR+<8yyU&V-oJyrj(^;4c)50{`0rzY6cQYVSINo(Aq4v%~H-?8)jeo-lVzIH~}jA_#|IJgf(kmHK#; z`(muK2XPF)#Nr6e!1}~>JYfQWBOp6CGw5C`!cB2X6h*XI73dU*i^d)D=(u+}l>FJ* zTMhrQcA$XzEv`qw<S8y-5?oQeN-MB!UIqMNXT_N_`Ls^FbSOBiNGww!q2bTCvYFYTS4PlMfkI zo@;th!A!v|c&O}S_D>F8N-pXOZ$H`W;RNy5(5vY*=-%)e+&8o}OG$C9LIHjz2`xP+ ziR|OCk;(1=X+6)7xy!y|K*E_TCKOi<60>o&8|WwzS_;9^%TKaABpd396i*vQ?-4_9 zwrp^L=$+`u3e17j`4j6!Z45ooDfYrp)kBD zLwqC^V*nhnRz#qRP)213`Y4P1U0MDa9uB7-kTtk>Yu@Wc%<7-jg5{>SV;{;v1-lv~ zT0syNQ*QF~vy_KYu;ssJ@20VAQO6kF||&V^W;FjTaV`3 zFuDd`)L4x!y2C5?IM|F)N*BpLtGae0B7m{$Y4Aomx`7&H$$!CL>L3Jr41tvcMz-`#+<3 zL(fId@m|M}d%^E1==*{8tL-5x;vb(EM5@Hf%rhUy8~KfanvcHiRrl+=kEfJSeE0M? z$=15g;(@hRK|mYWK8Ibe3Sw7%dd?Sd6JZyp9$-IZ3_4zgzF|gdES-$$Fyn>6N5_C} z744T8x<;nobEDovkAv#{Q7_Nz;OI%T^rxFjgJLpS;^M9#Q_)B@GUgu@D6L5eib>%S z#N7ibZ!G;(l;CogSV+U zM50v%@?Ta8kH2ylBi78758S`!d;=GA=Wu05G7Ns*4yl)_o>!WbBda>#GZ-C{BcG8T zAJb^Ap69dB)1l2F>3WorD?^*hSDVfsu&w+|Q;W?`Th!`fo>gKbWz=0x%dy&!%l+Xo zNjxS7e1#mzQmHk&nJGMByt#Yg$&GVwfSbY2dnqk;qbzL|fkb&z)Q= zHb*F~Uu5BMXDQ6a;Hf)WMWIRR#+t2Nba@8tEAh!DjSbkPe`{i`2?xpUICV|&(xKh7 zgTStQt>M|bXyF8Q9cH03|8sl#AZQP*Nxe-#=89{r6{2u8k7`I48K`Ext~_DeH$$V} z#zjIMvm~=!|3lo4H%8>Pd)`t>-)v#eqVBULxyM;;5BH?Hl<<21A_%xF!lEnS*F;iI z{4p4fT^^pm6W?h6akXKR*xU7XpYmcd1n$#;D(BY1bNwD(I#wK=6})b^qlD) z6f;7Rq}nm}{Y?i9lYqMb2AKxkB|6qz z&jV*rEEmyz^6(%HEF4Sw%9Djbo-aJnJzD7APNaS5(_v3F>V zG~pkZ{-G8X2%8SlB99}FRmyp5hvd8P{+{W2O=Y0qtn6AGXR;%U3F|YBHaFX&S7I&U zt3;7Uss+BP#*ibEg%?60s^dLF-b+e$Q)=F4C-8LP^|W=H^e=e@l!&)`MrWskKIwF& ze3*AWuMoGXB6mOCoznw&^#eUqL<>q^)3 zXSJ67zubHsXKUE8t&asH^n`dx&xfCWl|_fu1AU zLPwN8jE_BazH$t6aay`2jKHtE0pijP!%E^6fMYnm2opruf5k%fiD5jz!fs1rUR#JV zBj{T(cf+sLy+=1Eyx64fR46x0cdprKZ}hoU?otODQBWS&DPvG5%Rh`W{WoJd*yf~t z4Mmai=Df3ZQViT0n;9ecQ@$=fA5Vi0g<6Z}a=!NUlvkXFPDhzvv`3kmm+#!2lKW-{ z_0=%de?`eiH`BJ&oBqp>w_LOPAS%*3dTuoc)!u8qNJBMeZrAzKj|*P6ThAgg&vfIb zcYN}gj;!fqq3b})Dd0R#L&Tmi(_ zwFkeC^qB~ss(QQc`a1^iZsiZ5s_<^eg;QyX{$3@g1!kw2tLgddX1K zb#!{xMssi@JMSZ7B{g-W+31ZgJFJS~;{(qgXnZu2l|4$nE%(x9N0=hR8{Kg}Xk7ok zW21CXVThE`caz&$hCYhLjprQ;?dYL?nJ`G0Z85lp{d+3e`~I`>4>0IO!LaeX$5O?s zdNQR-5}n){9<7pfW@fItV)B|d+Src{vzciUo@6%oQqH|2?Psb{&+Nf^D&eZ5p%`I5K~FkO|_sMLw4&n4jP-RW&ei$tG|p z`yaos7-zy+z)`E;VDO%q~hX=Q#3?t%dg4a9n z9xyn0ScdP{V(tM(;RDT8KaMbf@gP>p^|sGt9&iK$Bng%P5{^ckG2sW}bs|5Rs%0DA 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 + + + + + Битва: Елена vs Балард (Сетевая Версия) + + + + + + + +

+
+ +
+
+
Ожидание подключения к серверу...
+
+ +
+

Вход / Регистрация

+
+

Регистрация

+ + + +
+
+
+

Вход

+ + + +
+
+ + + + +
+ + + + + + + + + \ No newline at end of file diff --git a/public/js/client.js b/public/js/client.js new file mode 100644 index 0000000..2b8afef --- /dev/null +++ b/public/js/client.js @@ -0,0 +1,583 @@ +// /public/js/client.js + +document.addEventListener('DOMContentLoaded', () => { + const socket = io({ + // Опции Socket.IO, если нужны + // transports: ['websocket'], // Можно попробовать для отладки, если есть проблемы с polling + }); + + // --- Состояние клиента --- + let currentGameState = null; + let myPlayerId = null; // Технический ID слота в игре ('player' или 'opponent') + let myUserId = null; // ID залогиненного пользователя (из БД) + let myCharacterKey = null; + let opponentCharacterKey = null; + let currentGameId = null; + let playerBaseStatsServer = null; + let opponentBaseStatsServer = null; + let playerAbilitiesServer = null; + let opponentAbilitiesServer = null; + let isLoggedIn = false; + let loggedInUsername = ''; + let isInGame = false; + + // --- DOM Элементы --- + const authSection = document.getElementById('auth-section'); + const registerForm = document.getElementById('register-form'); + const loginForm = document.getElementById('login-form'); + const authMessage = document.getElementById('auth-message'); + const statusContainer = document.getElementById('status-container'); + const userInfoDiv = document.getElementById('user-info'); + const loggedInUsernameSpan = document.getElementById('logged-in-username'); + const logoutButton = document.getElementById('logout-button'); + + const gameSetupDiv = document.getElementById('game-setup'); + const createAIGameButton = document.getElementById('create-ai-game'); + const createPvPGameButton = document.getElementById('create-pvp-game'); + const joinPvPGameButton = document.getElementById('join-pvp-game'); // Убедитесь, что ID в HTML 'join-pvp-game' + const findRandomPvPGameButton = document.getElementById('find-random-pvp-game'); + const gameIdInput = document.getElementById('game-id-input'); + const availableGamesDiv = document.getElementById('available-games-list'); + const gameStatusMessage = document.getElementById('game-status-message'); + const pvpCharacterRadios = document.querySelectorAll('input[name="pvp-character"]'); + + const gameWrapper = document.querySelector('.game-wrapper'); + const attackButton = document.getElementById('button-attack'); + const returnToMenuButton = document.getElementById('return-to-menu-button'); + const gameOverScreen = document.getElementById('game-over-screen'); + const abilitiesGrid = document.getElementById('abilities-grid'); + + const turnTimerSpan = document.getElementById('turn-timer'); + const turnTimerContainer = document.getElementById('turn-timer-container'); + + // --- Функции управления UI --- + function showAuthScreen() { + authSection.style.display = 'block'; + userInfoDiv.style.display = 'none'; + gameSetupDiv.style.display = 'none'; + gameWrapper.style.display = 'none'; + hideGameOverModal(); + setAuthMessage("Ожидание подключения к серверу..."); + statusContainer.style.display = 'block'; + isInGame = false; + disableGameControls(); + resetGameVariables(); + if (turnTimerContainer) turnTimerContainer.style.display = 'none'; + if (turnTimerSpan) turnTimerSpan.textContent = '--'; + } + + function showGameSelectionScreen(username) { + authSection.style.display = 'none'; + userInfoDiv.style.display = 'block'; + loggedInUsernameSpan.textContent = username; + gameSetupDiv.style.display = 'block'; + gameWrapper.style.display = 'none'; + hideGameOverModal(); + setGameStatusMessage("Выберите режим игры или присоединитесь к существующей."); + statusContainer.style.display = 'block'; + socket.emit('requestPvPGameList'); + updateAvailableGamesList([]); // Очищаем перед запросом + if (gameIdInput) gameIdInput.value = ''; + const elenaRadio = document.getElementById('char-elena'); + if (elenaRadio) elenaRadio.checked = true; + isInGame = false; + disableGameControls(); + resetGameVariables(); // Сбрасываем игровые переменные при выходе в меню + if (turnTimerContainer) turnTimerContainer.style.display = 'none'; + if (turnTimerSpan) turnTimerSpan.textContent = '--'; + enableSetupButtons(); // Включаем кнопки на экране выбора игры + } + + function showGameScreen() { + hideGameOverModal(); + authSection.style.display = 'none'; + userInfoDiv.style.display = 'block'; // Оставляем инфо о пользователе + gameSetupDiv.style.display = 'none'; + gameWrapper.style.display = 'flex'; + setGameStatusMessage(""); // Очищаем статус, т.к. есть индикатор хода + statusContainer.style.display = 'none'; // Скрываем общий статус контейнер + isInGame = true; + disableGameControls(); // Кнопки включатся, когда будет ход игрока + if (turnTimerContainer) turnTimerContainer.style.display = 'block'; // Показываем таймер + if (turnTimerSpan) turnTimerSpan.textContent = '--'; // Начальное значение + } + + function resetGameVariables() { + currentGameId = null; currentGameState = null; myPlayerId = null; + myCharacterKey = null; opponentCharacterKey = null; + playerBaseStatsServer = null; opponentBaseStatsServer = null; + playerAbilitiesServer = null; opponentAbilitiesServer = null; + window.gameState = null; window.gameData = null; window.myPlayerId = null; + } + + function hideGameOverModal() { + const hiddenClass = window.GAME_CONFIG?.CSS_CLASS_HIDDEN || 'hidden'; + if (gameOverScreen && !gameOverScreen.classList.contains(hiddenClass)) { + gameOverScreen.classList.add(hiddenClass); + if (window.gameUI?.uiElements?.gameOver?.modalContent) { + window.gameUI.uiElements.gameOver.modalContent.style.transform = 'scale(0.8) translateY(30px)'; + window.gameUI.uiElements.gameOver.modalContent.style.opacity = '0'; + } + const opponentPanel = window.gameUI?.uiElements?.opponent?.panel; + if (opponentPanel?.classList.contains('dissolving')) { + opponentPanel.classList.remove('dissolving'); + opponentPanel.style.opacity = '1'; opponentPanel.style.transform = 'scale(1) translateY(0)'; + } + } + } + + function setAuthMessage(message, isError = false) { + if (authMessage) { + authMessage.textContent = message; + authMessage.className = isError ? 'error' : 'success'; + authMessage.style.display = message ? 'block' : 'none'; + } + if (message && gameStatusMessage) gameStatusMessage.style.display = 'none'; + } + + function setGameStatusMessage(message, isError = false) { + if (gameStatusMessage) { + gameStatusMessage.textContent = message; + gameStatusMessage.style.display = message ? 'block' : 'none'; + gameStatusMessage.style.color = isError ? 'var(--damage-color, red)' : 'var(--turn-color, yellow)'; + if (statusContainer) statusContainer.style.display = message ? 'block' : 'none'; + } + if (message && authMessage) authMessage.style.display = 'none'; + } + + function getSelectedCharacterKey() { + let selectedKey = 'elena'; + if (pvpCharacterRadios) { + pvpCharacterRadios.forEach(radio => { if (radio.checked) selectedKey = radio.value; }); + } + return selectedKey; + } + + function enableGameControls(enableAttack = true, enableAbilities = true) { + if (attackButton) attackButton.disabled = !enableAttack; + if (abilitiesGrid) { + const cls = window.GAME_CONFIG?.CSS_CLASS_ABILITY_BUTTON || 'ability-button'; + abilitiesGrid.querySelectorAll(`.${cls}`).forEach(b => { b.disabled = !enableAbilities; }); + } + if (window.gameUI?.uiElements?.controls?.buttonBlock) window.gameUI.uiElements.controls.buttonBlock.disabled = true; + } + function disableGameControls() { enableGameControls(false, false); } + + function disableSetupButtons() { + if(createAIGameButton) createAIGameButton.disabled = true; + if(createPvPGameButton) createPvPGameButton.disabled = true; + if(joinPvPGameButton) joinPvPGameButton.disabled = true; + if(findRandomPvPGameButton) findRandomPvPGameButton.disabled = true; + if(availableGamesDiv) availableGamesDiv.querySelectorAll('button').forEach(btn => btn.disabled = true); + } + function enableSetupButtons() { + if(createAIGameButton) createAIGameButton.disabled = false; + if(createPvPGameButton) createPvPGameButton.disabled = false; + if(joinPvPGameButton) joinPvPGameButton.disabled = false; + if(findRandomPvPGameButton) findRandomPvPGameButton.disabled = false; + // Кнопки в списке игр включаются в updateAvailableGamesList + } + + // --- Инициализация обработчиков событий --- + if (registerForm) registerForm.addEventListener('submit', (e) => { + e.preventDefault(); + const u = document.getElementById('register-username').value; + const p = document.getElementById('register-password').value; + registerForm.querySelector('button').disabled = true; + if(loginForm) loginForm.querySelector('button').disabled = true; + socket.emit('register', { username: u, password: p }); + }); + if (loginForm) loginForm.addEventListener('submit', (e) => { + e.preventDefault(); + const u = document.getElementById('login-username').value; + const p = document.getElementById('login-password').value; + if(registerForm) registerForm.querySelector('button').disabled = true; + loginForm.querySelector('button').disabled = true; + socket.emit('login', { username: u, password: p }); + }); + if (logoutButton) logoutButton.addEventListener('click', () => { + logoutButton.disabled = true; socket.emit('logout'); + isLoggedIn = false; loggedInUsername = ''; myUserId = null; + resetGameVariables(); isInGame = false; disableGameControls(); + showAuthScreen(); setGameStatusMessage("Вы вышли из системы."); + logoutButton.disabled = false; + }); + if (createAIGameButton) createAIGameButton.addEventListener('click', () => { + if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } + disableSetupButtons(); + socket.emit('createGame', { mode: 'ai', characterKey: 'elena' }); // AI всегда за Елену + setGameStatusMessage("Создание игры против AI..."); + }); + if (createPvPGameButton) createPvPGameButton.addEventListener('click', () => { + if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } + disableSetupButtons(); + socket.emit('createGame', { mode: 'pvp', characterKey: getSelectedCharacterKey() }); + setGameStatusMessage("Создание PvP игры..."); + }); + if (joinPvPGameButton) joinPvPGameButton.addEventListener('click', () => { // Убедитесь, что ID кнопки 'join-pvp-game' + if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } + const gameId = gameIdInput.value.trim(); + if (gameId) { + disableSetupButtons(); + socket.emit('joinGame', { gameId: gameId }); + setGameStatusMessage(`Присоединение к игре ${gameId}...`); + } else setGameStatusMessage("Введите ID игры.", true); + }); + if (findRandomPvPGameButton) findRandomPvPGameButton.addEventListener('click', () => { + if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } + disableSetupButtons(); + socket.emit('findRandomGame', { characterKey: getSelectedCharacterKey() }); + setGameStatusMessage("Поиск случайной PvP игры..."); + }); + if (attackButton) attackButton.addEventListener('click', () => { + if (isLoggedIn && isInGame && currentGameId && currentGameState && !currentGameState.isGameOver) { + socket.emit('playerAction', { actionType: 'attack' }); + } else { /* обработка ошибки/некорректного состояния */ } + }); + function handleAbilityButtonClick(event) { + const abilityId = event.currentTarget.dataset.abilityId; + if (isLoggedIn && isInGame && currentGameId && abilityId && currentGameState && !currentGameState.isGameOver) { + socket.emit('playerAction', { actionType: 'ability', abilityId: abilityId }); + } else { /* обработка ошибки/некорректного состояния */ } + } + if (returnToMenuButton) returnToMenuButton.addEventListener('click', () => { + if (!isLoggedIn) { showAuthScreen(); return; } + returnToMenuButton.disabled = true; + resetGameVariables(); isInGame = false; disableGameControls(); hideGameOverModal(); + showGameSelectionScreen(loggedInUsername); // Возвращаемся на экран выбора + // Кнопка включится при следующем показе модалки + }); + + function initializeAbilityButtons() { + // ... (код без изменений, как был) + if (!abilitiesGrid || !window.gameUI || !window.GAME_CONFIG) { + if (abilitiesGrid) abilitiesGrid.innerHTML = '

Ошибка загрузки способностей.

'; + return; + } + abilitiesGrid.innerHTML = ''; + const config = window.GAME_CONFIG; + const abilitiesToDisplay = playerAbilitiesServer; + const baseStatsForResource = playerBaseStatsServer; + + if (!abilitiesToDisplay || abilitiesToDisplay.length === 0 || !baseStatsForResource) { + abilitiesGrid.innerHTML = '

Нет доступных способностей.

'; + return; + } + const resourceName = baseStatsForResource.resourceName || "Ресурс"; + const abilityButtonClass = config.CSS_CLASS_ABILITY_BUTTON || 'ability-button'; + + abilitiesToDisplay.forEach(ability => { + const button = document.createElement('button'); + button.id = `ability-btn-${ability.id}`; + button.classList.add(abilityButtonClass); + button.dataset.abilityId = ability.id; + let cooldown = ability.cooldown; + let cooldownText = (typeof cooldown === 'number' && cooldown > 0) ? ` (КД: ${cooldown} х.)` : ""; + let title = `${ability.name} (${ability.cost} ${resourceName})${cooldownText} - ${ability.description || 'Нет описания'}`; + button.setAttribute('title', title); + const nameSpan = document.createElement('span'); nameSpan.classList.add('ability-name'); nameSpan.textContent = ability.name; button.appendChild(nameSpan); + const descSpan = document.createElement('span'); descSpan.classList.add('ability-desc'); descSpan.textContent = `(${ability.cost} ${resourceName})`; button.appendChild(descSpan); + const cdDisplay = document.createElement('span'); cdDisplay.classList.add('ability-cooldown-display'); cdDisplay.style.display = 'none'; button.appendChild(cdDisplay); + button.addEventListener('click', handleAbilityButtonClick); + abilitiesGrid.appendChild(button); + }); + const placeholder = abilitiesGrid.querySelector('.placeholder-text'); + if (placeholder) placeholder.remove(); + } + + function updateAvailableGamesList(games) { + if (!availableGamesDiv) return; + availableGamesDiv.innerHTML = '

Доступные PvP игры:

'; + if (games && games.length > 0) { + const ul = document.createElement('ul'); + games.forEach(game => { + if (game && game.id) { + const li = document.createElement('li'); + li.textContent = `ID: ${game.id.substring(0, 8)}... - ${game.status || 'Ожидает игрока'}`; + const joinBtn = document.createElement('button'); + joinBtn.textContent = 'Присоединиться'; + joinBtn.dataset.gameId = game.id; + + // === ИЗМЕНЕНИЕ: Деактивация кнопки "Присоединиться" для своих игр === + if (isLoggedIn && myUserId && game.ownerIdentifier === myUserId) { + joinBtn.disabled = true; + joinBtn.title = "Вы не можете присоединиться к своей же ожидающей игре."; + } else { + joinBtn.disabled = false; + } + // === КОНЕЦ ИЗМЕНЕНИЯ === + + joinBtn.addEventListener('click', (e) => { + if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } + if (e.target.disabled) return; // Не обрабатывать клик по отключенной кнопке + disableSetupButtons(); + socket.emit('joinGame', { gameId: e.target.dataset.gameId }); + }); + li.appendChild(joinBtn); + ul.appendChild(li); + } + }); + availableGamesDiv.appendChild(ul); + } else { + availableGamesDiv.innerHTML += '

Нет доступных игр. Создайте свою!

'; + } + enableSetupButtons(); // Включаем основные кнопки создания/поиска + } + + + // --- Обработчики событий Socket.IO --- + socket.on('connect', () => { + console.log('[Client] Socket connected:', socket.id); + if (isLoggedIn && myUserId) { // Проверяем и isLoggedIn и myUserId + socket.emit('requestGameState'); // Запрашиваем состояние, если были залогинены + } else { + showAuthScreen(); // Иначе показываем экран логина + } + }); + + socket.on('registerResponse', (data) => { + setAuthMessage(data.message, !data.success); + if (data.success && registerForm) registerForm.reset(); + if(registerForm) registerForm.querySelector('button').disabled = false; + if(loginForm) loginForm.querySelector('button').disabled = false; + }); + + socket.on('loginResponse', (data) => { + setAuthMessage(data.message, !data.success); + if (data.success) { + isLoggedIn = true; + loggedInUsername = data.username; + myUserId = data.userId; // === ИЗМЕНЕНИЕ: Сохраняем ID пользователя === + setAuthMessage(""); + showGameSelectionScreen(data.username); + } else { + isLoggedIn = false; loggedInUsername = ''; myUserId = null; + if(registerForm) registerForm.querySelector('button').disabled = false; + if(loginForm) loginForm.querySelector('button').disabled = false; + } + }); + + socket.on('gameNotFound', (data) => { + console.log('[Client] Game not found/ended:', data?.message); + resetGameVariables(); isInGame = false; disableGameControls(); hideGameOverModal(); + if (turnTimerContainer) turnTimerContainer.style.display = 'none'; + if (turnTimerSpan) turnTimerSpan.textContent = '--'; + + if (isLoggedIn) { + showGameSelectionScreen(loggedInUsername); + setGameStatusMessage(data?.message || "Активная игровая сессия не найдена."); + } else { + showAuthScreen(); + setAuthMessage(data?.message || "Пожалуйста, войдите."); + } + }); + + socket.on('disconnect', (reason) => { + console.log('[Client] Disconnected:', reason); + setGameStatusMessage(`Отключено: ${reason}. Обновите страницу.`, true); + disableGameControls(); + if (turnTimerSpan) turnTimerSpan.textContent = 'Откл.'; + // Не сбрасываем isLoggedIn, чтобы при переподключении можно было восстановить сессию + }); + + socket.on('gameCreated', (data) => { // Сервер присылает это после успешного createGame + console.log('[Client] Game created by this client:', data); + currentGameId = data.gameId; + myPlayerId = data.yourPlayerId; // Сервер должен прислать роль создателя + // Остальные данные (gameState, baseStats) придут с gameStarted или gameState (если это PvP ожидание) + // Если это PvP и игра ожидает, сервер может прислать waitingForOpponent + }); + + + socket.on('gameStarted', (data) => { + if (!isLoggedIn) return; + console.log('[Client] Game started:', data); + // ... (остальной код gameStarted без изменений, как был) + if (window.gameUI?.uiElements?.opponent?.panel) { + const opponentPanel = window.gameUI.uiElements.opponent.panel; + if (opponentPanel.classList.contains('dissolving')) { + opponentPanel.classList.remove('dissolving'); + opponentPanel.style.opacity = '1'; opponentPanel.style.transform = 'scale(1) translateY(0)'; + } + } + currentGameId = data.gameId; myPlayerId = data.yourPlayerId; currentGameState = data.initialGameState; + playerBaseStatsServer = data.playerBaseStats; opponentBaseStatsServer = data.opponentBaseStats; + playerAbilitiesServer = data.playerAbilities; opponentAbilitiesServer = data.opponentAbilities; + myCharacterKey = playerBaseStatsServer?.characterKey; opponentCharacterKey = opponentBaseStatsServer?.characterKey; + + if (data.clientConfig) window.GAME_CONFIG = { ...data.clientConfig }; + else if (!window.GAME_CONFIG) { + window.GAME_CONFIG = { PLAYER_ID: 'player', OPPONENT_ID: 'opponent', CSS_CLASS_HIDDEN: 'hidden' }; + } + window.gameState = currentGameState; + window.gameData = { playerBaseStats: playerBaseStatsServer, opponentBaseStats: opponentBaseStatsServer, playerAbilities: playerAbilitiesServer, opponentAbilities: opponentAbilitiesServer }; + window.myPlayerId = myPlayerId; + + showGameScreen(); initializeAbilityButtons(); + if (window.gameUI?.uiElements?.log?.list) window.gameUI.uiElements.log.list.innerHTML = ''; + if (window.gameUI && typeof window.gameUI.addToLog === 'function' && data.log) { + data.log.forEach(logEntry => window.gameUI.addToLog(logEntry.message, logEntry.type)); + } + requestAnimationFrame(() => { + if (window.gameUI && typeof window.gameUI.updateUI === 'function') { + window.gameUI.updateUI(); + } + }); + hideGameOverModal(); setGameStatusMessage(""); + }); + + // Используется для восстановления состояния уже идущей игры + socket.on('gameState', (data) => { + if (!isLoggedIn) return; + console.log('[Client] Received full gameState (e.g. on reconnect):', data); + // Это событие теперь может дублировать 'gameStarted' для переподключения. + // Убедимся, что логика похожа на gameStarted. + currentGameId = data.gameId; + myPlayerId = data.yourPlayerId; + currentGameState = data.gameState; // Используем gameState вместо initialGameState + playerBaseStatsServer = data.playerBaseStats; + opponentBaseStatsServer = data.opponentBaseStats; + playerAbilitiesServer = data.playerAbilities; + opponentAbilitiesServer = data.opponentAbilities; + myCharacterKey = playerBaseStatsServer?.characterKey; + opponentCharacterKey = opponentBaseStatsServer?.characterKey; + + if (data.clientConfig) window.GAME_CONFIG = { ...data.clientConfig }; + else if (!window.GAME_CONFIG) { + window.GAME_CONFIG = { PLAYER_ID: 'player', OPPONENT_ID: 'opponent', CSS_CLASS_HIDDEN: 'hidden' }; + } + window.gameState = currentGameState; + window.gameData = { playerBaseStats: playerBaseStatsServer, opponentBaseStats: opponentBaseStatsServer, playerAbilities: playerAbilitiesServer, opponentAbilities: opponentAbilitiesServer }; + window.myPlayerId = myPlayerId; + + if (!isInGame) showGameScreen(); // Показываем экран игры, если еще не там + initializeAbilityButtons(); // Переинициализируем кнопки + + // Лог при 'gameState' может быть уже накопленным, добавляем его + if (window.gameUI?.uiElements?.log?.list && data.log) { // Очищаем лог перед добавлением нового при полном обновлении + window.gameUI.uiElements.log.list.innerHTML = ''; + } + if (window.gameUI && typeof window.gameUI.addToLog === 'function' && data.log) { + data.log.forEach(logEntry => window.gameUI.addToLog(logEntry.message, logEntry.type)); + } + + requestAnimationFrame(() => { + if (window.gameUI && typeof window.gameUI.updateUI === 'function') { + window.gameUI.updateUI(); + } + }); + hideGameOverModal(); + // Таймер будет обновлен следующим событием 'turnTimerUpdate' + }); + + + socket.on('gameStateUpdate', (data) => { + if (!isLoggedIn || !isInGame || !currentGameId || !window.GAME_CONFIG) return; + currentGameState = data.gameState; window.gameState = currentGameState; + if (window.gameUI?.updateUI) window.gameUI.updateUI(); + if (window.gameUI?.addToLog && data.log) { + data.log.forEach(log => window.gameUI.addToLog(log.message, log.type)); + } + }); + + socket.on('logUpdate', (data) => { + if (!isLoggedIn || !isInGame || !currentGameId || !window.GAME_CONFIG) return; + if (window.gameUI?.addToLog && data.log) { + data.log.forEach(log => window.gameUI.addToLog(log.message, log.type)); + } + }); + + socket.on('gameOver', (data) => { + // ... (код без изменений, как был) + if (!isLoggedIn || !currentGameId || !window.GAME_CONFIG) { + if (!currentGameId && isLoggedIn) socket.emit('requestGameState'); + else if (!isLoggedIn) showAuthScreen(); + return; + } + const playerWon = data.winnerId === myPlayerId; + currentGameState = data.finalGameState; window.gameState = currentGameState; + if (window.gameUI?.updateUI) window.gameUI.updateUI(); + if (window.gameUI?.addToLog && data.log) { + data.log.forEach(log => window.gameUI.addToLog(log.message, log.type)); + } + if (window.gameUI?.showGameOver) { + const oppKey = window.gameData?.opponentBaseStats?.characterKey; + window.gameUI.showGameOver(playerWon, data.reason, oppKey, data); + } + if (returnToMenuButton) returnToMenuButton.disabled = false; + setGameStatusMessage("Игра окончена. " + (playerWon ? "Вы победили!" : "Вы проиграли.")); + if (window.gameUI?.updateTurnTimerDisplay) { // Обновляем UI таймера + window.gameUI.updateTurnTimerDisplay(null, false, currentGameState?.gameMode); // Передаем null, чтобы показать "Конец" или скрыть + } + }); + + socket.on('waitingForOpponent', () => { + if (!isLoggedIn) return; + setGameStatusMessage("Ожидание присоединения оппонента..."); + disableGameControls(); // Боевые кнопки неактивны + disableSetupButtons(); // Кнопки создания/присоединения тоже, пока ждем + if (createPvPGameButton) createPvPGameButton.disabled = false; // Оставляем активной "Создать PvP" для отмены + if (window.gameUI?.updateTurnTimerDisplay) { + window.gameUI.updateTurnTimerDisplay(null, false, 'pvp'); // Таймер неактивен + } + }); + + socket.on('opponentDisconnected', (data) => { + if (!isLoggedIn || !isInGame || !currentGameId || !window.GAME_CONFIG) return; + const name = data.disconnectedCharacterName || 'Противник'; + if (window.gameUI?.addToLog) window.gameUI.addToLog(`🔌 Противник (${name}) отключился.`, 'system'); + if (currentGameState && !currentGameState.isGameOver) { + setGameStatusMessage(`Противник (${name}) отключился. Ожидание...`, true); + disableGameControls(); + } + }); + + socket.on('gameError', (data) => { + console.error('[Client] Server error:', data.message); + if (isLoggedIn && isInGame && currentGameState && !currentGameState.isGameOver && window.gameUI?.addToLog) { + window.gameUI.addToLog(`❌ Ошибка игры: ${data.message}`, 'system'); + disableGameControls(); setGameStatusMessage(`Ошибка: ${data.message}.`, true); + } else { + setGameStatusMessage(`❌ Ошибка: ${data.message}`, true); + if (isLoggedIn) enableSetupButtons(); // Если на экране выбора игры, включаем кнопки + else { // Если на экране логина + if(registerForm) registerForm.querySelector('button').disabled = false; + if(loginForm) loginForm.querySelector('button').disabled = false; + } + } + }); + + socket.on('availablePvPGamesList', (games) => { + if (!isLoggedIn) return; + updateAvailableGamesList(games); + }); + + socket.on('noPendingGamesFound', (data) => { // Вызывается, когда создается новая игра после поиска + if (!isLoggedIn) return; + setGameStatusMessage(data.message || "Свободных игр не найдено. Создана новая для вас."); + updateAvailableGamesList([]); // Очищаем список + // currentGameId и myPlayerId должны были прийти с gameCreated + isInGame = false; // Еще не в активной фазе боя + disableGameControls(); + disableSetupButtons(); // Мы в ожидающей игре + if (window.gameUI?.updateTurnTimerDisplay) { + window.gameUI.updateTurnTimerDisplay(null, false, 'pvp'); + } + }); + + socket.on('turnTimerUpdate', (data) => { + if (!isInGame || !currentGameState || currentGameState.isGameOver) { + if (window.gameUI?.updateTurnTimerDisplay && !currentGameState?.isGameOver) { // Только если не game over + window.gameUI.updateTurnTimerDisplay(null, false, currentGameState?.gameMode); + } + return; + } + if (window.gameUI && typeof window.gameUI.updateTurnTimerDisplay === 'function') { + // Определяем, является ли текущий ход ходом этого клиента + const isMyActualTurn = myPlayerId && currentGameState.isPlayerTurn === (myPlayerId === GAME_CONFIG.PLAYER_ID); + window.gameUI.updateTurnTimerDisplay(data.remainingTime, isMyActualTurn, currentGameState.gameMode); + } + }); + + showAuthScreen(); // Начальный экран +}); \ No newline at end of file diff --git a/public/js/ui.js b/public/js/ui.js new file mode 100644 index 0000000..3e69e9a --- /dev/null +++ b/public/js/ui.js @@ -0,0 +1,534 @@ +// /public/js/ui.js +// Этот файл отвечает за обновление DOM на основе состояния игры, +// полученного от client.js (который, в свою очередь, получает его от сервера). + +(function() { + // --- DOM Элементы --- + const uiElements = { + player: { + panel: document.getElementById('player-panel'), + name: document.getElementById('player-name'), + avatar: document.getElementById('player-panel')?.querySelector('.player-avatar'), + hpFill: document.getElementById('player-hp-fill'), hpText: document.getElementById('player-hp-text'), + resourceFill: document.getElementById('player-resource-fill'), resourceText: document.getElementById('player-resource-text'), + status: document.getElementById('player-status'), + effectsContainer: document.getElementById('player-effects'), + buffsList: document.getElementById('player-effects')?.querySelector('.player-buffs'), + debuffsList: document.getElementById('player-effects')?.querySelector('.player-debuffs') + }, + opponent: { + panel: document.getElementById('opponent-panel'), + name: document.getElementById('opponent-name'), + avatar: document.getElementById('opponent-panel')?.querySelector('.opponent-avatar'), + hpFill: document.getElementById('opponent-hp-fill'), hpText: document.getElementById('opponent-hp-text'), + resourceFill: document.getElementById('opponent-resource-fill'), resourceText: document.getElementById('opponent-resource-text'), + status: document.getElementById('opponent-status'), + effectsContainer: document.getElementById('opponent-effects'), + buffsList: document.getElementById('opponent-effects')?.querySelector('.opponent-buffs'), + debuffsList: document.getElementById('opponent-effects')?.querySelector('.opponent-debuffs') + }, + controls: { + turnIndicator: document.getElementById('turn-indicator'), + buttonAttack: document.getElementById('button-attack'), + buttonBlock: document.getElementById('button-block'), + abilitiesGrid: document.getElementById('abilities-grid'), + turnTimerContainer: document.getElementById('turn-timer-container'), + turnTimerSpan: document.getElementById('turn-timer') + }, + log: { + list: document.getElementById('log-list'), + }, + gameOver: { + screen: document.getElementById('game-over-screen'), + message: document.getElementById('result-message'), + returnToMenuButton: document.getElementById('return-to-menu-button'), + modalContent: document.getElementById('game-over-screen')?.querySelector('.modal-content') + }, + gameHeaderTitle: document.querySelector('.game-header h1'), + playerResourceTypeIcon: document.getElementById('player-resource-bar')?.closest('.stat-bar-container')?.querySelector('.bar-icon i'), + opponentResourceTypeIcon: document.getElementById('opponent-resource-bar')?.closest('.stat-bar-container')?.querySelector('.bar-icon i'), + playerResourceBarContainer: document.getElementById('player-resource-bar')?.closest('.stat-bar-container'), + opponentResourceBarContainer: document.getElementById('opponent-resource-bar')?.closest('.stat-bar-container'), + + // === НОВЫЕ ЭЛЕМЕНТЫ для переключателя панелей === + panelSwitcher: { + controlsContainer: document.querySelector('.panel-switcher-controls'), + showPlayerBtn: document.getElementById('show-player-panel-btn'), + showOpponentBtn: document.getElementById('show-opponent-panel-btn') + }, + battleArenaContainer: document.querySelector('.battle-arena-container') + // === КОНЕЦ НОВЫХ ЭЛЕМЕНТОВ === + }; + + function addToLog(message, type = 'info') { + const logListElement = uiElements.log.list; + if (!logListElement) return; + const li = document.createElement('li'); + li.textContent = message; + const config = window.GAME_CONFIG || {}; + const logTypeClass = config[`LOG_TYPE_${type.toUpperCase()}`] ? `log-${config[`LOG_TYPE_${type.toUpperCase()}`]}` : `log-${type}`; + li.className = logTypeClass; + logListElement.appendChild(li); + requestAnimationFrame(() => { logListElement.scrollTop = logListElement.scrollHeight; }); + } + + function updateFighterPanelUI(panelRole, fighterState, fighterBaseStats, isControlledByThisClient) { + const elements = uiElements[panelRole]; + const config = window.GAME_CONFIG || {}; + + if (!elements || !elements.hpFill || !elements.hpText || !elements.resourceFill || !elements.resourceText || !elements.status || !fighterState || !fighterBaseStats) { + if (elements) { + if(elements.name) elements.name.innerHTML = (panelRole === 'player') ? ' Ожидание данных...' : ' Ожидание игрока...'; + if(elements.hpText) elements.hpText.textContent = 'N/A'; + if(elements.resourceText) elements.resourceText.textContent = 'N/A'; + if(elements.status) elements.status.textContent = 'Неизвестно'; + if(elements.buffsList) elements.buffsList.innerHTML = 'Нет'; + if(elements.debuffsList) elements.debuffsList.innerHTML = 'Нет'; + if(elements.avatar) elements.avatar.src = 'images/default_avatar.png'; + if(panelRole === 'player' && uiElements.playerResourceTypeIcon) uiElements.playerResourceTypeIcon.className = 'fas fa-question'; + if(panelRole === 'opponent' && uiElements.opponentResourceTypeIcon) uiElements.opponentResourceTypeIcon.className = 'fas fa-question'; + if(panelRole === 'player' && uiElements.playerResourceBarContainer) uiElements.playerResourceBarContainer.classList.remove('mana', 'stamina', 'dark-energy'); + if(panelRole === 'opponent' && uiElements.opponentResourceBarContainer) uiElements.opponentResourceBarContainer.classList.remove('mana', 'stamina', 'dark-energy'); + if(elements.panel) elements.panel.style.opacity = '0.5'; + } + return; + } + if (elements.panel) elements.panel.style.opacity = '1'; + + if (elements.name) { + let iconClass = 'fa-question'; + const characterKey = fighterBaseStats.characterKey; + if (characterKey === 'elena') { iconClass = 'fa-hat-wizard icon-elena'; } + else if (characterKey === 'almagest') { iconClass = 'fa-staff-aesculapius icon-almagest'; } + else if (characterKey === 'balard') { iconClass = 'fa-khanda icon-balard'; } + let nameHtml = ` ${fighterBaseStats.name || 'Неизвестно'}`; + if (isControlledByThisClient) nameHtml += " (Вы)"; + elements.name.innerHTML = nameHtml; + } + + if (elements.avatar && fighterBaseStats.avatarPath) { + elements.avatar.src = fighterBaseStats.avatarPath; + elements.avatar.classList.remove('avatar-elena', 'avatar-almagest', 'avatar-balard'); + elements.avatar.classList.add(`avatar-${fighterBaseStats.characterKey}`); + } else if (elements.avatar) { + elements.avatar.src = 'images/default_avatar.png'; + elements.avatar.classList.remove('avatar-elena', 'avatar-almagest', 'avatar-balard'); + } + + const maxHp = Math.max(1, fighterBaseStats.maxHp); + const maxRes = Math.max(1, fighterBaseStats.maxResource); + const currentHp = Math.max(0, fighterState.currentHp); + const currentRes = Math.max(0, fighterState.currentResource); + elements.hpFill.style.width = `${(currentHp / maxHp) * 100}%`; + elements.hpText.textContent = `${Math.round(currentHp)} / ${fighterBaseStats.maxHp}`; + elements.resourceFill.style.width = `${(currentRes / maxRes) * 100}%`; + elements.resourceText.textContent = `${currentRes} / ${fighterBaseStats.maxResource}`; + + const resourceBarContainerToUpdate = (panelRole === 'player') ? uiElements.playerResourceBarContainer : uiElements.opponentResourceBarContainer; + const resourceIconElementToUpdate = (panelRole === 'player') ? uiElements.playerResourceTypeIcon : uiElements.opponentResourceTypeIcon; + if (resourceBarContainerToUpdate && resourceIconElementToUpdate) { + resourceBarContainerToUpdate.classList.remove('mana', 'stamina', 'dark-energy'); + let resourceClass = 'mana'; let iconClass = 'fa-flask'; + if (fighterBaseStats.resourceName === 'Ярость') { resourceClass = 'stamina'; iconClass = 'fa-fire-alt'; } + else if (fighterBaseStats.resourceName === 'Темная Энергия') { resourceClass = 'dark-energy'; iconClass = 'fa-skull'; } + resourceBarContainerToUpdate.classList.add(resourceClass); + resourceIconElementToUpdate.className = `fas ${iconClass}`; + } + + const statusText = fighterState.isBlocking ? (config.STATUS_BLOCKING || 'Защищается') : (config.STATUS_READY || 'Готов(а)'); + elements.status.textContent = statusText; + elements.status.classList.toggle(config.CSS_CLASS_BLOCKING || 'blocking', fighterState.isBlocking); + + if (elements.panel) { + let borderColorVar = 'var(--panel-border)'; + elements.panel.classList.remove('panel-elena', 'panel-almagest', 'panel-balard'); + if (fighterBaseStats.characterKey === 'elena') { elements.panel.classList.add('panel-elena'); borderColorVar = 'var(--accent-player)'; } + else if (fighterBaseStats.characterKey === 'almagest') { elements.panel.classList.add('panel-almagest'); borderColorVar = 'var(--accent-almagest)'; } + else if (fighterBaseStats.characterKey === 'balard') { elements.panel.classList.add('panel-balard'); borderColorVar = 'var(--accent-opponent)'; } + let glowColorVar = 'rgba(0, 0, 0, 0.4)'; + if (fighterBaseStats.characterKey === 'elena') glowColorVar = 'var(--panel-glow-player)'; + else if (fighterBaseStats.characterKey === 'almagest') glowColorVar = 'var(--panel-glow-almagest)'; + else if (fighterBaseStats.characterKey === 'balard') glowColorVar = 'var(--panel-glow-opponent)'; + elements.panel.style.borderColor = borderColorVar; + elements.panel.style.boxShadow = `0 0 15px ${glowColorVar}, inset 0 0 10px rgba(0, 0, 0, 0.3)`; + } + } + + function generateEffectsHTML(effectsArray) { + const config = window.GAME_CONFIG || {}; + if (!effectsArray || effectsArray.length === 0) return 'Нет'; + return effectsArray.map(eff => { + let effectClasses = config.CSS_CLASS_EFFECT || 'effect'; + const title = `${eff.name}${eff.description ? ` - ${eff.description}` : ''} (Осталось: ${eff.turnsLeft} х.)`; + const displayText = `${eff.name} (${eff.turnsLeft} х.)`; + if (eff.isFullSilence || eff.id.startsWith('playerSilencedOn_') || eff.type === config.ACTION_TYPE_DISABLE) effectClasses += ' effect-stun'; + else if (eff.grantsBlock) effectClasses += ' effect-block'; + else if (eff.type === config.ACTION_TYPE_DEBUFF) effectClasses += ' effect-debuff'; + else if (eff.type === config.ACTION_TYPE_BUFF || eff.type === config.ACTION_TYPE_HEAL) effectClasses += ' effect-buff'; + else effectClasses += ' effect-info'; + return `${displayText}`; + }).join(' '); + } + + function updateEffectsUI(currentGameState) { + if (!currentGameState || !window.GAME_CONFIG) return; + const mySlotId = window.myPlayerId; + const config = window.GAME_CONFIG; + if (!mySlotId) return; + const opponentSlotId = mySlotId === config.PLAYER_ID ? config.OPPONENT_ID : config.PLAYER_ID; + const myState = currentGameState[mySlotId]; + const opponentState = currentGameState[opponentSlotId]; + const typeOrder = { [config.ACTION_TYPE_BUFF]: 1, grantsBlock: 2, [config.ACTION_TYPE_HEAL]: 3, [config.ACTION_TYPE_DEBUFF]: 4, [config.ACTION_TYPE_DISABLE]: 5 }; + const sortEffects = (a, b) => { + let orderA = typeOrder[a.type] || 99; if (a.grantsBlock) orderA = typeOrder.grantsBlock; if (a.isFullSilence || a.id.startsWith('playerSilencedOn_')) orderA = typeOrder[config.ACTION_TYPE_DISABLE]; + let orderB = typeOrder[b.type] || 99; if (b.grantsBlock) orderB = typeOrder.grantsBlock; if (b.isFullSilence || b.id.startsWith('playerSilencedOn_')) orderB = typeOrder[config.ACTION_TYPE_DISABLE]; + return (orderA || 99) - (orderB || 99); + }; + + if (uiElements.player && uiElements.player.buffsList && uiElements.player.debuffsList && myState && myState.activeEffects) { + const myBuffs = []; const myDebuffs = []; + myState.activeEffects.forEach(e => { + const isBuff = e.type === config.ACTION_TYPE_BUFF || e.grantsBlock || e.type === config.ACTION_TYPE_HEAL; + const isDebuff = e.type === config.ACTION_TYPE_DEBUFF || e.type === config.ACTION_TYPE_DISABLE || e.isFullSilence || e.id.startsWith('playerSilencedOn_'); + if (isBuff) myBuffs.push(e); else if (isDebuff) myDebuffs.push(e); else myDebuffs.push(e); + }); + myBuffs.sort(sortEffects); myDebuffs.sort(sortEffects); + uiElements.player.buffsList.innerHTML = generateEffectsHTML(myBuffs); + uiElements.player.debuffsList.innerHTML = generateEffectsHTML(myDebuffs); + } else if (uiElements.player && uiElements.player.buffsList && uiElements.player.debuffsList) { + uiElements.player.buffsList.innerHTML = 'Нет'; uiElements.player.debuffsList.innerHTML = 'Нет'; + } + + if (uiElements.opponent && uiElements.opponent.buffsList && uiElements.opponent.debuffsList && opponentState && opponentState.activeEffects) { + const opponentBuffs = []; const opponentDebuffs = []; + opponentState.activeEffects.forEach(e => { + const isBuff = e.type === config.ACTION_TYPE_BUFF || e.grantsBlock || e.type === config.ACTION_TYPE_HEAL; + const isDebuff = e.type === config.ACTION_TYPE_DEBUFF || e.type === config.ACTION_TYPE_DISABLE || e.isFullSilence || e.id.startsWith('effect_'); + if (isBuff) opponentBuffs.push(e); else if (isDebuff) opponentDebuffs.push(e); else opponentDebuffs.push(e); + }); + opponentBuffs.sort(sortEffects); opponentDebuffs.sort(sortEffects); + uiElements.opponent.buffsList.innerHTML = generateEffectsHTML(opponentBuffs); + uiElements.opponent.debuffsList.innerHTML = generateEffectsHTML(opponentDebuffs); + } else if (uiElements.opponent && uiElements.opponent.buffsList && uiElements.opponent.debuffsList) { + uiElements.opponent.buffsList.innerHTML = 'Нет'; uiElements.opponent.debuffsList.innerHTML = 'Нет'; + } + } + + function updateTurnTimerDisplay(remainingTimeMs, isCurrentPlayerActualTurn, gameMode) { + const timerSpan = uiElements.controls.turnTimerSpan; + const timerContainer = uiElements.controls.turnTimerContainer; + + if (!timerSpan || !timerContainer) return; + + if (window.gameState && window.gameState.isGameOver) { + timerContainer.style.display = 'block'; + timerSpan.textContent = 'Конец'; + timerSpan.classList.remove('low-time'); + return; + } + + if (remainingTimeMs === null || remainingTimeMs === undefined) { + timerContainer.style.display = 'block'; + timerSpan.classList.remove('low-time'); + if (gameMode === 'ai' && !isCurrentPlayerActualTurn) { + timerSpan.textContent = 'Ход ИИ'; + } else if (gameMode === 'pvp' && !isCurrentPlayerActualTurn) { + timerSpan.textContent = 'Ход оппонента'; + } else { + timerSpan.textContent = '--'; + } + } else { + timerContainer.style.display = 'block'; + const seconds = Math.ceil(remainingTimeMs / 1000); + timerSpan.textContent = `0:${seconds < 10 ? '0' : ''}${seconds}`; + + if (seconds <= 10 && isCurrentPlayerActualTurn) { + timerSpan.classList.add('low-time'); + } else { + timerSpan.classList.remove('low-time'); + } + } + } + + + function updateUI() { + const currentGameState = window.gameState; + const gameDataGlobal = window.gameData; + const configGlobal = window.GAME_CONFIG; + const myActualPlayerId = window.myPlayerId; + + if (!currentGameState || !gameDataGlobal || !configGlobal || !myActualPlayerId) { + updateFighterPanelUI('player', null, null, true); + updateFighterPanelUI('opponent', null, null, false); + if(uiElements.gameHeaderTitle) uiElements.gameHeaderTitle.innerHTML = `Ожидание данных...`; + if(uiElements.controls.turnIndicator) uiElements.controls.turnIndicator.textContent = "Ожидание данных..."; + if(uiElements.controls.buttonAttack) uiElements.controls.buttonAttack.disabled = true; + if(uiElements.controls.buttonBlock) uiElements.controls.buttonBlock.disabled = true; + if(uiElements.controls.abilitiesGrid) uiElements.controls.abilitiesGrid.innerHTML = '

Загрузка способностей...

'; + if (uiElements.controls.turnTimerContainer) uiElements.controls.turnTimerContainer.style.display = 'none'; + if (uiElements.controls.turnTimerSpan) { + uiElements.controls.turnTimerSpan.textContent = '--'; + uiElements.controls.turnTimerSpan.classList.remove('low-time'); + } + return; + } + if (!uiElements.player.panel || !uiElements.opponent.panel || !uiElements.controls.turnIndicator || !uiElements.controls.abilitiesGrid || !uiElements.log.list) { + console.warn("updateUI: Некоторые базовые uiElements не найдены."); + return; + } + + const actorSlotWhoseTurnItIs = currentGameState.isPlayerTurn ? configGlobal.PLAYER_ID : configGlobal.OPPONENT_ID; + const opponentActualSlotId = myActualPlayerId === configGlobal.PLAYER_ID ? configGlobal.OPPONENT_ID : configGlobal.PLAYER_ID; + const myStateInGameState = currentGameState[myActualPlayerId]; + const myBaseStatsForUI = gameDataGlobal.playerBaseStats; + if (myStateInGameState && myBaseStatsForUI) updateFighterPanelUI('player', myStateInGameState, myBaseStatsForUI, true); + else updateFighterPanelUI('player', null, null, true); + + const opponentStateInGameState = currentGameState[opponentActualSlotId]; + const opponentBaseStatsForUI = gameDataGlobal.opponentBaseStats; + const isOpponentPanelDissolving = uiElements.opponent.panel?.classList.contains('dissolving'); + if (opponentStateInGameState && opponentBaseStatsForUI) { + if (uiElements.opponent.panel && (uiElements.opponent.panel.style.opacity !== '1' || (uiElements.opponent.panel.classList.contains('dissolving') && currentGameState.isGameOver === false) )) { + const panel = uiElements.opponent.panel; + if (panel.classList.contains('dissolving')) { + panel.classList.remove('dissolving'); panel.style.transition = 'none'; panel.offsetHeight; + panel.style.opacity = '1'; panel.style.transform = 'scale(1) translateY(0)'; panel.style.transition = ''; + } else { panel.style.opacity = '1'; panel.style.transform = 'scale(1) translateY(0)'; } + } else if (uiElements.opponent.panel && !isOpponentPanelDissolving) { + uiElements.opponent.panel.style.opacity = '1'; + } + updateFighterPanelUI('opponent', opponentStateInGameState, opponentBaseStatsForUI, false); + } else { + if (!isOpponentPanelDissolving) updateFighterPanelUI('opponent', null, null, false); + else console.log("[UI UPDATE DEBUG] Opponent panel is dissolving, skipping content update."); + } + + updateEffectsUI(currentGameState); + + if (uiElements.gameHeaderTitle && gameDataGlobal.playerBaseStats && gameDataGlobal.opponentBaseStats) { + const myName = gameDataGlobal.playerBaseStats.name; const opponentName = gameDataGlobal.opponentBaseStats.name; + const myKey = gameDataGlobal.playerBaseStats.characterKey; const opponentKey = gameDataGlobal.opponentBaseStats.characterKey; + let myClass = 'title-player'; let opponentClass = 'title-opponent'; + if (myKey === 'elena') myClass = 'title-enchantress'; else if (myKey === 'almagest') myClass = 'title-sorceress'; else if (myKey === 'balard') myClass = 'title-knight'; + if (opponentKey === 'elena') opponentClass = 'title-enchantress'; else if (opponentKey === 'almagest') opponentClass = 'title-sorceress'; else if (opponentKey === 'balard') opponentClass = 'title-knight'; + uiElements.gameHeaderTitle.innerHTML = `${myName} ${opponentName}`; + } else if (uiElements.gameHeaderTitle) { + const myName = gameDataGlobal.playerBaseStats?.name || 'Игрок 1'; const myKey = gameDataGlobal.playerBaseStats?.characterKey; + let myClass = 'title-player'; if (myKey === 'elena') myClass = 'title-enchantress'; else if (myKey === 'almagest') myClass = 'title-sorceress'; + uiElements.gameHeaderTitle.innerHTML = `${myName} Ожидание игрока...`; + } + + const canThisClientAct = actorSlotWhoseTurnItIs === myActualPlayerId; + const isGameActive = !currentGameState.isGameOver; + const myCharacterState = currentGameState[myActualPlayerId]; + + if (uiElements.controls.turnIndicator) { + if (isGameActive) { + const currentTurnActor = currentGameState.isPlayerTurn ? currentGameState.player : currentGameState.opponent; + uiElements.controls.turnIndicator.textContent = `Ход ${currentGameState.turnNumber}: ${currentTurnActor?.name || 'Неизвестно'}`; + uiElements.controls.turnIndicator.style.color = (currentTurnActor?.id === myActualPlayerId) ? 'var(--turn-color)' : 'var(--text-muted)'; + } else { + uiElements.controls.turnIndicator.textContent = "Игра окончена"; + uiElements.controls.turnIndicator.style.color = 'var(--text-muted)'; + } + } + + if (uiElements.controls.buttonAttack) { + uiElements.controls.buttonAttack.disabled = !(canThisClientAct && isGameActive); + const myCharKey = gameDataGlobal.playerBaseStats?.characterKey; + let attackBuffId = null; + if (myCharKey === 'elena') attackBuffId = configGlobal.ABILITY_ID_NATURE_STRENGTH; + else if (myCharKey === 'almagest') attackBuffId = configGlobal.ABILITY_ID_ALMAGEST_BUFF_ATTACK; + if (attackBuffId && myCharacterState && myCharacterState.activeEffects) { + const isAttackBuffReady = myCharacterState.activeEffects.some(eff => (eff.id === attackBuffId || eff.id === GAME_CONFIG.ABILITY_ID_NATURE_STRENGTH || eff.id === GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_ATTACK) && eff.isDelayed && eff.turnsLeft > 0 && !eff.justCast); + uiElements.controls.buttonAttack.classList.toggle(configGlobal.CSS_CLASS_ATTACK_BUFFED || 'attack-buffed', isAttackBuffReady && canThisClientAct && isGameActive); + } else { uiElements.controls.buttonAttack.classList.remove(configGlobal.CSS_CLASS_ATTACK_BUFFED || 'attack-buffed'); } + } + if (uiElements.controls.buttonBlock) uiElements.controls.buttonBlock.disabled = true; + + const actingPlayerState = myCharacterState; + const actingPlayerAbilities = gameDataGlobal.playerAbilities; + const actingPlayerResourceName = gameDataGlobal.playerBaseStats?.resourceName; + const opponentStateForDebuffCheck = currentGameState[opponentActualSlotId]; + + uiElements.controls.abilitiesGrid?.querySelectorAll(`.${configGlobal.CSS_CLASS_ABILITY_BUTTON || 'ability-button'}`).forEach(button => { + const abilityId = button.dataset.abilityId; + const abilityDataFromGameData = actingPlayerAbilities?.find(ab => ab.id === abilityId); + if (!(button instanceof HTMLButtonElement) || !isGameActive || !canThisClientAct || !actingPlayerState || !actingPlayerAbilities || !actingPlayerResourceName || !abilityDataFromGameData) { + if (button instanceof HTMLButtonElement) button.disabled = true; + button.classList.remove(configGlobal.CSS_CLASS_NOT_ENOUGH_RESOURCE||'not-enough-resource', configGlobal.CSS_CLASS_BUFF_IS_ACTIVE||'buff-is-active', configGlobal.CSS_CLASS_ABILITY_SILENCED||'is-silenced', configGlobal.CSS_CLASS_ABILITY_ON_COOLDOWN||'is-on-cooldown'); + const cooldownDisplay = button.querySelector('.ability-cooldown-display'); + if (cooldownDisplay) cooldownDisplay.style.display = 'none'; + return; + } + const hasEnoughResource = actingPlayerState.currentResource >= abilityDataFromGameData.cost; + const isOnCooldown = (actingPlayerState.abilityCooldowns?.[abilityId] || 0) > 0; + const isGenerallySilenced = actingPlayerState.activeEffects?.some(eff => eff.isFullSilence && eff.turnsLeft > 0); + const isAbilitySpecificallySilenced = actingPlayerState.disabledAbilities?.some(dis => dis.abilityId === abilityId && dis.turnsLeft > 0); + const isSilenced = isGenerallySilenced || isAbilitySpecificallySilenced; + const silenceTurnsLeft = isAbilitySpecificallySilenced ? (actingPlayerState.disabledAbilities?.find(dis => dis.abilityId === abilityId)?.turnsLeft || 0) : (isGenerallySilenced ? (actingPlayerState.activeEffects.find(eff => eff.isFullSilence)?.turnsLeft || 0) : 0); + const isBuffAlreadyActive = abilityDataFromGameData.type === configGlobal.ACTION_TYPE_BUFF && actingPlayerState.activeEffects?.some(eff => eff.id === abilityId); + const isTargetedDebuffAbility = abilityId === configGlobal.ABILITY_ID_SEAL_OF_WEAKNESS || abilityId === configGlobal.ABILITY_ID_ALMAGEST_DEBUFF; + const effectIdForDebuff = 'effect_' + abilityId; + const isDebuffAlreadyOnTarget = isTargetedDebuffAbility && opponentStateForDebuffCheck && opponentStateForDebuffCheck.activeEffects?.some(e => e.id === effectIdForDebuff); + button.disabled = !hasEnoughResource || isBuffAlreadyActive || isSilenced || isOnCooldown || isDebuffAlreadyOnTarget; + button.classList.remove(configGlobal.CSS_CLASS_NOT_ENOUGH_RESOURCE||'not-enough-resource', configGlobal.CSS_CLASS_BUFF_IS_ACTIVE||'buff-is-active', configGlobal.CSS_CLASS_ABILITY_SILENCED||'is-silenced', configGlobal.CSS_CLASS_ABILITY_ON_COOLDOWN||'is-on-cooldown'); + const cooldownDisplay = button.querySelector('.ability-cooldown-display'); + if (isOnCooldown) { + button.classList.add(configGlobal.CSS_CLASS_ABILITY_ON_COOLDOWN||'is-on-cooldown'); + if (cooldownDisplay) { cooldownDisplay.textContent = `КД: ${actingPlayerState.abilityCooldowns[abilityId]}`; cooldownDisplay.style.display = 'block'; } + } else if (isSilenced) { + button.classList.add(configGlobal.CSS_CLASS_ABILITY_SILENCED||'is-silenced'); + if (cooldownDisplay) { const icon = isGenerallySilenced ? '🔕' : '🔇'; cooldownDisplay.textContent = `${icon} ${silenceTurnsLeft}`; cooldownDisplay.style.display = 'block'; } + } else { + if (cooldownDisplay) cooldownDisplay.style.display = 'none'; + if (!isOnCooldown && !isSilenced) { + button.classList.toggle(configGlobal.CSS_CLASS_NOT_ENOUGH_RESOURCE||'not-enough-resource', !hasEnoughResource); + button.classList.toggle(configGlobal.CSS_CLASS_BUFF_IS_ACTIVE||'buff-is-active', isBuffAlreadyActive); + } + } + let titleText = `${abilityDataFromGameData.name} (${abilityDataFromGameData.cost} ${actingPlayerResourceName})`; + let descriptionTextFull = abilityDataFromGameData.description; + if (typeof abilityDataFromGameData.descriptionFunction === 'function') { + const opponentBaseStatsForDesc = gameDataGlobal.opponentBaseStats; + descriptionTextFull = abilityDataFromGameData.descriptionFunction(configGlobal, opponentBaseStatsForDesc); + } + if (descriptionTextFull) titleText += ` - ${descriptionTextFull}`; + let abilityBaseCooldown = abilityDataFromGameData.cooldown; + if (typeof abilityBaseCooldown === 'number' && abilityBaseCooldown > 0) titleText += ` (Исходный КД: ${abilityBaseCooldown} х.)`; + if (isOnCooldown) titleText += ` | На перезарядке! Осталось: ${actingPlayerState.abilityCooldowns[abilityId]} х.`; + if (isSilenced) titleText += ` | Под безмолвием! Осталось: ${silenceTurnsLeft} х.`; + if (isBuffAlreadyActive) { + const activeEffect = actingPlayerState.activeEffects?.find(eff => eff.id === abilityId); + const isDelayedBuffReady = isBuffAlreadyActive && activeEffect && activeEffect.isDelayed && !activeEffect.justCast && activeEffect.turnsLeft > 0; + if (isDelayedBuffReady) titleText += ` | Эффект активен и сработает при следующей базовой атаке (${activeEffect.turnsLeft} х.)`; + else if (isBuffAlreadyActive) titleText += ` | Эффект уже активен${activeEffect ? ` (${activeEffect.turnsLeft} х.)` : ''}. Нельзя применить повторно.`; + } + if (isDebuffAlreadyOnTarget && opponentStateForDebuffCheck) { + const activeDebuff = opponentStateForDebuffCheck.activeEffects?.find(e => e.id === 'effect_' + abilityId); + titleText += ` | Эффект уже наложен на ${gameDataGlobal.opponentBaseStats?.name || 'противника'}${activeDebuff ? ` (${activeDebuff.turnsLeft} х.)` : ''}.`; + } + if (!hasEnoughResource) titleText += ` | Недостаточно ${actingPlayerResourceName} (${actingPlayerState.currentResource}/${abilityDataFromGameData.cost})`; + button.setAttribute('title', titleText); + }); + } + + function showGameOver(playerWon, reason = "", opponentCharacterKeyFromClient = null, data = null) { + const config = window.GAME_CONFIG || {}; + const clientSpecificGameData = window.gameData; + const currentActualGameState = window.gameState; + const gameOverScreenElement = uiElements.gameOver.screen; + + if (!gameOverScreenElement) { return; } + + const resultMsgElement = uiElements.gameOver.message; + const myNameForResult = clientSpecificGameData?.playerBaseStats?.name || "Игрок"; + const opponentNameForResult = clientSpecificGameData?.opponentBaseStats?.name || "Противник"; + + if (resultMsgElement) { + let winText = `Победа! ${myNameForResult} празднует!`; + let loseText = `Поражение! ${opponentNameForResult} оказался(лась) сильнее!`; + if (reason === 'opponent_disconnected') { + let disconnectedName = data?.disconnectedCharacterName || opponentNameForResult; + winText = `${disconnectedName} покинул(а) игру. Победа присуждается вам!`; + } else if (reason === 'turn_timeout') { + if (!playerWon) { + loseText = `Время на ход истекло! Поражение. ${opponentNameForResult} побеждает!`; + } else { + winText = `Время на ход у ${opponentNameForResult} истекло! Победа!`; + } + } + resultMsgElement.textContent = playerWon ? winText : loseText; + resultMsgElement.style.color = playerWon ? 'var(--heal-color)' : 'var(--damage-color)'; + } + + const opponentPanelElement = uiElements.opponent.panel; + if (opponentPanelElement) { + opponentPanelElement.classList.remove('dissolving'); + opponentPanelElement.style.transition = 'none'; opponentPanelElement.offsetHeight; + const loserCharacterKeyForDissolve = data?.loserCharacterKey; + if (currentActualGameState && currentActualGameState.isGameOver === true && playerWon) { + if (loserCharacterKeyForDissolve === 'balard' || loserCharacterKeyForDissolve === 'almagest') { + opponentPanelElement.classList.add('dissolving'); + opponentPanelElement.style.opacity = '0'; + } else { + opponentPanelElement.style.opacity = '1'; opponentPanelElement.style.transform = 'scale(1) translateY(0)'; + } + } else { + opponentPanelElement.style.opacity = '1'; opponentPanelElement.style.transform = 'scale(1) translateY(0)'; + } + opponentPanelElement.style.transition = ''; + } + + setTimeout((finalStateInTimeout) => { + if (gameOverScreenElement && finalStateInTimeout && finalStateInTimeout.isGameOver === true) { + if (gameOverScreenElement.classList.contains(config.CSS_CLASS_HIDDEN || 'hidden')) { + gameOverScreenElement.classList.remove(config.CSS_CLASS_HIDDEN || 'hidden'); + } + if(window.getComputedStyle(gameOverScreenElement).display === 'none') gameOverScreenElement.style.display = 'flex'; + gameOverScreenElement.style.opacity = '0'; + requestAnimationFrame(() => { + gameOverScreenElement.style.opacity = '1'; + if (uiElements.gameOver.modalContent) { + uiElements.gameOver.modalContent.style.transition = 'transform 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.2), opacity 0.4s ease-out'; + uiElements.gameOver.modalContent.style.transform = 'scale(1) translateY(0)'; + uiElements.gameOver.modalContent.style.opacity = '1'; + } + }); + } else { + if (gameOverScreenElement) { + gameOverScreenElement.style.transition = 'none'; + if (uiElements.gameOver.modalContent) uiElements.gameOver.modalContent.style.transition = 'none'; + gameOverScreenElement.classList.add(config.CSS_CLASS_HIDDEN || 'hidden'); + gameOverScreenElement.style.opacity = '0'; + if (uiElements.gameOver.modalContent) { + uiElements.gameOver.modalContent.style.transform = 'scale(0.8) translateY(30px)'; + uiElements.gameOver.modalContent.style.opacity = '0'; + } + gameOverScreenElement.offsetHeight; + } + } + }, config.DELAY_BEFORE_VICTORY_MODAL || 1500, currentActualGameState); + } + + // === НОВАЯ ФУНКЦИЯ для настройки переключателя панелей === + function setupPanelSwitcher() { + const { showPlayerBtn, showOpponentBtn } = uiElements.panelSwitcher; + const battleArena = uiElements.battleArenaContainer; + + if (showPlayerBtn && showOpponentBtn && battleArena) { + showPlayerBtn.addEventListener('click', () => { + battleArena.classList.remove('show-opponent-panel'); + showPlayerBtn.classList.add('active'); + showOpponentBtn.classList.remove('active'); + }); + + showOpponentBtn.addEventListener('click', () => { + battleArena.classList.add('show-opponent-panel'); + showOpponentBtn.classList.add('active'); + showPlayerBtn.classList.remove('active'); + }); + + // По умолчанию при загрузке (если кнопки видимы) панель игрока активна + // CSS уже должен это обеспечивать, но для надежности можно убедиться + if (window.getComputedStyle(uiElements.panelSwitcher.controlsContainer).display !== 'none') { + battleArena.classList.remove('show-opponent-panel'); + showPlayerBtn.classList.add('active'); + showOpponentBtn.classList.remove('active'); + } + } + } + // === КОНЕЦ НОВОЙ ФУНКЦИИ === + + window.gameUI = { + uiElements, + addToLog, + updateUI, + showGameOver, + updateTurnTimerDisplay + }; + + // Настраиваем переключатель панелей при загрузке скрипта + setupPanelSwitcher(); + +})(); \ No newline at end of file diff --git a/public/style_alt.css b/public/style_alt.css new file mode 100644 index 0000000..ba69ff7 --- /dev/null +++ b/public/style_alt.css @@ -0,0 +1,1399 @@ +/* === style_alt.css (Изменения для user-info и game-header) === */ +@import url('https://fonts.googleapis.com/css2?family=MedievalSharp&family=Roboto:wght@300;400;700&display=swap'); +@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'); + +:root { + /* --- Переменные цветов и шрифтов (из локальной версии) --- */ + --font-main: 'Roboto', sans-serif; + --font-fancy: 'MedievalSharp', cursive; + + --bg-gradient-dark: linear-gradient(160deg, #1f243a, #10121c); + --panel-bg: rgba(16, 18, 28, 0.8); + --panel-border: #4a5072; + + --panel-glow-player: rgba(80, 150, 255, 0.3); + --panel-glow-opponent: rgba(255, 80, 80, 0.3); + --panel-glow-almagest: rgba(199, 108, 255, 0.3); + + + --text-light: #e8effc; + --text-muted: #9badce; + --text-heading: #ffffff; + + --accent-player: #6c95ff; + --accent-opponent: #ff6c6c; + --accent-almagest: #c76cff; + + --hp-color: #de4b4b; + --mana-color: #58a8d0; + --stamina-color: #ffb347; + --dark-energy-color: #ab47bc; + --bar-bg: #252a44; + + --button-bg: linear-gradient(145deg, #556190, #3f4a70); + --button-hover-bg: linear-gradient(145deg, #6a79b0, #556190); + --button-text: var(--text-light); + + --button-ability-bg: linear-gradient(145deg, #305a5e, #1f4043); + --button-ability-hover-bg: linear-gradient(145deg, #407a7e, #305a5e); + --button-ability-border: #4db0b5; + + --button-disabled-bg: #333950; + --button-disabled-text: #6b7491; + + --log-bg: rgba(10, 12, 20, 0.85); + --log-border: var(--panel-border); + --log-text: var(--text-muted); + + --icon-color: var(--text-muted); + --damage-color: #ff8080; + --heal-color: #90ee90; + --block-color: #add8e6; + --effect-color: #d8bfd8; + --turn-color: #ffd700; + --system-color: #7fffd4; + + --modal-bg: rgba(16, 18, 28, 0.97); + --modal-content-bg: #2a2f45; + + --scrollbar-thumb: #4a5072; + --scrollbar-track: #10121c; + + --shake-duration: 0.4s; + --cast-duration: 0.6s; + --dissolve-duration: 6.0s; + + --log-panel-fixed-height: 280px; + + --timer-text-color: var(--turn-color); + --timer-icon-color: #b0c4de; + --timer-low-time-color: var(--damage-color); + + /* === Переменные для переключателя панелей (мобильный вид) - ИЗ СЕРВЕРНОЙ ВЕРСИИ === */ + --panel-switcher-bg: rgba(10, 12, 20, 0.9); + --panel-switcher-border: var(--panel-border); + --panel-switcher-button-bg: var(--button-bg); + --panel-switcher-button-text: var(--button-text); + --panel-switcher-button-active-bg: var(--accent-player); + --panel-switcher-button-active-text: #fff; +} + +/* --- Базовые Стили и Сброс --- */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + height: 100%; +} + +body { + font-family: var(--font-main); + background: var(--bg-gradient-dark) fixed; + color: var(--text-light); + line-height: 1.5; + height: 100vh; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 10px; +} + +h1, h2, h3, h4 { + font-family: var(--font-fancy); + color: var(--text-heading); + margin-bottom: 0.75em; + font-weight: normal; +} + +button { + font-family: var(--font-main); +} + +i { + margin-right: 6px; + color: var(--icon-color); + width: 1.2em; + text-align: center; +} + +* { + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); +} + +*::-webkit-scrollbar { + width: 8px; +} + +*::-webkit-scrollbar-track { + background: var(--scrollbar-track); + border-radius: 4px; +} + +*::-webkit-scrollbar-thumb { + background-color: var(--scrollbar-thumb); + border-radius: 4px; + border: 2px solid var(--scrollbar-track); +} + + +/* === Стили для Экранов Аутентификации и Настройки Игры (из локальной версии) === */ +.auth-game-setup-wrapper { + width: 100%; + max-width: 700px; + margin: 20px auto; + background: var(--panel-bg); + border: 1px solid var(--panel-border); + border-radius: 10px; + box-shadow: 0 5px 20px rgba(0, 0, 0, 0.5); + color: var(--text-light); + text-align: center; + max-height: calc(100vh - 40px); + overflow-y: hidden; /* Сохраняем из локальной */ + position: relative; /* <<< Добавлено для позиционирования #user-info (из локальной) */ +} + +.auth-game-setup-wrapper h2, +.auth-game-setup-wrapper h3 { + font-family: var(--font-fancy); + color: var(--text-heading); + margin-bottom: 1em; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding-bottom: 0.5em; +} + +.auth-game-setup-wrapper h3 { + font-size: 1.2em; + margin-top: 1.5em; +} + +.auth-game-setup-wrapper button, +#auth-section form button { + font-family: var(--font-main); + background: var(--button-bg); + color: var(--button-text); + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 6px; + padding: 10px 18px; + margin: 8px 5px; + cursor: pointer; + transition: all 0.15s ease; + font-weight: bold; + text-transform: uppercase; + letter-spacing: 0.5px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + outline: none; +} + +.auth-game-setup-wrapper button:hover:enabled, +#auth-section form button:hover:enabled { + background: var(--button-hover-bg); + transform: translateY(-2px) scale(1.02); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); +} + +.auth-game-setup-wrapper button:active:enabled, +#auth-section form button:active:enabled { + transform: translateY(0px) scale(1); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); +} + +.auth-game-setup-wrapper button:disabled, +#auth-section form button:disabled { + background: var(--button-disabled-bg) !important; + color: var(--button-disabled-text) !important; + border-color: transparent !important; + cursor: not-allowed !important; + opacity: 0.7; + transform: none !important; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.4) !important; + filter: grayscale(50%); +} + +.auth-game-setup-wrapper input[type="text"], +#auth-section input[type="text"], +#auth-section input[type="password"] { + padding: 10px; + border-radius: 5px; + border: 1px solid var(--panel-border); + background-color: var(--bar-bg); + color: var(--text-light); + margin: 5px 5px 10px 5px; + font-size: 0.9em; + width: calc(100% - 22px); + max-width: 300px; + box-sizing: border-box; + outline: none; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.auth-game-setup-wrapper input[type="text"]:focus, +#auth-section input[type="text"]:focus, +#auth-section input[type="password"]:focus { + border-color: var(--accent-player); + box-shadow: 0 0 8px rgba(108, 149, 255, 0.4); +} + +#available-games-list { + margin-top: 20px; + text-align: left; + max-height: 250px; + height: 100px; /* Сохраняем из локальной */ + overflow-y: scroll; + padding: 10px 15px; + background-color: rgba(0, 0, 0, 0.25); + border: 1px solid var(--log-border); + border-radius: 6px; +} + +#available-games-list h3 { + margin-top: 0; + margin-bottom: 10px; + padding-bottom: 5px; + border-bottom: 1px dashed rgba(255, 255, 255, 0.1); +} + +#available-games-list ul { + list-style: none; + padding: 0; + margin: 0; +} + +#available-games-list li { + padding: 10px; + border-bottom: 1px solid rgba(74, 80, 114, 0.5); + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.9em; +} + +#available-games-list li:last-child { + border-bottom: none; +} + +#available-games-list li button { + padding: 6px 10px; + font-size: 0.8em; + margin-left: 10px; + flex-shrink: 0; +} + +#status-container { + height: 40px; /* Сохраняем из локальной */ +} + +#auth-message, +#game-status-message { + font-weight: bold; + font-size: 1.1em; + padding: 5px; + background-color: rgba(0, 0, 0, 0.1); + border-radius: 4px; + display: block; + margin-bottom: 5px; + text-align: center; +} + +#auth-message.success { + color: var(--heal-color); +} + +#auth-message.error { + color: var(--damage-color); +} + +#game-status-message { + color: var(--turn-color); +} + + +#auth-section form { + margin-bottom: 20px; +} + +/* === ИЗМЕНЕНИЕ: Стили для #user-info (из локальной версии) === */ +#user-info { + position: absolute; + top: 10px; /* Отступ сверху */ + right: 15px; /* Отступ справа */ + line-height: 1.5; + text-align: right; /* Выравнивание текста и кнопки вправо */ + z-index: 10; /* Чтобы был поверх другого контента в .auth-game-setup-wrapper */ +} + +#user-info p { + margin: 0 10px 0 0; /* Уменьшен нижний отступ */ + font-size: 0.9em; /* Уменьшен шрифт приветствия */ + color: var(--text-muted); + line-height: 2.5; +} + +#user-info p #logged-in-username { + font-weight: bold; + color: var(--text-light); +} + +#user-info div { + display: flex; + flex-direction: row; +} + +#logout-button { + background: linear-gradient(145deg, #6e3c3c, #502626) !important; /* Более темный красный */ + color: #f0d0d0 !important; /* Светло-розовый текст */ + padding: 6px 12px !important; /* Уменьшены паддинги */ + font-size: 0.8em !important; /* Уменьшен шрифт */ + margin: 0 !important; /* Убираем внешние отступы */ + letter-spacing: 0.2px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) !important; + border: 1px solid #422020 !important; /* Темная рамка */ +} + +#logout-button:hover:enabled { + background: linear-gradient(145deg, #834545, #6e3c3c) !important; + transform: translateY(-1px) !important; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4) !important; +} +#logout-button i { + margin-right: 4px; /* Уменьшен отступ иконки */ +} +/* === КОНЕЦ ИЗМЕНЕНИЯ === */ + + +.character-selection { + margin-top: 15px; + margin-bottom: 15px; + padding: 15px; + background-color: rgba(0, 0, 0, 0.2); + border-radius: 6px; + border: 1px solid rgba(74, 80, 114, 0.5); +} + +.character-selection h4 { + font-size: 1.1em; + color: var(--text-muted); + margin-bottom: 10px; + border: none; + padding: 0; + text-align: center; +} + +.character-selection label { + display: inline-block; + margin: 0 15px; + cursor: pointer; + font-size: 1.05em; + padding: 5px 10px; + border-radius: 4px; + transition: background-color 0.2s ease, color 0.2s ease; + user-select: none; +} + +.character-selection input[type="radio"] { + display: none; +} + +.character-selection input[type="radio"]:checked + label { + color: #fff; + font-weight: bold; +} + +.character-selection input[type="radio"][value="elena"]:checked + label { + background-color: var(--accent-player); + box-shadow: 0 0 8px rgba(108, 149, 255, 0.5); +} + +.character-selection input[type="radio"][value="almagest"]:checked + label { + background-color: var(--accent-almagest); + box-shadow: 0 0 8px rgba(199, 108, 255, 0.5); +} + +.character-selection label:hover { + background-color: rgba(255, 255, 255, 0.1); +} + +.character-selection label i { + margin-right: 8px; + vertical-align: middle; +} + +label[for="char-elena"] i { + color: var(--accent-player); +} + +label[for="char-almagest"] i { + color: var(--accent-almagest); +} + +/* --- Основная Структура Игры (.game-wrapper) --- */ +.game-wrapper { + width: 100%; + height: 100%; + max-width: 1400px; + margin: 0 auto; + padding: 10px; + display: flex; + flex-direction: column; + gap: 10px; + overflow: hidden; +} + +/* === ИЗМЕНЕНИЕ: .game-header удален, стили для него больше не нужны (из локальной версии) === */ + +/* Глобальные стили для кнопок переключения панелей - ИЗ СЕРВЕРНОЙ ВЕРСИИ */ +.panel-switcher-controls { + display: none; /* Скрыт по умолчанию для десктопа */ + flex-shrink: 0; + padding: 8px 5px; + background: var(--panel-switcher-bg); + border-bottom: 1px solid var(--panel-switcher-border); + gap: 10px; +} +.panel-switch-button { + flex: 1; + padding: 8px 10px; + font-size: 0.9em; + font-weight: bold; + text-transform: uppercase; + background: var(--panel-switcher-button-bg); + color: var(--panel-switcher-button-text); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 5px; + cursor: pointer; + transition: background-color 0.2s, color 0.2s, transform 0.1s; + display: flex; + align-items: center; + justify-content: center; +} +.panel-switch-button i { margin-right: 8px; } +.panel-switch-button:hover { filter: brightness(1.1); } +.panel-switch-button.active { + background: var(--panel-switcher-button-active-bg); + color: var(--panel-switcher-button-active-text); + box-shadow: 0 0 8px rgba(255,255,255,0.3); +} + +.battle-arena-container { + flex-grow: 1; + display: flex; + gap: 10px; + overflow: hidden; + /* === ИЗМЕНЕНИЕ: Добавляем верхний отступ, если .game-header был убран, а .game-wrapper виден (из локальной версии) === */ + /* margin-top: 10px; /* или padding-top: 10px; на .game-wrapper, если нужно */ + /* === Изменения из серверной для работы переключения панелей === */ + position: relative; + min-height: 0; +} + +.player-column, +.opponent-column { + flex: 1; + display: flex; + flex-direction: column; + gap: 10px; + min-width: 0; + overflow: hidden; +} + +/* Остальные стили панелей, кнопок, лога и т.д. из локальной версии */ +.fighter-panel, +.controls-panel-new, +.battle-log-new { + background: var(--panel-bg); + border: 1px solid var(--panel-border); + border-radius: 8px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.4), inset 0 0 10px rgba(0, 0, 0, 0.3); + padding: 15px; + display: flex; + flex-direction: column; + overflow: hidden; + transition: box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease-out, transform 0.3s ease-out; +} + +.fighter-panel.panel-elena { + border-color: var(--accent-player); +} +.fighter-panel.panel-almagest { + border-color: var(--accent-almagest); +} +.fighter-panel.panel-balard { + border-color: var(--accent-opponent); +} + + +.panel-header { + flex-shrink: 0; + display: flex; + align-items: center; + gap: 10px; + padding-bottom: 10px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + margin-bottom: 0; +} + +.fighter-name { + font-size: 1.6em; + margin: 0; + flex-grow: 1; + text-align: left; +} + +.fighter-name .icon-elena { color: var(--accent-player); } +.fighter-name .icon-almagest { color: var(--accent-almagest); } +.fighter-name .icon-balard { color: var(--accent-opponent); } + + +.character-visual { + flex-shrink: 0; + margin-bottom: 0; +} + +.avatar-image { + display: block; + max-width: 50px; + height: auto; + border-radius: 50%; + border: 2px solid var(--panel-border); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); +} + +.avatar-image.avatar-elena { border-color: var(--accent-player); } +.avatar-image.avatar-almagest { border-color: var(--accent-almagest); } +.avatar-image.avatar-balard { border-color: var(--accent-opponent); } + + +.panel-content { + flex-grow: 1; + overflow-y: auto; + padding-right: 5px; + display: flex; + flex-direction: column; + gap: 10px; /* Добавлено из серверной версии для консистентности */ + min-height: 0; + padding-top: 10px; + margin-top: 0; +} + +.stat-bar-container { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; +} + +.stat-bar-container .bar-icon { + flex-shrink: 0; + font-size: 1.4em; +} +.stat-bar-container.health .bar-icon { color: var(--hp-color); } +.stat-bar-container.mana .bar-icon { color: var(--mana-color); } +.stat-bar-container.stamina .bar-icon { color: var(--stamina-color); } +.stat-bar-container.dark-energy .bar-icon { color: var(--dark-energy-color); } + + +.bar-wrapper { + flex-grow: 1; +} + +.bar { + border-radius: 4px; + height: 20px; + border: 1px solid rgba(0, 0, 0, 0.5); + overflow: hidden; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5); + position: relative; + background-color: var(--bar-bg); +} + +.bar-fill { + display: block; + height: 100%; + border-radius: 3px; + position: relative; + z-index: 2; + transition: width 0.4s ease-out; +} + +.bar-text { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 3; + display: flex; + justify-content: center; + align-items: center; + font-size: 0.75em; + font-weight: bold; + color: #fff; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.9); + padding: 0 5px; + white-space: nowrap; + pointer-events: none; +} +.health .bar-fill { background-color: var(--hp-color); } +.mana .bar-fill { background-color: var(--mana-color); } +.stamina .bar-fill { background-color: var(--stamina-color); } +.dark-energy .bar-fill { background-color: var(--dark-energy-color); } + + +.status-area { + font-size: 0.9em; + display: flex; + align-items: baseline; + gap: 5px; + flex-shrink: 0; + min-height: 1.5em; +} + +.status-area .icon-status { + font-size: 1em; + flex-shrink: 0; + margin-top: 0.1em; +} + +.status-area strong { + color: var(--text-muted); + font-weight: normal; + flex-shrink: 0; + margin-right: 3px; +} + +.status-area span { + font-weight: bold; +} + +.status-area span.blocking { + color: var(--block-color); + font-style: italic; +} + +.effects-area { + font-size: 0.9em; + display: flex; + flex-direction: column; + gap: 8px; /* Добавлено из серверной версии для консистентности */ + flex-shrink: 0; + min-height: 3em; +} + +.effect-category { + display: flex; + align-items: baseline; + gap: 5px; +} + +.effect-category strong { + color: var(--text-muted); + font-weight: normal; + font-family: var(--font-main); + font-size: 0.9em; + flex-shrink: 0; + margin-right: 3px; +} + +.effect-category .icon-effects-buff, +.effect-category .icon-effects-debuff { + font-size: 1em; + flex-shrink: 0; + margin-top: 0.1em; + width: 1.2em; + text-align: center; +} + +.effect-category .icon-effects-buff { color: var(--heal-color); } +.effect-category .icon-effects-debuff { color: var(--damage-color); } + +.effect-list { + display: inline; + line-height: 1.4; + min-width: 0; + font-weight: bold; +} + +.effect { + display: inline-block; + margin: 2px 3px 2px 0; + padding: 1px 6px; + font-size: 0.8em; + border-radius: 10px; + border: 1px solid; + cursor: default; + font-weight: 600; + background-color: rgba(0, 0, 0, 0.2); + white-space: nowrap; + vertical-align: baseline; +} +.effect-buff { border-color: var(--heal-color); color: var(--heal-color); } +.effect-debuff { border-color: var(--damage-color); color: var(--damage-color); } +.effect-stun { border-color: var(--turn-color); color: var(--turn-color); } +.effect-block { border-color: var(--block-color); color: var(--block-color); } +.effect-info { border-color: var(--text-muted); color: var(--text-muted); } + +.controls-panel-new { + flex-grow: 1; + min-height: 0; + display: flex; + flex-direction: column; +} + +#turn-indicator { + flex-shrink: 0; + text-align: center; + font-size: 1.4em; + margin-bottom: 10px; + padding-bottom: 8px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + transition: color 0.3s ease; +} + +.turn-timer-display { + flex-shrink: 0; + text-align: center; + font-size: 0.9em; + color: var(--timer-text-color); + margin-top: -5px; + margin-bottom: 10px; + padding: 5px; + background-color: rgba(0,0,0,0.15); + border-radius: 4px; + border-top: 1px solid rgba(255,255,255,0.05); +} + +.turn-timer-display i { + color: var(--timer-icon-color); + margin-right: 8px; +} + +#turn-timer { + font-weight: bold; + font-size: 1.1em; + min-width: 35px; + display: inline-block; + text-align: left; +} + +#turn-timer.low-time { + color: var(--timer-low-time-color); + animation: pulse-timer-warning 1s infinite ease-in-out; +} + +.controls-layout { + flex-grow: 1; + display: flex; + flex-direction: column; + gap: 10px; + overflow: hidden; + min-height: 0; +} + +.control-group { + flex-shrink: 0; +} + +.control-group h4 { + font-size: 0.9em; + color: var(--text-muted); + margin-bottom: 5px; + padding-bottom: 5px; + border-bottom: 1px dashed var(--panel-border); + text-transform: uppercase; + letter-spacing: 1px; +} + +.basic-actions { + display: flex; + gap: 10px; +} + +.action-button.basic { + flex: 1; + padding: 8px 5px; + font-size: 0.85em; + font-weight: bold; + background: var(--button-bg); + color: var(--button-text); + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 5px; + cursor: pointer; + transition: all 0.15s ease; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); + outline: none; +} + +.action-button.basic:hover:enabled { + background: var(--button-hover-bg); + transform: translateY(-1px); + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.5); +} + +.action-button.basic:active:enabled { + transform: translateY(0px); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); +} + +#button-attack.attack-buffed:enabled { + border: 2px solid var(--heal-color); + box-shadow: 0 0 10px 2px rgba(144, 238, 144, 0.6), 0 3px 6px rgba(0, 0, 0, 0.5); + background: linear-gradient(145deg, #70c070, #5a9a5a); + transform: translateY(-1px); +} + + +.ability-list { + flex-grow: 1; + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; +} + +.ability-list h4 { + flex-shrink: 0; +} + +.abilities-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(75px, 1fr)); + gap: 8px; + padding: 8px; + padding-bottom: 12px; + background-color: rgba(0, 0, 0, 0.2); + border-radius: 4px; + overflow-y: auto; + border: 1px solid rgba(0, 0, 0, 0.3); + flex-grow: 1; + position: relative; +} + +.abilities-grid::after { + content: ''; + display: block; + height: 10px; + width: 100%; +} + +.abilities-grid .placeholder-text { + grid-column: 1 / -1; + text-align: center; + color: var(--text-muted); + align-self: center; + font-size: 0.9em; + padding: 15px 0; +} + +.ability-button { + aspect-ratio: 1 / 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 5px; + border-radius: 6px; + background: var(--button-ability-bg); + border: 1px solid var(--button-ability-border); + color: #fff; + text-align: center; + line-height: 1.15; + cursor: pointer; + transition: all 0.2s ease-out; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.6); + position: relative; + overflow: hidden; + outline: none; +} + +.ability-button .ability-name { + font-size: 0.75em; + font-weight: bold; + margin-bottom: 2px; + display: block; + width: 95%; +} + +.ability-button .ability-desc { + font-size: 0.65em; + font-weight: normal; + color: #aaccce; + opacity: 0.8; + text-shadow: none; + max-height: 2em; + overflow: hidden; + width: 95%; + display: block; + margin-top: auto; +} + +.ability-button:hover:enabled { + transform: scale(1.03) translateY(-1px); + background: var(--button-ability-hover-bg); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5), 0 0 8px rgba(77, 176, 181, 0.4); + border-color: #77d9dd; +} + +.ability-button:active:enabled { + transform: scale(1) translateY(0); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.3); + filter: brightness(0.9); +} + +.ability-button:disabled, +.action-button.basic:disabled { + background: var(--button-disabled-bg) !important; + border-color: transparent !important; + color: var(--button-disabled-text) !important; + cursor: not-allowed !important; + transform: none !important; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.4) !important; + opacity: 0.7; + text-shadow: none !important; + filter: grayscale(50%); +} + +.ability-button.not-enough-resource { + border: 2px dashed var(--damage-color); + animation: pulse-red-border 1s infinite ease-in-out; +} +.ability-button.not-enough-resource:disabled { + border-color: var(--damage-color); + box-shadow: inset 0 0 8px rgba(255, 80, 80, 0.2), 0 3px 6px rgba(0, 0, 0, 0.2), inset 0 1px 3px rgba(0, 0, 0, 0.4); +} + +.ability-button.buff-is-active { + border: 2px solid var(--heal-color); + box-shadow: 0 0 8px rgba(144, 238, 144, 0.5); +} + +.ability-button.buff-is-active:disabled { + border-color: var(--heal-color); +} + +.ability-button.is-on-cooldown, +.ability-button.is-silenced { + filter: grayscale(70%) brightness(0.8); +} + +.ability-button.is-on-cooldown:disabled, +.ability-button.is-silenced:disabled { + filter: grayscale(70%) brightness(0.7); +} + +.ability-button.is-on-cooldown .ability-name, +.ability-button.is-silenced .ability-name, +.ability-button.is-on-cooldown .ability-desc, +.ability-button.is-silenced .ability-desc { + opacity: 0.6; +} + +.ability-button.is-on-cooldown .ability-desc, +.ability-button.is-silenced .ability-desc { + display: none; +} + +.ability-cooldown-display { + position: absolute; + bottom: 5px; + left: 0; + width: 100%; + text-align: center; + font-size: 0.75em; + font-weight: bold; + color: var(--turn-color); + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7); + pointer-events: none; + display: none; + line-height: 1; +} + +.ability-button.is-on-cooldown .ability-cooldown-display, +.ability-button.is-silenced .ability-cooldown-display { + display: block !important; +} + + +.battle-log-new { + height: var(--log-panel-fixed-height); + flex-shrink: 0; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.battle-log-new h3 { + flex-shrink: 0; + font-size: 1.4em; + margin-bottom: 10px; + text-align: center; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding-bottom: 8px; +} + +#log-list { + list-style: none; + flex-grow: 1; + overflow-y: auto; + background-color: var(--log-bg); + border: 1px solid var(--log-border); + font-size: 0.85em; + border-radius: 6px; + color: var(--log-text); + padding: 10px; + min-height: 0; + word-wrap: break-word; +} + +#log-list li { + padding: 4px 8px; + border-bottom: 1px solid rgba(74, 80, 114, 0.5); + line-height: 1.35; + transition: background-color 0.3s; +} + +#log-list li:last-child { + border-bottom: none; +} + +#log-list li:hover { + background-color: rgba(255, 255, 255, 0.03); +} +.log-damage { color: var(--damage-color); font-weight: 500; } +.log-heal { color: var(--heal-color); font-weight: 500; } +.log-block { color: var(--block-color); font-style: italic; } +.log-info { color: #b0c4de; } +.log-turn { + font-weight: bold; + color: var(--turn-color); + margin-top: 6px; + border-top: 1px solid rgba(255, 215, 0, 0.3); + padding-top: 6px; + font-size: 1.05em; + display: block; +} +.log-system { + font-weight: bold; + color: var(--system-color); + font-style: italic; + opacity: 0.8; +} +.log-effect { + font-style: italic; + color: var(--effect-color); +} + +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--modal-bg); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + backdrop-filter: blur(4px) brightness(0.7); + opacity: 0; + pointer-events: none; + transition: opacity 0.4s ease-out; +} + +.modal.hidden { + display: none !important; +} + +.modal:not(.hidden) { + opacity: 1; + pointer-events: auto; +} + +.modal-content { + background: var(--modal-content-bg); + padding: 40px 50px; + border-radius: 10px; + text-align: center; + border: 1px solid var(--panel-border); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6); + color: var(--text-light); + transform: scale(0.8) translateY(30px); + opacity: 0; + transition: transform 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.2), opacity 0.4s ease-out; +} + +.modal:not(.hidden) .modal-content { + transform: scale(1) translateY(0); + opacity: 1; +} + +.modal-content h2#result-message { + margin-bottom: 25px; + font-family: var(--font-fancy); + font-size: 2.5em; + line-height: 1.2; +} + +.modal-action-button { + padding: 12px 30px; + font-size: 1.1em; + cursor: pointer; + background: var(--button-bg); + color: var(--button-text); + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 6px; + margin-top: 20px; + font-weight: bold; + text-transform: uppercase; + letter-spacing: 1px; + transition: all 0.2s ease; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); + outline: none; +} + +.modal-action-button:hover:enabled { + background: var(--button-hover-bg); + transform: scale(1.05) translateY(-1px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.5); +} + +.modal-action-button:active:enabled { + transform: scale(1) translateY(0); + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); +} + +.modal-action-button:disabled { + background: var(--button-disabled-bg); + color: var(--button-disabled-text); + cursor: not-allowed; + opacity: 0.7; +} + +.modal-action-button i { + margin-right: 8px; +} + +@keyframes pulse-red-border { + 0%, 100% { border-color: var(--damage-color); } + 50% { border-color: #ffb3b3; } +} + +@keyframes pulse-timer-warning { + 0%, 100% { color: var(--timer-low-time-color); transform: scale(1); } + 50% { color: #ff6347; transform: scale(1.05); } +} + +@keyframes flash-effect { + 0%, 100% { + box-shadow: var(--initial-box-shadow, 0 0 15px rgba(0, 0, 0, 0.4), inset 0 0 10px rgba(0, 0, 0, 0.3)); + border-color: var(--initial-border-color, var(--panel-border)); + transform: scale(1); + } + 50% { + box-shadow: 0 0 25px 10px var(--flash-color-outer, rgba(255, 255, 255, 0.7)), + inset 0 0 15px var(--flash-color-inner, rgba(255, 255, 255, 0.4)), + 0 0 15px rgba(0, 0, 0, 0.4); + border-color: var(--flash-border-color, #ffffff); + transform: scale(1.005); + } +} + +[class*="is-casting-"] { + animation: flash-effect var(--cast-duration) ease-out; +} + +#player-panel.is-casting-heal, #opponent-panel.is-casting-heal { + --flash-color-outer: rgba(144, 238, 144, 0.7); --flash-color-inner: rgba(144, 238, 144, 0.4); + --flash-border-color: var(--heal-color); +} +#player-panel.is-casting-fireball, #opponent-panel.is-casting-fireball { + --flash-color-outer: rgba(255, 100, 100, 0.7); --flash-color-inner: rgba(255, 100, 100, 0.4); + --flash-border-color: var(--damage-color); +} +#player-panel.is-casting-shadowBolt, #opponent-panel.is-casting-shadowBolt { + --flash-color-outer: rgba(138, 43, 226, 0.6); --flash-color-inner: rgba(138, 43, 226, 0.3); + --flash-border-color: var(--dark-energy-color); +} + +@keyframes shake-opponent { + 0%, 100% { transform: translateX(0); } + 10%, 30%, 50%, 70%, 90% { transform: translateX(-4px) rotate(-0.5deg); } + 20%, 40%, 60%, 80% { transform: translateX(4px) rotate(0.5deg); } +} + +#opponent-panel.is-shaking { + animation: shake-opponent var(--shake-duration) cubic-bezier(.36, .07, .19, .97) both; + transform: translate3d(0, 0, 0); + backface-visibility: hidden; + perspective: 1000px; +} + +#opponent-panel.dissolving { + opacity: 0; + transform: scale(0.9) translateY(20px); + transition: opacity var(--dissolve-duration) ease-in, transform var(--dissolve-duration) ease-in; + pointer-events: none; +} + +@keyframes shake-short { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-3px); } + 50% { transform: translateX(3px); } + 75% { transform: translateX(-3px); } +} + +.shake-short { + animation: shake-short 0.3s ease-in-out; +} + +/* --- Отзывчивость (Медиа-запросы) --- */ +@media (max-width: 900px) { + body { + height: auto; min-height: 100vh; /* Из серверной, чтобы обеспечить высоту */ + overflow-y: auto; + padding: 5px 0; font-size: 15px; + justify-content: flex-start; + } + .auth-game-setup-wrapper { + max-height: none; + padding-top: 60px; /* Отступ для #user-info из локальной */ + } + /* === ИЗМЕНЕНИЕ: Адаптация #user-info (из локальной версии) === */ + #user-info { top: 5px; right: 10px; } + #user-info p { font-size: 0.85em; } + #logout-button { padding: 5px 10px !important; font-size: 0.75em !important; } + /* === КОНЕЦ ИЗМЕНЕНИЯ === */ + + .game-wrapper { padding: 5px; gap: 5px; height: auto; min-height: calc(100vh - 10px); width: 100%; } /* min-height и width из серверной */ + /* === ИЗМЕНЕНИЕ: game-header удален (из локальной версии) === */ + + /* Показываем кнопки переключения на мобильных - ИЗ СЕРВЕРНОЙ ВЕРСИИ */ + .panel-switcher-controls { + display: flex; + } + + .battle-arena-container { + /* flex-direction: column; height: auto; overflow: visible; - из локальной версии заменяется логикой ниже */ + gap: 0; /* Убираем отступ между колонками, т.к. они будут накладываться - ИЗ СЕРВЕРНОЙ ВЕРСИИ */ + /* position: relative; overflow: hidden; flex-grow: 1; min-height: 350px; - Эти стили уже есть глобально, но тут подтверждаем */ + } + + /* Стили для колонок при переключении - ИЗ СЕРВЕРНОЙ ВЕРСИИ */ + .player-column, + .opponent-column { + /* width: 100%; height: auto; overflow: visible; - из локальной версии заменяется логикой ниже */ + position: absolute; /* Для наложения */ + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow-y: auto; /* Прокрутка содержимого колонки */ + transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; + padding: 5px; /* Добавлено для отступов внутри колонок на мобильных */ + gap: 8px; /* Добавлено для отступов между панелями внутри колонок */ + } + + .player-column { transform: translateX(0); opacity: 1; z-index: 10; pointer-events: auto; } + .opponent-column { transform: translateX(100%); opacity: 0; z-index: 5; pointer-events: none; } + + .battle-arena-container.show-opponent-panel .player-column { transform: translateX(-100%); opacity: 0; z-index: 5; pointer-events: none; } + .battle-arena-container.show-opponent-panel .opponent-column { transform: translateX(0); opacity: 1; z-index: 10; pointer-events: auto; } + + + .fighter-panel, .controls-panel-new, .battle-log-new { + min-height: auto; /* Высота по контенту */ + height: auto; + padding: 10px; + flex-grow: 0; /* Локальное */ + flex-shrink: 1; /* Локальное */ + } + .fighter-panel { flex-shrink: 0; } /* Из серверной для panel-switcher */ + .fighter-panel .panel-content { flex-grow: 1; min-height: 0; } /* Из серверной для panel-switcher */ + + .controls-panel-new { min-height: 200px; flex-shrink: 0; } /* flex-shrink из серверной */ + .battle-log-new { height: auto; min-height: 150px; flex-shrink: 0; } /* flex-shrink из серверной */ + + #log-list { max-height: 200px; } + .abilities-grid { max-height: none; overflow-y: visible; padding-bottom: 8px; } + .abilities-grid::after { display: none; } + .ability-list, .controls-layout { overflow: visible; } /* Локальное */ + .fighter-name { font-size: 1.3em; } + .panel-content { margin-top: 10px; /* Локальное, но теперь panel-content изменен для серверного panel-switcher, возможно, не нужно */ } + .stat-bar-container .bar-icon { font-size: 1.2em; } + .bar { height: 18px; } + .effects-area, .effect { font-size: 0.85em; } + #turn-indicator { font-size: 1.2em; margin-bottom: 8px; } + .turn-timer-display { font-size: 0.85em; margin-bottom: 8px; padding: 4px; } + #turn-timer { font-size: 1em; } + .action-button.basic { font-size: 0.8em; padding: 8px 4px; } + .abilities-grid { grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); gap: 8px; padding: 8px; } + .ability-button { font-size: 0.75em; padding: 5px; } + .ability-button .ability-name { margin-bottom: 2px; } + .ability-button .ability-desc { font-size: 0.65em; } + .modal-content { padding: 25px 30px; width: 90%; max-width: 400px; } + .modal-content h2#result-message { font-size: 1.8em; } + .modal-action-button { font-size: 1em; padding: 10px 20px; } + #game-setup { max-width: 95%; padding: 15px; } + #game-setup h2 { font-size: 1.6em; } + #game-setup h3 { font-size: 1.1em; } + #game-setup button { padding: 8px 12px; font-size: 0.9em; } + #game-setup input[type="text"] { width: calc(100% - 90px); max-width: 200px; padding: 8px; } + #available-games-list { max-height: 180px; } + .character-selection label { margin: 0 10px; font-size: 1em; } +} + +@media (max-width: 480px) { + body { font-size: 14px; } + /* === ИЗМЕНЕНИЕ: Адаптация #user-info для мобильных (из локальной версии) === */ + .auth-game-setup-wrapper { padding-top: 50px; /* Еще немного места сверху */ } + #user-info { + top: 5px; + right: 5px; + display: flex; /* В одну строку */ + flex-direction: row; + align-items: center; + gap: 8px; + } + #user-info p { margin-bottom: 0; font-size: 0.8em; } + #logout-button { padding: 4px 8px !important; font-size: 0.7em !important; } + #logout-button i { margin-right: 3px; } + /* === КОНЕЦ ИЗМЕНЕНИЯ === */ + + /* Стили для panel-switcher на очень маленьких экранах - ИЗ СЕРВЕРНОЙ ВЕРСИИ */ + .panel-switch-button .button-text { display: none; } /* Скрываем текст, оставляем иконки */ + .panel-switch-button i { margin-right: 0; font-size: 1.2em; } + .panel-switch-button { padding: 6px 8px; } + + /* Локальные изменения */ + .fighter-name { font-size: 1.2em; } + .avatar-image { max-width: 40px; } /* Из серверной, но не противоречит */ + .abilities-grid { grid-template-columns: repeat(auto-fit, minmax(65px, 1fr)); gap: 5px; padding: 5px; padding-bottom: 10px; } + .ability-button { font-size: 0.7em; padding: 4px; } + .ability-button .ability-name { margin-bottom: 1px; } + .ability-button .ability-desc { display: none; } + #log-list { font-size: 0.8em; max-height: 150px; } + .modal-content { padding: 20px; } + .modal-content h2#result-message { font-size: 1.6em; } + .modal-action-button { font-size: 0.9em; padding: 8px 16px; } + .auth-game-setup-wrapper { padding-left: 15px; padding-right: 15px; } + #game-setup { padding: 10px; } + #game-setup h2 { font-size: 1.4em; } + #game-setup button { padding: 7px 10px; font-size: 0.85em; margin: 5px; } + #game-setup input[type="text"], + #game-setup button { display: block; width: 100%; margin-left: 0; margin-right: 0; } + #game-setup input[type="text"] { max-width: none; margin-bottom: 10px; } + #game-setup div>input[type="text"]+button { margin-top: 5px; } + #available-games-list { max-height: 120px; } + #available-games-list li button { font-size: 0.75em; padding: 5px 8px; } + .character-selection { padding: 10px; } + .character-selection label { margin: 0 5px 5px 5px; font-size: 0.9em; display: block; } + .character-selection label i { margin-right: 5px; } + #turn-indicator { font-size: 1.1em; } + .turn-timer-display { font-size: 0.8em; margin-top: -3px; margin-bottom: 6px; } + #turn-timer { font-size: 0.95em; } +} \ No newline at end of file diff --git a/server/auth/authService.js b/server/auth/authService.js new file mode 100644 index 0000000..f0a6e79 --- /dev/null +++ b/server/auth/authService.js @@ -0,0 +1,133 @@ +// /server/auth/authService.js +const bcrypt = require('bcryptjs'); // Для хеширования паролей +const db = require('../core/db'); // Путь к вашему модулю для работы с базой данных (в папке core) + +const SALT_ROUNDS = 10; // Количество раундов для генерации соли bcrypt + +/** + * Регистрирует нового пользователя. + * @param {string} username - Имя пользователя. + * @param {string} password - Пароль пользователя. + * @returns {Promise} Объект с результатом: { success: boolean, message: string, userId?: number, username?: string } + */ +async function registerUser(username, password) { + console.log(`[AuthService DEBUG] registerUser called with username: "${username}"`); + + if (!username || !password) { + console.warn('[AuthService DEBUG] Validation failed: Username or password empty.'); + return { success: false, message: 'Имя пользователя и пароль не могут быть пустыми.' }; + } + if (password.length < 6) { + console.warn(`[AuthService DEBUG] Validation failed for "${username}": Password too short.`); + return { success: false, message: 'Пароль должен содержать не менее 6 символов.' }; + } + + try { + // Этап A: Проверка существующего пользователя + console.log(`[AuthService DEBUG] Stage A: Checking if user "${username}" exists...`); + // Предполагаем, что db.query возвращает массив, где первый элемент - это массив строк (результатов) + const [existingUsers] = await db.query('SELECT id FROM users WHERE username = ?', [username]); + console.log(`[AuthService DEBUG] Stage A: existingUsers query result length: ${existingUsers.length}`); + + if (existingUsers.length > 0) { + console.warn(`[AuthService DEBUG] Registration declined for "${username}": Username already taken.`); + return { success: false, message: 'Это имя пользователя уже занято.' }; + } + console.log(`[AuthService DEBUG] Stage A: Username "${username}" is available.`); + + // Этап B: Хеширование пароля + console.log(`[AuthService DEBUG] Stage B: Hashing password for user "${username}"...`); + const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS); + console.log(`[AuthService DEBUG] Stage B: Password for "${username}" hashed successfully.`); + + // Этап C: Сохранение пользователя в БД + console.log(`[AuthService DEBUG] Stage C: Attempting to insert user "${username}" into DB...`); + // Предполагаем, что db.query для INSERT возвращает объект результата с insertId + const [result] = await db.query( + 'INSERT INTO users (username, password_hash) VALUES (?, ?)', + [username, hashedPassword] + ); + console.log(`[AuthService DEBUG] Stage C: DB insert result for "${username}":`, result); + + if (result && result.insertId) { + console.log(`[AuthService] Пользователь "${username}" успешно зарегистрирован с ID: ${result.insertId}.`); + return { + success: true, + message: 'Регистрация прошла успешно!', + userId: result.insertId, + username: username // Возвращаем и имя пользователя + }; + } else { + console.error(`[AuthService] Ошибка БД при регистрации пользователя "${username}": Запись не была вставлена или insertId отсутствует. Result:`, result); + return { success: false, message: 'Ошибка сервера при регистрации (данные не сохранены). Попробуйте позже.' }; + } + + } catch (error) { + console.error(`[AuthService] КРИТИЧЕСКАЯ ОШИБКА (catch block) при регистрации пользователя "${username}":`, error); + if (error.sqlMessage) { + console.error(`[AuthService] MySQL Error Message: ${error.sqlMessage}`); + console.error(`[AuthService] MySQL Error Code: ${error.code}`); + console.error(`[AuthService] MySQL Errno: ${error.errno}`); + } + return { success: false, message: 'Внутренняя ошибка сервера при регистрации.' }; + } +} + +/** + * Выполняет вход пользователя. + * @param {string} username - Имя пользователя. + * @param {string} password - Пароль пользователя. + * @returns {Promise} Объект с результатом: { success: boolean, message: string, userId?: number, username?: string } + */ +async function loginUser(username, password) { + console.log(`[AuthService DEBUG] loginUser called with username: "${username}"`); + + if (!username || !password) { + console.warn('[AuthService DEBUG] Login validation failed: Username or password empty.'); + return { success: false, message: 'Имя пользователя и пароль не могут быть пустыми.' }; + } + + try { + console.log(`[AuthService DEBUG] Searching for user "${username}" in DB...`); + const [users] = await db.query('SELECT id, username, password_hash FROM users WHERE username = ?', [username]); + console.log(`[AuthService DEBUG] DB query result for user "${username}" (length): ${users.length}`); + + if (users.length === 0) { + console.warn(`[AuthService DEBUG] Login failed: User "${username}" not found.`); + return { success: false, message: 'Неверное имя пользователя или пароль.' }; + } + + const user = users[0]; + console.log(`[AuthService DEBUG] User "${username}" found. ID: ${user.id}. Comparing password...`); + + const passwordMatch = await bcrypt.compare(password, user.password_hash); + console.log(`[AuthService DEBUG] Password comparison result for "${username}": ${passwordMatch}`); + + if (passwordMatch) { + console.log(`[AuthService] Пользователь "${user.username}" (ID: ${user.id}) успешно вошел в систему.`); + return { + success: true, + message: 'Вход выполнен успешно!', + userId: user.id, + username: user.username // Возвращаем имя пользователя + }; + } else { + console.warn(`[AuthService DEBUG] Login failed for user "${user.username}": Incorrect password.`); + return { success: false, message: 'Неверное имя пользователя или пароль.' }; + } + + } catch (error) { + console.error(`[AuthService] КРИТИЧЕСКАЯ ОШИБКА (catch block) при входе пользователя "${username}":`, error); + if (error.sqlMessage) { + console.error(`[AuthService] MySQL Error Message: ${error.sqlMessage}`); + console.error(`[AuthService] MySQL Error Code: ${error.code}`); + console.error(`[AuthService] MySQL Errno: ${error.errno}`); + } + return { success: false, message: 'Внутренняя ошибка сервера при входе.' }; + } +} + +module.exports = { + registerUser, + loginUser +}; \ No newline at end of file diff --git a/server/bc.js b/server/bc.js new file mode 100644 index 0000000..888329b --- /dev/null +++ b/server/bc.js @@ -0,0 +1,179 @@ +// /server/bc.js - Главный файл сервера Battle Club + +// Загружаем переменные окружения В САМОМ НАЧАЛЕ, до всех других импортов, +// которые могут зависеть от process.env +// Убедитесь, что путь к .env правильный относительно места запуска приложения. +// Если bc.js запускается из папки server/, а .env в корне, то путь должен быть '../.env' +// Но обычно dotenv ищет .env в process.cwd() +require('dotenv').config({ path: require('node:path').resolve(process.cwd(), '.env') }); + + +const express = require('express'); +const http = require('http'); +const { Server } = require('socket.io'); +const path = require('path'); + +// Импорт серверных модулей из их новых местоположений +const authService = require('./auth/authService'); // Сервис аутентификации +const GameManager = require('./game/GameManager'); // Менеджер игр +const db = require('./core/db'); // Модуль базы данных (важно, чтобы он тоже использовал dotenv) +const GAME_CONFIG = require('./core/config'); // Глобальный конфиг игры + +const app = express(); +const server = http.createServer(app); + +// Настройка Socket.IO +const io = new Server(server, { + cors: { + // origin: process.env.CORS_ORIGIN || "https://pavel-chagovsky.com:3200", // Пример, если нужно CORS из .env + // methods: ["GET", "POST"] + }, + // pingInterval: 10000, + // pingTimeout: 5000, +}); + +// Раздача статических файлов из папки 'public' +app.use(express.static(path.join(__dirname, '..', 'public'))); + +// Создаем экземпляр GameManager +const gameManager = new GameManager(io); + +const loggedInUsers = {}; + +io.on('connection', (socket) => { + console.log(`[Socket.IO] Пользователь подключился: ${socket.id}`); + socket.userData = null; + + socket.on('register', async (data) => { + console.log(`[Socket.IO] Register attempt for username: "${data?.username}" from ${socket.id}`); + const result = await authService.registerUser(data?.username, data?.password); + if (result.success) { + console.log(`[Socket.IO] Registration successful for ${result.username} (${result.userId})`); + } else { + console.warn(`[Socket.IO] Registration failed for "${data?.username}": ${result.message}`); + } + socket.emit('registerResponse', result); + }); + + socket.on('login', async (data) => { + console.log(`[Socket.IO] Login attempt for username: "${data?.username}" from ${socket.id}`); + const result = await authService.loginUser(data?.username, data?.password); + if (result.success && result.userId && result.username) { + console.log(`[Socket.IO] Login successful for ${result.username} (${result.userId}). Assigning to socket ${socket.id}.`); + socket.userData = { userId: result.userId, username: result.username }; + loggedInUsers[socket.id] = socket.userData; + if (gameManager && typeof gameManager.handleRequestGameState === 'function') { + gameManager.handleRequestGameState(socket, result.userId); + } + } else { + console.warn(`[Socket.IO] Login failed for "${data?.username}": ${result.message}`); + socket.userData = null; + if (loggedInUsers[socket.id]) delete loggedInUsers[socket.id]; + } + socket.emit('loginResponse', result); + }); + + socket.on('logout', () => { + const username = socket.userData?.username || 'UnknownUser'; + const userId = socket.userData?.userId; + console.log(`[Socket.IO] Logout request from user ${username} (ID: ${userId}, Socket: ${socket.id})`); + if (gameManager && typeof gameManager.handleDisconnect === 'function' && userId) { + gameManager.handleDisconnect(socket.id, userId); + } + if (loggedInUsers[socket.id]) { + delete loggedInUsers[socket.id]; + } + socket.userData = null; + console.log(`[Socket.IO] User ${username} (Socket: ${socket.id}) logged out.`); + }); + + socket.on('createGame', (data) => { + const identifier = socket.userData?.userId || socket.id; + const mode = data?.mode || 'ai'; + if (mode === 'pvp' && !socket.userData) { + socket.emit('gameError', { message: 'Необходимо войти в систему для создания PvP игры.' }); + return; + } + console.log(`[Socket.IO] Create Game from ${socket.userData?.username || socket.id} (ID: ${identifier}). Mode: ${mode}, Char: ${data?.characterKey}`); + gameManager.createGame(socket, mode, data?.characterKey, identifier); + }); + + socket.on('joinGame', (data) => { + if (!socket.userData?.userId) { + socket.emit('gameError', { message: 'Необходимо войти для присоединения к PvP игре.' }); + return; + } + console.log(`[Socket.IO] Join Game from ${socket.userData.username} (ID: ${socket.userData.userId}). GameID: ${data?.gameId}`); + gameManager.joinGame(socket, data?.gameId, socket.userData.userId); + }); + + socket.on('findRandomGame', (data) => { + if (!socket.userData?.userId) { + socket.emit('gameError', { message: 'Необходимо войти для поиска случайной PvP игры.' }); + return; + } + console.log(`[Socket.IO] Find Random Game from ${socket.userData.username} (ID: ${socket.userData.userId}). PrefChar: ${data?.characterKey}`); + gameManager.findAndJoinRandomPvPGame(socket, data?.characterKey, socket.userData.userId); + }); + + socket.on('requestPvPGameList', () => { + const availableGames = gameManager.getAvailablePvPGamesListForClient(); + socket.emit('availablePvPGamesList', availableGames); + }); + + socket.on('requestGameState', () => { + if (!socket.userData?.userId) { + socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' }); + return; + } + gameManager.handleRequestGameState(socket, socket.userData.userId); + }); + + socket.on('playerAction', (actionData) => { + const identifier = socket.userData?.userId || socket.id; + gameManager.handlePlayerAction(identifier, actionData); + }); + + socket.on('disconnect', (reason) => { + const identifier = socket.userData?.userId || socket.id; + console.log(`[Socket.IO] Пользователь отключился: ${socket.id} (Причина: ${reason}). Identifier: ${identifier}`); + gameManager.handleDisconnect(socket.id, identifier); + if (loggedInUsers[socket.id]) { + delete loggedInUsers[socket.id]; + } + }); +}); + +// Запуск HTTP сервера +// Используем переменные окружения или значения по умолчанию +const PORT = parseInt(process.env.BC_APP_PORT || '3200', 10); +const HOSTNAME = process.env.BC_APP_HOSTNAME || '127.0.0.1'; + +// Проверка, что порт является числом +if (isNaN(PORT)) { + console.error(`[Server FATAL] Некорректное значение для BC_APP_PORT: "${process.env.BC_APP_PORT}". Ожидается число.`); + process.exit(1); +} + +server.listen(PORT, HOSTNAME, () => { + console.log(`Battle Club HTTP Application Server running at http://${HOSTNAME}:${PORT}`); + if (HOSTNAME === '127.0.0.1') { + console.log(`Server is listening on localhost only. This is suitable if a reverse proxy handles external traffic.`); + } else if (HOSTNAME === '0.0.0.0') { + console.log(`Server is listening on all available network interfaces.`); + } else { + console.log(`Server is listening on a specific interface: ${HOSTNAME}.`); + } + console.log(`Environment (NODE_ENV): ${process.env.NODE_ENV || 'not set (defaults to development behavior)'}`); + console.log(`Serving static files from: ${path.join(__dirname, '..', 'public')}`); +}); + +process.on('unhandledRejection', (reason, promise) => { + console.error('[Server FATAL] Unhandled Rejection at:', promise, 'reason:', reason); + // process.exit(1); +}); + +process.on('uncaughtException', (err) => { + console.error('[Server FATAL] Uncaught Exception:', err); + process.exit(1); +}); \ No newline at end of file diff --git a/server/core/config.js b/server/core/config.js new file mode 100644 index 0000000..864355a --- /dev/null +++ b/server/core/config.js @@ -0,0 +1,111 @@ +// /server/core/config.js + +const GAME_CONFIG = { + // --- Баланс Игры --- + BLOCK_DAMAGE_REDUCTION: 0.5, // Множитель урона при блоке (0.5 = 50% снижение) + DAMAGE_VARIATION_MIN: 0.9, // Минимальный множитель урона (0.9 = 90%) + DAMAGE_VARIATION_RANGE: 0.2, // Диапазон вариации урона (0.2 = от 90% до 110%) + HEAL_VARIATION_MIN: 0.8, // Минимальный множитель лечения (0.8 = 80%) + HEAL_VARIATION_RANGE: 0.4, // Диапазон вариации лечения (0.4 = от 80% до 120%) + NATURE_STRENGTH_MANA_REGEN: 10, // Количество маны, восстанавливаемое "Силой природы" (и ее аналогом) + + // --- Условия ИИ и Игрока --- + OPPONENT_HEAL_THRESHOLD_PERCENT: 50, // Процент HP Баларда, НИЖЕ которого он будет пытаться лечиться (для AI) + PLAYER_MERCY_TAUNT_THRESHOLD_PERCENT: 60, // Процент HP Баларда, НИЖЕ которого Елена использует "доминирующие" насмешки (для AI/текстов) + PLAYER_HP_BLEED_THRESHOLD_PERCENT: 60, // % HP Елены, НИЖЕ которого Балард предпочитает Кровотечение Безмолвию (для AI) + BALARD_MANA_DRAIN_HIGH_MANA_THRESHOLD: 60, // % Маны Елены, ВЫШЕ которого Балард может использовать "Похищение Света" (для AI) + + // --- Способности Баларда (AI) - Конфигурация --- + SILENCE_DURATION: 3, // Длительность Безмолвия в ходах Елены (после хода Баларда) + SILENCE_SUCCESS_RATE: 0.7, // Шанс успеха наложения Безмолвия (70%) + BALARD_SILENCE_ABILITY_COST: 15, // Стоимость "Эха Безмолвия" в Ярости + BALARD_SILENCE_INTERNAL_COOLDOWN: 5, // К-во ходов Баларда КД ПОСЛЕ успешного использования Безмолвия + // BALARD_BLEED_COST: 15, // Если будете добавлять способность Кровотечение + // BALARD_BLEED_POWER: 5, + // BALARD_BLEED_DURATION: 2, + // BALARD_BLEED_COOLDOWN: 3, + + // --- Таймер Хода --- + TURN_DURATION_SECONDS: 60, // Длительность хода в секундах + TURN_DURATION_MS: 60 * 1000, // Длительность хода в миллисекундах + TIMER_UPDATE_INTERVAL_MS: 1000, // Интервал обновления таймера на клиенте (в мс) + + // --- Идентификаторы и Типы --- + PLAYER_ID: 'player', // Технический идентификатор для слота 'Игрок 1' + OPPONENT_ID: 'opponent', // Технический идентификатор для слота 'Игрок 2' / 'Противник' + ACTION_TYPE_HEAL: 'heal', + ACTION_TYPE_DAMAGE: 'damage', + ACTION_TYPE_BUFF: 'buff', + ACTION_TYPE_DISABLE: 'disable', // Тип для контроля (безмолвие, стан и т.п.) + ACTION_TYPE_DEBUFF: 'debuff', // Тип для ослаблений, DoT и т.п. + ACTION_TYPE_DRAIN: 'drain', // Тип для Похищения Света + + // --- Строки для UI (могут быть полезны и на сервере для логов) --- + STATUS_READY: 'Готов(а)', // Сделал универсальным + STATUS_BLOCKING: 'Защищается', + + // --- Типы Логов (для CSS классов на клиенте, и для структурирования на сервере) --- + LOG_TYPE_INFO: 'info', + LOG_TYPE_DAMAGE: 'damage', + LOG_TYPE_HEAL: 'heal', + LOG_TYPE_TURN: 'turn', + LOG_TYPE_SYSTEM: 'system', + LOG_TYPE_BLOCK: 'block', + LOG_TYPE_EFFECT: 'effect', + + // --- CSS Классы (в основном для клиента, но константы могут быть полезны для согласованности) --- + CSS_CLASS_BLOCKING: 'blocking', + CSS_CLASS_NOT_ENOUGH_RESOURCE: 'not-enough-resource', + CSS_CLASS_BUFF_IS_ACTIVE: 'buff-is-active', + CSS_CLASS_ATTACK_BUFFED: 'attack-buffed', + CSS_CLASS_SHAKING: 'is-shaking', + CSS_CLASS_CASTING_PREFIX: 'is-casting-', // Например: is-casting-fireball + CSS_CLASS_HIDDEN: 'hidden', + CSS_CLASS_ABILITY_BUTTON: 'ability-button', + CSS_CLASS_ABILITY_SILENCED: 'is-silenced', + CSS_CLASS_ABILITY_ON_COOLDOWN: 'is-on-cooldown', // Для отображения кулдауна + + // --- Задержки (в миллисекундах) --- + // Эти задержки теперь в основном будут управляться сервером при отправке событий или планировании AI ходов + DELAY_OPPONENT_TURN: 1200, // Задержка перед ходом AI + DELAY_AFTER_PLAYER_ACTION: 500, // Сервер может использовать это для паузы перед следующим событием + // DELAY_AFTER_BLOCK: 500, // Менее релевантно для сервера напрямую + DELAY_INIT: 100, // Для клиентской инициализации, если нужна + DELAY_BEFORE_VICTORY_MODAL: 1500, // Для клиента, после получения gameOver + MODAL_TRANSITION_DELAY: 10, // Для анимации модалки на клиенте + + // --- Длительности анимаций (в миллисекундах, в основном для клиента, но сервер может знать для таймингов) --- + ANIMATION_SHAKE_DURATION: 400, + ANIMATION_CAST_DURATION: 600, + ANIMATION_DISSOLVE_DURATION: 6000, // var(--dissolve-duration) из CSS + + // --- Внутренние ID способностей (для удобства в логике, нужны и на сервере, и на клиенте) --- + // Игрока (Елена) + ABILITY_ID_HEAL: 'heal', // Малое Исцеление + ABILITY_ID_FIREBALL: 'fireball', // Огненный Шар + ABILITY_ID_NATURE_STRENGTH: 'naturesStrength', // Сила Природы + ABILITY_ID_DEFENSE_AURA: 'defenseAura', // Аура Защиты + ABILITY_ID_HYPNOTIC_GAZE: 'hypnoticGaze', // Гипнотический взгляд + ABILITY_ID_SEAL_OF_WEAKNESS: 'sealOfWeakness', // Печать Слабости + + // Противника (Балард - AI) + ABILITY_ID_BALARD_HEAL: 'darkPatronage', // Покровительство Тьмы + ABILITY_ID_BALARD_SILENCE: 'echoesOfSilence', // Эхо Безмолвия + ABILITY_ID_BALARD_MANA_DRAIN: 'manaDrainHeal', // Похищение Света + // ABILITY_ID_BALARD_BLEED: 'balardBleed', // Если будете добавлять + + // Противника (Альмагест - PvP - зеркало Елены) + ABILITY_ID_ALMAGEST_HEAL: 'darkHeal', // Темное Восстановление (Аналог heal) + ABILITY_ID_ALMAGEST_DAMAGE: 'shadowBolt', // Теневой Сгусток (Аналог fireball) + ABILITY_ID_ALMAGEST_BUFF_ATTACK: 'shadowEmpowerment', // Усиление Тьмой (Аналог naturesStrength) + ABILITY_ID_ALMAGEST_BUFF_DEFENSE: 'voidShield', // Щит Пустоты (Аналог defenseAura) + ABILITY_ID_ALMAGEST_DISABLE: 'mindShatter', // Раскол Разума (Аналог hypnoticGaze) + ABILITY_ID_ALMAGEST_DEBUFF: 'curseOfDecay', // Проклятие Увядания (Аналог sealOfWeakness) +}; + +// Для использования в Node.js модулях +if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = GAME_CONFIG; +} + +// console.log("config.js loaded from server/core/ and GAME_CONFIG object created/exported."); \ No newline at end of file diff --git a/server/core/db.js b/server/core/db.js new file mode 100644 index 0000000..eb9e4b6 --- /dev/null +++ b/server/core/db.js @@ -0,0 +1,94 @@ +// /server/core/db.js +require('dotenv').config({ path: require('node:path').resolve(process.cwd(), '.env') }); // Загружаем переменные из .env в process.env +const mysql = require('mysql2'); // Используем mysql2 для поддержки промисов и улучшенной производительности + +// Конфигурация подключения к вашей базе данных MySQL +// Значения теперь берутся из переменных окружения (файла .env) +const dbConfig = { + host: process.env.DB_HOST || 'localhost', + user: process.env.DB_USER, // Обязательно должно быть задано в .env + password: process.env.DB_PASSWORD, // Обязательно должно быть задано в .env + database: process.env.DB_NAME, // Обязательно должно быть задано в .env + port: parseInt(process.env.DB_PORT || '3306', 10), // Порт по умолчанию 3306, если не указан + waitForConnections: process.env.DB_WAIT_FOR_CONNECTIONS ? (process.env.DB_WAIT_FOR_CONNECTIONS === 'true') : true, + connectionLimit: parseInt(process.env.DB_CONNECTION_LIMIT || '10', 10), + queueLimit: parseInt(process.env.DB_QUEUE_LIMIT || '0', 10) +}; + +// Проверка, что все обязательные переменные окружения для БД заданы +if (!dbConfig.user || !dbConfig.password || !dbConfig.database || !dbConfig.host) { + console.error('[DB FATAL] Не все обязательные переменные окружения для БД заданы!'); + console.error('Убедитесь, что у вас есть файл .env в корне проекта и он содержит как минимум:'); + console.error('DB_HOST, DB_USER, DB_PASSWORD, DB_NAME'); + console.error('Текущие загруженные (некоторые могут быть undefined):'); + console.error(` DB_HOST: ${process.env.DB_HOST}`); + console.error(` DB_USER: ${process.env.DB_USER}`); + console.error(` DB_PASSWORD: ${process.env.DB_PASSWORD ? '****** (задано)' : 'undefined'}`); // Не выводим пароль в лог + console.error(` DB_NAME: ${process.env.DB_NAME}`); + console.error(` DB_PORT: ${process.env.DB_PORT}`); + process.exit(1); // Завершаем приложение, так как без БД оно не сможет работать корректно. +} + +// Создаем пул соединений. +let pool; +try { + pool = mysql.createPool(dbConfig); + console.log('[DB] Пул соединений MySQL успешно создан с конфигурацией из переменных окружения.'); +} catch (error) { + console.error('[DB FATAL] Не удалось создать пул соединений MySQL. Проверьте конфигурацию и переменные окружения. Ошибка:', error); + process.exit(1); +} + +// Обертка для выполнения запросов с использованием промисов из пула +const promisePool = pool.promise(); + +// Проверка соединения (опционально, но полезно для отладки при запуске) +if (promisePool) { + promisePool.getConnection() + .then(connection => { + console.log(`[DB] Успешно подключено к базе данных MySQL (${dbConfig.database}) на ${dbConfig.host}:${dbConfig.port} и получено соединение из пула.`); + connection.release(); + console.log('[DB] Соединение возвращено в пул.'); + }) + .catch(err => { + console.error('[DB] Ошибка при попытке получить соединение из пула или при подключении к MySQL:', err.message); + // Выводим полный объект ошибки для диагностики, если это не просто ошибка конфигурации + if (err.code !== 'ER_ACCESS_DENIED_ERROR' && err.code !== 'ER_BAD_DB_ERROR' && err.code !== 'ECONNREFUSED') { + console.error('[DB] Полные детали ошибки:', err); + } + + if (err.code === 'PROTOCOL_CONNECTION_LOST') { + console.error('[DB] Соединение с БД было потеряно.'); + } else if (err.code === 'ER_CON_COUNT_ERROR') { + console.error('[DB] В БД слишком много соединений.'); + } else if (err.code === 'ECONNREFUSED') { + console.error(`[DB] Соединение с БД было отклонено. Убедитесь, что сервер MySQL запущен и доступен по адресу ${dbConfig.host}:${dbConfig.port}.`); + } else if (err.code === 'ER_ACCESS_DENIED_ERROR') { + console.error(`[DB] Доступ к БД запрещен для пользователя '${dbConfig.user}'. Проверьте имя пользователя и пароль в вашем файле .env.`); + } else if (err.code === 'ER_BAD_DB_ERROR') { + console.error(`[DB] База данных "${dbConfig.database}" не найдена. Убедитесь, что она создана на сервере MySQL и указана верно в .env (DB_NAME).`); + } else { + console.error(`[DB] Неизвестная ошибка подключения к MySQL. Код: ${err.code}`); + } + // process.exit(1); // Раскомментируйте, если хотите падать при ошибке подключения + }); +} else { + console.error('[DB FATAL] promisePool не был создан. Это не должно было случиться.'); + process.exit(1); +} + +// Экспортируем пул с промисами +module.exports = promisePool; + +/* +Пример SQL для создания таблицы пользователей (если ее еще нет): + +CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +*/ \ No newline at end of file diff --git a/server/core/logger.js b/server/core/logger.js new file mode 100644 index 0000000..1a8485d --- /dev/null +++ b/server/core/logger.js @@ -0,0 +1,93 @@ +// /server/core/logger.js + +/** + * Простой логгер-обертка. + * В будущем можно заменить на более продвинутое решение (Winston, Pino), + * сохранив этот же интерфейс. + */ + +const LOG_LEVELS = { + DEBUG: 'DEBUG', + INFO: 'INFO', + WARN: 'WARN', + ERROR: 'ERROR', + FATAL: 'FATAL' +}; + +// Можно установить минимальный уровень логирования из переменной окружения или конфига +const CURRENT_LOG_LEVEL = process.env.LOG_LEVEL || LOG_LEVELS.INFO; + +function shouldLog(level) { + const levelsOrder = [LOG_LEVELS.DEBUG, LOG_LEVELS.INFO, LOG_LEVELS.WARN, LOG_LEVELS.ERROR, LOG_LEVELS.FATAL]; + return levelsOrder.indexOf(level) >= levelsOrder.indexOf(CURRENT_LOG_LEVEL); +} + +function formatMessage(level, moduleName, message, ...optionalParams) { + const timestamp = new Date().toISOString(); + let formattedMessage = `${timestamp} [${level}]`; + if (moduleName) { + formattedMessage += ` [${moduleName}]`; + } + formattedMessage += `: ${message}`; + + // Обработка дополнительных параметров (например, объектов ошибок) + const paramsString = optionalParams.map(param => { + if (param instanceof Error) { + return `\n${param.stack || param.message}`; + } + if (typeof param === 'object') { + try { + return `\n${JSON.stringify(param, null, 2)}`; + } catch (e) { + return '\n[Unserializable Object]'; + } + } + return param; + }).join(' '); + + return `${formattedMessage}${paramsString ? ' ' + paramsString : ''}`; +} + +const logger = { + debug: (moduleName, message, ...optionalParams) => { + if (shouldLog(LOG_LEVELS.DEBUG)) { + console.debug(formatMessage(LOG_LEVELS.DEBUG, moduleName, message, ...optionalParams)); + } + }, + info: (moduleName, message, ...optionalParams) => { + if (shouldLog(LOG_LEVELS.INFO)) { + console.info(formatMessage(LOG_LEVELS.INFO, moduleName, message, ...optionalParams)); + } + }, + warn: (moduleName, message, ...optionalParams) => { + if (shouldLog(LOG_LEVELS.WARN)) { + console.warn(formatMessage(LOG_LEVELS.WARN, moduleName, message, ...optionalParams)); + } + }, + error: (moduleName, message, ...optionalParams) => { + if (shouldLog(LOG_LEVELS.ERROR)) { + console.error(formatMessage(LOG_LEVELS.ERROR, moduleName, message, ...optionalParams)); + } + }, + fatal: (moduleName, message, ...optionalParams) => { // Fatal обычно означает, что приложение не может продолжать работу + if (shouldLog(LOG_LEVELS.FATAL)) { + console.error(formatMessage(LOG_LEVELS.FATAL, moduleName, message, ...optionalParams)); + // В реальном приложении здесь может быть process.exit(1) после логирования + } + }, + // Generic log function if needed, defaults to INFO + log: (moduleName, message, ...optionalParams) => { + logger.info(moduleName, message, ...optionalParams); + } +}; + +module.exports = logger; + +/* +Пример использования в другом файле: +const logger = require('../core/logger'); // Путь зависит от местоположения + +logger.info('GameManager', 'Новая игра создана', { gameId: '123', mode: 'pvp' }); +logger.error('AuthService', 'Ошибка аутентификации пользователя', new Error('Пароль неверный')); +logger.debug('GameInstance', 'Состояние игрока обновлено:', playerStateObject); +*/ \ No newline at end of file diff --git a/server/data/characterAbilities.js b/server/data/characterAbilities.js new file mode 100644 index 0000000..c90b926 --- /dev/null +++ b/server/data/characterAbilities.js @@ -0,0 +1,178 @@ +// /server/data/characterAbilities.js + +const GAME_CONFIG = require('../core/config'); // Путь к конфигу из server/data/ в server/core/ + +// Способности Игрока (Елена) +const elenaAbilities = [ + { + id: GAME_CONFIG.ABILITY_ID_HEAL, + name: 'Малое Исцеление', + cost: 20, + type: GAME_CONFIG.ACTION_TYPE_HEAL, + power: 30, + description: 'Восстанавливает ~30 HP' + }, + { + id: GAME_CONFIG.ABILITY_ID_FIREBALL, + name: 'Огненный Шар', + cost: 30, + type: GAME_CONFIG.ACTION_TYPE_DAMAGE, + power: 25, + description: 'Наносит ~25 урона врагу' + }, + { + id: GAME_CONFIG.ABILITY_ID_NATURE_STRENGTH, + name: 'Сила Природы', + cost: 15, + type: GAME_CONFIG.ACTION_TYPE_BUFF, + duration: 4, // Общая длительность эффекта + // Описание теперь может использовать configToUse (который будет GAME_CONFIG) + descriptionFunction: (configToUse, opponentBaseStats) => `Восст. ${configToUse.NATURE_STRENGTH_MANA_REGEN} маны при след. атаке. Эффект длится ${4 - 1} хода после применения.`, + isDelayed: true // Этот эффект применяется ПОСЛЕ следующей атаки, а не сразу + }, + { + id: GAME_CONFIG.ABILITY_ID_DEFENSE_AURA, + name: 'Аура Защиты', + cost: 15, + type: GAME_CONFIG.ACTION_TYPE_BUFF, + duration: 3, + grantsBlock: true, // Дает эффект блока на время действия + descriptionFunction: (configToUse, opponentBaseStats) => `Снижает урон на ${configToUse.BLOCK_DAMAGE_REDUCTION * 100}% (${3} хода)` + }, + { + id: GAME_CONFIG.ABILITY_ID_HYPNOTIC_GAZE, + name: 'Гипнотический взгляд', + cost: 30, + type: GAME_CONFIG.ACTION_TYPE_DISABLE, + effectDuration: 2, // Длительность безмолвия в ходах противника + cooldown: 6, + power: 5, // Урон в ход от взгляда + description: 'Накладывает на противника полное безмолвие на 2 хода и наносит 5 урона каждый его ход. КД: 6 х.' + }, + { + id: GAME_CONFIG.ABILITY_ID_SEAL_OF_WEAKNESS, + name: 'Печать Слабости', + cost: 30, + type: GAME_CONFIG.ACTION_TYPE_DEBUFF, + effectDuration: 3, // Длительность дебаффа + power: 10, // Количество ресурса противника, сжигаемое каждый ход + cooldown: 5, + // Описание теперь может адаптироваться к ресурсу оппонента + descriptionFunction: (configToUse, oppStats) => `Накладывает печать, сжигающую 10 ${oppStats ? oppStats.resourceName : 'ресурса'} противника каждый его ход в течение 3 ходов. КД: 5 х.` + } +]; + +// Способности Противника (Балард - AI) +const balardAbilities = [ + { + id: GAME_CONFIG.ABILITY_ID_BALARD_HEAL, + name: 'Покровительство Тьмы', + cost: 20, + type: GAME_CONFIG.ACTION_TYPE_HEAL, + power: 25, + successRate: 0.60, // Шанс успеха + description: 'Исцеляет ~25 HP с 60% шансом', + // Условие для AI: HP ниже порога + condition: (opSt, plSt, currentGameState, configToUse) => { + return (opSt.currentHp / opSt.maxHp) * 100 < configToUse.OPPONENT_HEAL_THRESHOLD_PERCENT; + } + }, + { + id: GAME_CONFIG.ABILITY_ID_BALARD_SILENCE, + name: 'Эхо Безмолвия', + cost: GAME_CONFIG.BALARD_SILENCE_ABILITY_COST, + type: GAME_CONFIG.ACTION_TYPE_DISABLE, + descriptionFunction: (configToUse, opponentBaseStats) => `Шанс ${configToUse.SILENCE_SUCCESS_RATE * 100}% заглушить случайное заклинание Елены на ${configToUse.SILENCE_DURATION} х.`, + condition: (opSt, plSt, currentGameState, configToUse) => { + const hpPercent = (opSt.currentHp / opSt.maxHp) * 100; + const isElenaAlreadySilenced = currentGameState?.player.disabledAbilities?.length > 0 || + currentGameState?.player.activeEffects?.some(eff => eff.id.startsWith('playerSilencedOn_')); // Проверяем и специфичное, и общее безмолвие на цели + const isElenaFullySilenced = currentGameState?.player.activeEffects?.some(eff => eff.isFullSilence && eff.turnsLeft > 0); + + return hpPercent >= configToUse.OPPONENT_HEAL_THRESHOLD_PERCENT && !isElenaAlreadySilenced && !isElenaFullySilenced && (opSt.silenceCooldownTurns === undefined || opSt.silenceCooldownTurns <= 0); + }, + successRateFromConfig: 'SILENCE_SUCCESS_RATE', + durationFromConfig: 'SILENCE_DURATION', + internalCooldownFromConfig: 'BALARD_SILENCE_INTERNAL_COOLDOWN' + }, + { + id: GAME_CONFIG.ABILITY_ID_BALARD_MANA_DRAIN, + name: 'Похищение Света', + cost: 10, + type: GAME_CONFIG.ACTION_TYPE_DRAIN, + powerManaDrain: 5, + powerDamage: 5, + powerHealthGainFactor: 1.0, + description: `Вытягивает 5 Маны у Елены, наносит 5 урона и восстанавливает себе здоровье (100% от украденного).`, + condition: (opSt, plSt, currentGameState, configToUse) => { + const playerManaPercent = (plSt.currentResource / plSt.maxResource) * 100; + const playerHasHighMana = playerManaPercent > (configToUse.BALARD_MANA_DRAIN_HIGH_MANA_THRESHOLD || 60); + return playerHasHighMana && (opSt.manaDrainCooldownTurns === undefined || opSt.manaDrainCooldownTurns <= 0); + }, + internalCooldownValue: 3 + } +]; + +// Способности Альмагест (PvP - зеркало Елены) +const almagestAbilities = [ + { + id: GAME_CONFIG.ABILITY_ID_ALMAGEST_HEAL, + name: 'Темное Восстановление', + cost: 20, + type: GAME_CONFIG.ACTION_TYPE_HEAL, + power: 30, + description: 'Поглощает жизненные тени, восстанавливая ~30 HP' + }, + { + id: GAME_CONFIG.ABILITY_ID_ALMAGEST_DAMAGE, + name: 'Теневой Сгусток', + cost: 30, + type: GAME_CONFIG.ACTION_TYPE_DAMAGE, + power: 25, + description: 'Запускает сгусток чистой тьмы, нанося ~25 урона врагу' + }, + { + id: GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_ATTACK, + name: 'Усиление Тьмой', + cost: 15, + type: GAME_CONFIG.ACTION_TYPE_BUFF, + duration: 4, + descriptionFunction: (configToUse, opponentBaseStats) => `Восст. ${configToUse.NATURE_STRENGTH_MANA_REGEN} Темной Энергии при след. атаке. Эффект длится ${4 - 1} хода после применения.`, + isDelayed: true + }, + { + id: GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_DEFENSE, + name: 'Щит Пустоты', + cost: 15, + type: GAME_CONFIG.ACTION_TYPE_BUFF, + duration: 3, + grantsBlock: true, + descriptionFunction: (configToUse, opponentBaseStats) => `Создает щит, снижающий урон на ${configToUse.BLOCK_DAMAGE_REDUCTION * 100}% (${3} хода)` + }, + { + id: GAME_CONFIG.ABILITY_ID_ALMAGEST_DISABLE, + name: 'Раскол Разума', + cost: 30, + type: GAME_CONFIG.ACTION_TYPE_DISABLE, + effectDuration: 2, + cooldown: 6, + power: 5, + description: 'Вторгается в разум противника, накладывая полное безмолвие на 2 хода и нанося 5 урона каждый его ход. КД: 6 х.' + }, + { + id: GAME_CONFIG.ABILITY_ID_ALMAGEST_DEBUFF, + name: 'Проклятие Увядания', + cost: 30, + type: GAME_CONFIG.ACTION_TYPE_DEBUFF, + effectDuration: 3, + power: 10, + cooldown: 5, + descriptionFunction: (configToUse, oppStats) => `Накладывает проклятие, истощающее 10 ${oppStats ? oppStats.resourceName : 'ресурса'} противника каждый его ход в течение 3 ходов. КД: 5 х.` + } +]; + +module.exports = { + elenaAbilities, + balardAbilities, + almagestAbilities +}; \ No newline at end of file diff --git a/server/data/characterStats.js b/server/data/characterStats.js new file mode 100644 index 0000000..3695bc4 --- /dev/null +++ b/server/data/characterStats.js @@ -0,0 +1,47 @@ +// /server/data/characterStats.js + +const GAME_CONFIG = require('../core/config'); // Путь к конфигу из server/data/ в server/core/ + +// --- Базовые Статы Персонажей --- + +const elenaBaseStats = { + id: GAME_CONFIG.PLAYER_ID, // Технический ID слота (может быть player или opponent в PvP) + characterKey: 'elena', // Уникальный ключ персонажа + name: "Елена", + maxHp: 120, + maxResource: 150, + attackPower: 15, + resourceName: "Мана", + avatarPath: 'images/elena_avatar.webp' // Путь к аватару +}; + +const balardBaseStats = { // Балард (для AI и, возможно, PvP) + id: GAME_CONFIG.OPPONENT_ID, // Технический ID слота (обычно opponent) + characterKey: 'balard', // Уникальный ключ персонажа + name: "Балард", + maxHp: 140, + maxResource: 100, + attackPower: 20, + resourceName: "Ярость", + avatarPath: 'images/balard_avatar.jpg' // Путь к аватару +}; + +const almagestBaseStats = { // Альмагест (для PvP) + id: GAME_CONFIG.OPPONENT_ID, // Технический ID слота (может быть player или opponent в PvP) + characterKey: 'almagest', // Уникальный ключ персонажа + name: "Альмагест", + maxHp: 120, // Статы как у Елены для зеркальности + maxResource: 150, + attackPower: 15, + resourceName: "Темная Энергия", + avatarPath: 'images/almagest_avatar.webp' // Путь к аватару +}; + +// Можно добавить других персонажей здесь, если потребуется + +module.exports = { + elenaBaseStats, + balardBaseStats, + almagestBaseStats + // ...и другие персонажи +}; \ No newline at end of file diff --git a/server/data/dataUtils.js b/server/data/dataUtils.js new file mode 100644 index 0000000..e9f0c9b --- /dev/null +++ b/server/data/dataUtils.js @@ -0,0 +1,72 @@ +// /server/data/dataUtils.js + +// Импортируем непосредственно определенные статы и способности +const { elenaBaseStats, balardBaseStats, almagestBaseStats } = require('./characterStats'); +const { elenaAbilities, balardAbilities, almagestAbilities } = require('./characterAbilities'); +// const { tauntSystem } = require('./taunts'); // Если нужны утилиты для насмешек + +/** + * Получает полный набор данных для персонажа по его ключу. + * Включает базовые статы и список способностей. + * @param {string} characterKey - Ключ персонажа ('elena', 'balard', 'almagest'). + * @returns {{baseStats: object, abilities: Array}|null} Объект с данными или null, если ключ неизвестен. + */ +function getCharacterData(characterKey) { + if (!characterKey) { + console.warn("[DataUtils] getCharacterData_called_with_null_or_undefined_key"); + return null; + } + switch (characterKey.toLowerCase()) { // Приводим к нижнему регистру для надежности + case 'elena': + return { baseStats: elenaBaseStats, abilities: elenaAbilities }; + case 'balard': + return { baseStats: balardBaseStats, abilities: balardAbilities }; + case 'almagest': + return { baseStats: almagestBaseStats, abilities: almagestAbilities }; + default: + console.error(`[DataUtils] getCharacterData: Unknown character key "${characterKey}"`); + return null; + } +} + +/** + * Получает только базовые статы для персонажа по его ключу. + * @param {string} characterKey - Ключ персонажа. + * @returns {object|null} Объект базовых статов или null. + */ +function getCharacterBaseStats(characterKey) { + const charData = getCharacterData(characterKey); + return charData ? charData.baseStats : null; +} + +/** + * Получает только список способностей для персонажа по его ключу. + * @param {string} characterKey - Ключ персонажа. + * @returns {Array|null} Массив способностей или null. + */ +function getCharacterAbilities(characterKey) { + const charData = getCharacterData(characterKey); + return charData ? charData.abilities : null; +} + +/** + * Получает имя персонажа по его ключу. + * @param {string} characterKey - Ключ персонажа. + * @returns {string|null} Имя персонажа или null. + */ +function getCharacterName(characterKey) { + const baseStats = getCharacterBaseStats(characterKey); + return baseStats ? baseStats.name : null; +} + +// Можно добавить другие утилитарные функции по мере необходимости, +// например, для поиска конкретной способности по ID у персонажа, +// или для получения данных для инициализации gameState и т.д. + +module.exports = { + getCharacterData, + getCharacterBaseStats, + getCharacterAbilities, + getCharacterName + // ...другие экспортируемые утилиты +}; \ No newline at end of file diff --git a/server/data/index.js b/server/data/index.js new file mode 100644 index 0000000..b126ba9 --- /dev/null +++ b/server/data/index.js @@ -0,0 +1,75 @@ +// /server/data/index.js + +// Импортируем отдельные части игровых данных +const { elenaBaseStats, balardBaseStats, almagestBaseStats } = require('./characterStats'); +const { elenaAbilities, balardAbilities, almagestAbilities } = require('./characterAbilities'); +const { tauntSystem } = require('./taunts'); // Предполагается, что taunts.js экспортирует объект tauntSystem + +// Собираем все данные в один объект gameData, +// который будет использоваться в других частях серверной логики (например, gameLogic, GameInstance). +// Эта структура аналогична той, что была в вашем исходном большом файле data.js. +const gameData = { + // Базовые статы персонажей по их ключам для удобного доступа + // (хотя dataUtils.js теперь предоставляет функции для этого, + // можно оставить и такую структуру для обратной совместимости или прямого доступа, если нужно) + baseStats: { + elena: elenaBaseStats, + balard: balardBaseStats, + almagest: almagestBaseStats + }, + + // Способности персонажей по их ключам + abilities: { + elena: elenaAbilities, + balard: balardAbilities, + almagest: almagestAbilities + }, + + // Система насмешек + tauntSystem: tauntSystem, + + + // Если вы хотите сохранить оригинальную структуру вашего предыдущего data.js, + // где были прямые ссылки на playerBaseStats, opponentBaseStats и т.д., + // вы можете добавить их сюда. Однако, с новой структурой dataUtils.js + // это становится менее необходимым, так как dataUtils предоставляет + // функции для получения данных по characterKey. + // Для примера, если бы playerBaseStats всегда был Елена, а opponentBaseStats всегда Балард: + // playerBaseStats: elenaBaseStats, // Обычно Елена + // opponentBaseStats: balardBaseStats, // Обычно Балард (AI) + // almagestBaseStats: almagestBaseStats, // Для Альмагест (PvP) + // playerAbilities: elenaAbilities, + // opponentAbilities: balardAbilities, // Способности Баларда (AI) + // almagestAbilities: almagestAbilities, + + // Рекомендуемый подход: экспортировать данные, сгруппированные по персонажам, + // а для получения данных конкретного "игрока" или "оппонента" в игре + // использовать dataUtils.getCharacterData(characterKey) в GameInstance/GameManager. + // Это более гибко, так как в PvP Елена может быть оппонентом, а Альмагест - игроком. +}; + +// Экспортируем собранный объект gameData +module.exports = gameData; + +/* +Примечание: +В GameInstance, GameManager, gameLogic и других модулях, где раньше был: +const gameData = require('./data'); // или другой путь к старому data.js + +Теперь будет: +const gameData = require('../data'); // или '../data/index.js' - Node.js поймет и так +или +const dataUtils = require('../data/dataUtils'); + +И если вы используете gameData напрямую: +const elenaStats = gameData.baseStats.elena; +const balardAbils = gameData.abilities.balard; + +Если используете dataUtils: +const elenaFullData = dataUtils.getCharacterData('elena'); +const balardAbils = dataUtils.getCharacterAbilities('balard'); + +Выбор зависит от того, насколько гранулированный доступ вам нужен в каждом конкретном месте. +Объект gameData, экспортируемый этим файлом, может быть полезен для gameLogic, +где функции могут ожидать всю структуру данных сразу. +*/ \ No newline at end of file diff --git a/server/data/taunts.js b/server/data/taunts.js new file mode 100644 index 0000000..d0f5fce --- /dev/null +++ b/server/data/taunts.js @@ -0,0 +1,118 @@ +// /server/data/taunts.js + +// Предполагается, что GAME_CONFIG будет доступен в контексте, где используются эти насмешки, +// обычно он передается в функции игровой логики (например, serverGameLogic.getRandomTaunt). +// Если вы хотите использовать GAME_CONFIG.ABILITY_ID_... прямо здесь, вам нужно его импортировать: +const GAME_CONFIG = require('../core/config'); // Путь к конфигу + +const tauntSystem = { + elena: { // Насмешки Елены + balard: { // Против Баларда (AI) + // Триггер: Елена использует СВОЮ способность + selfCastAbility: { + [GAME_CONFIG.ABILITY_ID_HEAL]: [ "Свет лечит, Балард. Но не искаженную завистью искру.", "Я черпаю силы в Истине." ], + [GAME_CONFIG.ABILITY_ID_FIREBALL]: [ "Прими очищающее пламя Света!", "Пусть твой мрак сгорит!" ], + [GAME_CONFIG.ABILITY_ID_NATURE_STRENGTH]: [ "Сама земля отвергает тебя, я черпаю её силу!", "Гармония природы со мной." ], + [GAME_CONFIG.ABILITY_ID_DEFENSE_AURA]: [ "Порядок восторжествует над твоим хаосом.", "Моя вера - моя защита." ], + [GAME_CONFIG.ABILITY_ID_HYPNOTIC_GAZE]: [ "Смотри мне в глаза, Балард. И слушай тишину.", "Твой разум - в моей власти." ], + [GAME_CONFIG.ABILITY_ID_SEAL_OF_WEAKNESS]: [ "Твоя ярость иссякнет, как вода в песке, Балард!", "Твоя сила угасает." ] + }, + // Триггер: Противник (Балард) совершает действие + onOpponentAction: { + [GAME_CONFIG.ABILITY_ID_BALARD_HEAL]: [ "Пытаешься отсрочить неизбежное жалкой темной силой?" ], + [GAME_CONFIG.ABILITY_ID_BALARD_SILENCE]: { // Реакция на "Эхо Безмолвия" Баларда + success: [ "(Сдавленный вздох)... Ничтожная попытка заглушить Слово!" ], // Если Балард успешно заглушил Елену + fail: [ "Твой шепот Тьмы слаб против Света Истины!" ] // Если попытка Баларда провалилась + }, + [GAME_CONFIG.ABILITY_ID_BALARD_MANA_DRAIN]: [ "Ты питаешься Светом, как паразит?!" ], + // Эти два триггера используются, когда АТАКА ОППОНЕНТА (Баларда) попадает по Елене или блокируется Еленой + attackBlocked: [ "Твои удары тщетны перед щитом Порядка." ], // Елена блокирует атаку Баларда + attackHits: [ "(Шипение боли)... Боль – лишь напоминание о твоем предательстве." ] // Атака Баларда попадает по Елене + }, + // Триггер: Базовая атака Елены + basicAttack: { + // 'merciful' и 'dominating' используются в gameLogic.getRandomTaunt в зависимости от HP Баларда + merciful: [ "Балард, прошу, остановись. Еще не поздно.", "Подумай о том, что потерял." ], + dominating: [ + "Глина не спорит с гончаром, Балард!", + "Ты ИЗБРАЛ эту гниль! Получай возмездие!", + "Самый страшный грех - грех неблагодарности!", + "Я сотру тебя с лика этой земли!" + ], + general: [ // Общие фразы, если специфичные не подходят (например, если PLAYER_MERCY_TAUNT_THRESHOLD_PERCENT не используется) + "Свет покарает тебя, Балард!", + "За все свои деяния ты ответишь!" + ] + }, + // Триггер: Изменение состояния боя + onBattleState: { + start: [ "Балард, есть ли еще путь назад?" ], // Начало AI боя с Балардом + opponentNearDefeat: [ "Конец близок, Балард. Прими свою судьбу." ] // Балард почти побежден + } + }, + almagest: { // Против Альмагест (PvP) + selfCastAbility: { + [GAME_CONFIG.ABILITY_ID_HEAL]: [ "Я исцеляюсь Светом, который ты отвергла.", "Жизнь восторжествует над твоей некромантией!", "Мое сияние не померкнет." ], + [GAME_CONFIG.ABILITY_ID_FIREBALL]: [ "Очищающий огонь для твоей тьмы!", "Почувствуй гнев праведного Света!", "Это пламя ярче твоих теней!" ], + [GAME_CONFIG.ABILITY_ID_NATURE_STRENGTH]: [ "Природа дает мне силу, а тебе - лишь презрение.", "Я черпаю из источника жизни, ты - из могилы." ], + [GAME_CONFIG.ABILITY_ID_DEFENSE_AURA]: [ "Мой щит отразит твою злобу.", "Свет - лучшая защита.", "Твои темные чары не пройдут!" ], + [GAME_CONFIG.ABILITY_ID_HYPNOTIC_GAZE]: [ "Смотри в глаза Истине, колдунья!", "Твои лживые речи умолкнут!", "Хватит прятаться за иллюзиями!" ], + [GAME_CONFIG.ABILITY_ID_SEAL_OF_WEAKNESS]: [ "Твоя темная сила иссякнет!", "Я ослабляю твою связь с бездной!", "Почувствуй, как тает твоя энергия!" ] + }, + onOpponentAction: { // Реакции Елены на действия Альмагест + [GAME_CONFIG.ABILITY_ID_ALMAGEST_HEAL]: [ "Лечишь раны тьмой? Она лишь глубже проникнет в тебя.", "Твоя магия несет лишь порчу, даже исцеляя." ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_DAMAGE]: [ "Твоя тень лишь царапает, не ранит.", "Слабый удар! Тьма делает тебя немощной." ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_ATTACK]: [ "Черпаешь силы из бездны? Она поглотит и тебя.", "Твое усиление - лишь агония искаженной энергии." ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_DEFENSE]: [ "Щит из теней? Он рассыпется прахом!", "Твоя защита иллюзорна, как и твоя сила." ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_DISABLE]: [ "(Сдавленно) Твои ментальные атаки отвратительны!", "Тьма в моей голове... я вырвусь!" ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_DEBUFF]: [ "Истощаешь мою силу? Я восстановлю ее Светом!", "Твое проклятие слабо." ], + attackBlocked: [ "Твоя атака разбилась о мой щит Света!", "Предсказуемо и слабо, Альмагест." ], + attackHits: [ "(Резкий вздох) Коснулась... Но Свет исцелит рану.", "Эта царапина - ничто!", "Ты заплатишь за это!" ] + }, + basicAttack: { + general: [ "Тьма не победит, Альмагест!", "Твои иллюзии рассеются перед Светом!", "Пока я стою, порядок будет восстановлен!" ] + }, + onBattleState: { + start: [ "Альмагест! Твоим темным делам пришел конец!", "Во имя Света, я остановлю тебя!", "Приготовься к битве, служительница тьмы!" ], + opponentNearDefeat: [ "Твоя тьма иссякает, колдунья!", "Сдавайся, пока Свет не испепелил тебя!", "Конец твоим злодеяниям близок!" ] + } + } + }, + almagest: { // Насмешки Альмагест + elena: { // Против Елены (PvP) + selfCastAbility: { + [GAME_CONFIG.ABILITY_ID_ALMAGEST_HEAL]: [ "Я питаюсь слабостью, Елена!", "Тьма дает мне силу!" ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_DAMAGE]: [ "Почувствуй холод бездны!", "Твой Свет померкнет перед моей тенью!" ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_ATTACK]: [ "Силы Бездны со мной!", "Моя тень становится гуще!" ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_DEFENSE]: [ "Мой щит выкован из самой тьмы!", "Попробуй пробить это, служительница Света!" ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_DISABLE]: [ "Твой разум сломлен!", "Умолкни, Светлая!", "Я владею твоими мыслями!" ], + [GAME_CONFIG.ABILITY_ID_ALMAGEST_DEBUFF]: [ "Твоя сила тает!", "Почувствуй гниль!", "Я истощаю твой Свет!" ] + }, + onOpponentAction: { // Реакции Альмагест на действия Елены + [GAME_CONFIG.ABILITY_ID_HEAL]: [ "Исцеляешься? Твои раны слишком глубоки!" ], + [GAME_CONFIG.ABILITY_ID_FIREBALL]: [ "Жалкое пламя! Мои тени поглотят его!" ], + [GAME_CONFIG.ABILITY_ID_NATURE_STRENGTH]: [ "Сила земли? Смешно! Бездну ничто не остановит." ], + [GAME_CONFIG.ABILITY_ID_DEFENSE_AURA]: [ "Твой щит из Света не спасет тебя от Тьмы!" ], + [GAME_CONFIG.ABILITY_ID_HYPNOTIC_GAZE]: [ "(Сдавленно, затем смех) Попытка управлять моим разумом? Жалко!", "Ты пытаешься заглянуть в Бездну?!" ], + [GAME_CONFIG.ABILITY_ID_SEAL_OF_WEAKNESS]: [ "Моя энергия вечна, дура!", "Это лишь раздражение!" ], + attackBlocked: [ "Твой блок не спасет тебя вечно, Елена!", "Это лишь задержка." ], + attackHits: [ "Ха! Чувствуешь силу Тьмы?", "Это только начало!", "Слабость!" ] + }, + basicAttack: { + general: [ "Почувствуй мою силу!", "Тени атакуют!", "Я наношу удар!" ] + }, + onBattleState: { + start: [ "Тысяча лет в заточении лишь усилили меня, Елена!", "Твой Свет скоро погаснет!", "Пора положить конец твоему господству!" ], + opponentNearDefeat: [ "Твой Свет гаснет!", "Ты побеждена!", "Бездне нужен твой дух!" ] + } + } + // Можно добавить секцию для Альмагест против Баларда, если такой бой возможен и нужен + // balard: { ... } + } + // Балард пока не имеет своей системы насмешек (он AI и его "реплики" могут быть частью логов его действий) + // Если Балард станет играбельным PvP персонажем, сюда можно будет добавить секцию balard: { elena: {...}, almagest: {...} } +}; + +module.exports = { + tauntSystem +}; \ No newline at end of file diff --git a/server/game/GameManager.js b/server/game/GameManager.js new file mode 100644 index 0000000..6e15e5a --- /dev/null +++ b/server/game/GameManager.js @@ -0,0 +1,343 @@ +// /server/game/GameManager.js +const { v4: uuidv4 } = require('uuid'); +const GameInstance = require('./instance/GameInstance'); +const dataUtils = require('../data/dataUtils'); +const GAME_CONFIG = require('../core/config'); + +class GameManager { + constructor(io) { + this.io = io; + this.games = {}; + this.userIdentifierToGameId = {}; + this.pendingPvPGames = []; + console.log("[GameManager] Initialized."); + } + + _removePreviousPendingGames(currentSocketId, identifier, excludeGameId = null) { + const oldPendingGameId = this.userIdentifierToGameId[identifier]; + if (oldPendingGameId && oldPendingGameId !== excludeGameId && this.games[oldPendingGameId]) { + const gameToRemove = this.games[oldPendingGameId]; + if (gameToRemove.mode === 'pvp' && gameToRemove.playerCount === 1 && this.pendingPvPGames.includes(oldPendingGameId)) { + const oldOwnerInfo = Object.values(gameToRemove.players).find(p => p.id === GAME_CONFIG.PLAYER_ID); + if (oldOwnerInfo && (oldOwnerInfo.identifier === identifier)) { + console.log(`[GameManager] Пользователь ${identifier} (сокет: ${currentSocketId}) создал/присоединился к новой игре. Удаляем его предыдущую ожидающую игру: ${oldPendingGameId}`); + this._cleanupGame(oldPendingGameId, 'replaced_by_new_game'); + } + } + } + } + + createGame(socket, mode = 'ai', chosenCharacterKey = 'elena', identifier) { + this._removePreviousPendingGames(socket.id, identifier); + if (this.userIdentifierToGameId[identifier] && this.games[this.userIdentifierToGameId[identifier]]) { + socket.emit('gameError', { message: 'Вы уже находитесь в активной или ожидающей игре.' }); + this.handleRequestGameState(socket, identifier); + return; + } + + const gameId = uuidv4(); + const game = new GameInstance(gameId, this.io, mode, this); + game.ownerIdentifier = identifier; // Устанавливаем владельца игры + this.games[gameId] = game; + const charKeyForInstance = (mode === 'pvp') ? chosenCharacterKey : 'elena'; + + if (game.addPlayer(socket, charKeyForInstance, identifier)) { + this.userIdentifierToGameId[identifier] = gameId; + console.log(`[GameManager] Игра создана: ${gameId} (режим: ${mode}) игроком ${identifier} (выбран: ${charKeyForInstance})`); + const assignedPlayerId = game.players[socket.id]?.id; + + if (!assignedPlayerId) { + this._cleanupGame(gameId, 'player_add_failed_no_role'); + socket.emit('gameError', { message: 'Ошибка сервера при создании игры (роль).' }); + return; + } + socket.emit('gameCreated', { gameId: gameId, mode: mode, yourPlayerId: assignedPlayerId }); + + if ((game.mode === 'ai' && game.playerCount === 1) || (game.mode === 'pvp' && game.playerCount === 2)) { + const isInitialized = game.initializeGame(); + if (isInitialized) game.startGame(); + else this._cleanupGame(gameId, 'initialization_failed_on_create'); + + if (game.mode === 'pvp' && game.playerCount === 2) { // Если PvP заполнилась + const idx = this.pendingPvPGames.indexOf(gameId); + if (idx > -1) this.pendingPvPGames.splice(idx, 1); + this.broadcastAvailablePvPGames(); + } + } else if (mode === 'pvp' && game.playerCount === 1) { + if (!this.pendingPvPGames.includes(gameId)) this.pendingPvPGames.push(gameId); + game.initializeGame(); + socket.emit('waitingForOpponent'); + this.broadcastAvailablePvPGames(); + } + } else { + this._cleanupGame(gameId, 'player_add_failed_instance'); + } + } + + joinGame(socket, gameId, identifier) { // identifier - это userId присоединяющегося + const game = this.games[gameId]; + if (!game) { socket.emit('gameError', { message: 'Игра с таким ID не найдена.' }); return; } + if (game.mode !== 'pvp') { socket.emit('gameError', { message: 'К этой игре нельзя присоединиться как к PvP.' }); return; } + if (game.playerCount >= 2) { socket.emit('gameError', { message: 'Эта PvP игра уже заполнена.' }); return; } + + // === ИЗМЕНЕНИЕ: Запрет присоединения к своей же игре === + if (game.ownerIdentifier === identifier) { + socket.emit('gameError', { message: 'Вы не можете присоединиться к игре, которую сами создали и ожидаете.' }); + // Можно отправить состояние этой игры, если она действительно ожидает + this.handleRequestGameState(socket, identifier); + return; + } + // === КОНЕЦ ИЗМЕНЕНИЯ === + + if (this.userIdentifierToGameId[identifier] && this.userIdentifierToGameId[identifier] !== gameId) { + socket.emit('gameError', { message: 'Вы уже находитесь в другой активной игре.' }); + this.handleRequestGameState(socket, identifier); + return; + } + // Проверка на случай, если игрок пытается присоединиться к игре, где он уже есть (хотя ownerIdentifier проверка выше это частично покрывает для создателя) + const existingPlayerInThisGame = Object.values(game.players).find(p => p.identifier === identifier); + if (existingPlayerInThisGame) { + socket.emit('gameError', { message: 'Вы уже находитесь в этой игре.' }); + this.handleRequestGameState(socket, identifier); // Отправляем состояние игры + return; + } + + + this._removePreviousPendingGames(socket.id, identifier, gameId); + + if (game.addPlayer(socket, null, identifier)) { + this.userIdentifierToGameId[identifier] = gameId; + console.log(`[GameManager] Игрок ${identifier} присоединился к PvP игре ${gameId}`); + + if (game.mode === 'pvp' && game.playerCount === 2) { + const isInitialized = game.initializeGame(); + if (isInitialized) game.startGame(); + else this._cleanupGame(gameId, 'initialization_failed_on_join'); + + const idx = this.pendingPvPGames.indexOf(gameId); + if (idx > -1) this.pendingPvPGames.splice(idx, 1); + this.broadcastAvailablePvPGames(); + } + } + } + + findAndJoinRandomPvPGame(socket, chosenCharacterKeyForCreation = 'elena', identifier) { + this._removePreviousPendingGames(socket.id, identifier); + if (this.userIdentifierToGameId[identifier] && this.games[this.userIdentifierToGameId[identifier]]) { + socket.emit('gameError', { message: 'Вы уже находитесь в активной или ожидающей игре.' }); + this.handleRequestGameState(socket, identifier); + return; + } + + let gameIdToJoin = null; + const preferredOpponentKey = chosenCharacterKeyForCreation === 'elena' ? 'almagest' : 'elena'; + + // Ищем игру, созданную НЕ текущим пользователем + for (const id of [...this.pendingPvPGames]) { + const pendingGame = this.games[id]; + // === ИЗМЕНЕНИЕ: Убеждаемся, что не присоединяемся к игре, которую сами создали и ожидаем === + if (pendingGame && pendingGame.mode === 'pvp' && pendingGame.playerCount === 1 && pendingGame.ownerIdentifier !== identifier) { + // === КОНЕЦ ИЗМЕНЕНИЯ === + const firstPlayerInfo = Object.values(pendingGame.players).find(p => p.id === GAME_CONFIG.PLAYER_ID); + if (firstPlayerInfo && firstPlayerInfo.chosenCharacterKey === preferredOpponentKey) { + gameIdToJoin = id; break; + } + if (!gameIdToJoin) gameIdToJoin = id; // Берем первую подходящую, если нет с нужным персонажем + } + } + + if (gameIdToJoin) { + this.joinGame(socket, gameIdToJoin, identifier); + } else { + this.createGame(socket, 'pvp', chosenCharacterKeyForCreation, identifier); + // Сообщение о создании новой игры отправляется из createGame/initializeGame/startGame + } + } + + handlePlayerAction(identifier, actionData) { + const gameId = this.userIdentifierToGameId[identifier]; + const game = this.games[gameId]; + if (game) { + const playerInfo = Object.values(game.players).find(p => p.identifier === identifier); + const currentSocketId = playerInfo?.socket?.id; + if (playerInfo && currentSocketId) { + const actualSocket = this.io.sockets.sockets.get(currentSocketId); + if (actualSocket?.connected) game.processPlayerAction(currentSocketId, actionData); + else console.warn(`[GameManager] Игрок ${identifier}: действие, но сокет ${currentSocketId} отключен.`); + } else { + console.warn(`[GameManager] Игрок ${identifier}: действие для игры ${gameId}, но не найден в game.players.`); + delete this.userIdentifierToGameId[identifier]; + const s = this.io.sockets.sockets.get(identifier) || playerInfo?.socket; + if (s) s.emit('gameNotFound', { message: 'Ваша игровая сессия потеряна (ошибка игрока).' }); + } + } else { + console.warn(`[GameManager] Игрок ${identifier}: действие, но игра ${gameId} не найдена.`); + delete this.userIdentifierToGameId[identifier]; + const s = this.io.sockets.sockets.get(identifier); + if (s) s.emit('gameNotFound', { message: 'Ваша игровая сессия не найдена.' }); + } + } + + handleDisconnect(socketId, identifier) { + const gameId = this.userIdentifierToGameId[identifier]; + const game = this.games[gameId]; + + if (game) { + // Ищем игрока по ИДЕНТИФИКАТОРУ, так как сокет мог уже обновиться при переподключении + const playerInfo = Object.values(game.players).find(p => p.identifier === identifier); + + if (playerInfo) { + // Проверяем, действительно ли отключается АКТУАЛЬНЫЙ сокет этого игрока + if (playerInfo.socket.id === socketId) { + console.log(`[GameManager] Актуальный сокет ${socketId} игрока ${identifier} отключился из игры ${gameId}.`); + const dPlayerRole = playerInfo.id; + const dCharKey = playerInfo.chosenCharacterKey; + + game.removePlayer(socketId); // Удаляем именно этот сокет из игры + + if (game.playerCount === 0) { + this._cleanupGame(gameId, 'all_players_disconnected'); + } else if (game.mode === 'pvp' && game.playerCount === 1 && game.gameState && !game.gameState.isGameOver) { + game.endGameDueToDisconnect(socketId, dPlayerRole, dCharKey); + } else if (game.mode === 'ai' && game.playerCount === 0 && game.gameState && !game.gameState.isGameOver) { + game.endGameDueToDisconnect(socketId, dPlayerRole, dCharKey); // Завершаем AI игру, если игрок ушел + } + // Если игра уже была isGameOver, _cleanupGame был вызван ранее. + // userIdentifierToGameId[identifier] для отключившегося игрока УДАЛЯЕТСЯ здесь, + // чтобы он мог начать новую игру или переподключиться. + delete this.userIdentifierToGameId[identifier]; + } else { + // Отключился старый сокет (socketId), но у игрока (identifier) уже новый активный сокет. + // Ничего не делаем с игрой, так как игрок по-прежнему в ней с новым сокетом. + // Просто логируем, что старый сокет отвалился. + console.log(`[GameManager] Отключился старый сокет ${socketId} для игрока ${identifier}, который уже переподключился с сокетом ${playerInfo.socket.id} в игре ${gameId}.`); + // Связь userIdentifierToGameId[identifier] остается, так как он все еще в игре. + } + } else { + // Игрока с таким identifier нет в этой игре. + // Это может случиться, если игра была очищена до того, как пришло событие disconnect. + // console.log(`[GameManager] Отключившийся сокет ${socketId} (identifier: ${identifier}) не найден в активных игроках игры ${gameId} (возможно, игра уже очищена).`); + delete this.userIdentifierToGameId[identifier]; // На всякий случай. + } + } else { + // console.log(`[GameManager] Отключился сокет ${socketId} (identifier: ${identifier}). Активная игра не найдена по идентификатору.`); + delete this.userIdentifierToGameId[identifier]; + } + } + + _cleanupGame(gameId, reason = 'unknown') { + const game = this.games[gameId]; + if (!game) return false; + console.log(`[GameManager] Очистка игры ${gameId} (Причина: ${reason}).`); + + if (typeof game.turnTimer?.clear === 'function') game.turnTimer.clear(); + + Object.values(game.players).forEach(pInfo => { + if (pInfo?.identifier && this.userIdentifierToGameId[pInfo.identifier] === gameId) { + delete this.userIdentifierToGameId[pInfo.identifier]; + } + }); + if(game.ownerIdentifier && this.userIdentifierToGameId[game.ownerIdentifier] === gameId){ + delete this.userIdentifierToGameId[game.ownerIdentifier]; + } + + const pendingIdx = this.pendingPvPGames.indexOf(gameId); + if (pendingIdx > -1) this.pendingPvPGames.splice(pendingIdx, 1); + + delete this.games[gameId]; + this.broadcastAvailablePvPGames(); + return true; + } + + getAvailablePvPGamesListForClient() { + return this.pendingPvPGames.map(gameId => { + const game = this.games[gameId]; + if (game && game.mode === 'pvp' && game.playerCount === 1 && game.gameState && !game.gameState.isGameOver) { + const p1Info = Object.values(game.players).find(p => p.id === GAME_CONFIG.PLAYER_ID); + let p1Username = 'Игрок'; + let p1CharName = ''; + let ownerId = game.ownerIdentifier; // === ИЗМЕНЕНИЕ: Получаем ownerId === + + if (p1Info) { + p1Username = p1Info.socket?.userData?.username || `User#${String(p1Info.identifier).substring(0,4)}`; + const charData = dataUtils.getCharacterBaseStats(p1Info.chosenCharacterKey); + p1CharName = charData?.name || p1Info.chosenCharacterKey; + } + return { + id: gameId, + status: `Ожидает (Создал: ${p1Username} за ${p1CharName})`, + ownerIdentifier: ownerId // === ИЗМЕНЕНИЕ: Отправляем ownerIdentifier клиенту === + }; + } + return null; + }).filter(info => info !== null); + } + + broadcastAvailablePvPGames() { + this.io.emit('availablePvPGamesList', this.getAvailablePvPGamesListForClient()); + } + + handleRequestGameState(socket, identifier) { + const gameId = this.userIdentifierToGameId[identifier]; + const game = gameId ? this.games[gameId] : null; + + if (game) { + const playerInfoInGameInstance = Object.values(game.players).find(p => p.identifier === identifier); + if (playerInfoInGameInstance) { + if (game.gameState?.isGameOver) { + delete this.userIdentifierToGameId[identifier]; + socket.emit('gameNotFound', { message: 'Ваша предыдущая игра уже завершена.' }); + return; + } + console.log(`[GameManager] Восстановление игры ${gameId} для ${identifier}. Новый сокет ${socket.id}.`); + const oldSocketId = playerInfoInGameInstance.socket?.id; // Добавил ?. на случай если сокета нет + if (oldSocketId && oldSocketId !== socket.id && game.players[oldSocketId]) { + delete game.players[oldSocketId]; + if(game.playerSockets[playerInfoInGameInstance.id]?.id === oldSocketId) { + delete game.playerSockets[playerInfoInGameInstance.id]; + } + } + playerInfoInGameInstance.socket = socket; + game.players[socket.id] = playerInfoInGameInstance; + game.playerSockets[playerInfoInGameInstance.id] = socket; + socket.join(game.id); + + const pCharKey = playerInfoInGameInstance.chosenCharacterKey; + const pData = dataUtils.getCharacterData(pCharKey); + const opponentRole = playerInfoInGameInstance.id === GAME_CONFIG.PLAYER_ID ? GAME_CONFIG.OPPONENT_ID : GAME_CONFIG.PLAYER_ID; + const oCharKey = game.gameState?.[opponentRole]?.characterKey || (playerInfoInGameInstance.id === GAME_CONFIG.PLAYER_ID ? game.opponentCharacterKey : game.playerCharacterKey); + const oData = oCharKey ? dataUtils.getCharacterData(oCharKey) : null; // oData может быть null, если оппонента нет + + if (pData && (oData || !game.opponentCharacterKey) && game.gameState) { + socket.emit('gameStarted', { + gameId: game.id, yourPlayerId: playerInfoInGameInstance.id, initialGameState: game.gameState, + playerBaseStats: pData.baseStats, + opponentBaseStats: oData?.baseStats || dataUtils.getCharacterBaseStats(null) || {name: 'Ожидание...', maxHp:1}, // Заглушка если оппонента нет + playerAbilities: pData.abilities, + opponentAbilities: oData?.abilities || [], + log: game.consumeLogBuffer(), clientConfig: { ...GAME_CONFIG } + }); + if(game.mode === 'pvp' && game.playerCount === 1 && game.ownerIdentifier === identifier) socket.emit('waitingForOpponent'); + if (!game.gameState.isGameOver && game.turnTimer?.start) { + game.turnTimer.start(game.gameState.isPlayerTurn, (game.mode === 'ai' && !game.gameState.isPlayerTurn)); + } + } else { + this._handleGameRecoveryError(socket, gameId, identifier, 'data_load_fail_reconnect'); + } + } else { + this._handleGameRecoveryError(socket, gameId, identifier, 'player_not_in_instance_reconnect'); + } + } else { + socket.emit('gameNotFound', { message: 'Активная игровая сессия не найдена.' }); + } + } + + _handleGameRecoveryError(socket, gameId, identifier, reasonCode) { + console.error(`[GameManager] Ошибка восстановления игры ${gameId} для ${identifier} (причина: ${reasonCode}).`); + socket.emit('gameError', { message: 'Ошибка сервера при восстановлении состояния игры.' }); + this._cleanupGame(gameId, `recovery_error_${reasonCode}`); + socket.emit('gameNotFound', { message: 'Ваша игровая сессия была завершена из-за ошибки.' }); + } +} + +module.exports = GameManager; \ No newline at end of file diff --git a/server/game/instance/GameInstance.js b/server/game/instance/GameInstance.js new file mode 100644 index 0000000..7ac1175 --- /dev/null +++ b/server/game/instance/GameInstance.js @@ -0,0 +1,474 @@ +// /server/game/instance/GameInstance.js +const { v4: uuidv4 } = require('uuid'); +const TurnTimer = require('./TurnTimer'); +const gameLogic = require('../logic'); // Импортирует index.js из папки logic +const dataUtils = require('../../data/dataUtils'); +const GAME_CONFIG = require('../../core/config'); // <--- УБЕДИТЕСЬ, ЧТО GAME_CONFIG ИМПОРТИРОВАН + +class GameInstance { + constructor(gameId, io, mode = 'ai', gameManager) { + this.id = gameId; + this.io = io; + this.mode = mode; + this.players = {}; + this.playerSockets = {}; + this.playerCount = 0; + this.gameState = null; + this.aiOpponent = (mode === 'ai'); + this.logBuffer = []; + this.playerCharacterKey = null; + this.opponentCharacterKey = null; + this.ownerIdentifier = null; + this.gameManager = gameManager; + + this.turnTimer = new TurnTimer( + GAME_CONFIG.TURN_DURATION_MS, + GAME_CONFIG.TIMER_UPDATE_INTERVAL_MS, + () => this.handleTurnTimeout(), + (remainingTime, isPlayerTurnForTimer) => { + this.io.to(this.id).emit('turnTimerUpdate', { remainingTime, isPlayerTurn: isPlayerTurnForTimer }); + } + ); + + if (!this.gameManager || typeof this.gameManager._cleanupGame !== 'function') { + console.error(`[GameInstance ${this.id}] CRITICAL ERROR: GameManager reference invalid.`); + } + console.log(`[GameInstance ${this.id}] Created. Mode: ${mode}.`); + } + + addPlayer(socket, chosenCharacterKey = 'elena', identifier) { + if (this.players[socket.id]) { + socket.emit('gameError', { message: 'Ваш сокет уже зарегистрирован в этой игре.' }); + return false; + } + const existingPlayerByIdentifier = Object.values(this.players).find(p => p.identifier === identifier); + if (existingPlayerByIdentifier) { + socket.emit('gameError', { message: 'Вы уже находитесь в этой игре под другим подключением.' }); + return false; + } + if (this.playerCount >= 2) { + socket.emit('gameError', { message: 'Эта игра уже заполнена.' }); + return false; + } + + let assignedPlayerId; + let actualCharacterKey; + + if (this.mode === 'ai') { + if (this.playerCount > 0) { + socket.emit('gameError', { message: 'Нельзя присоединиться к AI игре как второй игрок.' }); + return false; + } + assignedPlayerId = GAME_CONFIG.PLAYER_ID; + actualCharacterKey = 'elena'; + this.ownerIdentifier = identifier; + } else { // PvP + if (this.playerCount === 0) { + assignedPlayerId = GAME_CONFIG.PLAYER_ID; + actualCharacterKey = (chosenCharacterKey === 'almagest') ? 'almagest' : 'elena'; + this.ownerIdentifier = identifier; + } else { + assignedPlayerId = GAME_CONFIG.OPPONENT_ID; + const firstPlayerInfo = Object.values(this.players).find(p => p.id === GAME_CONFIG.PLAYER_ID); + actualCharacterKey = (firstPlayerInfo?.chosenCharacterKey === 'elena') ? 'almagest' : 'elena'; + } + } + + this.players[socket.id] = { + id: assignedPlayerId, socket: socket, + chosenCharacterKey: actualCharacterKey, identifier: identifier + }; + this.playerSockets[assignedPlayerId] = socket; + this.playerCount++; + socket.join(this.id); + + const characterBaseStats = dataUtils.getCharacterBaseStats(actualCharacterKey); + console.log(`[GameInstance ${this.id}] Игрок ${identifier} (сокет ${socket.id}) (${characterBaseStats?.name || 'N/A'}) присоединился как ${assignedPlayerId} (персонаж: ${actualCharacterKey}). Игроков: ${this.playerCount}.`); + return true; + } + + removePlayer(socketId) { + const playerInfo = this.players[socketId]; + if (playerInfo) { + const playerRole = playerInfo.id; + console.log(`[GameInstance ${this.id}] Игрок ${playerInfo.identifier} (сокет: ${socketId}, роль: ${playerRole}) покинул игру.`); + if (playerInfo.socket) { try { playerInfo.socket.leave(this.id); } catch (e) { /* ignore */ } } + delete this.players[socketId]; + this.playerCount--; + if (this.playerSockets[playerRole]?.id === socketId) { + delete this.playerSockets[playerRole]; + } + if (this.gameState && !this.gameState.isGameOver) { + const isTurnOfDisconnected = (this.gameState.isPlayerTurn && playerRole === GAME_CONFIG.PLAYER_ID) || + (!this.gameState.isPlayerTurn && playerRole === GAME_CONFIG.OPPONENT_ID); + if (isTurnOfDisconnected) this.turnTimer.clear(); + } + } + } + + initializeGame() { + console.log(`[GameInstance ${this.id}] Инициализация состояния игры. Режим: ${this.mode}. Игроков: ${this.playerCount}.`); + if (this.mode === 'ai' && this.playerCount === 1) { + this.playerCharacterKey = 'elena'; this.opponentCharacterKey = 'balard'; + } else if (this.mode === 'pvp' && this.playerCount === 2) { + const p1Info = Object.values(this.players).find(p => p.id === GAME_CONFIG.PLAYER_ID); + this.playerCharacterKey = p1Info?.chosenCharacterKey || 'elena'; + this.opponentCharacterKey = (this.playerCharacterKey === 'elena') ? 'almagest' : 'elena'; + } else if (this.mode === 'pvp' && this.playerCount === 1) { + const p1Info = Object.values(this.players).find(p => p.id === GAME_CONFIG.PLAYER_ID); + this.playerCharacterKey = p1Info?.chosenCharacterKey || 'elena'; + this.opponentCharacterKey = null; + } else { + console.error(`[GameInstance ${this.id}] Некорректное состояние для инициализации!`); return false; + } + + const playerData = dataUtils.getCharacterData(this.playerCharacterKey); + let opponentData = null; + const isOpponentDefined = !!this.opponentCharacterKey; + if (isOpponentDefined) opponentData = dataUtils.getCharacterData(this.opponentCharacterKey); + + if (!playerData || (isOpponentDefined && !opponentData)) { + this._handleCriticalError('init_char_data_fail', 'Ошибка загрузки данных персонажей при инициализации.'); + return false; + } + if (isOpponentDefined && (!opponentData.baseStats.maxHp || opponentData.baseStats.maxHp <= 0)) { + this._handleCriticalError('init_opponent_hp_fail', 'Некорректные HP оппонента при инициализации.'); + return false; + } + + this.gameState = { + player: this._createFighterState(GAME_CONFIG.PLAYER_ID, playerData.baseStats, playerData.abilities), + opponent: isOpponentDefined ? + this._createFighterState(GAME_CONFIG.OPPONENT_ID, opponentData.baseStats, opponentData.abilities) : + this._createFighterState(GAME_CONFIG.OPPONENT_ID, { name: 'Ожидание игрока...', maxHp: 1, maxResource: 0, resourceName: 'Ресурс', attackPower: 0, characterKey: null }, []), // Плейсхолдер + isPlayerTurn: isOpponentDefined ? Math.random() < 0.5 : true, + isGameOver: false, turnNumber: 1, gameMode: this.mode + }; + + if (isOpponentDefined) { + this.logBuffer = []; + this.addToLog('⚔️ Новая битва начинается! ⚔️', GAME_CONFIG.LOG_TYPE_SYSTEM); + const pCharKey = this.gameState.player.characterKey; + const oCharKey = this.gameState.opponent.characterKey; // Нужен ключ оппонента для контекста + if ((pCharKey === 'elena' || pCharKey === 'almagest') && oCharKey) { + const opponentFullDataForTaunt = dataUtils.getCharacterData(oCharKey); // Получаем полные данные оппонента + const startTaunt = gameLogic.getRandomTaunt(pCharKey, 'battleStart', {}, GAME_CONFIG, opponentFullDataForTaunt, this.gameState); + if (startTaunt !== "(Молчание)") this.addToLog(`${this.gameState.player.name}: "${startTaunt}"`, GAME_CONFIG.LOG_TYPE_INFO); + } + } + console.log(`[GameInstance ${this.id}] Состояние игры инициализировано. Готовность к старту: ${isOpponentDefined}`); + return isOpponentDefined; + } + + _createFighterState(roleId, baseStats, abilities) { + const fighterState = { + id: roleId, characterKey: baseStats.characterKey, name: baseStats.name, + currentHp: baseStats.maxHp, maxHp: baseStats.maxHp, + currentResource: baseStats.maxResource, maxResource: baseStats.maxResource, + resourceName: baseStats.resourceName, attackPower: baseStats.attackPower, + isBlocking: false, activeEffects: [], disabledAbilities: [], abilityCooldowns: {} + }; + (abilities || []).forEach(ability => { // Добавлена проверка abilities + if (typeof ability.cooldown === 'number' && ability.cooldown >= 0) { + fighterState.abilityCooldowns[ability.id] = 0; + } + }); + if (baseStats.characterKey === 'balard') { + fighterState.silenceCooldownTurns = 0; + fighterState.manaDrainCooldownTurns = 0; + } + return fighterState; + } + + startGame() { + if (!this.gameState || !this.gameState.opponent?.characterKey) { + this._handleCriticalError('start_game_not_ready', 'Попытка старта не полностью готовой игры.'); + return; + } + console.log(`[GameInstance ${this.id}] Запуск игры.`); + + const pData = dataUtils.getCharacterData(this.playerCharacterKey); + const oData = dataUtils.getCharacterData(this.opponentCharacterKey); + if (!pData || !oData) { this._handleCriticalError('start_char_data_fail', 'Ошибка данных персонажей при старте.'); return; } + + Object.values(this.players).forEach(playerInfo => { + if (playerInfo.socket?.connected) { + const dataForClient = playerInfo.id === GAME_CONFIG.PLAYER_ID ? + { playerBaseStats: pData.baseStats, opponentBaseStats: oData.baseStats, playerAbilities: pData.abilities, opponentAbilities: oData.abilities } : + { playerBaseStats: oData.baseStats, opponentBaseStats: pData.baseStats, playerAbilities: oData.abilities, opponentAbilities: pData.abilities }; + playerInfo.socket.emit('gameStarted', { + gameId: this.id, yourPlayerId: playerInfo.id, initialGameState: this.gameState, + ...dataForClient, log: this.consumeLogBuffer(), clientConfig: { ...GAME_CONFIG } + }); + } + }); + + const firstTurnActor = this.gameState.isPlayerTurn ? this.gameState.player : this.gameState.opponent; + this.addToLog(`--- Начинается ход ${this.gameState.turnNumber} для: ${firstTurnActor.name} ---`, GAME_CONFIG.LOG_TYPE_TURN); + this.broadcastLogUpdate(); + this.turnTimer.start(this.gameState.isPlayerTurn, (this.mode === 'ai' && !this.gameState.isPlayerTurn)); + + if (!this.gameState.isPlayerTurn && this.aiOpponent) { + setTimeout(() => this.processAiTurn(), GAME_CONFIG.DELAY_OPPONENT_TURN); + } + } + + processPlayerAction(requestingSocketId, actionData) { + if (!this.gameState || this.gameState.isGameOver) return; + const actingPlayerInfo = this.players[requestingSocketId]; + if (!actingPlayerInfo) { console.error(`[GameInstance ${this.id}] Действие от неизвестного сокета ${requestingSocketId}`); return; } + + const actingPlayerRole = actingPlayerInfo.id; + const isCorrectTurn = (this.gameState.isPlayerTurn && actingPlayerRole === GAME_CONFIG.PLAYER_ID) || + (!this.gameState.isPlayerTurn && actingPlayerRole === GAME_CONFIG.OPPONENT_ID); + if (!isCorrectTurn) { console.warn(`[GameInstance ${this.id}] Действие от ${actingPlayerInfo.identifier}: не его ход.`); return; } + + this.turnTimer.clear(); + + const attackerState = this.gameState[actingPlayerRole]; + const defenderRole = actingPlayerRole === GAME_CONFIG.PLAYER_ID ? GAME_CONFIG.OPPONENT_ID : GAME_CONFIG.PLAYER_ID; + const defenderState = this.gameState[defenderRole]; + const attackerData = dataUtils.getCharacterData(attackerState.characterKey); + const defenderData = dataUtils.getCharacterData(defenderState.characterKey); + + if (!attackerData || !defenderData) { this._handleCriticalError('action_char_data_fail', 'Ошибка данных персонажа при действии.'); return; } + + let actionValid = true; + let tauntContextTargetData = defenderData; // Данные цели для контекста насмешек + + if (actionData.actionType === 'attack') { + const taunt = gameLogic.getRandomTaunt(attackerState.characterKey, 'basicAttack', {}, GAME_CONFIG, tauntContextTargetData, this.gameState); + if (taunt !== "(Молчание)") this.addToLog(`${attackerState.name}: "${taunt}"`, GAME_CONFIG.LOG_TYPE_INFO); + gameLogic.performAttack(attackerState, defenderState, attackerData.baseStats, defenderData.baseStats, this.gameState, this.addToLog.bind(this), GAME_CONFIG, tauntContextTargetData); + const delayedBuff = attackerState.activeEffects.find(eff => eff.isDelayed && (eff.id === GAME_CONFIG.ABILITY_ID_NATURE_STRENGTH || eff.id === GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_ATTACK)); + if (delayedBuff && !delayedBuff.justCast) { + const regen = Math.min(GAME_CONFIG.NATURE_STRENGTH_MANA_REGEN, attackerData.baseStats.maxResource - attackerState.currentResource); + if (regen > 0) { + attackerState.currentResource = Math.round(attackerState.currentResource + regen); + this.addToLog(`🌿 ${attackerState.name} восстанавливает ${regen} ${attackerState.resourceName} от "${delayedBuff.name}"!`, GAME_CONFIG.LOG_TYPE_HEAL); + } + } + } else if (actionData.actionType === 'ability' && actionData.abilityId) { + const ability = attackerData.abilities.find(ab => ab.id === actionData.abilityId); + if (!ability) { + actionValid = false; + actingPlayerInfo.socket.emit('gameError', { message: "Неизвестная способность." }); + this.turnTimer.start(this.gameState.isPlayerTurn, (this.mode === 'ai' && !this.gameState.isPlayerTurn)); // Перезапуск таймера + return; + } + const validityCheck = gameLogic.checkAbilityValidity(ability, attackerState, defenderState, GAME_CONFIG); + if (!validityCheck.isValid) { + this.addToLog(validityCheck.reason, GAME_CONFIG.LOG_TYPE_INFO); + actionValid = false; + } + + if (actionValid) { + attackerState.currentResource = Math.round(attackerState.currentResource - ability.cost); + const taunt = gameLogic.getRandomTaunt(attackerState.characterKey, 'selfCastAbility', { abilityId: ability.id }, GAME_CONFIG, tauntContextTargetData, this.gameState); + if (taunt !== "(Молчание)") this.addToLog(`${attackerState.name}: "${taunt}"`, GAME_CONFIG.LOG_TYPE_INFO); + gameLogic.applyAbilityEffect(ability, attackerState, defenderState, attackerData.baseStats, defenderData.baseStats, this.gameState, this.addToLog.bind(this), GAME_CONFIG, tauntContextTargetData); + gameLogic.setAbilityCooldown(ability, attackerState, GAME_CONFIG); + } + } else { + console.warn(`[GameInstance ${this.id}] Неизвестный тип действия: ${actionData?.actionType}`); + actionValid = false; + } + + if (this.checkGameOver()) { + this.broadcastGameStateUpdate(); return; + } + if (actionValid) { + setTimeout(() => this.switchTurn(), GAME_CONFIG.DELAY_AFTER_PLAYER_ACTION); + } else { + this.broadcastLogUpdate(); + this.turnTimer.start(this.gameState.isPlayerTurn, (this.mode === 'ai' && !this.gameState.isPlayerTurn)); // Перезапуск таймера + } + } + + switchTurn() { + if (!this.gameState || this.gameState.isGameOver) return; + this.turnTimer.clear(); + + const endingTurnActorRole = this.gameState.isPlayerTurn ? GAME_CONFIG.PLAYER_ID : GAME_CONFIG.OPPONENT_ID; + const endingTurnActor = this.gameState[endingTurnActorRole]; + const endingTurnData = dataUtils.getCharacterData(endingTurnActor.characterKey); + + if (!endingTurnData) { this._handleCriticalError('switch_turn_data_fail', 'Ошибка данных при смене хода.'); return; } + + gameLogic.processEffects(endingTurnActor.activeEffects, endingTurnActor, endingTurnData.baseStats, endingTurnActorRole, this.gameState, this.addToLog.bind(this), GAME_CONFIG, dataUtils); + gameLogic.updateBlockingStatus(this.gameState.player); + gameLogic.updateBlockingStatus(this.gameState.opponent); + if (endingTurnActor.abilityCooldowns && endingTurnData.abilities) gameLogic.processPlayerAbilityCooldowns(endingTurnActor.abilityCooldowns, endingTurnData.abilities, endingTurnActor.name, this.addToLog.bind(this), GAME_CONFIG); + if (endingTurnActor.characterKey === 'balard') gameLogic.processBalardSpecialCooldowns(endingTurnActor); + if (endingTurnActor.disabledAbilities?.length > 0 && endingTurnData.abilities) gameLogic.processDisabledAbilities(endingTurnActor.disabledAbilities, endingTurnData.abilities, endingTurnActor.name, this.addToLog.bind(this), GAME_CONFIG); + + if (this.checkGameOver()) { this.broadcastGameStateUpdate(); return; } + + this.gameState.isPlayerTurn = !this.gameState.isPlayerTurn; + if (this.gameState.isPlayerTurn) this.gameState.turnNumber++; + + const currentTurnActor = this.gameState.isPlayerTurn ? this.gameState.player : this.gameState.opponent; + this.addToLog(`--- Начинается ход ${this.gameState.turnNumber} для: ${currentTurnActor.name} ---`, GAME_CONFIG.LOG_TYPE_TURN); + this.broadcastGameStateUpdate(); + this.turnTimer.start(this.gameState.isPlayerTurn, (this.mode === 'ai' && !this.gameState.isPlayerTurn)); + + if (!this.gameState.isPlayerTurn && this.aiOpponent) { + setTimeout(() => this.processAiTurn(), GAME_CONFIG.DELAY_OPPONENT_TURN); + } + } + + processAiTurn() { + if (!this.gameState || this.gameState.isGameOver || this.gameState.isPlayerTurn || !this.aiOpponent || this.gameState.opponent?.characterKey !== 'balard') { + if (this.gameState && !this.gameState.isGameOver) this.switchTurn(); + return; + } + + const attacker = this.gameState.opponent; + const defender = this.gameState.player; + const attackerData = dataUtils.getCharacterData('balard'); + const defenderData = dataUtils.getCharacterData(defender.characterKey); + + if (!attackerData || !defenderData) { this._handleCriticalError('ai_char_data_fail', 'Ошибка данных AI.'); this.switchTurn(); return; } + + if (gameLogic.isCharacterFullySilenced(attacker, GAME_CONFIG)) { + this.addToLog(`😵 ${attacker.name} под действием Безмолвия! Атакует в смятении.`, GAME_CONFIG.LOG_TYPE_EFFECT); + gameLogic.performAttack(attacker, defender, attackerData.baseStats, defenderData.baseStats, this.gameState, this.addToLog.bind(this), GAME_CONFIG, defenderData); + if (this.checkGameOver()) { this.broadcastGameStateUpdate(); return; } + setTimeout(() => this.switchTurn(), GAME_CONFIG.DELAY_AFTER_PLAYER_ACTION); + return; + } + + const aiDecision = gameLogic.decideAiAction(this.gameState, dataUtils, GAME_CONFIG, this.addToLog.bind(this)); + let tauntContextTargetData = defenderData; + + if (aiDecision.actionType === 'attack') { + gameLogic.performAttack(attacker, defender, attackerData.baseStats, defenderData.baseStats, this.gameState, this.addToLog.bind(this), GAME_CONFIG, tauntContextTargetData); + } else if (aiDecision.actionType === 'ability' && aiDecision.ability) { + attacker.currentResource = Math.round(attacker.currentResource - aiDecision.ability.cost); + gameLogic.applyAbilityEffect(aiDecision.ability, attacker, defender, attackerData.baseStats, defenderData.baseStats, this.gameState, this.addToLog.bind(this), GAME_CONFIG, tauntContextTargetData); + gameLogic.setAbilityCooldown(aiDecision.ability, attacker, GAME_CONFIG); + } // 'pass' уже залогирован в decideAiAction + + if (this.checkGameOver()) { this.broadcastGameStateUpdate(); return; } + setTimeout(() => this.switchTurn(), GAME_CONFIG.DELAY_AFTER_PLAYER_ACTION); + } + + checkGameOver() { + if (!this.gameState || this.gameState.isGameOver) return this.gameState?.isGameOver ?? true; + if (!this.gameState.player || !this.gameState.opponent?.characterKey) return false; + + const gameOverResult = gameLogic.getGameOverResult(this.gameState, GAME_CONFIG, this.mode); + if (gameOverResult.isOver) { + this.gameState.isGameOver = true; + this.turnTimer.clear(); + this.addToLog(gameOverResult.logMessage, GAME_CONFIG.LOG_TYPE_SYSTEM); + + const winnerState = this.gameState[gameOverResult.winnerRole]; + const loserState = this.gameState[gameOverResult.loserRole]; + if (winnerState && (winnerState.characterKey === 'elena' || winnerState.characterKey === 'almagest') && loserState) { + const loserFullData = dataUtils.getCharacterData(loserState.characterKey); + if (loserFullData) { // Убедимся, что данные проигравшего есть + const taunt = gameLogic.getRandomTaunt(winnerState.characterKey, 'opponentNearDefeatCheck', {}, GAME_CONFIG, loserFullData, this.gameState); + if (taunt !== "(Молчание)") this.addToLog(`${winnerState.name}: "${taunt}"`, GAME_CONFIG.LOG_TYPE_INFO); + } + } + if (loserState) { + if (loserState.characterKey === 'balard') this.addToLog(`Елена исполнила свой тяжкий долг. ${loserState.name} развоплощен...`, GAME_CONFIG.LOG_TYPE_SYSTEM); + else if (loserState.characterKey === 'almagest') this.addToLog(`Над полем битвы воцаряется тишина. ${loserState.name} побежден(а).`, GAME_CONFIG.LOG_TYPE_SYSTEM); + else if (loserState.characterKey === 'elena') this.addToLog(`Свет погас. ${loserState.name} повержен(а).`, GAME_CONFIG.LOG_TYPE_SYSTEM); + } + + console.log(`[GameInstance ${this.id}] Игра окончена. Победитель: ${gameOverResult.winnerRole || 'Нет'}. Причина: ${gameOverResult.reason}.`); + this.io.to(this.id).emit('gameOver', { + winnerId: gameOverResult.winnerRole, reason: gameOverResult.reason, + finalGameState: this.gameState, log: this.consumeLogBuffer(), + loserCharacterKey: loserState?.characterKey || 'unknown' + }); + this.gameManager._cleanupGame(this.id, gameOverResult.reason); + return true; + } + return false; + } + + endGameDueToDisconnect(disconnectedSocketId, disconnectedPlayerRole, disconnectedCharacterKey) { + if (this.gameState && !this.gameState.isGameOver) { + this.gameState.isGameOver = true; + this.turnTimer.clear(); + + const result = gameLogic.getGameOverResult(this.gameState, GAME_CONFIG, this.mode, 'opponent_disconnected', + disconnectedPlayerRole === GAME_CONFIG.PLAYER_ID ? GAME_CONFIG.OPPONENT_ID : GAME_CONFIG.PLAYER_ID, // winner + disconnectedPlayerRole // loser + ); + + this.addToLog(result.logMessage, GAME_CONFIG.LOG_TYPE_SYSTEM); + + console.log(`[GameInstance ${this.id}] Игра завершена из-за дисконнекта. Победитель: ${result.winnerRole || 'Нет'}. Отключился: ${disconnectedPlayerRole}.`); + this.io.to(this.id).emit('gameOver', { + winnerId: result.winnerRole, reason: result.reason, + finalGameState: this.gameState, log: this.consumeLogBuffer(), + loserCharacterKey: disconnectedCharacterKey // Ключ того, кто отключился + }); + this.gameManager._cleanupGame(this.id, result.reason); + } + } + + handleTurnTimeout() { + if (!this.gameState || this.gameState.isGameOver) return; + // this.turnTimer.clear(); // TurnTimer сам себя очистит при вызове onTimeout + + const timedOutPlayerRole = this.gameState.isPlayerTurn ? GAME_CONFIG.PLAYER_ID : GAME_CONFIG.OPPONENT_ID; + const winnerPlayerRole = timedOutPlayerRole === GAME_CONFIG.PLAYER_ID ? GAME_CONFIG.OPPONENT_ID : GAME_CONFIG.PLAYER_ID; + + const result = gameLogic.getGameOverResult(this.gameState, GAME_CONFIG, this.mode, 'turn_timeout', winnerPlayerRole, timedOutPlayerRole); + + if (!this.gameState[winnerPlayerRole]?.characterKey) { // Если победитель не определен (например, ожидание в PvP) + this._handleCriticalError('timeout_winner_undefined', `Таймаут, но победитель (${winnerPlayerRole}) не определен.`); + return; + } + + this.gameState.isGameOver = true; // Устанавливаем здесь, т.к. getGameOverResult мог не знать, что игра уже окончена + this.addToLog(result.logMessage, GAME_CONFIG.LOG_TYPE_SYSTEM); + console.log(`[GameInstance ${this.id}] Таймаут хода для ${this.gameState[timedOutPlayerRole]?.name}. Победитель: ${this.gameState[winnerPlayerRole]?.name}.`); + + this.io.to(this.id).emit('gameOver', { + winnerId: result.winnerRole, reason: result.reason, + finalGameState: this.gameState, log: this.consumeLogBuffer(), + loserCharacterKey: this.gameState[timedOutPlayerRole]?.characterKey || 'unknown' + }); + this.gameManager._cleanupGame(this.id, result.reason); + } + + _handleCriticalError(reasonCode, logMessage) { + console.error(`[GameInstance ${this.id}] КРИТИЧЕСКАЯ ОШИБКА: ${logMessage} (Код: ${reasonCode})`); + if (this.gameState && !this.gameState.isGameOver) this.gameState.isGameOver = true; + this.turnTimer.clear(); + this.addToLog(`Критическая ошибка сервера: ${logMessage}`, GAME_CONFIG.LOG_TYPE_SYSTEM); + this.io.to(this.id).emit('gameOver', { + winnerId: null, reason: `server_error_${reasonCode}`, + finalGameState: this.gameState, log: this.consumeLogBuffer(), + loserCharacterKey: 'unknown' + }); + if (this.gameManager && typeof this.gameManager._cleanupGame === 'function') { + this.gameManager._cleanupGame(this.id, `critical_error_${reasonCode}`); + } + } + + addToLog(message, type = GAME_CONFIG.LOG_TYPE_INFO) { + if (!message) return; + this.logBuffer.push({ message, type, timestamp: Date.now() }); + } + consumeLogBuffer() { + const logs = [...this.logBuffer]; this.logBuffer = []; return logs; + } + broadcastGameStateUpdate() { + if (!this.gameState) return; + this.io.to(this.id).emit('gameStateUpdate', { gameState: this.gameState, log: this.consumeLogBuffer() }); + } + broadcastLogUpdate() { + if (this.logBuffer.length > 0) { + this.io.to(this.id).emit('logUpdate', { log: this.consumeLogBuffer() }); + } + } +} + +module.exports = GameInstance; \ No newline at end of file diff --git a/server/game/instance/Player.js b/server/game/instance/Player.js new file mode 100644 index 0000000..e69de29 diff --git a/server/game/instance/TurnTimer.js b/server/game/instance/TurnTimer.js new file mode 100644 index 0000000..e8b3c87 --- /dev/null +++ b/server/game/instance/TurnTimer.js @@ -0,0 +1,120 @@ +// /server/game/instance/TurnTimer.js + +class TurnTimer { + /** + * Конструктор таймера хода. + * @param {number} turnDurationMs - Длительность хода в миллисекундах. + * @param {number} updateIntervalMs - Интервал для отправки обновлений времени клиентам (в мс). + * @param {function} onTimeoutCallback - Колбэк, вызываемый при истечении времени хода. + * @param {function} onTickCallback - Колбэк, вызываемый на каждом тике обновления (передает remainingTime, isPlayerTurnForTimer). + */ + constructor(turnDurationMs, updateIntervalMs, onTimeoutCallback, onTickCallback) { + this.turnDurationMs = turnDurationMs; + this.updateIntervalMs = updateIntervalMs; + this.onTimeoutCallback = onTimeoutCallback; + this.onTickCallback = onTickCallback; + + this.timerId = null; // ID для setTimeout (обработка таймаута) + this.updateIntervalId = null; // ID для setInterval (обновление клиента) + this.startTime = 0; // Время начала текущего отсчета (Date.now()) + this.isRunning = false; + this.isCurrentPlayerActualTurnForTick = false; // Храним, для чьего хода запущен таймер (для onTickCallback) + this.isAiCurrentlyMakingMove = false; // Флаг, что сейчас ход AI (таймер не тикает для игрока) + + // console.log(`[TurnTimer] Initialized with duration: ${turnDurationMs}ms, update interval: ${updateIntervalMs}ms`); + } + + /** + * Запускает или перезапускает таймер хода. + * @param {boolean} isPlayerTurn - true, если сейчас ход слота 'player', false - если ход слота 'opponent'. + * @param {boolean} isAiTurn - true, если текущий ход делает AI (в этом случае таймер для реального игрока не тикает). + */ + start(isPlayerTurn, isAiTurn = false) { + this.clear(); // Сначала очищаем предыдущие таймеры + + this.isCurrentPlayerActualTurnForTick = isPlayerTurn; // Сохраняем, чей ход для onTick + this.isAiCurrentlyMakingMove = isAiTurn; + + // Таймер и отсчет времени запускаются только если это НЕ ход AI + if (this.isAiCurrentlyMakingMove) { + this.isRunning = false; + // console.log(`[TurnTimer] Start called, but it's AI's turn. Timer not started for player.`); + // Уведомляем один раз, что таймер неактивен (ход AI) + if (this.onTickCallback) { + this.onTickCallback(null, this.isCurrentPlayerActualTurnForTick); + } + return; + } + + this.startTime = Date.now(); + this.isRunning = true; + // console.log(`[TurnTimer] Started for ${isPlayerTurn ? 'Player' : 'Opponent'} at ${new Date(this.startTime).toLocaleTimeString()}. AI turn: ${isAiTurn}`); + + // Таймер на истечение общего времени хода + this.timerId = setTimeout(() => { + // console.log(`[TurnTimer] Timeout occurred! Was running: ${this.isRunning}`); + if (this.isRunning) { // Дополнительная проверка, что таймер все еще должен был работать + this.isRunning = false; // Помечаем, что таймер больше не работает + if (this.onTimeoutCallback) { + this.onTimeoutCallback(); + } + this.clear(); // Очищаем и интервал обновления после таймаута + } + }, this.turnDurationMs); + + // Интервал для отправки обновлений клиентам + this.updateIntervalId = setInterval(() => { + if (!this.isRunning) { // Если таймер был остановлен (например, ход сделан или игра окончена) + this.clear(); // Убедимся, что интервал тоже очищен + return; + } + + const elapsedTime = Date.now() - this.startTime; + const remainingTime = Math.max(0, this.turnDurationMs - elapsedTime); + + if (this.onTickCallback) { + // Передаем isCurrentPlayerActualTurnForTick, чтобы клиент знал, для чьего хода это время + this.onTickCallback(remainingTime, this.isCurrentPlayerActualTurnForTick); + } + + if (remainingTime <= 0 && this.isRunning) { // Если время вышло по интервалу (на всякий случай, setTimeout должен сработать) + // console.log(`[TurnTimer] Remaining time reached 0 in interval. Forcing timeout logic.`); + // Не вызываем onTimeoutCallback здесь напрямую, чтобы избежать двойного вызова, + // setTimeout должен это обработать. Просто очищаем интервал. + this.clear(); // Очищаем интервал, setTimeout сработает для onTimeoutCallback + } + }, this.updateIntervalMs); + + // Отправляем начальное значение немедленно + if (this.onTickCallback) { + this.onTickCallback(this.turnDurationMs, this.isCurrentPlayerActualTurnForTick); + } + } + + /** + * Очищает (останавливает) все активные таймеры (setTimeout и setInterval). + */ + clear() { + if (this.timerId) { + clearTimeout(this.timerId); + this.timerId = null; + } + if (this.updateIntervalId) { + clearInterval(this.updateIntervalId); + this.updateIntervalId = null; + } + this.isRunning = false; + this.startTime = 0; + // console.log(`[TurnTimer] Cleared.`); + } + + /** + * Проверяет, активен ли таймер в данный момент. + * @returns {boolean} + */ + isActive() { + return this.isRunning; + } +} + +module.exports = TurnTimer; \ No newline at end of file diff --git a/server/game/logic/aiLogic.js b/server/game/logic/aiLogic.js new file mode 100644 index 0000000..8c2ef34 --- /dev/null +++ b/server/game/logic/aiLogic.js @@ -0,0 +1,133 @@ +// /server/game/logic/aiLogic.js + +// GAME_CONFIG и gameData (или dataUtils) будут передаваться в decideAiAction как параметры, +// но для удобства можно импортировать GAME_CONFIG здесь, если он нужен для внутренних констант AI, +// не зависящих от передаваемого конфига. +// const GAME_CONFIG_STATIC = require('../../core/config'); // Если нужно для чего-то внутреннего + +/** + * Логика принятия решения для AI (Балард). + * @param {object} currentGameState - Текущее состояние игры. + * @param {object} dataUtils - Утилиты для доступа к данным игры (getCharacterData, getCharacterAbilities и т.д.). + * @param {object} configToUse - Конфигурационный объект игры (переданный GAME_CONFIG). + * @param {function} addToLogCallback - Функция для добавления лога (опционально, если AI должен логировать свои "мысли"). + * @returns {object} Объект с действием AI ({ actionType: 'attack' | 'ability' | 'pass', ability?: object, logMessage?: {message, type} }). + */ +function decideAiAction(currentGameState, dataUtils, configToUse, addToLogCallback) { + const opponentState = currentGameState.opponent; // AI Балард всегда в слоте opponent + const playerState = currentGameState.player; // Игрок всегда в слоте player (в AI режиме) + + // Убеждаемся, что это AI Балард и есть необходимые данные + if (opponentState.characterKey !== 'balard' || !dataUtils) { + console.warn("[AI Logic] decideAiAction called for non-Balard opponent or missing dataUtils. Passing turn."); + if (addToLogCallback) addToLogCallback(`${opponentState.name || 'AI'} пропускает ход из-за внутренней ошибки.`, configToUse.LOG_TYPE_SYSTEM); + return { actionType: 'pass', logMessage: { message: `${opponentState.name || 'AI'} пропускает ход.`, type: configToUse.LOG_TYPE_INFO } }; + } + + const balardCharacterData = dataUtils.getCharacterData('balard'); + if (!balardCharacterData || !balardCharacterData.abilities) { + console.warn("[AI Logic] Failed to get Balard's character data or abilities. Passing turn."); + if (addToLogCallback) addToLogCallback(`AI Балард пропускает ход из-за ошибки загрузки данных.`, configToUse.LOG_TYPE_SYSTEM); + return { actionType: 'pass', logMessage: { message: `Балард пропускает ход.`, type: configToUse.LOG_TYPE_INFO } }; + } + const balardAbilities = balardCharacterData.abilities; + + // Проверка полного безмолвия Баларда (от Гипнотического Взгляда Елены и т.п.) + const isBalardFullySilenced = opponentState.activeEffects.some( + eff => eff.isFullSilence && eff.turnsLeft > 0 + ); + + if (isBalardFullySilenced) { + // AI под полным безмолвием просто атакует. + // Лог о безмолвии добавляется в GameInstance перед вызовом этой функции или при обработке атаки. + // Здесь можно добавить лог о "вынужденной" атаке, если нужно. + if (addToLogCallback) { + // Проверяем, не был ли лог о безмолвии уже добавлен в этом ходу (чтобы не спамить) + // Это упрощенная проверка, в реальном приложении можно использовать флаги или более сложную логику. + // if (!currentGameState.logContainsThisTurn || !currentGameState.logContainsThisTurn.includes('под действием Безмолвия')) { + // addToLogCallback(`😵 ${opponentState.name} под действием Безмолвия! Атакует в смятении.`, configToUse.LOG_TYPE_EFFECT); + // if(currentGameState) currentGameState.logContainsThisTurn = (currentGameState.logContainsThisTurn || "") + 'под действием Безмолвия'; + // } + } + return { actionType: 'attack' }; + } + + const availableActions = []; + + // 1. Проверяем способность "Покровительство Тьмы" (Лечение) + const healAbility = balardAbilities.find(a => a.id === configToUse.ABILITY_ID_BALARD_HEAL); + if (healAbility && opponentState.currentResource >= healAbility.cost && + (opponentState.abilityCooldowns?.[healAbility.id] || 0) <= 0 && // Общий КД + healAbility.condition(opponentState, playerState, currentGameState, configToUse)) { + availableActions.push({ weight: 80, type: 'ability', ability: healAbility, requiresSuccessCheck: true, successRate: healAbility.successRate }); + } + + // 2. Проверяем способность "Эхо Безмолвия" + const silenceAbility = balardAbilities.find(a => a.id === configToUse.ABILITY_ID_BALARD_SILENCE); + if (silenceAbility && opponentState.currentResource >= silenceAbility.cost && + (opponentState.silenceCooldownTurns === undefined || opponentState.silenceCooldownTurns <= 0) && // Спец. КД + (opponentState.abilityCooldowns?.[silenceAbility.id] || 0) <= 0 && // Общий КД + silenceAbility.condition(opponentState, playerState, currentGameState, configToUse)) { + // Условие в silenceAbility.condition уже проверяет, что Елена не под безмолвием + availableActions.push({ weight: 60, type: 'ability', ability: silenceAbility, requiresSuccessCheck: true, successRate: configToUse.SILENCE_SUCCESS_RATE }); + } + + // 3. Проверяем способность "Похищение Света" (Вытягивание маны и урон) + const drainAbility = balardAbilities.find(a => a.id === configToUse.ABILITY_ID_BALARD_MANA_DRAIN); + if (drainAbility && opponentState.currentResource >= drainAbility.cost && + (opponentState.manaDrainCooldownTurns === undefined || opponentState.manaDrainCooldownTurns <= 0) && // Спец. КД + (opponentState.abilityCooldowns?.[drainAbility.id] || 0) <= 0 && // Общий КД + drainAbility.condition(opponentState, playerState, currentGameState, configToUse)) { + availableActions.push({ weight: 50, type: 'ability', ability: drainAbility }); + } + + // 4. Базовая атака - всегда доступна как запасной вариант с низким весом + availableActions.push({ weight: 30, type: 'attack' }); + + + if (availableActions.length === 0) { + // Этого не должно происходить, так как атака всегда добавляется + if (addToLogCallback) addToLogCallback(`${opponentState.name} не может совершить действие (нет доступных).`, configToUse.LOG_TYPE_INFO); + return { actionType: 'pass', logMessage: { message: `${opponentState.name} пропускает ход.`, type: configToUse.LOG_TYPE_INFO } }; + } + + // Сортируем действия по весу в порядке убывания (самые приоритетные в начале) + availableActions.sort((a, b) => b.weight - a.weight); + + // console.log(`[AI Logic] Available actions for Balard, sorted by weight:`, JSON.stringify(availableActions.map(a => ({type: a.type, name: a.ability?.name, weight: a.weight})), null, 2)); + + + // Перебираем действия в порядке приоритета и выбираем первое подходящее + for (const action of availableActions) { + if (action.type === 'ability') { + if (action.requiresSuccessCheck) { + // Для способностей с шансом успеха, "бросаем кубик" + if (Math.random() < action.successRate) { + if (addToLogCallback) addToLogCallback(`⭐ ${opponentState.name} решает использовать "${action.ability.name}" (попытка успешна)...`, configToUse.LOG_TYPE_INFO); + return { actionType: action.type, ability: action.ability }; + } else { + // Провал шанса, добавляем лог и ИИ переходит к следующему действию в списке (если есть) + if (addToLogCallback) addToLogCallback(`💨 ${opponentState.name} пытался использовать "${action.ability.name}", но шанс не сработал!`, configToUse.LOG_TYPE_INFO); + continue; // Пробуем следующее приоритетное действие + } + } else { + // Способность без проверки шанса (например, Похищение Света) + if (addToLogCallback) addToLogCallback(`⭐ ${opponentState.name} решает использовать "${action.ability.name}"...`, configToUse.LOG_TYPE_INFO); + return { actionType: action.type, ability: action.ability }; + } + } else if (action.type === 'attack') { + // Атака - если дошли до нее, значит, более приоритетные способности не были выбраны или провалили шанс + if (addToLogCallback) addToLogCallback(`🦶 ${opponentState.name} решает атаковать...`, configToUse.LOG_TYPE_INFO); + return { actionType: 'attack' }; + } + } + + // Фоллбэк, если по какой-то причине ни одно действие не было выбрано (не должно происходить, если атака всегда есть) + console.warn("[AI Logic] AI Balard failed to select any action after iterating. Defaulting to pass."); + if (addToLogCallback) addToLogCallback(`${opponentState.name} не смог выбрать подходящее действие. Пропускает ход.`, configToUse.LOG_TYPE_INFO); + return { actionType: 'pass', logMessage: { message: `${opponentState.name} пропускает ход.`, type: configToUse.LOG_TYPE_INFO } }; +} + +module.exports = { + decideAiAction +}; \ No newline at end of file diff --git a/server/game/logic/combatLogic.js b/server/game/logic/combatLogic.js new file mode 100644 index 0000000..ade9b45 --- /dev/null +++ b/server/game/logic/combatLogic.js @@ -0,0 +1,313 @@ +// /server/game/logic/combatLogic.js + +// GAME_CONFIG и gameData/dataUtils будут передаваться в функции как параметры. +// const GAME_CONFIG_STATIC = require('../../core/config'); // Можно импортировать для внутренних нужд, если не все приходит через параметры + +/** + * Обрабатывает базовую атаку одного бойца по другому. + * @param {object} attackerState - Состояние атакующего бойца из gameState. + * @param {object} defenderState - Состояние защищающегося бойца из gameState. + * @param {object} attackerBaseStats - Базовые статы атакующего (из dataUtils.getCharacterBaseStats). + * @param {object} defenderBaseStats - Базовые статы защищающегося (из dataUtils.getCharacterBaseStats). + * @param {object} currentGameState - Текущее полное состояние игры (для getRandomTaunt). + * @param {function} addToLogCallback - Функция для добавления сообщений в лог игры. + * @param {object} configToUse - Конфигурационный объект игры (GAME_CONFIG). + * @param {object} defenderFullData - Полные данные защищающегося персонажа (baseStats, abilities) из dataUtils.getCharacterData(defenderKey), для getRandomTaunt. + */ +function performAttack( + attackerState, + defenderState, + attackerBaseStats, + defenderBaseStats, + currentGameState, // Добавлен для контекста насмешек + addToLogCallback, + configToUse, + defenderFullData // Добавлен для контекста насмешек цели +) { + // Расчет базового урона с вариацией + let damage = Math.floor( + attackerBaseStats.attackPower * + (configToUse.DAMAGE_VARIATION_MIN + Math.random() * configToUse.DAMAGE_VARIATION_RANGE) + ); + let tauntMessagePart = ""; + + // Проверка на блок + if (defenderState.isBlocking) { + const initialDamage = damage; + damage = Math.floor(damage * configToUse.BLOCK_DAMAGE_REDUCTION); + + // Проверка на насмешку ОТ защищающегося (если это Елена или Альмагест) при блокировании атаки + if (defenderState.characterKey === 'elena' || defenderState.characterKey === 'almagest') { + // Импортируем getRandomTaunt здесь или передаем как параметр, если он в другом файле logic + // Предположим, getRandomTaunt доступен в gameLogic (который будет передан или импортирован) + // Для примера, если бы он был в этом же файле или импортирован: + // const blockTaunt = getRandomTaunt(defenderState.characterKey, 'onOpponentAttackBlocked', {}, configToUse, gameData, currentGameState); + // Поскольку getRandomTaunt теперь в gameLogic.js, он должен быть вызван оттуда или передан. + // В GameInstance.js мы вызываем gameLogic.getRandomTaunt, так что здесь это дублирование. + // Лучше, чтобы GameInstance сам обрабатывал насмешки или передавал их как результат. + // Для простоты здесь оставим, но это кандидат на рефакторинг вызова насмешек в GameInstance. + // Однако, если defenderFullData передается, мы можем вызвать его, предполагая, что gameLogic.getRandomTaunt будет импортирован + // или доступен в объекте gameLogic, переданном в GameInstance. + // const blockTaunt = require('./index').getRandomTaunt(...) // Пример циклической зависимости, так не надо + // Будем считать, что GameInstance готовит насмешку заранее или эта функция вызывается с уже готовой насмешкой. + // Либо, если getRandomTaunt - это часть 'gameLogic' объекта, то: + // const blockTaunt = gameLogicFunctions.getRandomTaunt(...) + // Сейчас для простоты оставим вызов, но это архитектурный момент. + // Предположим, что gameLogic.getRandomTaunt доступен через какой-то объект, например, `sharedLogic` + } + + + if (addToLogCallback) { + addToLogCallback( + `🛡️ ${defenderBaseStats.name} блокирует атаку ${attackerBaseStats.name}! Урон снижен (${initialDamage} -> ${damage}).${tauntMessagePart}`, + configToUse.LOG_TYPE_BLOCK + ); + } + } else { + // Насмешка при попадании также должна обрабатываться централизованно или передаваться + if (addToLogCallback) { + addToLogCallback( + `${attackerBaseStats.name} атакует ${defenderBaseStats.name}! Наносит ${damage} урона.${tauntMessagePart}`, + configToUse.LOG_TYPE_DAMAGE + ); + } + } + + // Применяем урон, убеждаемся, что HP не ниже нуля + defenderState.currentHp = Math.max(0, Math.round(defenderState.currentHp - damage)); +} + + +/** + * Применяет эффект способности (урон, лечение, наложение баффа/дебаффа и т.д.). + * Насмешки, связанные с самим КАСТОМ способности (selfCastAbility), должны быть обработаны до вызова этой функции. + * Насмешки, связанные с РЕАКЦИЕЙ цели на эффект, могут быть обработаны здесь или после. + * @param {object} ability - Объект способности. + * @param {object} casterState - Состояние бойца, применившего способность. + * @param {object} targetState - Состояние цели способности. + * @param {object} casterBaseStats - Базовые статы кастера. + * @param {object} targetBaseStats - Базовые статы цели. + * @param {object} currentGameState - Текущее полное состояние игры (для getRandomTaunt, если он здесь вызывается). + * @param {function} addToLogCallback - Функция для добавления лога. + * @param {object} configToUse - Конфигурация игры. + * @param {object} targetFullData - Полные данные цели (baseStats, abilities) для getRandomTaunt. + */ +function applyAbilityEffect( + ability, + casterState, + targetState, + casterBaseStats, + targetBaseStats, + currentGameState, + addToLogCallback, + configToUse, + targetFullData // Для насмешек цели +) { + let tauntMessagePart = ""; // Для насмешки цели + + // Насмешка цели (если это Елена/Альмагест) на применение способности противником + // Этот вызов лучше делать в GameInstance, передавая результат сюда, или эта функция должна иметь доступ к getRandomTaunt + // if ((targetState.characterKey === 'elena' || targetState.characterKey === 'almagest') && casterState.id !== targetState.id) { + // const reactionTaunt = require('./index').getRandomTaunt(targetState.characterKey, 'onOpponentAction', { abilityId: ability.id }, configToUse, targetFullData, currentGameState); + // if (reactionTaunt !== "(Молчание)") tauntMessagePart = ` (${reactionTaunt})`; + // } + + switch (ability.type) { + case configToUse.ACTION_TYPE_HEAL: + const healAmount = Math.floor(ability.power * (configToUse.HEAL_VARIATION_MIN + Math.random() * configToUse.HEAL_VARIATION_RANGE)); + const actualHeal = Math.min(healAmount, casterBaseStats.maxHp - casterState.currentHp); + if (actualHeal > 0) { + casterState.currentHp = Math.round(casterState.currentHp + actualHeal); + if (addToLogCallback) addToLogCallback(`💚 ${casterBaseStats.name} применяет "${ability.name}" и восстанавливает ${actualHeal} HP!${tauntMessagePart}`, configToUse.LOG_TYPE_HEAL); + } else { + if (addToLogCallback) addToLogCallback(`✨ ${casterBaseStats.name} применяет "${ability.name}", но не получает лечения.${tauntMessagePart}`, configToUse.LOG_TYPE_INFO); + } + break; + + case configToUse.ACTION_TYPE_DAMAGE: + let damage = Math.floor(ability.power * (configToUse.DAMAGE_VARIATION_MIN + Math.random() * configToUse.DAMAGE_VARIATION_RANGE)); + if (targetState.isBlocking) { + const initialDamage = damage; + damage = Math.floor(damage * configToUse.BLOCK_DAMAGE_REDUCTION); + if (addToLogCallback) addToLogCallback(`🛡️ ${targetBaseStats.name} блокирует "${ability.name}" от ${casterBaseStats.name}! Урон снижен (${initialDamage} -> ${damage}).${tauntMessagePart}`, configToUse.LOG_TYPE_BLOCK); + } + targetState.currentHp = Math.max(0, Math.round(targetState.currentHp - damage)); + if (addToLogCallback && !targetState.isBlocking) { + addToLogCallback(`💥 ${casterBaseStats.name} применяет "${ability.name}" на ${targetBaseStats.name}, нанося ${damage} урона!${tauntMessagePart}`, configToUse.LOG_TYPE_DAMAGE); + } + break; + + case configToUse.ACTION_TYPE_BUFF: + // Проверка на уже активный бафф должна быть сделана до вызова этой функции (в GameInstance) + let effectDescriptionBuff = ability.description; + if (typeof ability.descriptionFunction === 'function') { + // Для описания баффа может потребоваться информация о противнике (цели баффа, если бафф накладывается на другого) + // В данном случае, баффы накладываются на себя, так что targetBaseStats не всегда релевантен для описания. + // Передаем targetBaseStats (оппонента кастера), если описание функции его ожидает. + effectDescriptionBuff = ability.descriptionFunction(configToUse, targetBaseStats); + } + casterState.activeEffects.push({ + id: ability.id, name: ability.name, description: effectDescriptionBuff, + type: ability.type, duration: ability.duration, + turnsLeft: ability.duration, // Длительность эффекта в ходах владельца + grantsBlock: !!ability.grantsBlock, + isDelayed: !!ability.isDelayed, + justCast: true + }); + if (ability.grantsBlock) require('./effectsLogic').updateBlockingStatus(casterState); // Обновляем статус блока + if (addToLogCallback) addToLogCallback(`✨ ${casterBaseStats.name} накладывает эффект "${ability.name}"!${tauntMessagePart}`, configToUse.LOG_TYPE_EFFECT); + break; + + case configToUse.ACTION_TYPE_DISABLE: + // Логика для 'Гипнотический взгляд' / 'Раскол Разума' (полное безмолвие) + if (ability.id === configToUse.ABILITY_ID_HYPNOTIC_GAZE || ability.id === configToUse.ABILITY_ID_ALMAGEST_DISABLE) { + const effectIdFullSilence = ability.id === configToUse.ABILITY_ID_HYPNOTIC_GAZE ? 'fullSilenceByElena' : 'fullSilenceByAlmagest'; + if (!targetState.activeEffects.some(e => e.id === effectIdFullSilence)) { + targetState.activeEffects.push({ + id: effectIdFullSilence, name: ability.name, description: ability.description, + type: ability.type, duration: ability.effectDuration, turnsLeft: ability.effectDuration, + power: ability.power, isFullSilence: true, justCast: true + }); + if (addToLogCallback) addToLogCallback(`🌀 ${casterBaseStats.name} применяет "${ability.name}" на ${targetBaseStats.name}! Способности заблокированы на ${ability.effectDuration} хода и наносится урон!${tauntMessagePart}`, configToUse.LOG_TYPE_EFFECT); + } else { + if (addToLogCallback) addToLogCallback(`${casterBaseStats.name} пытается применить "${ability.name}", но эффект уже активен на ${targetState.name}!${tauntMessagePart}`, configToUse.LOG_TYPE_INFO); + } + } + // Логика для 'Эхо Безмолвия' Баларда + else if (ability.id === configToUse.ABILITY_ID_BALARD_SILENCE && casterState.characterKey === 'balard') { + const success = Math.random() < configToUse.SILENCE_SUCCESS_RATE; + // Насмешка цели на успех/провал должна быть обработана в GameInstance, т.к. результат известен только здесь + if (success) { + const targetAbilitiesList = require('../../data/dataUtils').getCharacterAbilities(targetState.characterKey); // Получаем абилки цели + const availableAbilitiesToSilence = targetAbilitiesList.filter(pa => + !targetState.disabledAbilities?.some(d => d.abilityId === pa.id) && + !targetState.activeEffects?.some(eff => eff.id === `playerSilencedOn_${pa.id}`) + ); + if (availableAbilitiesToSilence.length > 0) { + const abilityToSilence = availableAbilitiesToSilence[Math.floor(Math.random() * availableAbilitiesToSilence.length)]; + const turns = configToUse.SILENCE_DURATION; + targetState.disabledAbilities.push({ abilityId: abilityToSilence.id, turnsLeft: turns + 1 }); + targetState.activeEffects.push({ + id: `playerSilencedOn_${abilityToSilence.id}`, name: `Безмолвие: ${abilityToSilence.name}`, + description: `Способность "${abilityToSilence.name}" временно недоступна.`, + type: configToUse.ACTION_TYPE_DISABLE, sourceAbilityId: ability.id, + duration: turns, turnsLeft: turns + 1, justCast: true + }); + if (addToLogCallback) addToLogCallback(`🔇 Эхо Безмолвия! "${abilityToSilence.name}" у ${targetBaseStats.name} заблокировано на ${turns} хода!${tauntMessagePart}`, configToUse.LOG_TYPE_EFFECT); + } else { + if (addToLogCallback) addToLogCallback(`${casterBaseStats.name} пытается наложить Безмолвие, но у ${targetBaseStats.name} нечего глушить!${tauntMessagePart}`, configToUse.LOG_TYPE_INFO); + } + } else { + if (addToLogCallback) addToLogCallback(`💨 Попытка ${casterBaseStats.name} наложить Безмолвие на ${targetBaseStats.name} провалилась!${tauntMessagePart}`, configToUse.LOG_TYPE_INFO); + } + } + break; + + case configToUse.ACTION_TYPE_DEBUFF: + // Логика для 'Печать Слабости' / 'Проклятие Увядания' + if (ability.id === configToUse.ABILITY_ID_SEAL_OF_WEAKNESS || ability.id === configToUse.ABILITY_ID_ALMAGEST_DEBUFF) { + const effectIdDebuff = 'effect_' + ability.id; + if (!targetState.activeEffects.some(e => e.id === effectIdDebuff)) { + let effectDescriptionDebuff = ability.description; + if (typeof ability.descriptionFunction === 'function') { + effectDescriptionDebuff = ability.descriptionFunction(configToUse, targetBaseStats); + } + targetState.activeEffects.push({ + id: effectIdDebuff, name: ability.name, description: effectDescriptionDebuff, + type: configToUse.ACTION_TYPE_DEBUFF, sourceAbilityId: ability.id, + duration: ability.effectDuration, turnsLeft: ability.effectDuration, + power: ability.power, justCast: true + }); + if (addToLogCallback) addToLogCallback(`📉 ${casterBaseStats.name} накладывает "${ability.name}" на ${targetBaseStats.name}! Ресурс будет сжигаться.${tauntMessagePart}`, configToUse.LOG_TYPE_EFFECT); + } else { + if (addToLogCallback) addToLogCallback(`${casterBaseStats.name} пытается применить "${ability.name}", но эффект уже активен на ${targetState.name}!${tauntMessagePart}`, configToUse.LOG_TYPE_INFO); + } + } + break; + + case configToUse.ACTION_TYPE_DRAIN: // Похищение Света Баларда + if (casterState.characterKey === 'balard') { + let manaDrained = 0; let healthGained = 0; let damageDealtDrain = 0; + if (ability.powerDamage > 0) { + let baseDamageDrain = ability.powerDamage; + if (targetState.isBlocking) baseDamageDrain = Math.floor(baseDamageDrain * configToUse.BLOCK_DAMAGE_REDUCTION); + damageDealtDrain = Math.max(0, baseDamageDrain); + targetState.currentHp = Math.max(0, Math.round(targetState.currentHp - damageDealtDrain)); + } + const potentialDrain = ability.powerManaDrain; + const actualDrain = Math.min(potentialDrain, targetState.currentResource); + if (actualDrain > 0) { + targetState.currentResource = Math.max(0, Math.round(targetState.currentResource - actualDrain)); + manaDrained = actualDrain; + const potentialHeal = Math.floor(manaDrained * ability.powerHealthGainFactor); + const actualHealGain = Math.min(potentialHeal, casterBaseStats.maxHp - casterState.currentHp); + casterState.currentHp = Math.round(casterState.currentHp + actualHealGain); + healthGained = actualHealGain; + } + let logMsgDrain = `⚡ ${casterBaseStats.name} применяет "${ability.name}"! `; + if (damageDealtDrain > 0) logMsgDrain += `Наносит ${damageDealtDrain} урона. `; + if (manaDrained > 0) logMsgDrain += `Вытягивает ${manaDrained} ${targetBaseStats.resourceName} у ${targetBaseStats.name} и исцеляется на ${healthGained} HP!`; + else if (damageDealtDrain > 0) logMsgDrain += `У ${targetBaseStats.name} нет ${targetBaseStats.resourceName} для похищения.`; + else logMsgDrain += `У ${targetBaseStats.name} нет ${targetBaseStats.resourceName} для похищения.`; + logMsgDrain += tauntMessagePart; + if (addToLogCallback) addToLogCallback(logMsgDrain, (manaDrained > 0 || damageDealtDrain > 0) ? configToUse.LOG_TYPE_DAMAGE : configToUse.LOG_TYPE_INFO); + } + break; + + default: + if (addToLogCallback) addToLogCallback(`Неизвестный тип способности: ${ability?.type} для "${ability?.name}"`, configToUse.LOG_TYPE_SYSTEM); + console.warn(`applyAbilityEffect: Неизвестный тип способности: ${ability?.type}`); + } +} + +/** + * Проверяет валидность использования способности (ресурс, КД, безмолвие и т.д.). + * @param {object} ability - Объект способности. + * @param {object} casterState - Состояние кастера. + * @param {object} targetState - Состояние цели. + * @param {object} configToUse - Конфигурация игры. + * @returns {{isValid: boolean, reason: string|null}} Результат проверки. + */ +function checkAbilityValidity(ability, casterState, targetState, configToUse) { + if (!ability) return { isValid: false, reason: "Способность не найдена." }; + + if (casterState.currentResource < ability.cost) { + return { isValid: false, reason: `${casterState.name} пытается применить "${ability.name}", но не хватает ${casterState.resourceName}!` }; + } + if ((casterState.abilityCooldowns?.[ability.id] || 0) > 0) { + return { isValid: false, reason: `"${ability.name}" еще на перезарядке.` }; + } + // Проверка специальных КД Баларда + if (casterState.characterKey === 'balard') { + if (ability.id === configToUse.ABILITY_ID_BALARD_SILENCE && (casterState.silenceCooldownTurns || 0) > 0) { + return { isValid: false, reason: `"${ability.name}" (спец. КД) еще на перезарядке.` }; + } + if (ability.id === configToUse.ABILITY_ID_BALARD_MANA_DRAIN && (casterState.manaDrainCooldownTurns || 0) > 0) { + return { isValid: false, reason: `"${ability.name}" (спец. КД) еще на перезарядке.` }; + } + } + + const isCasterFullySilenced = casterState.activeEffects.some(eff => eff.isFullSilence && eff.turnsLeft > 0); + const isAbilitySpecificallySilenced = casterState.disabledAbilities?.some(dis => dis.abilityId === ability.id && dis.turnsLeft > 0); + if (isCasterFullySilenced || isAbilitySpecificallySilenced) { + return { isValid: false, reason: `${casterState.name} не может использовать способности из-за безмолвия!` }; + } + + if (ability.type === configToUse.ACTION_TYPE_BUFF && casterState.activeEffects.some(e => e.id === ability.id)) { + return { isValid: false, reason: `Эффект "${ability.name}" уже активен!` }; + } + + const isTargetedDebuff = ability.id === configToUse.ABILITY_ID_SEAL_OF_WEAKNESS || ability.id === configToUse.ABILITY_ID_ALMAGEST_DEBUFF; + if (isTargetedDebuff && targetState.activeEffects.some(e => e.id === 'effect_' + ability.id)) { + return { isValid: false, reason: `Эффект "${ability.name}" уже наложен на ${targetState.name}!` }; + } + + return { isValid: true, reason: null }; +} + + +module.exports = { + performAttack, + applyAbilityEffect, + checkAbilityValidity // Экспортируем новую функцию +}; \ No newline at end of file diff --git a/server/game/logic/cooldownLogic.js b/server/game/logic/cooldownLogic.js new file mode 100644 index 0000000..73e48a7 --- /dev/null +++ b/server/game/logic/cooldownLogic.js @@ -0,0 +1,154 @@ +// /server/game/logic/cooldownLogic.js + +// GAME_CONFIG будет передаваться в функции как параметр configToUse +// const GAME_CONFIG_STATIC = require('../../core/config'); // Если нужен для внутренних констант + +/** + * Обрабатывает отсчет общих кулдаунов для способностей игрока в конце его хода. + * Длительность кулдауна уменьшается на 1. + * @param {object} cooldownsObject - Объект с кулдаунами способностей ({ abilityId: turnsLeft }). + * @param {Array} characterAbilities - Полный список способностей персонажа (для получения имени). + * @param {string} characterName - Имя персонажа (для лога). + * @param {function} addToLogCallback - Функция для добавления лога. + * @param {object} configToUse - Конфигурационный объект игры (GAME_CONFIG). + */ +function processPlayerAbilityCooldowns(cooldownsObject, characterAbilities, characterName, addToLogCallback, configToUse) { + if (!cooldownsObject || !characterAbilities) { + // console.warn(`[CooldownLogic] processPlayerAbilityCooldowns: Missing cooldownsObject or characterAbilities for ${characterName}`); + return; + } + + for (const abilityId in cooldownsObject) { + // Проверяем, что свойство принадлежит самому объекту, а не прототипу, и что кулдаун активен + if (Object.prototype.hasOwnProperty.call(cooldownsObject, abilityId) && cooldownsObject[abilityId] > 0) { + cooldownsObject[abilityId]--; // Уменьшаем кулдаун + + if (cooldownsObject[abilityId] === 0) { + const ability = characterAbilities.find(ab => ab.id === abilityId); + if (ability && addToLogCallback) { + addToLogCallback( + `Способность "${ability.name}" персонажа ${characterName} снова готова!`, + configToUse.LOG_TYPE_INFO // Используем LOG_TYPE_INFO из переданного конфига + ); + } + } + } + } +} + +/** + * Обрабатывает отсчет для отключенных (заглушенных) способностей игрока в конце его хода. + * Длительность заглушения уменьшается на 1. + * @param {Array} disabledAbilitiesArray - Массив объектов заглушенных способностей. + * @param {Array} characterAbilities - Полный список способностей персонажа (для получения имени). + * @param {string} characterName - Имя персонажа (для лога). + * @param {function} addToLogCallback - Функция для добавления лога. + * @param {object} configToUse - Конфигурационный объект игры (GAME_CONFIG). + */ +function processDisabledAbilities(disabledAbilitiesArray, characterAbilities, characterName, addToLogCallback, configToUse) { + if (!disabledAbilitiesArray || disabledAbilitiesArray.length === 0) { + return; + } + + const stillDisabled = []; // Новый массив для активных заглушений + for (let i = 0; i < disabledAbilitiesArray.length; i++) { + const dis = disabledAbilitiesArray[i]; + dis.turnsLeft--; // Уменьшаем длительность заглушения + + if (dis.turnsLeft > 0) { + stillDisabled.push(dis); + } else { + // Заглушение закончилось + if (addToLogCallback) { + const ability = characterAbilities.find(ab => ab.id === dis.abilityId); + if (ability) { + addToLogCallback( + `Способность "${ability.name}" персонажа ${characterName} больше не заглушена!`, + configToUse.LOG_TYPE_INFO + ); + } else { + // Если способность не найдена по ID (маловероятно, но возможно при ошибках данных) + addToLogCallback( + `Заглушение для неизвестной способности персонажа ${characterName} (ID: ${dis.abilityId}) закончилось.`, + configToUse.LOG_TYPE_INFO + ); + } + } + // Также нужно удалить соответствующий эффект из activeEffects, если он там был (например, playerSilencedOn_X) + // Это должно происходить в effectsLogic.processEffects, когда эффект с id `playerSilencedOn_${dis.abilityId}` истекает. + // Здесь мы только управляем массивом `disabledAbilities`. + } + } + + // Обновляем исходный массив, удаляя истекшие заглушения + disabledAbilitiesArray.length = 0; // Очищаем массив (сохраняя ссылку, если она используется где-то еще) + disabledAbilitiesArray.push(...stillDisabled); // Добавляем обратно только те, что еще активны +} + +/** + * Устанавливает или обновляет кулдаун для способности. + * Также обрабатывает специальные внутренние кулдауны для Баларда. + * @param {object} ability - Объект способности, для которой устанавливается кулдаун. + * @param {object} casterState - Состояние персонажа, применившего способность. + * @param {object} configToUse - Конфигурационный объект игры (GAME_CONFIG). + */ +function setAbilityCooldown(ability, casterState, configToUse) { + if (!ability || !casterState || !casterState.abilityCooldowns) { + console.warn("[CooldownLogic] setAbilityCooldown: Missing ability, casterState, or casterState.abilityCooldowns."); + return; + } + + let baseCooldown = 0; + if (typeof ability.cooldown === 'number' && ability.cooldown > 0) { // Убедимся, что исходный КД > 0 + baseCooldown = ability.cooldown; + } + + // Специальные внутренние КД для Баларда - они могут перебивать общий КД + if (casterState.characterKey === 'balard') { + if (ability.id === configToUse.ABILITY_ID_BALARD_SILENCE && + typeof ability.internalCooldownFromConfig === 'string' && // Проверяем, что есть ключ для конфига + typeof configToUse[ability.internalCooldownFromConfig] === 'number') { + // Устанавливаем значение для специального счетчика КД Баларда + casterState.silenceCooldownTurns = configToUse[ability.internalCooldownFromConfig]; + // Этот специальный КД также становится текущим общим КД для этой способности + baseCooldown = configToUse[ability.internalCooldownFromConfig]; + } else if (ability.id === configToUse.ABILITY_ID_BALARD_MANA_DRAIN && + typeof ability.internalCooldownValue === 'number') { // Здесь КД задан прямо в данных способности + casterState.manaDrainCooldownTurns = ability.internalCooldownValue; + baseCooldown = ability.internalCooldownValue; + } + } + + if (baseCooldown > 0) { + // Устанавливаем кулдаун. Добавляем +1, так как кулдаун уменьшится в конце текущего хода + // (когда будет вызван processPlayerAbilityCooldowns для этого персонажа). + casterState.abilityCooldowns[ability.id] = baseCooldown + 1; + } else { + // Если у способности нет базового кулдауна (baseCooldown === 0), + // убеждаемся, что в abilityCooldowns для нее стоит 0. + casterState.abilityCooldowns[ability.id] = 0; + } +} + +/** + * Обрабатывает специальные кулдауны для Баларда в конце его хода. + * @param {object} balardState - Состояние Баларда. + */ +function processBalardSpecialCooldowns(balardState) { + if (balardState.characterKey !== 'balard') return; + + if (balardState.silenceCooldownTurns !== undefined && balardState.silenceCooldownTurns > 0) { + balardState.silenceCooldownTurns--; + } + if (balardState.manaDrainCooldownTurns !== undefined && balardState.manaDrainCooldownTurns > 0) { + balardState.manaDrainCooldownTurns--; + } +} + + +module.exports = { + processPlayerAbilityCooldowns, + processDisabledAbilities, + setAbilityCooldown, + processBalardSpecialCooldowns +}; \ No newline at end of file diff --git a/server/game/logic/effectsLogic.js b/server/game/logic/effectsLogic.js new file mode 100644 index 0000000..69295d5 --- /dev/null +++ b/server/game/logic/effectsLogic.js @@ -0,0 +1,153 @@ +// /server/game/logic/effectsLogic.js + +// GAME_CONFIG и dataUtils будут передаваться в функции как параметры. +// const GAME_CONFIG_STATIC = require('../../core/config'); // Если нужен для внутренних констант +// const DATA_UTILS_STATIC = require('../../data/dataUtils'); // Если нужен для внутренних констант + +/** + * Обрабатывает активные эффекты (баффы/дебаффы) для бойца в конце его хода. + * Длительность эффекта уменьшается на 1. + * Периодические эффекты (DoT, сжигание ресурса и т.п.) срабатывают, если эффект не "justCast" в этом ходу. + * @param {Array} activeEffectsArray - Массив активных эффектов бойца (из gameState.player.activeEffects или gameState.opponent.activeEffects). + * @param {object} ownerState - Состояние бойца, на котором эффекты (currentHp, currentResource и т.д.). + * @param {object} ownerBaseStats - Базовые статы бойца (включая characterKey, name, maxHp, maxResource). + * @param {string} ownerRoleInGame - Роль бойца в игре ('player' или 'opponent'), для контекста. + * @param {object} currentGameState - Полное текущее состояние игры. + * @param {function} addToLogCallback - Функция для добавления сообщений в лог игры. + * @param {object} configToUse - Конфигурационный объект игры (GAME_CONFIG). + * @param {object} dataUtils - Утилиты для доступа к данным игры (getCharacterData, getCharacterAbilities и т.д.). + */ +function processEffects( + activeEffectsArray, + ownerState, + ownerBaseStats, + ownerRoleInGame, // 'player' или 'opponent' + currentGameState, + addToLogCallback, + configToUse, + dataUtils +) { + if (!activeEffectsArray || activeEffectsArray.length === 0) { + return; + } + + const ownerName = ownerBaseStats.name; + const effectsToRemoveIndexes = []; + + for (let i = 0; i < activeEffectsArray.length; i++) { + const effect = activeEffectsArray[i]; + + // --- Применяем периодический эффект (DoT, сжигание ресурса и т.п.), если он не только что наложен --- + if (!effect.justCast) { + // 1. Урон от эффектов полного безмолвия (Гипнотический Взгляд, Раскол Разума) + // Эти эффекты наносят урон цели В КОНЦЕ ее хода. + if (effect.isFullSilence && typeof effect.power === 'number' && effect.power > 0) { + const damage = effect.power; // Урон, заложенный в эффекте + ownerState.currentHp = Math.max(0, Math.round(ownerState.currentHp - damage)); + if (addToLogCallback) { + addToLogCallback( + `😵 Эффект "${effect.name}" наносит ${damage} урона персонажу ${ownerName}! (HP: ${ownerState.currentHp}/${ownerBaseStats.maxHp})`, + configToUse.LOG_TYPE_DAMAGE + ); + } + } + + // 2. Сжигание ресурса (Печать Слабости, Проклятие Увядания) + // Эти эффекты сжигают ресурс цели В КОНЦЕ ее хода. + // ID эффекта на цели имеет префикс 'effect_' + ID способности, которая его наложила. + const isResourceBurnDebuff = effect.id === 'effect_' + configToUse.ABILITY_ID_SEAL_OF_WEAKNESS || + effect.id === 'effect_' + configToUse.ABILITY_ID_ALMAGEST_DEBUFF; + if (isResourceBurnDebuff && typeof effect.power === 'number' && effect.power > 0) { + const resourceToBurn = effect.power; // Количество ресурса, сжигаемое за ход + if (ownerState.currentResource > 0) { + const actualBurn = Math.min(ownerState.currentResource, resourceToBurn); + ownerState.currentResource = Math.max(0, Math.round(ownerState.currentResource - actualBurn)); + if (addToLogCallback) { + addToLogCallback( + `🔥 Эффект "${effect.name}" сжигает ${actualBurn} ${ownerBaseStats.resourceName} у ${ownerName}! (Ресурс: ${ownerState.currentResource}/${ownerBaseStats.maxResource})`, + configToUse.LOG_TYPE_EFFECT + ); + } + } + } + // Примечание: Отложенные эффекты (isDelayed: true, например, Сила Природы) + // применяют свою основную силу в GameInstance.processPlayerAction (после атаки), а не здесь. + // Здесь они просто тикают по длительности. + } + + // --- Уменьшаем длительность --- + effect.turnsLeft--; + effect.justCast = false; // Эффект больше не считается "just cast" после обработки этого хода + + // --- Отмечаем для удаления, если длительность закончилась --- + if (effect.turnsLeft <= 0) { + effectsToRemoveIndexes.push(i); + if (addToLogCallback) { + addToLogCallback( + `Эффект "${effect.name}" на персонаже ${ownerName} закончился.`, + configToUse.LOG_TYPE_EFFECT + ); + } + // Если это был эффект, дающий блок, нужно обновить статус блокировки + if (effect.grantsBlock) { + updateBlockingStatus(ownerState); // Вызываем сразу, т.к. эффект удаляется + } + // Если это был эффект заглушения конкретной способности (playerSilencedOn_X), + // то соответствующая запись в ownerState.disabledAbilities должна быть удалена в cooldownLogic.processDisabledAbilities. + // Здесь мы просто удаляем сам эффект из activeEffects. + } + } + + // Удаляем эффекты с конца массива, чтобы не нарушить индексы при удалении + for (let i = effectsToRemoveIndexes.length - 1; i >= 0; i--) { + activeEffectsArray.splice(effectsToRemoveIndexes[i], 1); + } + + // После удаления всех истекших эффектов, еще раз обновляем статус блока, + // так как какой-то из удаленных эффектов мог быть последним дающим блок. + // (хотя updateBlockingStatus вызывается и при удалении конкретного блокирующего эффекта) + updateBlockingStatus(ownerState); +} + +/** + * Обновляет статус 'isBlocking' для бойца на основе его активных эффектов. + * Боец считается блокирующим, если у него есть хотя бы один активный эффект с флагом grantsBlock: true. + * @param {object} fighterState - Состояние бойца (объект из gameState.player или gameState.opponent). + */ +function updateBlockingStatus(fighterState) { + if (!fighterState || !fighterState.activeEffects) { + // console.warn("[EffectsLogic] updateBlockingStatus: fighterState or activeEffects missing."); + if (fighterState) fighterState.isBlocking = false; // Если нет эффектов, то точно не блокирует + return; + } + // Боец блокирует, если есть ХОТЯ БЫ ОДИН активный эффект, дающий блок + const wasBlocking = fighterState.isBlocking; + fighterState.isBlocking = fighterState.activeEffects.some(eff => eff.grantsBlock && eff.turnsLeft > 0); + + // Можно добавить лог, если статус блока изменился, для отладки + // if (wasBlocking !== fighterState.isBlocking && addToLogCallback) { + // addToLogCallback(`${fighterState.name} ${fighterState.isBlocking ? 'встает в защиту' : 'перестает защищаться'} из-за эффектов.`, 'info'); + // } +} + +/** + * Проверяет, находится ли персонаж под действием полного безмолвия. + * @param {object} characterState - Состояние персонажа из gameState. + * @param {object} configToUse - Конфигурационный объект игры. + * @returns {boolean} true, если персонаж под полным безмолвием, иначе false. + */ +function isCharacterFullySilenced(characterState, configToUse) { + if (!characterState || !characterState.activeEffects) { + return false; + } + return characterState.activeEffects.some( + eff => eff.isFullSilence && eff.turnsLeft > 0 + ); +} + + +module.exports = { + processEffects, + updateBlockingStatus, + isCharacterFullySilenced +}; \ No newline at end of file diff --git a/server/game/logic/gameStateLogic.js b/server/game/logic/gameStateLogic.js new file mode 100644 index 0000000..cafe475 --- /dev/null +++ b/server/game/logic/gameStateLogic.js @@ -0,0 +1,133 @@ +// /server/game/logic/gameStateLogic.js + +// GAME_CONFIG будет передаваться в функции как параметр configToUse. +// dataUtils также может передаваться, если нужен для какой-то логики здесь. + +/** + * Внутренняя проверка условий конца игры (основано на HP). + * @param {object} currentGameState - Текущее состояние игры. + * // configToUse и dataUtils здесь не используются, но могут понадобиться для более сложных условий + * @param {object} configToUse - Конфигурация игры. + * @param {object} dataUtils - Утилиты для доступа к данным. + * @returns {boolean} true, если игра окончена по HP, иначе false. + */ +function checkGameOverInternal(currentGameState, configToUse, dataUtils) { + if (!currentGameState || currentGameState.isGameOver) { + // Если игра уже помечена как оконченная, или нет состояния, возвращаем текущий статус + return currentGameState ? currentGameState.isGameOver : true; + } + + // Убеждаемся, что оба бойца определены в gameState и не являются плейсхолдерами + if (!currentGameState.player || !currentGameState.opponent || + !currentGameState.player.characterKey || !currentGameState.opponent.characterKey || // Проверяем, что персонажи назначены + currentGameState.opponent.name === 'Ожидание игрока...' || // Дополнительная проверка на плейсхолдер + !currentGameState.opponent.maxHp || currentGameState.opponent.maxHp <= 0) { + return false; // Игра не может закончиться по HP, если один из бойцов не готов/не определен + } + + const playerDead = currentGameState.player.currentHp <= 0; + const opponentDead = currentGameState.opponent.currentHp <= 0; + + return playerDead || opponentDead; // Игра окончена, если хотя бы один мертв +} + +/** + * Определяет результат завершения игры (победитель, проигравший, причина). + * Вызывается, когда checkGameOverInternal вернул true или игра завершается по другой причине (дисконнект, таймаут). + * @param {object} currentGameState - Текущее состояние игры. + * @param {object} configToUse - Конфигурация игры (GAME_CONFIG). + * @param {string} gameMode - Режим игры ('ai' или 'pvp'). + * @param {string} [explicitReason=null] - Явная причина завершения (например, 'turn_timeout', 'opponent_disconnected'). + * Если null, причина определяется по HP. + * @param {string} [explicitWinnerRole=null] - Явный победитель (если известен, например, при дисконнекте). + * @param {string} [explicitLoserRole=null] - Явный проигравший (если известен). + * @returns {{isOver: boolean, winnerRole: string|null, loserRole: string|null, reason: string, logMessage: string}} + */ +function getGameOverResult( + currentGameState, + configToUse, + gameMode, + explicitReason = null, + explicitWinnerRole = null, + explicitLoserRole = null +) { + if (!currentGameState) { + return { isOver: true, winnerRole: null, loserRole: null, reason: 'error_no_gamestate', logMessage: 'Ошибка: нет состояния игры.' }; + } + + // Если причина уже задана (например, дисконнект или таймаут), используем ее + if (explicitReason) { + let winnerName = explicitWinnerRole ? (currentGameState[explicitWinnerRole]?.name || explicitWinnerRole) : 'Никто'; + let loserName = explicitLoserRole ? (currentGameState[explicitLoserRole]?.name || explicitLoserRole) : 'Никто'; + let logMsg = ""; + + if (explicitReason === 'turn_timeout') { + logMsg = `⏱️ Время хода для ${loserName} истекло! Победа присуждается ${winnerName}!`; + } else if (explicitReason === 'opponent_disconnected') { + logMsg = `🔌 Игрок ${loserName} отключился. Победа присуждается ${winnerName}!`; + if (gameMode === 'ai' && explicitLoserRole === configToUse.PLAYER_ID) { // Игрок отключился в AI игре + winnerName = currentGameState.opponent?.name || 'AI'; // AI "выиграл" по факту, но не формально + logMsg = `🔌 Игрок ${loserName} отключился. Игра завершена.`; + explicitWinnerRole = null; // В AI режиме нет формального победителя при дисконнекте игрока + } + } else { + logMsg = `Игра завершена. Причина: ${explicitReason}. Победитель: ${winnerName}.`; + } + + return { + isOver: true, + winnerRole: explicitWinnerRole, + loserRole: explicitLoserRole, + reason: explicitReason, + logMessage: logMsg + }; + } + + // Если явной причины нет, проверяем по HP + const playerDead = currentGameState.player?.currentHp <= 0; + const opponentDead = currentGameState.opponent?.currentHp <= 0; + + if (!playerDead && !opponentDead) { + return { isOver: false, winnerRole: null, loserRole: null, reason: 'not_over_hp', logMessage: "" }; // Игра еще не окончена по HP + } + + let winnerRole = null; + let loserRole = null; + let reason = 'hp_zero'; + let logMessage = ""; + + if (gameMode === 'ai') { + if (playerDead) { // Игрок проиграл AI + winnerRole = configToUse.OPPONENT_ID; // AI победил + loserRole = configToUse.PLAYER_ID; + logMessage = `😭 ПОРАЖЕНИЕ! ${currentGameState.opponent.name} оказался сильнее! 😭`; + } else { // Игрок победил AI (opponentDead) + winnerRole = configToUse.PLAYER_ID; + loserRole = configToUse.OPPONENT_ID; + logMessage = `🏁 ПОБЕДА! Вы одолели ${currentGameState.opponent.name}! 🏁`; + } + } else { // PvP режим + if (playerDead && opponentDead) { // Ничья - победа присуждается игроку в слоте 'player' (или по другим правилам) + winnerRole = configToUse.PLAYER_ID; + loserRole = configToUse.OPPONENT_ID; + logMessage = `⚔️ Ничья! Оба бойца пали! Победа присуждается ${currentGameState.player.name} по правилам арены!`; + reason = 'draw_player_wins'; + } else if (playerDead) { + winnerRole = configToUse.OPPONENT_ID; + loserRole = configToUse.PLAYER_ID; + logMessage = `🏁 ПОБЕДА! ${currentGameState.opponent.name} одолел(а) ${currentGameState.player.name}! 🏁`; + } else { // opponentDead + winnerRole = configToUse.PLAYER_ID; + loserRole = configToUse.OPPONENT_ID; + logMessage = `🏁 ПОБЕДА! ${currentGameState.player.name} одолел(а) ${currentGameState.opponent.name}! 🏁`; + } + } + + return { isOver: true, winnerRole, loserRole, reason, logMessage }; +} + + +module.exports = { + checkGameOverInternal, + getGameOverResult +}; \ No newline at end of file diff --git a/server/game/logic/index.js b/server/game/logic/index.js new file mode 100644 index 0000000..780102b --- /dev/null +++ b/server/game/logic/index.js @@ -0,0 +1,66 @@ +// /server/game/logic/index.js + +// Импортируем функции из всех специализированных логических модулей + +const { + performAttack, + applyAbilityEffect, + checkAbilityValidity +} = require('./combatLogic'); + +const { + processPlayerAbilityCooldowns, + processDisabledAbilities, + setAbilityCooldown, + processBalardSpecialCooldowns +} = require('./cooldownLogic'); + +const { + processEffects, + updateBlockingStatus, + isCharacterFullySilenced +} = require('./effectsLogic'); + +const { + decideAiAction +} = require('./aiLogic'); + +const { + getRandomTaunt +} = require('./tauntLogic'); // Предполагаем, что getRandomTaunt вынесен в tauntLogic.js + +const { + checkGameOverInternal, // Внутренняя проверка на HP + getGameOverResult // Определяет победителя и причину для checkGameOver в GameInstance +} = require('./gameStateLogic'); // Предполагаем, что логика завершения игры вынесена + + +// Экспортируем все импортированные функции, чтобы они были доступны +// через единый объект 'gameLogic' в GameInstance.js +module.exports = { + // Combat Logic + performAttack, + applyAbilityEffect, + checkAbilityValidity, + + // Cooldown Logic + processPlayerAbilityCooldowns, + processDisabledAbilities, + setAbilityCooldown, + processBalardSpecialCooldowns, + + // Effects Logic + processEffects, + updateBlockingStatus, + isCharacterFullySilenced, + + // AI Logic + decideAiAction, + + // Taunt Logic + getRandomTaunt, + + // Game State Logic (например, для условий завершения) + checkGameOverInternal, + getGameOverResult +}; \ No newline at end of file diff --git a/server/game/logic/tauntLogic.js b/server/game/logic/tauntLogic.js new file mode 100644 index 0000000..89eb243 --- /dev/null +++ b/server/game/logic/tauntLogic.js @@ -0,0 +1,90 @@ +// /server/game/logic/tauntLogic.js +const GAME_CONFIG = require('../../core/config'); // Путь к config.js +// Вам понадобится доступ к gameData.tauntSystem здесь. +// Либо импортируйте весь gameData, либо только tauntSystem из data/taunts.js +const gameData = require('../../data'); // Импортируем собранный gameData из data/index.js + +/** + * Получает случайную насмешку из системы насмешек. + * (Ваша существующая функция getRandomTaunt) + */ +function getRandomTaunt(speakerCharacterKey, trigger, context = {}, configToUse, opponentFullData, currentGameState) { + // Проверяем наличие системы насмешек для говорящего персонажа + const speakerTauntSystem = gameData.tauntSystem?.[speakerCharacterKey]; // Используем gameData.tauntSystem + if (!speakerTauntSystem) return "(Молчание)"; + + const opponentCharacterKey = opponentFullData?.baseStats?.characterKey || currentGameState?.opponent?.characterKey; // Получаем ключ оппонента + if (!opponentCharacterKey) { // Если оппонент не определен (например, начало игры с AI, где оппонент еще не fully в gameState) + // console.warn(`getRandomTaunt: Opponent character key not determined for speaker ${speakerCharacterKey}, trigger ${trigger}`); + // Можно попробовать определить оппонента по-другому или вернуть общую фразу / молчание + if (trigger === 'battleStart' && speakerCharacterKey === 'elena' && currentGameState.gameMode === 'ai') { + // Для Елены против AI Баларда в начале боя + const balardTaunts = speakerTauntSystem.balard; + if (balardTaunts?.onBattleState?.start) { + const potentialTaunts = balardTaunts.onBattleState.start; + return potentialTaunts[Math.floor(Math.random() * potentialTaunts.length)] || "(Молчание)"; + } + } + return "(Молчание)"; + } + + + const tauntBranch = speakerTauntSystem[opponentCharacterKey]; + if (!tauntBranch) { + return "(Молчание)"; + } + + let potentialTaunts = []; + + if (trigger === 'battleStart') { + potentialTaunts = tauntBranch.onBattleState?.start; + } else if (trigger === 'opponentNearDefeatCheck') { + const opponentState = currentGameState?.player?.characterKey === opponentCharacterKey ? currentGameState.player : currentGameState.opponent; + if (opponentState && opponentState.maxHp > 0 && opponentState.currentHp / opponentState.maxHp < 0.20) { + potentialTaunts = tauntBranch.onBattleState?.opponentNearDefeat; + } + } else if (trigger === 'selfCastAbility' && context.abilityId) { + potentialTaunts = tauntBranch.selfCastAbility?.[context.abilityId]; + } else if (trigger === 'basicAttack' && tauntBranch.basicAttack) { + const opponentState = currentGameState?.player?.characterKey === opponentCharacterKey ? currentGameState.player : currentGameState.opponent; + if (speakerCharacterKey === 'elena' && opponentCharacterKey === 'balard' && opponentState) { + const opponentHpPerc = (opponentState.currentHp / opponentState.maxHp) * 100; + if (opponentHpPerc <= configToUse.PLAYER_MERCY_TAUNT_THRESHOLD_PERCENT) { + potentialTaunts = tauntBranch.basicAttack.dominating; + } else { + potentialTaunts = tauntBranch.basicAttack.merciful; + } + } else { + potentialTaunts = tauntBranch.basicAttack.general || []; // Фоллбэк на пустой массив + } + } else if (trigger === 'onOpponentAction' && context.abilityId) { + const actionResponses = tauntBranch.onOpponentAction?.[context.abilityId]; + if (actionResponses) { + if (typeof actionResponses === 'object' && !Array.isArray(actionResponses) && context.outcome && context.outcome in actionResponses) { + potentialTaunts = actionResponses[context.outcome]; + } else if (Array.isArray(actionResponses)) { + potentialTaunts = actionResponses; + } + } + } else if (trigger === 'onOpponentAttackBlocked' && tauntBranch.onOpponentAction?.attackBlocked) { + potentialTaunts = tauntBranch.onOpponentAction.attackBlocked; + } else if (trigger === 'onOpponentAttackHit' && tauntBranch.onOpponentAction?.attackHits) { + potentialTaunts = tauntBranch.onOpponentAction.attackHits; + } + + if (!Array.isArray(potentialTaunts) || potentialTaunts.length === 0) { + // Фоллбэк на общие фразы при basicAttack, если специфичные не найдены + if (trigger === 'basicAttack' && tauntBranch.basicAttack?.general && tauntBranch.basicAttack.general.length > 0) { + potentialTaunts = tauntBranch.basicAttack.general; + } else { + return "(Молчание)"; + } + } + + const selectedTaunt = potentialTaunts[Math.floor(Math.random() * potentialTaunts.length)]; + return selectedTaunt || "(Молчание)"; +} + +module.exports = { + getRandomTaunt +}; \ No newline at end of file diff --git a/server/services/SocketService.js b/server/services/SocketService.js new file mode 100644 index 0000000..e69de29 diff --git a/udo systemctl status gitea b/udo systemctl status gitea new file mode 100644 index 0000000..a6dfbc7 --- /dev/null +++ b/udo systemctl status gitea @@ -0,0 +1,5 @@ +● gitea.service - Gitea (Git with a cup of tea) + Loaded: loaded (/etc/systemd/system/gitea.service; enabled; vendor preset: enabled) + Active: activating (auto-restart) (Result: exit-code) since Fri 2025-05-09 13:00:38 UTC; 13ms ago + Process: 137377 ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini (code=exited, status=217/USER) + Main PID: 137377 (code=exited, status=217/USER)