mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-04-21 19:38:58 -05:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9aed479296 | |||
| 47c156d9b1 | |||
| e6418e4356 | |||
| 3c23989e99 | |||
| 2d1f341256 | |||
| 44adfd611a | |||
| ab50c41287 | |||
| c43abe82fe | |||
| ac0b4a324f | |||
| efa0a325a2 | |||
| bc970d33ac | |||
| 09e432cf32 | |||
| 40aa2e214d | |||
| 9f563b741d | |||
| 9db541f4c3 | |||
| ce4a20e3d8 | |||
| cc7683a871 | |||
| 31b2781333 | |||
| d37d3bc5ac | |||
| ef3a28791e | |||
| c3c2607bca | |||
| 7a6d64f158 | |||
| 836e63246d | |||
| 61585b2549 | |||
| 07275bd522 | |||
| 74fc543ef2 | |||
| 8c90e5472b | |||
| c8d9ba6c8e | |||
| 1675c2749b | |||
| e85a2bae14 | |||
| 3355500fba | |||
| 486f15df0f | |||
| f0b6b5611c | |||
| 7e45c79714 | |||
| 8da6065355 |
@@ -75,7 +75,6 @@
|
||||
"S3_ACCESS_KEY_ID": "accessKeyId",
|
||||
"S3_BUCKET": "bucket",
|
||||
"S3_SECRET_ACCESS_KEY": "secretAccessKey",
|
||||
"SESSION_SECRET_IV": "12345678912345678912345678912345",
|
||||
"SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891",
|
||||
"SESSION_SECRET": "YOUR SECRET HERE",
|
||||
"SITE_HTTP_AUTH_ENABLED": "false",
|
||||
|
||||
+1
-1
Submodule habitica-images updated: 359153997e...32a4678c6b
Generated
+272
-29
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "5.46.1",
|
||||
"version": "5.46.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "habitica",
|
||||
"version": "5.46.1",
|
||||
"version": "5.46.4",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
@@ -24,6 +24,7 @@
|
||||
"bcrypt": "^5.1.1",
|
||||
"body-parser": "^1.20.3",
|
||||
"bootstrap": "^4.6.2",
|
||||
"bullmq": "^5.71.1",
|
||||
"compression": "^1.8.1",
|
||||
"cookie-session": "^2.1.1",
|
||||
"coupon-code": "^0.4.5",
|
||||
@@ -44,10 +45,11 @@
|
||||
"gulp-filter": "^7.0.0",
|
||||
"gulp-imagemin": "^7.1.0",
|
||||
"gulp.spritesmith": "^6.13.0",
|
||||
"habitica-markdown": "github:HabitRPG/habitica-markdown#fiz/emojis-update",
|
||||
"habitica-markdown": "^4.1.0",
|
||||
"heapdump": "^0.3.15",
|
||||
"helmet": "^4.6.0",
|
||||
"in-app-purchase": "^1.11.3",
|
||||
"ioredis": "^5.10.1",
|
||||
"js2xmlparser": "^5.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwks-rsa": "^2.1.5",
|
||||
@@ -71,7 +73,6 @@
|
||||
"pp-ipn": "^1.1.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"rate-limiter-flexible": "^2.4.2",
|
||||
"redis": "^3.1.2",
|
||||
"remove-markdown": "^0.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"short-uuid": "^4.2.2",
|
||||
@@ -2824,6 +2825,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
|
||||
"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw=="
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz",
|
||||
"integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||
@@ -3060,6 +3067,84 @@
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
|
||||
"integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
|
||||
"integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
|
||||
"integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -6603,6 +6688,52 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bullmq": {
|
||||
"version": "5.71.1",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.71.1.tgz",
|
||||
"integrity": "sha512-kOBfdcsHmO6wwmIjpersoVdYQ7jkjTgky4Yop0loc7QwSdgxliSzD69U9ijZuRrkyCJwz5p5eqxeGeQkJ0YGZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron-parser": "4.9.0",
|
||||
"ioredis": "5.10.1",
|
||||
"msgpackr": "1.11.5",
|
||||
"node-abort-controller": "3.1.1",
|
||||
"semver": "7.7.4",
|
||||
"tslib": "2.8.1",
|
||||
"uuid": "11.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bullmq/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/bullmq/node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/bullmq/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/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -7083,6 +7214,15 @@
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cluster-key-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/coa": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz",
|
||||
@@ -7606,6 +7746,18 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/cron-parser": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
|
||||
"integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"luxon": "^3.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz",
|
||||
@@ -8144,6 +8296,7 @@
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
|
||||
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
@@ -12531,8 +12684,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/habitica-markdown": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "git+ssh://git@github.com/HabitRPG/habitica-markdown.git#204545c1e028f22b937c0a73c5ef250c8973db16",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-4.1.0.tgz",
|
||||
"integrity": "sha512-iqiFT8BbuWIrrs6v9eIXSG7UfWOBdJVGDi9FjGiFFOe8+dOPi62yyXhm81vrx79/5Y2s+jG+7LItyaemD310uQ==",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"markdown-it": "^14.0.0",
|
||||
@@ -13452,6 +13606,39 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.10.1",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz",
|
||||
"integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "1.5.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.1.0",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ioredis/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/iota-array": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz",
|
||||
@@ -14641,6 +14828,12 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.flattendeep": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
|
||||
@@ -14657,6 +14850,12 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
||||
},
|
||||
"node_modules/lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
@@ -14846,6 +15045,15 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||
@@ -15864,6 +16072,37 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/msgpackr": {
|
||||
"version": "1.11.5",
|
||||
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz",
|
||||
"integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==",
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"msgpackr-extract": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/msgpackr-extract": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
|
||||
"integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build-optional-packages": "5.2.2"
|
||||
},
|
||||
"bin": {
|
||||
"download-msgpackr-prebuilds": "bin/download-prebuilds.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/multimatch": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz",
|
||||
@@ -16130,6 +16369,12 @@
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
|
||||
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw=="
|
||||
},
|
||||
"node_modules/node-abort-controller": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
|
||||
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
@@ -16201,6 +16446,21 @@
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp-build-optional-packages": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
|
||||
"integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"node-gyp-build-optional-packages": "bin.js",
|
||||
"node-gyp-build-optional-packages-optional": "optional.js",
|
||||
"node-gyp-build-optional-packages-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/node-loggly-bulk": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-loggly-bulk/-/node-loggly-bulk-4.0.1.tgz",
|
||||
@@ -18314,29 +18574,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/redis": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
|
||||
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
|
||||
"dependencies": {
|
||||
"denque": "^1.5.0",
|
||||
"redis-commands": "^1.7.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-redis"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-commands": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
|
||||
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
|
||||
},
|
||||
"node_modules/redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
@@ -19852,6 +20089,12 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/static-extend": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "5.46.1",
|
||||
"version": "5.46.4",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
@@ -19,6 +19,7 @@
|
||||
"bcrypt": "^5.1.1",
|
||||
"body-parser": "^1.20.3",
|
||||
"bootstrap": "^4.6.2",
|
||||
"bullmq": "^5.71.1",
|
||||
"compression": "^1.8.1",
|
||||
"cookie-session": "^2.1.1",
|
||||
"coupon-code": "^0.4.5",
|
||||
@@ -43,6 +44,7 @@
|
||||
"heapdump": "^0.3.15",
|
||||
"helmet": "^4.6.0",
|
||||
"in-app-purchase": "^1.11.3",
|
||||
"ioredis": "^5.10.1",
|
||||
"js2xmlparser": "^5.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwks-rsa": "^2.1.5",
|
||||
@@ -66,7 +68,6 @@
|
||||
"pp-ipn": "^1.1.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"rate-limiter-flexible": "^2.4.2",
|
||||
"redis": "^3.1.2",
|
||||
"remove-markdown": "^0.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"short-uuid": "^4.2.2",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable global-require */
|
||||
import got from 'got';
|
||||
import nconf from 'nconf';
|
||||
import requireAgain from 'require-again';
|
||||
import { TAVERN_ID } from '../../../../website/server/models/group';
|
||||
import { defer } from '../../../helpers/api-unit.helper';
|
||||
import worker from '../../../../website/server/libs/worker';
|
||||
|
||||
function getUser () {
|
||||
return {
|
||||
@@ -127,7 +127,7 @@ describe('emails', () => {
|
||||
let sendTxn = null;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox.stub(got, 'post').returns(defer().promise);
|
||||
sandbox.stub(worker, 'sendJob').returns(defer().promise);
|
||||
|
||||
const nconfGetStub = sandbox.stub(nconf, 'get');
|
||||
nconfGetStub.withArgs('IS_PROD').returns(true);
|
||||
@@ -149,13 +149,12 @@ describe('emails', () => {
|
||||
};
|
||||
|
||||
sendTxn(mailingInfo, emailType);
|
||||
expect(got.post).to.be.called;
|
||||
expect(got.post).to.be.calledWith('http://example.com/job', sinon.match({
|
||||
json: {
|
||||
data: {
|
||||
emailType: sinon.match.same(emailType),
|
||||
to: sinon.match(value => Array.isArray(value) && value[0].name === mailingInfo.name, 'matches mailing info array'),
|
||||
},
|
||||
expect(worker.sendJob).to.be.called;
|
||||
expect(worker.sendJob).to.be.calledWith('email', sinon.match({
|
||||
identifier: emailType,
|
||||
data: {
|
||||
emailType: sinon.match.same(emailType),
|
||||
to: sinon.match(value => Array.isArray(value) && value[0].name === mailingInfo.name, 'matches mailing info array'),
|
||||
},
|
||||
}));
|
||||
});
|
||||
@@ -168,7 +167,7 @@ describe('emails', () => {
|
||||
};
|
||||
|
||||
sendTxn(mailingInfo, emailType);
|
||||
expect(got.post).not.to.be.called;
|
||||
expect(worker.sendJob).not.to.be.called;
|
||||
});
|
||||
|
||||
it('throws error when mail target is only a string', async () => {
|
||||
@@ -233,13 +232,12 @@ describe('emails', () => {
|
||||
const mailingInfo = getUser();
|
||||
|
||||
sendTxn(mailingInfo, emailType);
|
||||
expect(got.post).to.be.called;
|
||||
expect(got.post).to.be.calledWith('http://example.com/job', sinon.match({
|
||||
json: {
|
||||
data: {
|
||||
emailType: sinon.match.same(emailType),
|
||||
to: sinon.match(val => val[0]._id === mailingInfo._id),
|
||||
},
|
||||
expect(worker.sendJob).to.be.called;
|
||||
expect(worker.sendJob).to.be.calledWith('email', sinon.match({
|
||||
identifier: emailType,
|
||||
data: {
|
||||
emailType: sinon.match.same(emailType),
|
||||
to: sinon.match(val => val[0]._id === mailingInfo._id),
|
||||
},
|
||||
}));
|
||||
});
|
||||
@@ -253,15 +251,14 @@ describe('emails', () => {
|
||||
const variables = [];
|
||||
|
||||
sendTxn(mailingInfo, emailType, variables);
|
||||
expect(got.post).to.be.called;
|
||||
expect(got.post).to.be.calledWith('http://example.com/job', sinon.match({
|
||||
json: {
|
||||
data: {
|
||||
variables: sinon.match(value => value[0].name === 'BASE_URL', 'matches variables'),
|
||||
personalVariables: sinon.match(value => value[0].rcpt === mailingInfo.email
|
||||
&& value[0].vars[0].name === 'RECIPIENT_NAME'
|
||||
expect(worker.sendJob).to.be.called;
|
||||
expect(worker.sendJob).to.be.calledWith('email', sinon.match({
|
||||
identifier: emailType,
|
||||
data: {
|
||||
variables: sinon.match(value => value[0].name === 'BASE_URL', 'matches variables'),
|
||||
personalVariables: sinon.match(value => value[0].rcpt === mailingInfo.email
|
||||
&& value[0].vars[0].name === 'RECIPIENT_NAME'
|
||||
&& value[0].vars[1].name === 'RECIPIENT_UNSUB_URL', 'matches personal variables'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -66,13 +66,15 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
group.purchased.plan.customerId = 'customer-id';
|
||||
group.purchased.plan.planId = subKey;
|
||||
group.purchased.plan.lastBillingDate = new Date();
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
|
||||
subscriptionBlock = common.content.subscriptionBlocks[subKey];
|
||||
subscriptionLength = subscriptionBlock.months * 30;
|
||||
|
||||
@@ -30,12 +30,14 @@ describe('Amazon Payments - Subscribe', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
group.purchased.plan.customerId = 'customer-id';
|
||||
group.purchased.plan.planId = subKey;
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
|
||||
amount = common.content.subscriptionBlocks[subKey].price;
|
||||
billingAgreementId = 'billingAgreementId';
|
||||
@@ -246,11 +248,6 @@ describe('Amazon Payments - Subscribe', () => {
|
||||
user.guilds.push(groupId);
|
||||
await user.save();
|
||||
|
||||
// Add existing users
|
||||
user = new User();
|
||||
user.guilds.push(groupId);
|
||||
await user.save();
|
||||
|
||||
// Set expected amount
|
||||
sub.key = 'group_monthly';
|
||||
sub.price = 9;
|
||||
|
||||
@@ -128,11 +128,12 @@ describe('Purchasing a group plan for group', () => {
|
||||
expect(publicGroup.purchased.plan.planId).to.not.exist;
|
||||
data.groupId = publicGroup._id;
|
||||
|
||||
// Public Guilds are no longer even findable
|
||||
await expect(api.createSubscription(data))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
message: i18n.t('onlyPrivateGuildsCanUpgrade'),
|
||||
httpCode: 404,
|
||||
name: 'NotFound',
|
||||
message: i18n.t('groupNotFound'),
|
||||
});
|
||||
|
||||
const updatedGroup = await Group.findById(publicGroup._id).exec();
|
||||
|
||||
@@ -30,13 +30,15 @@ describe('paypal - subscribeCancel', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
group.purchased.plan.customerId = groupCustomerId;
|
||||
group.purchased.plan.planId = subKey;
|
||||
group.purchased.plan.lastBillingDate = new Date();
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
|
||||
nextBillingDate = new Date();
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ describe('Stripe - Checkout', () => {
|
||||
const group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
const groupId = group._id;
|
||||
@@ -376,11 +376,13 @@ describe('Stripe - Checkout', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
groupId = group._id;
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
});
|
||||
|
||||
it('throws if user is not allowed to change group plan', async () => {
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('Stripe - Subscriptions', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
groupId = group._id;
|
||||
@@ -315,12 +315,14 @@ describe('Stripe - Subscriptions', () => {
|
||||
group = generateGroup({
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
privacy: 'private',
|
||||
leader: user._id,
|
||||
});
|
||||
group.purchased.plan.customerId = 'customer-id';
|
||||
group.purchased.plan.planId = subKey;
|
||||
await group.save();
|
||||
user.guilds.push(group._id);
|
||||
await user.save();
|
||||
|
||||
groupId = group._id;
|
||||
});
|
||||
|
||||
@@ -50,5 +50,59 @@ describe('UserNotification Model', () => {
|
||||
expect(safeNotifications[0].type).to.equal('NEW_CHAT_MESSAGE');
|
||||
expect(safeNotifications[0].id).to.equal('123');
|
||||
});
|
||||
|
||||
it('removes duplicate STREAK_ACHIEVEMENT notifications', () => {
|
||||
// Fixes issue #13325 - Users receiving duplicate streak achievement notifications
|
||||
const notifications = [
|
||||
new UserNotification({
|
||||
type: 'STREAK_ACHIEVEMENT',
|
||||
id: 123,
|
||||
data: {},
|
||||
}),
|
||||
new UserNotification({
|
||||
type: 'STREAK_ACHIEVEMENT',
|
||||
id: 456,
|
||||
data: {},
|
||||
}),
|
||||
new UserNotification({
|
||||
type: 'CRON',
|
||||
id: 789,
|
||||
data: {},
|
||||
}), // different type, should be kept
|
||||
];
|
||||
|
||||
const safeNotifications = UserNotification.cleanupCorruptData(notifications);
|
||||
expect(safeNotifications.length).to.equal(2);
|
||||
expect(safeNotifications[0].type).to.equal('STREAK_ACHIEVEMENT');
|
||||
expect(safeNotifications[0].id).to.equal('123');
|
||||
expect(safeNotifications[1].type).to.equal('CRON');
|
||||
expect(safeNotifications[1].id).to.equal('789');
|
||||
});
|
||||
|
||||
it('handles multiple STREAK_ACHIEVEMENT duplicates correctly', () => {
|
||||
// Test case: 3 duplicate STREAK_ACHIEVEMENT notifications
|
||||
const notifications = [
|
||||
new UserNotification({
|
||||
type: 'STREAK_ACHIEVEMENT',
|
||||
id: 111,
|
||||
data: {},
|
||||
}),
|
||||
new UserNotification({
|
||||
type: 'STREAK_ACHIEVEMENT',
|
||||
id: 222,
|
||||
data: {},
|
||||
}),
|
||||
new UserNotification({
|
||||
type: 'STREAK_ACHIEVEMENT',
|
||||
id: 333,
|
||||
data: {},
|
||||
}),
|
||||
];
|
||||
|
||||
const safeNotifications = UserNotification.cleanupCorruptData(notifications);
|
||||
expect(safeNotifications.length).to.equal(1);
|
||||
expect(safeNotifications[0].type).to.equal('STREAK_ACHIEVEMENT');
|
||||
expect(safeNotifications[0].id).to.equal('111'); // Keep first one
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
import { TAVERN_ID } from '../../../../../website/common/script/constants';
|
||||
|
||||
describe('POST /challenges/:challengeId/join', () => {
|
||||
it('returns error when challengeId is not a valid UUID', async () => {
|
||||
@@ -27,6 +29,37 @@ describe('POST /challenges/:challengeId/join', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('public Guild', () => {
|
||||
let group;
|
||||
let groupLeader;
|
||||
let members;
|
||||
let challenge;
|
||||
before(async () => {
|
||||
({ group, groupLeader, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
upgradeToGroupPlan: true,
|
||||
}));
|
||||
challenge = await generateChallenge(groupLeader, group);
|
||||
// Creation API is shut down, we need to simulate an extant public group
|
||||
await Group.updateOne({ _id: group._id }, { $set: { privacy: 'public' }, $unset: { 'purchased.plan': 1 } }).exec();
|
||||
});
|
||||
|
||||
it('returns error when challengeId is in an old public Guild', async () => {
|
||||
const authorizedUser = members[0]; // eslint-disable-line prefer-destructuring
|
||||
|
||||
await expect(authorizedUser.post(`/challenges/${challenge._id}/join`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('challengeNotFound'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Joining a valid challenge', () => {
|
||||
let groupLeader;
|
||||
let group;
|
||||
@@ -66,6 +99,15 @@ describe('POST /challenges/:challengeId/join', () => {
|
||||
expect(res.name).to.equal(challenge.name);
|
||||
});
|
||||
|
||||
it('succeeds when it\'s a Tavern challenge, even if the user isn\'t a "member" of Tavern', async () => {
|
||||
const tavern = await groupLeader.get(`/groups/${TAVERN_ID}`);
|
||||
const tavernChallenge = await generateChallenge(groupLeader, tavern, { prize: 1 });
|
||||
const generalUser = await generateUser();
|
||||
|
||||
const res = await generalUser.post(`/challenges/${tavernChallenge._id}/join`);
|
||||
expect(res.name).to.equal(tavernChallenge.name);
|
||||
});
|
||||
|
||||
it('returns challenge data', async () => {
|
||||
const res = await authorizedUser.post(`/challenges/${challenge._id}/join`);
|
||||
|
||||
|
||||
@@ -62,9 +62,9 @@ describe('GET /groups/:groupId/chat', () => {
|
||||
|
||||
it('returns error if user attempts to fetch a sunset Guild', async () => {
|
||||
await expect(user.get(`/groups/${group._id}/chat`)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('featureRetired'),
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('groupNotFound'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,9 +121,9 @@ describe('POST /chat/:chatId/like', () => {
|
||||
|
||||
await expect(user.post(`/groups/${groupWithChat._id}/chat/${message.message.id}/like`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('featureRetired'),
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('groupNotFound'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -193,23 +193,6 @@ describe('POST /groups/:groupId/quests/force-start', () => {
|
||||
expect(questingGroup.quest.members[notInPartyUser._id]).to.not.exist;
|
||||
});
|
||||
|
||||
it('removes users who have been deleted from quest.members', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
await partyMembers[0].del('/user', {
|
||||
password: 'password',
|
||||
});
|
||||
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/force-start`);
|
||||
|
||||
await sleep(0.5);
|
||||
|
||||
await questingGroup.sync();
|
||||
|
||||
expect(questingGroup.quest.members[partyMembers[0]._id]).to.not.exist;
|
||||
});
|
||||
|
||||
it('removes users who don\'t have true value in quest.members from quest.members', async () => {
|
||||
const partyMemberThatRejects = partyMembers[1];
|
||||
const partyMemberThatIgnores = partyMembers[2];
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import {
|
||||
each,
|
||||
map,
|
||||
} from 'lodash';
|
||||
import {
|
||||
checkExistence,
|
||||
createAndPopulateGroup,
|
||||
generateGroup,
|
||||
generateUser,
|
||||
generateChallenge,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import {
|
||||
@@ -15,6 +9,7 @@ import {
|
||||
sha1Encrypt as sha1EncryptPassword,
|
||||
} from '../../../../../website/server/libs/password';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
import sendJob from '../../../../../website/server/libs/worker';
|
||||
|
||||
const DELETE_CONFIRMATION = 'DELETE';
|
||||
|
||||
@@ -47,12 +42,13 @@ describe('DELETE /user', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes the user', async () => {
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(true);
|
||||
it('sends deletion job to worker', async () => {
|
||||
const workerStub = sandbox.stub(sendJob, 'sendJob');
|
||||
await user.del('/user', {
|
||||
password,
|
||||
});
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
||||
expect(workerStub).to.be.calledOnce;
|
||||
workerStub.restore();
|
||||
});
|
||||
|
||||
it('returns an error if excessive feedback is supplied', async () => {
|
||||
@@ -84,53 +80,6 @@ describe('DELETE /user', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes the user\'s tasks', async () => {
|
||||
await user.post('/tasks/user', {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
});
|
||||
await user.sync();
|
||||
|
||||
// gets the user's tasks ids
|
||||
const ids = [];
|
||||
each(user.tasksOrder, idsForOrder => {
|
||||
ids.push(...idsForOrder);
|
||||
});
|
||||
|
||||
expect(ids.length).to.be.above(0); // make sure the user has some task to delete
|
||||
|
||||
await user.del('/user', {
|
||||
password,
|
||||
});
|
||||
|
||||
await Promise.all(map(ids, id => expect(checkExistence('tasks', id)).to.eventually.eql(false)));
|
||||
});
|
||||
|
||||
it('reduces memberCount in challenges user is linked to', async () => {
|
||||
const populatedGroup = await createAndPopulateGroup({
|
||||
members: 2,
|
||||
});
|
||||
|
||||
const { group } = populatedGroup;
|
||||
const authorizedUser = populatedGroup.members[1];
|
||||
|
||||
const challenge = await generateChallenge(populatedGroup.groupLeader, group);
|
||||
await populatedGroup.groupLeader.post(`/challenges/${challenge._id}/join`);
|
||||
await authorizedUser.post(`/challenges/${challenge._id}/join`);
|
||||
|
||||
await challenge.sync();
|
||||
|
||||
expect(challenge.memberCount).to.eql(2);
|
||||
|
||||
await authorizedUser.del('/user', {
|
||||
password,
|
||||
});
|
||||
|
||||
await challenge.sync();
|
||||
|
||||
expect(challenge.memberCount).to.eql(1);
|
||||
});
|
||||
|
||||
it('sends feedback to the admin email', async () => {
|
||||
sandbox.spy(email, 'sendTxn');
|
||||
|
||||
@@ -158,10 +107,10 @@ describe('DELETE /user', () => {
|
||||
});
|
||||
|
||||
it('deletes the user with a legacy sha1 password', async () => {
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(true);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const salt = sha1MakeSalt();
|
||||
const sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
const workerStub = sandbox.stub(sendJob, 'sendJob');
|
||||
|
||||
await user.updateOne({
|
||||
'auth.local.hashed_password': sha1HashedPassword,
|
||||
@@ -179,7 +128,8 @@ describe('DELETE /user', () => {
|
||||
await user.del('/user', {
|
||||
password: textPassword,
|
||||
});
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
||||
expect(workerStub).to.be.calledOnce;
|
||||
workerStub.restore();
|
||||
});
|
||||
|
||||
context('last member of a party', () => {
|
||||
@@ -213,11 +163,12 @@ describe('DELETE /user', () => {
|
||||
});
|
||||
|
||||
it('deletes a Google user', async () => {
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(true);
|
||||
const workerStub = sandbox.stub(sendJob, 'sendJob');
|
||||
await user.del('/user', {
|
||||
password: DELETE_CONFIRMATION,
|
||||
});
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
||||
expect(workerStub).to.be.calledOnce;
|
||||
workerStub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -232,12 +183,13 @@ describe('DELETE /user', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes a Apple user', async () => {
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(true);
|
||||
it('deletes an Apple user', async () => {
|
||||
const workerStub = sandbox.stub(sendJob, 'sendJob');
|
||||
await user.del('/user', {
|
||||
password: DELETE_CONFIRMATION,
|
||||
});
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
||||
expect(workerStub).to.be.calledOnce;
|
||||
workerStub.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -211,22 +211,32 @@ describe('shared.ops.rebirth', () => {
|
||||
expect(user.achievements.rebirthLevel).to.equal(2);
|
||||
});
|
||||
|
||||
it('does not increment rebirth achievements when level is lower than previous', async () => {
|
||||
it('increments rebirth achievements even when level is lower than previous', async () => {
|
||||
user.stats.lvl = 2;
|
||||
user.achievements.rebirths = 1;
|
||||
user.achievements.rebirthLevel = 3;
|
||||
|
||||
await rebirth(user);
|
||||
|
||||
expect(user.achievements.rebirths).to.equal(1);
|
||||
expect(user.achievements.rebirths).to.equal(2);
|
||||
expect(user.achievements.rebirthLevel).to.equal(3);
|
||||
});
|
||||
|
||||
it('always increments rebirth achievements when level is MAX_LEVEL', async () => {
|
||||
it('updates rebirthLevel when current level is higher than previous', async () => {
|
||||
user.stats.lvl = 5;
|
||||
user.achievements.rebirths = 1;
|
||||
user.achievements.rebirthLevel = 3;
|
||||
|
||||
await rebirth(user);
|
||||
|
||||
expect(user.achievements.rebirths).to.equal(2);
|
||||
expect(user.achievements.rebirthLevel).to.equal(5);
|
||||
});
|
||||
|
||||
it('increments rebirth achievements when level is MAX_LEVEL', async () => {
|
||||
user.stats.lvl = MAX_LEVEL;
|
||||
user.achievements.rebirths = 1;
|
||||
// this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test
|
||||
user.achievements.rebirthLevel = MAX_LEVEL + 1;
|
||||
user.achievements.rebirthLevel = MAX_LEVEL;
|
||||
|
||||
await rebirth(user);
|
||||
|
||||
@@ -234,11 +244,10 @@ describe('shared.ops.rebirth', () => {
|
||||
expect(user.achievements.rebirthLevel).to.equal(MAX_LEVEL);
|
||||
});
|
||||
|
||||
it('always increments rebirth achievements when level is greater than MAX_LEVEL', async () => {
|
||||
it('increments rebirth achievements when level is greater than MAX_LEVEL', async () => {
|
||||
user.stats.lvl = MAX_LEVEL + 1;
|
||||
user.achievements.rebirths = 1;
|
||||
// this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test
|
||||
user.achievements.rebirthLevel = MAX_LEVEL + 2;
|
||||
user.achievements.rebirthLevel = MAX_LEVEL;
|
||||
|
||||
await rebirth(user);
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { getMatchingSwap, makeSubstitutionMap } from '../../website/common/script/content/constants/aprilFools';
|
||||
|
||||
describe('April Fools', () => {
|
||||
describe('getMatchingSwap', () => {
|
||||
it('returns Veggie for 2020', () => {
|
||||
const swap = getMatchingSwap(new Date('2020-04-01'));
|
||||
expect(swap).to.equal('Veggie');
|
||||
});
|
||||
it('returns Alien for 2026', () => {
|
||||
const swap = getMatchingSwap(new Date('2026-04-01'));
|
||||
expect(swap).to.equal('Alien');
|
||||
});
|
||||
it('Cycles through swaps correctly', () => {
|
||||
const swap = getMatchingSwap(new Date('2027-04-01'));
|
||||
expect(swap).to.equal('Veggie');
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSubstitutionMap', () => {
|
||||
it('returns correct substitution for Veggie', () => {
|
||||
const substitutions = makeSubstitutionMap('Veggie');
|
||||
expect(substitutions.pets['Pet-Wolf-']).to.equal('Pet-Wolf-Veggie');
|
||||
expect(substitutions.pets['Pet-TigerCub-']).to.equal('Pet-TigerCub-Veggie');
|
||||
expect(substitutions.pets['Pet-Yarn-']).to.equal('Pet-BearCub-Veggie');
|
||||
expect(substitutions.pets.default).to.equal('Pet-Dragon-Veggie');
|
||||
expect(substitutions.pets.noPet).to.equal('Pet-Wolf-Veggie');
|
||||
expect(substitutions.pets.noPetIOS).to.equal('Pet-TigerCub-Veggie');
|
||||
expect(substitutions.pets.noPetAndroid).to.equal('Pet-Cactus-Veggie');
|
||||
});
|
||||
|
||||
it('returns correct substitution for Cryptid', () => {
|
||||
const substitutions = makeSubstitutionMap('Cryptid');
|
||||
expect(substitutions.pets['Pet-Fox-']).to.equal('Pet-Fox-Cryptid');
|
||||
expect(substitutions.pets['Pet-FlyingPig-']).to.equal('Pet-FlyingPig-Cryptid');
|
||||
expect(substitutions.pets['Pet-Yarn-']).to.equal('Pet-BearCub-Cryptid');
|
||||
expect(substitutions.pets.default).to.equal('Pet-Dragon-Cryptid');
|
||||
expect(substitutions.pets.noPet).to.equal('Pet-Wolf-Cryptid');
|
||||
expect(substitutions.pets.noPetAndroid).to.equal('Pet-Cactus-Cryptid');
|
||||
});
|
||||
});
|
||||
});
|
||||
Generated
+43
-83
@@ -23,7 +23,7 @@
|
||||
"eslint-config-habitrpg": "6.2.0",
|
||||
"eslint-plugin-mocha": "5.3.0",
|
||||
"eslint-plugin-vue": "7.20.0",
|
||||
"habitica-markdown": "^3.0.0",
|
||||
"habitica-markdown": "^4.0.0",
|
||||
"hellojs": "^1.20.0",
|
||||
"intro.js": "^7.2.0",
|
||||
"jquery": "^3.7.1",
|
||||
@@ -6929,69 +6929,17 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/habitica-markdown": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-3.0.0.tgz",
|
||||
"integrity": "sha512-rw1LJ5Vsjx8sfjNa4e2wFuZf5eqqyb5/kfZXPxqfMMgJCCgIhWStDqY3nIclnpGWpemlKd+qbdh2rLiLgm9kng==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-4.1.0.tgz",
|
||||
"integrity": "sha512-iqiFT8BbuWIrrs6v9eIXSG7UfWOBdJVGDi9FjGiFFOe8+dOPi62yyXhm81vrx79/5Y2s+jG+7LItyaemD310uQ==",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"habitica-markdown-emoji": "1.2.4",
|
||||
"markdown-it": "10.0.0",
|
||||
"markdown-it-link-attributes": "3.0.0",
|
||||
"markdown-it-linkify-images": "^1.1.1"
|
||||
"markdown-it": "^14.0.0",
|
||||
"markdown-it-emoji": "^2.0.2",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"markdown-it-linkify-images": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/habitica-markdown-emoji": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown-emoji/-/habitica-markdown-emoji-1.2.4.tgz",
|
||||
"integrity": "sha512-UV0AxpDToldFQULuhTxC1y4sdNTApaIOh7ZuV/92HCPmCGkv3DAlHtYE67OmCqLVfs26HWAGVJaU3+OEnW3gjg==",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"markdown-it-emoji": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/habitica-markdown/node_modules/entities": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
|
||||
"integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/habitica-markdown/node_modules/linkify-it": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
|
||||
"integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/habitica-markdown/node_modules/markdown-it": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
|
||||
"integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"entities": "~2.0.0",
|
||||
"linkify-it": "^2.0.0",
|
||||
"mdurl": "^1.0.1",
|
||||
"uc.micro": "^1.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"markdown-it": "bin/markdown-it.js"
|
||||
}
|
||||
},
|
||||
"node_modules/habitica-markdown/node_modules/mdurl": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
|
||||
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/habitica-markdown/node_modules/uc.micro": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/has-bigints": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
|
||||
@@ -8084,50 +8032,62 @@
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it-emoji": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz",
|
||||
"integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-2.0.2.tgz",
|
||||
"integrity": "sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/markdown-it-link-attributes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-link-attributes/-/markdown-it-link-attributes-3.0.0.tgz",
|
||||
"integrity": "sha512-B34ySxVeo6MuEGSPCWyIYryuXINOvngNZL87Mp7YYfKIf6DcD837+lXA8mo6EBbauKsnGz22ZH0zsbOiQRWTNg==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz",
|
||||
"integrity": "sha512-pg5OK0jPLg62H4k7M9mRJLT61gUp9nvG0XveKYHMOOluASo9OEF13WlXrpAp2aj35LbedAy3QOCgQCw0tkLKAQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/markdown-it-linkify-images": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-1.1.1.tgz",
|
||||
"integrity": "sha512-1IEmAaAjIgAwY+tZI0sxDXdy9QKHutj5cN0lH2JBiSZt+2NYKrWRJj0cloQW3OFIfP2MLFA1E+6OLJhXPiLgNw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-3.0.0.tgz",
|
||||
"integrity": "sha512-Vs5yGJa5MWjFgytzgtn8c1U6RcStj3FZKhhx459U8dYbEE5FTWZ6mMRkYMiDlkFO0j4VCsQT1LT557bY0ETgtg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"markdown-it": "^8.4.2"
|
||||
"markdown-it": "^13.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it-linkify-images/node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/markdown-it-linkify-images/node_modules/entities": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
|
||||
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
|
||||
"license": "BSD-2-Clause"
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
|
||||
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it-linkify-images/node_modules/linkify-it": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
|
||||
"integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
|
||||
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it-linkify-images/node_modules/markdown-it": {
|
||||
"version": "8.4.2",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz",
|
||||
"integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==",
|
||||
"version": "13.0.2",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz",
|
||||
"integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"entities": "~1.1.1",
|
||||
"linkify-it": "^2.0.0",
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "~3.0.1",
|
||||
"linkify-it": "^4.0.1",
|
||||
"mdurl": "^1.0.1",
|
||||
"uc.micro": "^1.0.5"
|
||||
},
|
||||
|
||||
@@ -1,57 +1,3 @@
|
||||
.quest_lostMasterclasser4 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_lostMasterclasser4.gif") no-repeat;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
|
||||
.quest_windup {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_windup.gif") no-repeat;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
|
||||
.quest_solarSystem {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_solarSystem.gif") no-repeat;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
|
||||
.quest_virtualpet {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/quest_virtualpet.gif") no-repeat;
|
||||
width: 219px;
|
||||
height: 219px;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_Dessert, .Pet_HatchingPotion_Veggie, .Pet_HatchingPotion_Windup,
|
||||
.Pet_HatchingPotion_VirtualPet, .Pet_HatchingPotion_Fungi, .Pet_HatchingPotion_Cryptid {
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_Dessert {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Dessert.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_Veggie {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Veggie.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_Windup {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Windup.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_VirtualPet {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_VirtualPet.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_Fungi {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Fungi.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Pet_HatchingPotion_Cryptid {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Cryptid.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Gems {
|
||||
display:inline-block;
|
||||
margin-right:5px;
|
||||
@@ -80,6 +26,7 @@
|
||||
margin-left: -3px;
|
||||
margin-top: -18px;
|
||||
}
|
||||
|
||||
.slim_armor_special_0, .broad_armor_special_0, .shield_special_0 {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
@@ -87,7 +34,6 @@
|
||||
|
||||
/* Critical */
|
||||
.weapon_special_critical {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_critical.gif") no-repeat;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
margin-left:-12px;
|
||||
@@ -98,6 +44,7 @@
|
||||
.weapon_special_1 {
|
||||
margin-left: -12px;
|
||||
}
|
||||
|
||||
.broad_armor_special_1, .slim_armor_special_1, .head_special_1 {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
@@ -106,36 +53,15 @@
|
||||
.back_special_heroicAureole {
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/back_special_heroicAureole.gif") no-repeat;
|
||||
}
|
||||
|
||||
.head_special_0 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-ShadeHelmet.gif") no-repeat;
|
||||
}
|
||||
.head_special_1 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/ContributorOnly-Equip-CrystalHelmet.gif") no-repeat;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.broad_armor_special_0,.slim_armor_special_0 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-ShadeArmor.gif") no-repeat;
|
||||
}
|
||||
.broad_armor_special_1,.slim_armor_special_1 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/ContributorOnly-Equip-CrystalArmor.gif") no-repeat;
|
||||
}
|
||||
|
||||
.shield_special_0 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Shield-TormentedSkull.gif") no-repeat;
|
||||
}
|
||||
|
||||
.weapon_special_0 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Weapon-DarkSoulsBlade.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Pet-Wolf-Cerberus {
|
||||
width: 105px;
|
||||
height: 72px;
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Pet-CerberusPup.gif") no-repeat;
|
||||
}
|
||||
|
||||
.broad_armor_special_ks2019, .slim_armor_special_ks2019, .eyewear_special_ks2019, .head_special_ks2019, .shield_special_ks2019 {
|
||||
@@ -143,36 +69,17 @@
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.broad_armor_special_ks2019, .slim_armor_special_ks2019 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonArmor.gif") no-repeat;
|
||||
}
|
||||
|
||||
.eyewear_special_ks2019 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonVisor.gif") no-repeat;
|
||||
}
|
||||
|
||||
.head_special_ks2019 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonHelm.gif") no-repeat;
|
||||
}
|
||||
|
||||
.shield_special_ks2019 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonShield.gif") no-repeat;
|
||||
}
|
||||
|
||||
.weapon_special_ks2019 {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Equip-MythicGryphonGlaive.gif") no-repeat;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.Pet-Gryphon-Gryphatrice {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Pet-Gryphatrice.gif") no-repeat;
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
|
||||
.Pet-Gryphatrice-Jubilant {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Gryphatrice-Jubilant.gif") no-repeat;
|
||||
width: 81px;
|
||||
height: 96px;
|
||||
}
|
||||
@@ -182,39 +89,11 @@
|
||||
height: 135px;
|
||||
}
|
||||
|
||||
.Mount_Head_Gryphon-Gryphatrice {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Mount-Head-Gryphatrice.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Mount_Body_Gryphon-Gryphatrice {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Mount-Body-Gryphatrice.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Mount_Head_Dragon-Hydra {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Mount_Head_Dragon-Hydra.gif") no-repeat;
|
||||
}
|
||||
|
||||
.Mount_Body_Dragon-Hydra {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Mount_Body_Dragon-Hydra.gif") no-repeat;
|
||||
}
|
||||
|
||||
.background_airship, .background_clocktower, .background_steamworks {
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
|
||||
.background_airship {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_airship.gif") no-repeat;
|
||||
}
|
||||
|
||||
.background_clocktower {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_clocktower.gif") no-repeat;
|
||||
}
|
||||
|
||||
.background_steamworks {
|
||||
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_steamworks.gif") no-repeat;
|
||||
}
|
||||
|
||||
[class*="Mount_Head_"],
|
||||
[class*="Mount_Body_"] {
|
||||
margin-top:18px; /* Sprite accommodates 105x123 box */
|
||||
|
||||
@@ -1801,6 +1801,11 @@
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_on_a_strange_planet {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_on_a_strange_planet.png');
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_on_tree_branch {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_on_tree_branch.png');
|
||||
width: 141px;
|
||||
@@ -31485,8 +31490,8 @@
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.slim_armor_armoire_handstandOutfit {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_handstandOutfit .png');
|
||||
.slim_armor_armoire_handstandOutfit {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_handstandOutfit.png');
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -53223,6 +53228,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-BearCub-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-BearCub-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-BearCub-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-BearCub-Amber.png');
|
||||
width: 81px;
|
||||
@@ -53713,6 +53723,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Cactus-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Cactus-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Cactus-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Cactus-Amber.png');
|
||||
width: 81px;
|
||||
@@ -54503,6 +54518,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Dragon-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Dragon-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Dragon-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Dragon-Amber.png');
|
||||
width: 81px;
|
||||
@@ -54998,6 +55018,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-FlyingPig-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-FlyingPig-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-FlyingPig-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-FlyingPig-Amber.png');
|
||||
width: 81px;
|
||||
@@ -55333,6 +55358,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Fox-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Fox-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Fox-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Fox-Amber.png');
|
||||
width: 81px;
|
||||
@@ -56113,6 +56143,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-LionCub-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-LionCub-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-LionCub-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-LionCub-Amber.png');
|
||||
width: 81px;
|
||||
@@ -56718,6 +56753,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-PandaCub-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-PandaCub-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-PandaCub-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-PandaCub-Amber.png');
|
||||
width: 81px;
|
||||
@@ -58113,6 +58153,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-TigerCub-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-TigerCub-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-TigerCub-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-TigerCub-Amber.png');
|
||||
width: 81px;
|
||||
@@ -58758,6 +58803,11 @@
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Wolf-Alien {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Wolf-Alien.png');
|
||||
width: 81px;
|
||||
height: 99px;
|
||||
}
|
||||
.Pet-Wolf-Amber {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Wolf-Amber.png');
|
||||
width: 81px;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 375 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="330" height="80" viewBox="0 0 330 80" preserveAspectRatio="none" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M159.797 60.5502C165.534 61.8466 171.631 62.7536 178.221 63.1767C208.94 65.1492 233.733 56.6838 260.68 47.483C282.33 40.091 305.37 32.2243 333.99 28.9144L336 16.3018C260.81 7.08865 233.373 23.1672 205.362 39.5825C192.037 47.3908 178.583 55.2753 159.797 60.5502Z" fill="#BDA8FF"/>
|
||||
<path d="M0 80L331.948 79.9998V29.1594C268.976 36.9871 233.03 66.6959 178.221 63.1767C112.951 58.9858 95.9516 7.31934 0.000104656 0L0 80Z" fill="#925CF3"/>
|
||||
<path d="M203.54 40.6496C166.339 36.8525 141.531 39.6251 122.334 45.4666C133.94 51.8989 145.792 57.3851 159.797 60.5502C177.727 55.5155 190.801 48.1036 203.54 40.6496Z" fill="#D5C8FF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 803 B |
@@ -1,53 +1,228 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="rebirth"
|
||||
:title="$t('modalAchievement')"
|
||||
size="md"
|
||||
:hide-footer="true"
|
||||
size="sm"
|
||||
:hide-header="true"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="col-12">
|
||||
<!-- @TODO: +achievementAvatar('sun',0)--><achievement-avatar class="avatar" />
|
||||
</div><div class="col-6 offset-3 text-center">
|
||||
<div v-if="user.achievements.rebirthLevel < 100">
|
||||
{{ $t('rebirthAchievement', {
|
||||
number: user.achievements.rebirths,
|
||||
level: user.achievements.rebirthLevel}) }}
|
||||
</div><div v-if="user.achievements.rebirthLevel >= 100">
|
||||
{{ $t('rebirthAchievement100', {number: user.achievements.rebirths}) }}
|
||||
</div><br><button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('huzzah') }}
|
||||
</button>
|
||||
<div
|
||||
class="close-x"
|
||||
@click.stop="close()"
|
||||
>
|
||||
<div
|
||||
class="svg-icon svg-close"
|
||||
v-html="icons.close"
|
||||
></div>
|
||||
</div>
|
||||
<div class="content text-center">
|
||||
<h2
|
||||
v-once
|
||||
class="header"
|
||||
>
|
||||
{{ $t('rebirthNewAchievement') }}
|
||||
</h2>
|
||||
<div class="d-flex align-items-center justify-content-center icon-area">
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon sparkles mirror"
|
||||
v-html="icons.starGroup"
|
||||
></div>
|
||||
<Sprite
|
||||
class="achievement-icon"
|
||||
image-name="achievement-sun2x"
|
||||
/>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon sparkles"
|
||||
v-html="icons.starGroup"
|
||||
></div>
|
||||
</div>
|
||||
</div><achievement-footer />
|
||||
<p class="subtitle">
|
||||
{{ $t('rebirthNewAdventure') }}
|
||||
</p>
|
||||
<p
|
||||
class="description"
|
||||
v-html="achievementText"
|
||||
></p>
|
||||
<p
|
||||
v-once
|
||||
class="stack-info"
|
||||
>
|
||||
{{ $t('rebirthStackInfo') }}
|
||||
</p>
|
||||
<button
|
||||
v-once
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('onwards') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
slot="modal-footer"
|
||||
class="footer-wave"
|
||||
v-html="icons.purpleWaves"
|
||||
></div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.avatar {
|
||||
width: 140px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 1.5em;
|
||||
margin-top: 1.5em;
|
||||
<style lang="scss">
|
||||
@import '@/assets/scss/colors.scss';
|
||||
|
||||
#rebirth {
|
||||
.modal-dialog {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 14px 28px 0 rgba($black, 0.24), 0 10px 10px 0 rgba($black, 0.28);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 0;
|
||||
border-top: none;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/scss/colors.scss';
|
||||
|
||||
.content {
|
||||
padding: 24px 24px 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
color: $purple-200;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.icon-area {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.sparkles {
|
||||
width: 40px;
|
||||
height: 64px;
|
||||
|
||||
&.mirror {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.close-x {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
|
||||
&:hover .svg-close {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.svg-close {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.achievement-icon {
|
||||
margin: 0 24px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0;
|
||||
margin-bottom: 12px;
|
||||
color: $gray-10;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.71;
|
||||
margin-bottom: 12px;
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
.stack-info {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.71;
|
||||
color: $gray-50;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.footer-wave {
|
||||
width: 100%;
|
||||
|
||||
::v-deep svg {
|
||||
display: block;
|
||||
width: calc(100% + 8px);
|
||||
height: auto;
|
||||
margin: 0 -4px -4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import closeIcon from '@/assets/svg/close.svg?raw';
|
||||
import Sprite from '@/components/ui/sprite';
|
||||
import starGroup from '@/assets/svg/star-group.svg?raw';
|
||||
import purpleWaves from '@/assets/svg/purple-waves.svg?raw';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
Sprite,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
starGroup,
|
||||
purpleWaves,
|
||||
close: closeIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
achievementText () {
|
||||
const rebirths = this.user.achievements.rebirths || 0;
|
||||
const level = this.user.achievements.rebirthLevel || 0;
|
||||
|
||||
if (level >= 100) {
|
||||
return this.$t('rebirthAchievement100', { number: rebirths, level });
|
||||
}
|
||||
|
||||
if (rebirths === 1) {
|
||||
return this.$t('rebirthAchievement', { number: rebirths, level });
|
||||
}
|
||||
|
||||
return this.$t('rebirthAchievementPlural', { number: rebirths, level });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
|
||||
@@ -1,41 +1,186 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="rebirth-enabled"
|
||||
:title="$t('rebirthNew')"
|
||||
size="md"
|
||||
:hide-footer="true"
|
||||
size="sm"
|
||||
:hide-header="true"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="col-12">
|
||||
<div class="rebirth_orb"></div>
|
||||
<p>
|
||||
<span>{{ $t('rebirthUnlock') }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="close-x"
|
||||
@click.stop="close()"
|
||||
>
|
||||
<div
|
||||
class="svg-icon svg-close"
|
||||
v-html="icons.close"
|
||||
></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="col-12 text-center">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
<div class="content text-center">
|
||||
<h2
|
||||
v-once
|
||||
class="header"
|
||||
>
|
||||
{{ $t('rebirthUnlockedNewItem') }}
|
||||
</h2>
|
||||
<div class="d-flex align-items-center justify-content-center icon-area">
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon sparkles mirror"
|
||||
v-html="icons.starGroup"
|
||||
></div>
|
||||
<img
|
||||
class="orb-icon"
|
||||
src="@/assets/images/rebirth-orb.png"
|
||||
alt="Orb of Rebirth"
|
||||
>
|
||||
{{ $t('close') }}
|
||||
</button>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon sparkles"
|
||||
v-html="icons.starGroup"
|
||||
></div>
|
||||
</div>
|
||||
<p
|
||||
v-once
|
||||
class="subtitle"
|
||||
>
|
||||
{{ $t('rebirthUnlockedOrb') }}
|
||||
</p>
|
||||
<p
|
||||
v-once
|
||||
class="description"
|
||||
>
|
||||
{{ $t('rebirthUnlockedDesc') }}
|
||||
</p>
|
||||
<button
|
||||
v-once
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('onwards') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
slot="modal-footer"
|
||||
class="clearfix"
|
||||
></div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.rebirth_orb {
|
||||
margin: 0 auto;
|
||||
<style lang="scss">
|
||||
@import '@/assets/scss/colors.scss';
|
||||
|
||||
#rebirth-enabled {
|
||||
.modal-dialog {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 14px 28px 0 rgba($black, 0.24), 0 10px 10px 0 rgba($black, 0.28);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 0;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/scss/colors.scss';
|
||||
|
||||
.content {
|
||||
padding: 24px 24px 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
color: $purple-200;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.icon-area {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.sparkles {
|
||||
width: 40px;
|
||||
height: 64px;
|
||||
|
||||
&.mirror {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.close-x {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
|
||||
&:hover .svg-close {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.svg-close {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.orb-icon {
|
||||
width: 62px;
|
||||
height: 62px;
|
||||
margin: 0 24px;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0;
|
||||
margin-bottom: 12px;
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.71;
|
||||
margin-bottom: 24px;
|
||||
color: $gray-100;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import closeIcon from '@/assets/svg/close.svg?raw';
|
||||
import starGroup from '@/assets/svg/star-group.svg?raw';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
starGroup,
|
||||
close: closeIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
|
||||
@@ -321,10 +321,11 @@ export default {
|
||||
return null;
|
||||
},
|
||||
petClass () {
|
||||
const foolEvent = this.currentEventList?.find(event => event.aprilFools && moment()
|
||||
.isBetween(event.start, event.end));
|
||||
if (foolEvent) {
|
||||
return this.foolPet(this.member.items.currentPet, foolEvent.aprilFools);
|
||||
const substitutionEvent = this.currentEventList?.find(event => event.spriteSubstitutions
|
||||
&& moment().isBetween(event.start, event.end));
|
||||
if (substitutionEvent && substitutionEvent.spriteSubstitutions.pets) {
|
||||
return this.foolPet(`Pet-${this.member.items.currentPet}`,
|
||||
substitutionEvent.spriteSubstitutions.pets);
|
||||
}
|
||||
if (this.member?.items.currentPet) return `Pet-${this.member.items.currentPet}`;
|
||||
return '';
|
||||
|
||||
@@ -187,7 +187,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="user.purchased.background.birthday_bash"
|
||||
v-if="user.purchased.background.birthday_bash
|
||||
|| user.purchased.background.on_a_strange_planet"
|
||||
>
|
||||
<div
|
||||
class="row justify-content-center title-row mb-3"
|
||||
|
||||
@@ -182,12 +182,10 @@ export default {
|
||||
return 'GreyedOut';
|
||||
},
|
||||
imageName () {
|
||||
const foolEvent = this.currentEventList?.find(event => moment()
|
||||
.isBetween(event.start, event.end) && event.aprilFools);
|
||||
if (this.isOwned() && foolEvent) {
|
||||
if (this.isSpecial()) return `stable_${this.foolPet(this.item.key, foolEvent.aprilFools)}`;
|
||||
const petString = `${this.item.eggKey}-${this.item.key}`;
|
||||
return `stable_${this.foolPet(petString, foolEvent.aprilFools)}`;
|
||||
const substitutionEvent = this.currentEventList?.find(event => moment()
|
||||
.isBetween(event.start, event.end) && event.spriteSubstitutions);
|
||||
if (this.isOwned() && substitutionEvent && substitutionEvent.spriteSubstitutions.pets) {
|
||||
return `stable_${this.foolPet(`Pet-${this.item.key}`, substitutionEvent.spriteSubstitutions.pets)}`;
|
||||
}
|
||||
|
||||
if (this.isOwned() || (this.mountOwned() && this.isHatchable())) {
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
>
|
||||
<div class="modal-body">
|
||||
<news-content ref="newsContent" />
|
||||
<close-x
|
||||
@close="dismissAlert()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer d-flex align-items-center pb-0">
|
||||
@@ -30,12 +33,18 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import newsContent from './newsContent';
|
||||
import closeX from '../ui/closeX.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
closeX,
|
||||
newsContent,
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
async onShow () {
|
||||
this.$refs.newsContent.getPosts();
|
||||
|
||||
@@ -330,6 +330,7 @@ export default {
|
||||
handledNotifications,
|
||||
isInitialLoadComplete: false,
|
||||
pendingRebirthNotification: null,
|
||||
lastShownStreakCount: null, // Track last shown streak to prevent duplicates
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -726,17 +727,24 @@ export default {
|
||||
this.$root.$emit('habitica:won-challenge', notification);
|
||||
break;
|
||||
case 'REBIRTH_ACHIEVEMENT':
|
||||
if (localStorage.getItem('show-rebirth-confirmation') === 'true') {
|
||||
markAsRead = false;
|
||||
} else if (!this.isInitialLoadComplete) {
|
||||
this.pendingRebirthNotification = notification;
|
||||
markAsRead = false;
|
||||
} else {
|
||||
this.playSound('Achievement_Unlocked');
|
||||
this.$root.$emit('bv::show::modal', 'rebirth');
|
||||
if (localStorage.getItem('show-rebirth-confirmation') !== 'true') {
|
||||
if (!this.isInitialLoadComplete) {
|
||||
this.pendingRebirthNotification = notification;
|
||||
markAsRead = false;
|
||||
} else {
|
||||
this.playSound('Achievement_Unlocked');
|
||||
this.$root.$emit('bv::show::modal', 'rebirth');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'STREAK_ACHIEVEMENT':
|
||||
// Client-side deduplication: prevent showing duplicate streak achievements
|
||||
if (this.lastShownStreakCount === this.user.achievements.streak) {
|
||||
// Same streak already shown, skip this notification
|
||||
break;
|
||||
}
|
||||
this.lastShownStreakCount = this.user.achievements.streak;
|
||||
|
||||
this.text(`${this.$t('streaks')}: ${this.user.achievements.streak}`, () => {
|
||||
this.$root.$emit('bv::show::modal', 'streak');
|
||||
}, this.user.preferences.suppressModals.streak);
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
v-if="addNew || availableToSelect.length > 0"
|
||||
:class="{
|
||||
'item-group': true,
|
||||
'add-new': availableToSelect.length === 0 && search !== '',
|
||||
'add-new': search !== '' && !hasExactMatch,
|
||||
'scroll': availableToSelect.length > 5
|
||||
}"
|
||||
>
|
||||
@@ -86,7 +86,7 @@
|
||||
</b-dropdown-item-button>
|
||||
|
||||
<div
|
||||
v-if="addNew"
|
||||
v-if="addNew && search !== '' && !hasExactMatch"
|
||||
class="hint"
|
||||
>
|
||||
{{ $t('pressEnterToAddTag', { tagName: search }) }}
|
||||
@@ -171,7 +171,8 @@ $itemHeight: 2rem;
|
||||
max-height: #{5*$itemHeight};
|
||||
|
||||
&.add-new {
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
height: auto;
|
||||
|
||||
.hint {
|
||||
display: block;
|
||||
@@ -245,6 +246,7 @@ export default {
|
||||
selected: this.selectedItems,
|
||||
search: '',
|
||||
textbox: null,
|
||||
itemsAdded: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -272,6 +274,16 @@ export default {
|
||||
|
||||
return filteredItems;
|
||||
},
|
||||
hasExactMatch () {
|
||||
const searchTerm = this.search.trim().toLowerCase();
|
||||
if (!searchTerm) return false;
|
||||
if (this.itemsAdded.indexOf(searchTerm) !== -1) return true;
|
||||
if (this.availableToSelect.length === 0) return false;
|
||||
if (this.availableToSelect[0].name.toLowerCase() === searchTerm) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selected () {
|
||||
@@ -310,6 +322,7 @@ export default {
|
||||
this.closeSelectPopup();
|
||||
},
|
||||
selectItem (item) {
|
||||
if (!item) return;
|
||||
this.selectedItems.push(item.id);
|
||||
this.$emit('toggle', item.id);
|
||||
this.preventHide = true;
|
||||
@@ -371,9 +384,16 @@ export default {
|
||||
handleSubmit () {
|
||||
if (!this.addNew) return;
|
||||
const { search } = this;
|
||||
this.$emit('addNew', search);
|
||||
|
||||
this.search = '';
|
||||
// If there is a existing tag
|
||||
if (this.hasExactMatch) {
|
||||
this.selectItem(this.availableToSelect[0]);
|
||||
this.search = '';
|
||||
} else {
|
||||
// Creating a new tag as there is no existing tag present
|
||||
this.$emit('addNew', search);
|
||||
this.itemsAdded.push(search.toLowerCase());
|
||||
this.search = '';
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,56 +1,14 @@
|
||||
import includes from 'lodash/includes';
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
foolPet (pet, prank) {
|
||||
const SPECIAL_PETS = [
|
||||
'Bear-Veteran',
|
||||
'BearCub-Polar',
|
||||
'Cactus-Veteran',
|
||||
'Dragon-Hydra',
|
||||
'Dragon-Veteran',
|
||||
'Fox-Veteran',
|
||||
'Gryphatrice-Jubilant',
|
||||
'Gryphon-Gryphatrice',
|
||||
'Gryphon-RoyalPurple',
|
||||
'Hippogriff-Hopeful',
|
||||
'Jackalope-RoyalPurple',
|
||||
'JackOLantern-Base',
|
||||
'JackOLantern-Ghost',
|
||||
'JackOLantern-Glow',
|
||||
'JackOLantern-RoyalPurple',
|
||||
'Lion-Veteran',
|
||||
'MagicalBee-Base',
|
||||
'Mammoth-Base',
|
||||
'MantisShrimp-Base',
|
||||
'Orca-Base',
|
||||
'Phoenix-Base',
|
||||
'Tiger-Veteran',
|
||||
'Turkey-Base',
|
||||
'Turkey-Gilded',
|
||||
'Wolf-Cerberus',
|
||||
'Wolf-Veteran',
|
||||
];
|
||||
const BASE_PETS = [
|
||||
'BearCub',
|
||||
'Cactus',
|
||||
'Dragon',
|
||||
'FlyingPig',
|
||||
'Fox',
|
||||
'LionCub',
|
||||
'PandaCub',
|
||||
'TigerCub',
|
||||
'Wolf',
|
||||
];
|
||||
if (!pet) return `Pet-TigerCub-${prank}`;
|
||||
if (SPECIAL_PETS.indexOf(pet) !== -1) {
|
||||
return `Pet-Dragon-${prank}`;
|
||||
foolPet (pet, substitutions) {
|
||||
if (!pet || pet === 'Pet-') return substitutions.noPet;
|
||||
if (substitutions[pet]) return substitutions[pet];
|
||||
for (const key in substitutions) {
|
||||
if (pet.startsWith(key)) {
|
||||
return substitutions[key];
|
||||
}
|
||||
}
|
||||
const species = pet.slice(0, pet.indexOf('-'));
|
||||
if (includes(BASE_PETS, species)) {
|
||||
return `Pet-${species}-${prank}`;
|
||||
}
|
||||
return `Pet-BearCub-${prank}`;
|
||||
return substitutions.default;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
<div
|
||||
v-once
|
||||
class="feedback"
|
||||
class="feedback mt-3"
|
||||
v-html="$t('feedback')"
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -85,6 +85,13 @@ const router = new VueRouter({
|
||||
props: true,
|
||||
},
|
||||
{ name: 'profile', path: '/user/profile' },
|
||||
{
|
||||
name: 'avatar',
|
||||
path: '/avatar',
|
||||
children: [
|
||||
{ name: 'backgrounds', path: 'backgrounds' },
|
||||
],
|
||||
},
|
||||
{ name: 'stats', path: '/user/stats' },
|
||||
{ name: 'achievements', path: '/user/achievements' },
|
||||
{
|
||||
@@ -410,6 +417,13 @@ router.beforeEach(async (to, from, next) => {
|
||||
router.app.$root.$emit('bv::hide::modal', 'profile');
|
||||
}
|
||||
|
||||
if (to.name === 'backgrounds') {
|
||||
store.state.avatarEditorOptions.editingUser = true;
|
||||
store.state.avatarEditorOptions.startingPage = 'backgrounds';
|
||||
router.app.$root.$emit('bv::show::modal', 'avatar-modal');
|
||||
return null;
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
|
||||
@@ -735,5 +735,8 @@
|
||||
"backgroundMaskMakersWorkshopText": "Maskářova dílna",
|
||||
"backgroundMaskMakersWorkshopNotes": "Vyzkoušej novou tvář v maskářově dílně.",
|
||||
"backgroundCemeteryGateText": "Hřbitovní brána",
|
||||
"backgroundCemeteryGateNotes": "Straš u hřbitovní brány."
|
||||
"backgroundCemeteryGateNotes": "Straš u hřbitovní brány.",
|
||||
"backgroundAutumnBridgeText": "Podzimní most",
|
||||
"backgroundAutumnBridgeNotes": "Obdivuj krásu podzimního mostu.",
|
||||
"backgroundInsideACrystalText": "Uvnitř krystalu."
|
||||
}
|
||||
|
||||
@@ -1086,6 +1086,8 @@
|
||||
"eventBackgrounds": "Event Backgrounds",
|
||||
"backgroundBirthdayBashText": "Birthday Bash",
|
||||
"backgroundBirthdayBashNotes": "Habitica's having a birthday party, and everyone's invited!",
|
||||
"backgroundOnAStrangePlanetText": "On a Strange Planet",
|
||||
"backgroundOnAStrangePlanetNotes": "Venture where no Habitican has gone before: On a Strange Planet.",
|
||||
|
||||
"monthlyBackgrounds": "Monthly Backgrounds"
|
||||
}
|
||||
|
||||
@@ -356,6 +356,7 @@
|
||||
"hatchingPotionBalloon": "Balloon",
|
||||
"hatchingPotionCryptid": "Cryptid",
|
||||
"hatchingPotionOpal": "Opal",
|
||||
"hatchingPotionAlien": "Alien",
|
||||
|
||||
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText %> Pet.",
|
||||
"premiumPotionUnlimitedNotes": "Not usable on Quest Pet eggs.",
|
||||
|
||||
@@ -221,7 +221,7 @@
|
||||
"questTRexUndeadBoss": "Skeletal Tyrannosaur",
|
||||
"questTRexUndeadRageTitle": "Skeleton Healing",
|
||||
"questTRexUndeadRageDescription": "This bar fills when you don't complete your Dailies. When it is full, the Skeletal Tyrannosaur will heal 30% of its remaining health!",
|
||||
"questTRexUndeadRageEffect": "`Skeletal Tyrannosaur uses SKELETON HEALING!`\n\nThe monster lets forth an unearthly roar, and some of its damaged bones knit back together!",
|
||||
"questTRexUndeadRageEffect": "Skeletal Tyrannosaur uses SKELETON HEALING!\n\nThe monster lets forth an unearthly roar, and some of its damaged bones knit back together!",
|
||||
|
||||
"questTRexDropTRexEgg": "Tyrannosaur (Egg)",
|
||||
"questTRexUnlockText": "Unlocks Tyrannosaur Eggs for purchase in the Market",
|
||||
@@ -282,7 +282,7 @@
|
||||
"questDilatoryDistress2Boss": "Water Skull Swarm",
|
||||
"questDilatoryDistress2RageTitle": "Swarm Respawn",
|
||||
"questDilatoryDistress2RageDescription": "Swarm Respawn: This bar fills when you don't complete your Dailies. When it is full, the Water Skull Swarm will heal 30% of its remaining health!",
|
||||
"questDilatoryDistress2RageEffect": "`Water Skull Swarm uses SWARM RESPAWN!`\n\nEmboldened by their victories, more skulls pour forth from the crevasse, bolstering the swarm!",
|
||||
"questDilatoryDistress2RageEffect": "Water Skull Swarm uses SWARM RESPAWN!\n\nEmboldened by their victories, more skulls pour forth from the crevasse, bolstering the swarm!",
|
||||
"questDilatoryDistress2DropSkeletonPotion": "Skeleton Hatching Potion",
|
||||
"questDilatoryDistress2DropCottonCandyBluePotion": "Cotton Candy Blue Hatching Potion",
|
||||
"questDilatoryDistress2DropHeadgear": "Fire Coral Circlet (Headgear)",
|
||||
@@ -398,7 +398,7 @@
|
||||
"questAxolotlUnlockText": "Unlocks Axolotl Eggs for purchase in the Market",
|
||||
"questAxolotlRageTitle": "Axolotl Regeneration",
|
||||
"questAxolotlRageDescription": "This bar fills when you don't complete your Dailies. When it is full, the Magical Axolotl will heal 30% of its remaining health!",
|
||||
"questAxolotlRageEffect": "`Magical Axolotl uses AXOLOTL REGENERATION!`\n\n`A curtain of colorful bubbles obscures the monster for a moment, and when it clears, some of its wounds have vanished!`",
|
||||
"questAxolotlRageEffect": "Magical Axolotl uses AXOLOTL REGENERATION!\n\nA curtain of colorful bubbles obscures the monster for a moment, and when it clears, some of its wounds have vanished!",
|
||||
|
||||
"questTurtleText": "Guide the Turtle",
|
||||
"questTurtleNotes": "Help! This giant sea turtle cannot find her way to her nesting beach. She returns there every year to lay her eggs, but this year Inkomplete Bay is filled with toxic Task Flotsam made of red Dailies and unchecked To Do's. \"She's thrashing in a panic!\" @JessicaChase says.<br><br>@UncommonCriminal nods. \"It's because her guiding senses are fogged and confused.\"<br><br>@Scarabsi grabs your arm. \"Can you help clear the Task Flotsam blocking her path? It may be hazardous, but we have to help her!\"",
|
||||
@@ -435,7 +435,7 @@
|
||||
"questTaskwoodsTerror1Boss": "Fire Skull Swarm",
|
||||
"questTaskwoodsTerror1RageTitle": "Swarm Respawn",
|
||||
"questTaskwoodsTerror1RageDescription": "Swarm Respawn: This bar fills when you don't complete your Dailies. When it is full, the Fire Skull Swarm will heal 30% of its remaining health!",
|
||||
"questTaskwoodsTerror1RageEffect": "`Fire Skull Swarm uses SWARM RESPAWN!`\n\nEmboldened by their victories, more skulls swirl around you in a gout of flame!",
|
||||
"questTaskwoodsTerror1RageEffect": "Fire Skull Swarm uses SWARM RESPAWN!\n\nEmboldened by their victories, more skulls swirl around you in a gout of flame!",
|
||||
"questTaskwoodsTerror1DropSkeletonPotion": "Skeleton Hatching Potion",
|
||||
"questTaskwoodsTerror1DropRedPotion": "Red Hatching Potion",
|
||||
"questTaskwoodsTerror1DropHeadgear": "Pyromancer's Turban (Headgear)",
|
||||
@@ -507,7 +507,7 @@
|
||||
"questStoikalmCalamity1Boss": "Earth Skull Swarm",
|
||||
"questStoikalmCalamity1RageTitle": "Swarm Respawn",
|
||||
"questStoikalmCalamity1RageDescription": "Swarm Respawn: This bar fills when you don't complete your Dailies. When it is full, the Earth Skull Swarm will heal 30% of its remaining health!",
|
||||
"questStoikalmCalamity1RageEffect": "`Earth Skull Swarm uses SWARM RESPAWN!`\n\nMore skulls break free from the ground, their teeth chattering in the cold!",
|
||||
"questStoikalmCalamity1RageEffect": "Earth Skull Swarm uses SWARM RESPAWN!\n\nMore skulls break free from the ground, their teeth chattering in the cold!",
|
||||
"questStoikalmCalamity1DropSkeletonPotion": "Skeleton Hatching Potion",
|
||||
"questStoikalmCalamity1DropDesertPotion": "Desert Hatching Potion",
|
||||
"questStoikalmCalamity1DropArmor": "Mammoth Rider Armor",
|
||||
@@ -554,7 +554,7 @@
|
||||
"questMayhemMistiflying1Boss": "Air Skull Swarm",
|
||||
"questMayhemMistiflying1RageTitle": "Swarm Respawn",
|
||||
"questMayhemMistiflying1RageDescription": "Swarm Respawn: This bar fills when you don't complete your Dailies. When it is full, the Air Skull Swarm will heal 30% of its remaining health!",
|
||||
"questMayhemMistiflying1RageEffect": "`Air Skull Swarm uses SWARM RESPAWN!`\n\nEmboldened by their victories, more skulls come whirling out of the clouds!",
|
||||
"questMayhemMistiflying1RageEffect": "Air Skull Swarm uses SWARM RESPAWN!\n\nEmboldened by their victories, more skulls come whirling out of the clouds!",
|
||||
"questMayhemMistiflying1DropSkeletonPotion": "Skeleton Hatching Potion",
|
||||
"questMayhemMistiflying1DropWhitePotion": "White Hatching Potion",
|
||||
"questMayhemMistiflying1DropArmor": "Roguish Rainbow Messenger Robes (Armor)",
|
||||
@@ -623,7 +623,7 @@
|
||||
"questLostMasterclasser3Boss": "Void Skull Swarm",
|
||||
"questLostMasterclasser3RageTitle": "Swarm Respawn",
|
||||
"questLostMasterclasser3RageDescription": "Swarm Respawn: This bar fills when you don't complete your Dailies. When it is full, the Void Skull Swarm will heal 30% of its remaining health!",
|
||||
"questLostMasterclasser3RageEffect": "`Void Skull Swarm uses SWARM RESPAWN!`\n\nEmboldened by their victories, more skulls scream down from the heavens, bolstering the swarm!",
|
||||
"questLostMasterclasser3RageEffect": "Void Skull Swarm uses SWARM RESPAWN!\n\nEmboldened by their victories, more skulls scream down from the heavens, bolstering the swarm!",
|
||||
"questLostMasterclasser3DropBodyAccessory": "Aether Amulet (Body Accessory)",
|
||||
"questLostMasterclasser3DropBasePotion": "Base Hatching Potion",
|
||||
"questLostMasterclasser3DropGoldenPotion": "Golden Hatching Potion",
|
||||
@@ -637,7 +637,7 @@
|
||||
"questLostMasterclasser4Boss": "Anti'zinnya",
|
||||
"questLostMasterclasser4RageTitle": "Siphoning Void",
|
||||
"questLostMasterclasser4RageDescription": "Siphoning Void: This bar fills when you don't complete your Dailies. When it is full, Anti'zinnya will remove the party's Mana!",
|
||||
"questLostMasterclasser4RageEffect": "`Anti'zinnya uses SIPHONING VOID!` In a twisted inversion of the Ethereal Surge spell, you feel your magic drain away into the darkness!",
|
||||
"questLostMasterclasser4RageEffect": "Anti'zinnya uses SIPHONING VOID! In a twisted inversion of the Ethereal Surge spell, you feel your magic drain away into the darkness!",
|
||||
"questLostMasterclasser4DropBackAccessory": "Aether Cloak (Back Accessory)",
|
||||
"questLostMasterclasser4DropWeapon": "Aether Crystals (Two-Handed Weapon)",
|
||||
"questLostMasterclasser4DropMount": "Invisible Aether Mount",
|
||||
@@ -804,7 +804,7 @@
|
||||
"questWaffleBoss": "Awful Waffle",
|
||||
"questWaffleRageTitle": "Maple Mire",
|
||||
"questWaffleRageDescription": "Maple Mire: This bar fills when you don't complete your Dailies. When it is full, the Awful Waffle will subtract from the pending damage that party members have built up!",
|
||||
"questWaffleRageEffect": "`Awful Waffle uses MAPLE MIRE!` Sticky sappy syrup slows your swings and spells! Pending damage reduced.",
|
||||
"questWaffleRageEffect": "Awful Waffle uses MAPLE MIRE! Sticky sappy syrup slows your swings and spells! Pending damage reduced.",
|
||||
"questWaffleDropDessertPotion": "Confection Hatching Potion",
|
||||
"questWaffleUnlockText": "Unlocks Confection Hatching Potions for purchase in the Market",
|
||||
|
||||
@@ -875,7 +875,7 @@
|
||||
"questVirtualPetBoss": "Wotchimon",
|
||||
"questVirtualPetRageTitle": "The Beepening",
|
||||
"questVirtualPetRageDescription": "This bar fills when you don't complete your Dailies. When it is full, the Wotchimon will take away some of your party's pending damage!",
|
||||
"questVirtualPetRageEffect": "`Wotchimon uses Bothersome Beep!` Wotchimon sounds a bothersome beep, and its happiness bar suddenly disappears! Pending damage reduced.",
|
||||
"questVirtualPetRageEffect": "Wotchimon uses Bothersome Beep! Wotchimon sounds a bothersome beep, and its happiness bar suddenly disappears! Pending damage reduced.",
|
||||
"questVirtualPetDropVirtualPetPotion": "Virtual Pet Hatching Potion",
|
||||
"questVirtualPetUnlockText": "Unlocks Virtual Pet Hatching Potion for purchase in the Market",
|
||||
|
||||
@@ -885,7 +885,7 @@
|
||||
"questPinkMarbleBoss": "Cupido",
|
||||
"questPinkMarbleRageTitle": "Pink Punch",
|
||||
"questPinkMarbleRageDescription": "This bar fills when you don't complete your Dailies. When it is full, Cupido will take away some of your party's pending damage!",
|
||||
"questPinkMarbleRageEffect": "`Cupido uses Pink Punch!` That wasn't affectionate at all! Your partymates are taken aback. Pending damage reduced.",
|
||||
"questPinkMarbleRageEffect": "Cupido uses Pink Punch! That wasn't affectionate at all! Your partymates are taken aback. Pending damage reduced.",
|
||||
"questPinkMarbleDropPinkMarblePotion": "Pink Marble Hatching Potion",
|
||||
"questPinkMarbleUnlockText": "Unlocks Pink Marble Hatching Potions for purchase in the Market.",
|
||||
|
||||
@@ -997,5 +997,15 @@
|
||||
"questFungiRageDescription": "This bar fills when you don't complete your Dailies. When it's full, the Moody Mushroom will take away some of your party's pending damage",
|
||||
"questFungiRageEffect": "A Mist emanates from the Moody Mushroom and surrounds your party, dampening the mood and subduing your magic. The party's MP is reduced!",
|
||||
"questFungiDropFungiPotion": "Fungi Hatching Potion",
|
||||
"questFungiUnlockText": "Unlocks Fungi Hatching Potions for purchase in the Market."
|
||||
"questFungiUnlockText": "Unlocks Fungi Hatching Potions for purchase in the Market.",
|
||||
|
||||
"questAlienText": "Invasion of the Motivation Snatchers",
|
||||
"questAlienNotes": "It’s been a strange few days in Habitica. The great flying saucer still hovers near the Flourishing Fields. It hums oddly. Why is it lingering? April Fool’s Day has passed, and the Master of Rogues' time in the spotlight has ended.<br><br>You wander toward the light of the space ship. You may as well check it out and get a few steps in while you’re at it.<br><br>As you get closer you see the April Fool, looking a bit grim. His face appears greenish in the light of the ship’s beam.<br><br>”'Twas my plan to get some potions for everyone, a little gift so all can enjoy their little extraterrestrial pals again! But I just can’t work up the gumption… I do believe I know why,” the Fool says, nodding toward the beam.<br><br>Little symbols are being sucked up into the ship. It’s all your checked off tasks! No wonder your motivation’s been lackluster.<br><br>”Our motivation is being abducted!” you exclaim. “We have to rescue it before it ends up in deep space somewhere!”<br><br>The Fool smiles. “Concentrate your thoughts on the tasks you know you need to finish! I’ll do the rest with a bit of magic.”",
|
||||
"questAlienCompletion": "You’ve managed to wrestle back the stolen motivation with your determination and the Fool’s magic power. As you feel your drive returning, the UFO descends, and a ramp slowly comes out along with a large, green, one-eyed creature. While strange-looking, it doesn’t seem threatening.<br><br>“Looks like we went a little far trying to harvest a little extra encouragement from your fine city,” it says. “Apologies for that, and fantastic work getting it back. The extra aura of your efforts actually charged up the ship’s engine enough to get us home! Please, take these with our thanks.”<br><br>“Ooh potions,” says the Fool, “how delightful, and how convenient for me that you have them all ready to go!”",
|
||||
"questAlienBoss": "Encouragement Thief, the Extraterrestrial",
|
||||
"questAlienRageTitle": "Intergalactic Impediment",
|
||||
"questAlienRageDescription": "This bar fills when you don't complete your Dailies. When it is full, the Extraterrestrial will discourage you by recovering some of its Health!",
|
||||
"questAlienRageEffect": "Encouragement Thief uses Intergalactic Impediment! You've backslid right through hyperspace. Your opponent recovers HP!",
|
||||
"questAlienDropAlienPotion": "Alien Hatching Potion",
|
||||
"questAlienUnlockText": "Unlocks Alien Hatching Potion for purchase in the Market"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"rebirthNew": "Rebirth: New Adventure Available!",
|
||||
"rebirthUnlock": "You've unlocked Rebirth! This special Market item allows you to begin a new game at level 1 while keeping your tasks, achievements, pets, and more. Use it to breathe new life into Habitica if you feel you've achieved it all, or to experience new features with the fresh eyes of a beginning character!",
|
||||
"rebirthAchievement": "You've begun a new adventure! This is Rebirth <%= number %> for you, and the highest Level you've attained is <%= level %>. To stack this Achievement, begin your next new adventure when you've reached an even higher Level!",
|
||||
"rebirthAchievement100": "You've begun a new adventure! This is Rebirth <%= number %> for you, and the highest Level you've attained is 100 or higher. To stack this Achievement, begin your next new adventure when you've reached at least 100!",
|
||||
"rebirthUnlockedNewItem": "Orb of Rebirth Unlocked",
|
||||
"rebirthUnlockedOrb": "A new adventure is available!",
|
||||
"rebirthUnlockedDesc": "Use the Orb of Rebirth to breathe new life into your Habitica adventure once you feel you've achieved it all! Begin again at level 1 while keeping your tasks, Achievements, and Pets with this special item found in the Market.",
|
||||
"rebirthNewAchievement": "New Achievement",
|
||||
"rebirthNewAdventure": "A new adventure begins now!",
|
||||
"rebirthAchievement": "You've used the Orb of Rebirth <strong><%= number %></strong> time, and your highest level reached is <strong><%= level %></strong>.",
|
||||
"rebirthAchievementPlural": "You've used the Orb of Rebirth <strong><%= number %></strong> times, and your highest level reached is <strong><%= level %></strong>.",
|
||||
"rebirthAchievement100": "You've used the Orb of Rebirth <strong><%= number %></strong> times, and your highest level reached is <strong>100</strong> or higher.",
|
||||
"rebirthStackInfo": "This achievement will stack each time you use the Orb of Rebirth.",
|
||||
"rebirthBegan": "Began a New Adventure",
|
||||
"rebirthText": "Began <%= rebirths %> New Adventures",
|
||||
"rebirthOrb": "Used an Orb of Rebirth to start over after attaining Level <%= level %>.",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"resetAccPop": "Start over, removing all levels, gold, gear, history, and tasks.",
|
||||
"deleteAccount": "Delete Account",
|
||||
"deleteAccPop": "Cancel and remove your Habitica account.",
|
||||
"feedback": "If you'd like to give us feedback, please enter it below - we'd love to hear your feedback! It will be anonymous unless you choose to enter your contact details. Don't speak English well? No problem! Use the language you prefer.",
|
||||
"feedback": "We'd love to hear your feedback! If you'd like to share any, enter it below. It will be anonymous unless you choose to include your contact details.",
|
||||
"feedbackPlaceholder": "Add your feedback",
|
||||
"dataExport": "Data Export",
|
||||
"saveData": "Here are a few options for saving your data.",
|
||||
@@ -82,8 +82,8 @@
|
||||
"resetText2": "Another option is using an <b>Orb of Rebirth</b>, which will reset everything else while preserving your Tasks and Equipment.",
|
||||
"resetTextLocal": "If you're absolutely certain, type your password into the text box below.",
|
||||
"resetTextSocial": "If you're absolutely certain, type <b>\"<%= magicWord %>\"</b> into the text box below.",
|
||||
"deleteLocalAccountText": "<b>Are you sure?</b> This will delete your account forever, and it can never be restored! You will need to register a new account to use Habitica again. Banked or spent Gems will not be refunded. If you're absolutely certain, type your password into the text box below.",
|
||||
"deleteSocialAccountText": "<b>Are you sure?</b> This will delete your account forever, and it can never be restored! You will need to register a new account to use Habitica again. Banked or spent Gems will not be refunded. If you're absolutely certain, type <b>\"<%= magicWord %>\"</b> into the text box below.",
|
||||
"deleteLocalAccountText": "<b>Are you sure?</b> This action is permanent. Deleting your account will remove all of your data, and it cannot be recovered. Gems will not be refunded.<br><br>Please allow up to 24 hours for account deletion to complete, and up to 30 days for analytics data to be removed if you opted in. Once complete, you'll be able to register for a new Habitica account using your previous login information.<br><br>To continue, type your password below.",
|
||||
"deleteSocialAccountText": "<b>Are you sure?</b> This action is permanent. Deleting your account will remove all of your data, and it cannot be recovered. Gems will not be refunded.<br><br>Please allow up to 24 hours for account deletion to complete, and up to 30 days for analytics data to be removed if you opted in. Once complete, you'll be able to register for a new Habitica account using your previous login information.<br><br>To continue, type <%= magicWord %> below.",
|
||||
"API": "API",
|
||||
"APICopied": "API token copied to clipboard.",
|
||||
"APITokenTitle": "API Token",
|
||||
|
||||
@@ -274,5 +274,8 @@
|
||||
"mysterySet202512": "Ensemble Champion·ne Cookie",
|
||||
"mysterySet202601": "Ensemble Égide Hivernale",
|
||||
"mysterySet202602": "Ensemble Renarde Sakura",
|
||||
"subscriptionBillingFYIShort": "Les abonnements se renouvellent automatiquement à moins que vous n'annuliez votre engagement 24 heures avant la fin de la période en cours. Vous serez prélevé·e 24 heures avant la date de votre renouvellement, au même prix que celui que vous avez initialement payé."
|
||||
"subscriptionBillingFYIShort": "Les abonnements se renouvellent automatiquement à moins que vous n'annuliez votre engagement 24 heures avant la fin de la période en cours. Vous serez prélevé·e 24 heures avant la date de votre renouvellement, au même prix que celui que vous avez initialement payé.",
|
||||
"mysterySet202604": "Ensemble Astronaute Audacieu·x·se",
|
||||
"mysterySet202605": "Ensemble Nimbus Tombée de la Nuit",
|
||||
"mysterySet202603": "Ensemble Mage de la Glycine"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,7 @@
|
||||
"mysterySet202512": "クッキーチャンピオンセット",
|
||||
"mysterySet202601": "冬のイージスセット",
|
||||
"subscriptionBillingFYI": "有料プランは現在加入している期間が終了する24時間前までに解約しない限り、自動的に更新されます。設定の「有料プラン」タブから有料プランを管理できます。あなたのアカウントは更新日の24時間以内に、最初にお支払った料金で、請求されます。",
|
||||
"subscriptionBillingFYIShort": "有料プランは現在加入している期間が終了する24時間前までに解約しない限り、自動的に更新されます。あなたのアカウントは更新日の24時間以内に、最初にお支払った料金で、請求されます。"
|
||||
"subscriptionBillingFYIShort": "有料プランは現在加入している期間が終了する24時間前までに解約しない限り、自動的に更新されます。あなたのアカウントは更新日の24時間以内に、最初にお支払った料金で、請求されます。",
|
||||
"mysterySet202603": "藤の魔術師セット",
|
||||
"mysterySet202604": "大胆な宇宙飛行士セット"
|
||||
}
|
||||
|
||||
@@ -930,5 +930,9 @@
|
||||
"monthlyBackgrounds": "월별 배경",
|
||||
"backgroundElegantPalaceText": "우아한 궁전",
|
||||
"timeTravelBackgrounds": "스팀펑크 배경",
|
||||
"backgroundSteamworksNotes": "증기 공장에서 증기와 강철의 거대 조작 장치를 제작하세요."
|
||||
"backgroundSteamworksNotes": "증기 공장에서 증기와 강철의 거대 조작 장치를 제작하세요.",
|
||||
"backgrounds032026": "세트",
|
||||
"backgroundWaterfallWithRainbowText": "폭포와 무지개",
|
||||
"backgroundWaterfallWithRainbowNotes": "아름다운 폭포와 무지개를 감상하세요.",
|
||||
"backgrounds042026": "세트"
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"clearBrowserData": "브라우저 데이터 삭제하기",
|
||||
"footerMobile": "모바일",
|
||||
"footerCommunity": "커뮤니티",
|
||||
"marketing3Header": "앱 및 확장",
|
||||
"marketing2Header": "친구와 경쟁하고, 관심 그룹에 참여하세요",
|
||||
"marketing1Lead1Title": "당신의 삶, 롤플레잉 게임",
|
||||
"marketing3Header": "Habitica의 더 많은 활용",
|
||||
"marketing2Header": "친구와 한 팀이 되세요",
|
||||
"marketing1Lead1Title": "삶을 게임처럼",
|
||||
"logout": "로그아웃",
|
||||
"login": "로그인",
|
||||
"history": "역사학",
|
||||
@@ -39,22 +39,22 @@
|
||||
"learnMore": "더 배우기",
|
||||
"wrongPassword": "잘못된 암호.",
|
||||
"muchmuchMore": "그리고 훨씬 더!",
|
||||
"marketing1Lead2Title": "좋은 장비를 얻으세요",
|
||||
"marketing1Lead1": "Habitica는 실생활의 습관들을 향상시키도록 돕는 비디오 게임입니다. 당신의 모든 과제들(습관, 일과 및 해야할 일)을 당신이 정복해야할 작은 괴물들로 바꾸어서 당신의 삶을 게임화하는 것입니다. 당신이 잘 할수록 게임에서 더 큰 성과를 얻습니다. 만약 잘못한다면 당신의 캐릭터는 게임에서 퇴보하게 될 것입니다.",
|
||||
"marketing1Header": "게임을 해서 당신의 습관들을 향상시키세요",
|
||||
"marketing1Lead2Title": "멋있는 장비를 얻으세요",
|
||||
"marketing1Lead1": "Habitica는 해야할 일들과 씨름하는 여러분을 위한 완벽한 앱입니다. Habitica는 여러분이 과업을 완수할 때마다 골드, 경험치, 아이템 같은 익숙한 보상을 제공합니다. 여러분이 생산성과 성취감을 더 잘 느끼도록요. 과업을 잘 완수할수록, 여러분은 게임 속에서도 발전합니다.",
|
||||
"marketing1Header": "좋은 습관에 한 걸음씩 가까워지세요!",
|
||||
"invalidEmail": "암호 재설정을 수행하려면 유효한 전자 메일 주소가 필요합니다.",
|
||||
"guidanceForBlacksmiths": "대장장이를 위한 안내",
|
||||
"marketing1Lead3Title": "무작위로 나오는 상품을 찾아보세요",
|
||||
"marketing1Lead2": "당신의 아바타를 꾸미기 위해서 습관들을 개선시키세요. 당신이 획득한 좋은 장비들 자랑해보세요!",
|
||||
"marketing1Lead3Title": "노력의 보상을 받으세요",
|
||||
"marketing1Lead2": "과제를 완수하세요. 그리고 검과 갑옷, 수많은 골드를 모으세요. 수백 종류가 만들어내는 끝없는 조합을 즐기세요. 능력치나 스타일을 최적화해보세요. 어쩌면 둘 다를요! ",
|
||||
"mobileAndroid": "안드로이드",
|
||||
"marketing4Lead1": "조직적 사용",
|
||||
"marketing2Lead2Title": "몬스터와 싸우기",
|
||||
"marketing2Lead1": "Habitica를 혼자서 하는 동안에도 당신이 협력하거나 경쟁 그리고 서로 책임을 묻기 시작할 때 더 빛이 나게 됩니다. 자기계발 프로그램의 가장 효과적인 부분은 사회적 책임이며, 비디오 게임보다 더 책임과 경쟁을 위한 환경이 어디 있겠습니까?",
|
||||
"marketing2Lead2": "전투가 없으면 롤플레잉 게임이겠어요? 파티와 함께 몬스터와 싸우세요. 몬스터들은 \"초책임모드\"입니다. - 당신이 운동을 빼먹은 날은 몬스터가 *모두*를 다치게 만드는 날입니다.",
|
||||
"marketing2Lead3Title": "서로에게 도전심이 들게 해 보세요",
|
||||
"marketing2Lead3": "도전과제는 친구들과 낯선 사람들과 경쟁을 하게 해줍니다. 도전과제가 끝났을때 최고인 사람이 특별한 상을 받게 됩니다.",
|
||||
"marketing4Lead1": "교육만큼 작게 게임화하기 좋은 게 없죠! 매일 있는 수업의 지루함을 게임과 함께 깨버리세요. Habitica는 숙제를 따라가고, 학급 도전을 만들고, 학생들이 자신의 성취를 자랑하는 즐거운 방법이 될 겁니다.",
|
||||
"marketing2Lead2Title": "퀘스트에서 몬스터와 싸우기",
|
||||
"marketing2Lead1": "다른 사람이 있으면 동기가 샘솟습니다. 협력과 경쟁, 상호작용을 통해서요! Habitica는 사회적 책임을 활용합니다. 자기계발에 언제나 효율적인 길이죠.",
|
||||
"marketing2Lead2": "친구와 함께 수백 개의 퀘스트에 도전하고 전투에 참여하세요. 퀘스트 몬스터를 위해서는 최대한의 책임이 필요합니다. 치실을 까먹으면 모두에게 대미지가 가해집니다!",
|
||||
"marketing2Lead3Title": "서로 도전해 보세요",
|
||||
"marketing2Lead3": "저희가 만든 도전에 참여해보세요. 여러분의 관심과 목표에 맞는 과제 리스트를 받아보세요. 최선을 다해 경쟁하고 승자로서 보석을 가져가세요!",
|
||||
"marketing4Lead1Title": "교육의 게임화",
|
||||
"marketing4Header": "조직적 용도",
|
||||
"marketing4Header": "가사일 너머",
|
||||
"marketing4Lead2": "건강 관리 비용이 증가하고 있으며 무엇인가를 제공해야 합니다. 수백개의 프로그램들이 비용절감과 건강개선을 위해 만들어지고 있습니다. 우리는 Habitica가 건강한 생활 방식을 향한 실질적인 길을 열 수 있다고 믿습니다.",
|
||||
"username": "아이디",
|
||||
"emailOrUsername": "이메일 혹은 아이디 (대소문자 구별)",
|
||||
@@ -68,11 +68,11 @@
|
||||
"invalidLoginCredentialsLong": "이런! 당신의 이메일 주소 / 아이디 혹은 비밀번호가 바르지 않습니다.\n- 올바르게 입력됐는지 확인해주세요. 아이디와 비밀번호는 대소문자를 구별합니다.\n- Facebook이나 Google 연동으로 계정을 생성하셨다면, 다시 한 번 시도해보세요.\n- 만약 비밀번호를 잊으셨다면, \"비밀번호 찾기\"를 누르세요.",
|
||||
"usernamePlaceholder": "예: Honggildong",
|
||||
"emailUsernamePlaceholder": "예: Honggildong 또는 gildong@example.com",
|
||||
"marketing1Lead3": "\"확률적 보상\" 시스템을 통해 도박처럼 스릴 있는 동기 부여를 얻으세요. Habitica는 스스로를 다양한 방식으로 격려할 수 있습니다. 스스로에게 긍정적인 보상을 주거나, 부정적인 습관에 벌을 주거나, 정해진 보상도 얻고, 랜덤한 보상도 노릴 수 있죠.",
|
||||
"marketing3Lead1": "**아이폰과 안드로이드** 앱은 이동 중에도 업무를 처리할 수 있게 돕습니다. 웹사이트에 로그인해서 작업 완료 버튼을 누리는 것이 귀찮을 때가 있지요.",
|
||||
"marketing2Lead1Title": "친구와 함께 올라가는 생산성",
|
||||
"marketing1Lead3": "몇 주간의 힘든 과제도 기대가 있으면 할 만하죠. 삶은 보상을 주지 않더라도, Habitica는 아닙니다! 과제를 완수할 때마다 보상이 있습니다. 놀라실 거예요. 그러니 계속 진행하세요! ",
|
||||
"marketing3Lead1": "어디서든 과제를 확인해보세요. 안드로이드와 iOS 기기에서도 Habitica를 이용할 수 있습니다. 과제를 완료하는 새로운 방법을 확인해보세요. 수상 경력이 있는 저희 앱에서요.",
|
||||
"marketing2Lead1Title": "함께하는 생산성",
|
||||
"marketing3Lead2": "**서드 파티 툴**은 Habitica를 삶의 다양한 측면과 연결합니다. 우리의 API 서비스는 [Chrome 확장 프로그램](https://chrome.google.com/webstore/detail/habitica/pidkmpibnnnhneohdgjclfdjpijggmjj?hl=en-US) 등과 손쉬운 통합을 가능하게 합니다. 비생산적인 웹서핑할 때 포인트를 잃게 하거나, 생산적일 때는 포인트를 얻게할 수 있죠. [자세한 정보는 여기를 클릭하세요](https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations).",
|
||||
"marketing3Lead2Title": "서드 파티 지원",
|
||||
"marketing3Lead2Title": "오픈 소스 커뮤니티",
|
||||
"marketing4Lead3-1": "삶을 게임화하고 싶으세요?",
|
||||
"joinMany": "목표를 달성하면서 <%= userCountInMillions %> million이 넘는 유저들과 함께 즐기세요!",
|
||||
"marketing4Lead2Title": "건강과 웰빙의 게임화",
|
||||
@@ -89,5 +89,6 @@
|
||||
"playButton": "플레이",
|
||||
"enterHabitica": "Habitica 들어가기",
|
||||
"pkQuestion2": "해비티카는 어떻게 작동하나요?",
|
||||
"passwordResetPage": "비밀번호 초기화"
|
||||
"passwordResetPage": "비밀번호 초기화",
|
||||
"marketing3Lead1Title": "안드로이드 & iOS 앱"
|
||||
}
|
||||
|
||||
@@ -930,5 +930,14 @@
|
||||
"backgrounds022026": "SET 141: Uitgebracht Februari 2026",
|
||||
"backgroundElegantPalaceText": "Elegant Paleis",
|
||||
"backgroundElegantPalaceNotes": "Bewonder de kleurrijke hallen van een Elegant Paleis.",
|
||||
"backgroundBirthdayBashNotes": "Habitica viert zijn verjaardagsfeest en iedereen is uitgenodigd!"
|
||||
"backgroundBirthdayBashNotes": "Habitica viert zijn verjaardagsfeest en iedereen is uitgenodigd!",
|
||||
"backgrounds032026": "SET 142: Uitgebracht Maart 2026",
|
||||
"backgroundWaterfallWithRainbowText": "Waterval met Regenboog",
|
||||
"backgroundWaterfallWithRainbowNotes": "Bewonder de adembenemende schoonheid van een Waterval met een Regenboog.",
|
||||
"backgrounds042026": "SET 143: Uitgebracht April 2026",
|
||||
"backgroundRidingACometText": "Rijden op een Komeet",
|
||||
"backgroundRidingACometNotes": "Reis door de ruimte terwijl je rijdt op een Komeet!",
|
||||
"backgrounds052026": "SET 144: Uitgebracht Mei 2026",
|
||||
"backgroundElvenCitadelText": "Elfenburcht",
|
||||
"backgroundElvenCitadelNotes": "Onderneem de schilderachtige reis naar een Elfenburcht."
|
||||
}
|
||||
|
||||
@@ -147,5 +147,17 @@
|
||||
"subscriptionBenefitsAdjustments": "Aanpassingen aan de voordelen voor abonnees",
|
||||
"contentAnswer501": "Huidskleuren",
|
||||
"contentAnswer400": "Huisdier Queesten",
|
||||
"contentAnswer402": "Magische Uitbroeddranken"
|
||||
"contentAnswer402": "Magische Uitbroeddranken",
|
||||
"webFaqAnswer60": "Hier zijn enkele snelle tips om te starten met je nieuwe Habitica Groepsplan:\n\n* Promoveer een lid tot manager zodat die taken kan aanmaken en bewerken\n* Laat taken niet toegewezen als iedereen ze kan voltooien en ze maar één keer gedaan moeten worden\n* Wijs een taak toe aan één persoon om te zorgen dat niemand anders die taak kan voltooien\n* Wijs een taak toe aan meerdere personen als iedereen die taak moet voltooien\n* Schakel de optie in om gedeelde taken op je persoonlijke bord te tonen, zodat je niets mist\n* Je krijgt beloningen voor de taken die je voltooit, zelfs als ze aan meerdere personen zijn toegewezen\n* Taakbeloningen worden niet verdeeld tussen leden\n* Gebruik de taakkleur op het teambord om het gemiddelde voltooiingspercentage van taken te beoordelen\n* Controleer regelmatig de taken op het gedeelde takenbord om te zien of ze nog relevant zijn\n* Als je een Daily mist, krijg jij of je team geen schade, maar de taak zal wel in kleur verslechteren",
|
||||
"webFaqAnswer67": "Klassen zijn verschillende rollen die je personage kan aannemen. Elke klasse heeft zijn eigen unieke voordelen en vaardigheden naarmate je in niveau stijgt. Deze vaardigheden kunnen de manier waarop je met je taken omgaat ondersteunen of je helpen bij het voltooien van Quests met je Party.\n\nJe klasse bepaalt ook welke uitrusting beschikbaar is om te kopen bij je Beloningen, op de Markt en in de Seizoenswinkel.\n\nHieronder vind je een overzicht van elke klasse om je te helpen kiezen welke het beste bij jouw speelstijl past:\n####**Warrior**\n* Warriors richten veel schade aan bij bazen en hebben een grote kans op kritieke treffers wanneer ze taken voltooien, waardoor je extra Ervaring en Goud krijgt.\n* Strength is hun primaire stat en verhoogt de schade die ze doen.\n* Constitution is hun secundaire stat en vermindert de schade die ze ontvangen.\n* De vaardigheden van Warriors versterken de Constitution en Strength van hun Partyleden.\n* Overweeg Warrior te spelen als je graag bazen bevecht, maar ook wat bescherming wilt wanneer je af en toe een taak mist.\n####**Healer**\n* Healers hebben hoge verdediging en kunnen zowel zichzelf als hun Partyleden genezen.\n* Constitution is hun primaire stat en verhoogt hun genezingen terwijl het schade vermindert.\n* Intelligence is hun secundaire stat en verhoogt hun Mana en Ervaring.\n* De vaardigheden van Healers maken hun taken minder rood en versterken de Constitution van hun Partyleden.\n* Overweeg Healer te spelen als je vaak taken mist en jezelf of je Partyleden wilt kunnen genezen. Healers stijgen ook snel in niveau.\n####**Mage**\n* Mages stijgen snel in niveau, krijgen veel Mana en richten schade aan bij bazen in Quests.\n* Intelligence is hun primaire stat en verhoogt hun Mana en Ervaring.\n* Perception is hun secundaire stat en verhoogt hun Goud en itemdrops.\n* De vaardigheden van Mages bevriezen taakreeksen, herstellen Mana van Partyleden en versterken hun Intelligence.\n* Overweeg Mage te spelen als je gemotiveerd wordt door snel levels te behalen en schade bij te dragen in boss Quests.\n#### **Rogue**\n* Rogues krijgen de meeste itemdrops en Goud door taken te voltooien en hebben een grote kans op kritieke treffers, waardoor ze nog meer Ervaring en Goud krijgen.\n* Perception is hun primaire stat en verhoogt Goud en itemdrops.\n* Strength is hun secundaire stat en verhoogt de schade die ze doen.\n* De vaardigheden van Rogues helpen hen gemiste Dailies te ontwijken, Goud te stelen en de Perception van hun Partyleden te versterken.\n* Overweeg Rogue te spelen als je sterk gemotiveerd wordt door beloningen.",
|
||||
"webFaqAnswer68": "Als je merkt dat je vaak HP verliest, probeer dan een van deze tips:\n\n- Pauzeer je Dailies. De knop “Schade pauzeren” in Instellingen voorkomt dat je HP verliest wanneer je Dailies mist.\n- Pas het schema van je Dailies aan. Door ze in te stellen zodat ze nooit vervallen, kun je ze nog steeds voltooien voor beloningen zonder het risico op HP-verlies.\n- Probeer klasseskills te gebruiken:\n\t- Rogues kunnen Stealth gebruiken om schade van gemiste Dailies te voorkomen\n\t- Warriors kunnen Brutal Smash gebruiken om de roodheid van een Daily te verminderen, waardoor je minder schade krijgt als je die mist\n\t- Healers kunnen Searing Brightness gebruiken om de roodheid van Dailies te verminderen, waardoor je minder schade krijgt als je die mist",
|
||||
"webFaqAnswer70": "Statpunten laten je de kernstatistieken van je personage verhogen. Je verdient één statpunt telkens wanneer je in niveau stijgt (tot en met niveau 100). Deze kun je handmatig toewijzen of automatisch laten verdelen met de functie Automatische toewijzing. Het toewijzen van statpunten wordt ontgrendeld samen met het Klassesysteem op niveau 10.",
|
||||
"webFaqAnswer57": "Zodra je lid wordt van een Party, ontvang je geen verdere uitnodigingen meer.\nAls je uitnodigingen en toekomstige communicatie van een specifieke speler wilt voorkomen, ga dan naar hun profiel en klik op de knop Blokkeren. Op mobiele profielen tik je op de drie puntjes in de bovenhoek en kies je “Blokkeren”.\n\nAls je een situatie tegenkomt waarin je denkt dat een andere speler onze Communityrichtlijnen heeft overtreden in hun naam, profiel of in een bericht dat ze hebben gestuurd, rapporteer het bericht dan of neem contact met ons op via admin@habitica.com.",
|
||||
"webFaqAnswer55": "Ja! Als je de gebruikersnaam of het e-mailadres van een Habitica-speler hebt, kun je die uitnodigen om lid te worden van je Party. Zo stuur je een uitnodiging op de verschillende platforms:\n\nIemand uitnodigen via de mobiele apps:\n1. Open het menu, selecteer “Party” en scroll naar de sectie Leden\n2. Tik op “Leden zoeken” en ga vervolgens naar het tabblad “Via uitnodiging”\n3. Voer de gebruikersnamen of e-mailadressen in van de spelers die je wilt uitnodigen en tik op “Uitnodiging verzenden”\n\nIemand uitnodigen via de website:\n1. Ga naar je Party en klik op “Uitnodigen voor Party”\n2. Voer de gebruikersnamen of e-mailadressen in van de spelers die je wilt uitnodigen en klik op “Uitnodigingen verzenden”",
|
||||
"webFaqAnswer59": "Habitica Groepsplannen bieden een gedeelde ervaring doordat leden eenvoudig taken kunnen toevoegen, toewijzen en voltooien via een gedeeld takenbord. Met functies zoals ledenrollen, statusoverzicht en het toewijzen van taken zijn Groepsplannen ideaal voor gezinnen of teams van collega’s met gezamenlijke doelen. Ze zijn ook een geweldige manier om elkaar gemotiveerd te houden tijdens jullie reis om monsters te verslaan en je leven te verbeteren.",
|
||||
"webFaqAnswer62": "Groepsplannen geven je de unieke mogelijkheid om gedeelde taken toe te wijzen aan andere leden van je Groepsplan. Wanneer een gedeelde taak aan een lid wordt toegewezen, kunnen andere leden deze taak niet meer voltooien.\n\nJe kunt een taak ook aan meerdere leden tegelijk toewijzen. Als bijvoorbeeld iedereen zijn tanden moet poetsen, maak je één taak aan en wijs je die toe aan elk lid. Elk lid kan de taak voltooien en zijn of haar individuele beloningen verdienen. De hoofdtaak wordt als voltooid weergegeven zodra iedereen de taak heeft afgerond.",
|
||||
"webFaqAnswer61": "Alleen de leider van het Groepsplan en managers kunnen gedeelde taken aanmaken. Als je wilt dat een lid taken kan aanmaken, moet je die persoon promoveren tot manager.\n\nZo promoveer je een lid van het Groepsplan tot manager op de website:\n1. Ga naar je Groepsplan en schakel naar het tabblad “Groepsinformatie”\n2. Bekijk de ledenlijst en klik op het puntjes-icoon naast het lid dat je wilt promoveren\n3. Selecteer “Manager toewijzen”",
|
||||
"webFaqAnswer64": "Gedeelde taken worden voor iedereen op hetzelfde moment gereset zodat het gedeelde takenbord synchroon blijft. Dit tijdstip is zichtbaar op het gedeelde takenbord en wordt bepaald door de dagstarttijd van de leider van het Groepsplan. Omdat gedeelde taken automatisch resetten, krijg je de volgende ochtend geen kans meer om gisteren niet voltooide gedeelde Dailies alsnog af te werken.\n\nGedeelde Dailies veroorzaken geen schade als ze worden gemist, maar ze zullen in kleur achteruitgaan om de voortgang visueel weer te geven.",
|
||||
"webFaqAnswer65": "Hoewel de mobiele apps nog niet alle functies van Groepsplannen volledig ondersteunen, kun je gedeelde taken wel voltooien via de iOS- en Android-apps!\n\nOp Android kun je bovenaan het scherm op je weergavenaam tikken wanneer je taken bekijkt om over te schakelen naar het gedeelde takenbord. Van daaruit kun je leden bekijken, de chat openen en taken aanmaken, voltooien of toewijzen.\n\nJe kunt ook een instelling inschakelen om gedeelde taken naar het persoonlijke takenbord te kopiëren, zodat alle taken op één plek voltooid kunnen worden.\n\nZo doe je dit in de mobiele apps:\n*Open Instellingen en schakel “Gedeelde taken kopiëren” in\n\nZo doe je dit op de Habitica-website:\n* Ga naar het Groepsplan en schakel de “Taken kopiëren”-schakelaar in op het gedeelde takenbord",
|
||||
"webFaqAnswer66": "De gedeelde takenborden van Groepsplannen zijn dynamischer dan Challenges, omdat ze voortdurend kunnen worden bijgewerkt en gebruikt door leden. Challenges zijn vooral handig wanneer je één vaste set taken naar veel mensen wilt sturen.\n\nGroepsplannen zijn bovendien een betaalde functie, terwijl Challenges gratis beschikbaar zijn voor iedereen.\n\nJe kunt in Challenges geen specifieke taken toewijzen, en Challenges hebben geen gedeelde dagreset. Over het algemeen bieden Challenges minder controle en directe interactie."
|
||||
}
|
||||
|
||||
@@ -2672,5 +2672,6 @@
|
||||
"weaponSpecialFall2024MageNotes": "Met een aanraking van dit schitterende wapen worden je de stappen van je taak direct versimpeld. Verhoogt Intelligentie met <%= int %> en Perceptie met <%= per %>. Beperkte Oplage Herfst 2024 Uitrusting.",
|
||||
"weaponSpecialSummer2024MageNotes": "Deze verschrikkelijke tentakels kunnen tegelijkertijd magie afleiden, afbuigen en sturen. Verhoogt Intelligentie met <%= int %> en Perceptie met <%= per %>. Beperkte Oplage Zomer 2024 Uitrusting.",
|
||||
"weaponSpecialSummer2024HealerNotes": "Het zal je verbazen wanneer je ontdekt hoe hard de schelp aan het eind van deze staf is. Verhoogt Intelligentie met <%= int %>. Beperkte Oplage Zomer 2024 Uitrusting.",
|
||||
"weaponSpecialWinter2025RogueNotes": "Verblind die moeilijke taken tot onderwerping! Je zult niet te stoppen zijn! Verhoogt Kracht met <%= str %>. Beperkte Oplage Winter 2024-2025 Uitrusting."
|
||||
"weaponSpecialWinter2025RogueNotes": "Verblind die moeilijke taken tot onderwerping! Je zult niet te stoppen zijn! Verhoogt Kracht met <%= str %>. Beperkte Oplage Winter 2024-2025 Uitrusting.",
|
||||
"weaponSpecialWinter2025HealerNotes": "Wat je nu nodig hebt zijn meer lichtjes met een lichtgevende ster er bovenop! Je zal niet te stoppen zijn! Verhoogt Intelligentie met <%= int %>. Beperkte Oplage Winter 2024-2025 Uitrusting."
|
||||
}
|
||||
|
||||
@@ -11,5 +11,8 @@
|
||||
"rebirthPop": "Herstart je personage direct als een Niveau 1 Krijger zonder je prestaties, verzamelobjecten en uitrusting te verliezen. Je Taken en hun geschiedenis zal hetzelfde blijven maar ze worden gereset naar een gele kleur. Je Reeks word verwijderd behalve van taken die horen bij een actieve Uitdaging of een Groepsplan. Je Goud, Ervaring, Mana en de effecten van al je Vaardigheden gaan verloren. Dit alles gaat direct in werking.",
|
||||
"rebirthName": "Bol der Hergeboorte",
|
||||
"rebirthComplete": "Je bent herboren!",
|
||||
"nextFreeRebirth": "<strong><%= days %> dagen</strong> tot <strong>GRATIS</strong> Bol der Hergeboorte"
|
||||
"nextFreeRebirth": "<strong><%= days %> dagen</strong> tot <strong>GRATIS</strong> Bol der Hergeboorte",
|
||||
"rebirthNewAchievement": "Nieuwe prestatie",
|
||||
"rebirthNewAdventure": "Een nieuw avontuur begint nu!",
|
||||
"rebirthUnlockedOrb": "Een nieuw avontuur is beschikbaar!"
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
"backgrounds052016": "Conjunto 24: Lançado em Maio de 2016",
|
||||
"backgroundBeehiveText": "Colmeia",
|
||||
"backgroundBeehiveNotes": "Faça zumbidos e dance em uma colméia.",
|
||||
"backgroundGazeboText": "Gazebo",
|
||||
"backgroundGazeboText": "Pavilhão",
|
||||
"backgroundGazeboNotes": "Enfrente um Gazebo.",
|
||||
"backgroundTreeRootsText": "Raízes da árvore",
|
||||
"backgroundTreeRootsNotes": "Explore as Raízes da Árvore.",
|
||||
@@ -923,12 +923,21 @@
|
||||
"backgroundSirensLairNotes": "Ouse mergulhar na toca da sereia.",
|
||||
"backgroundSunnyStreetWithShopsNotes": "Aprecie as imagens e os sons de uma rua ensolarada com lojas.",
|
||||
"backgrounds122025": "SET 139: Lançado em Dezembro de 2025",
|
||||
"backgroundNighttimeStreetWithShopsText": "Rua à noite com lojas.",
|
||||
"backgroundNighttimeStreetWithShopsText": "Rua noturna com lojas",
|
||||
"backgroundNighttimeStreetWithShopsNotes": "Aproveite do brilho acolhedor de uma rua noturna com lojas.",
|
||||
"backgrounds012026": "SET 140: Lançado em Janeiro de 2026",
|
||||
"backgrounds022026": "SET 141: Lançado em Fevereiro de 2026",
|
||||
"backgroundElegantPalaceText": "Palácio Elegante",
|
||||
"backgroundWinterDesertWithSaguarosText": "Deserto de Inverno com Saguaros",
|
||||
"backgroundWinterDesertWithSaguarosNotes": "Respire o ar fresco de um Deserto de Inverno com Saguaros.",
|
||||
"backgroundElegantPalaceNotes": "Admire os salões coloridos de um Palácio Elegante."
|
||||
"backgroundElegantPalaceNotes": "Admire os salões coloridos de um Palácio Elegante.",
|
||||
"backgroundWaterfallWithRainbowText": "Cachoeira com arco-íris",
|
||||
"backgroundWaterfallWithRainbowNotes": "Admire a beleza estonteante de uma cachoeira com arco-íris.",
|
||||
"backgroundRidingACometText": "Andando em um cometa",
|
||||
"backgrounds032026": "SET 142: Lançado em Março de 2026",
|
||||
"backgrounds042026": "SET 143: Lançado em Abril de 2026",
|
||||
"backgroundRidingACometNotes": "Viajem pelo espaço a bordo de um cometa!",
|
||||
"backgroundElvenCitadelText": "Cidadela Élfica",
|
||||
"backgroundElvenCitadelNotes": "Faça uma viagem panorâmica até uma cidadela élfica.",
|
||||
"backgrounds052026": "SET 144: Lançado em Maio de 2026"
|
||||
}
|
||||
|
||||
@@ -189,5 +189,61 @@
|
||||
"sunsetFaqPara3": "Estamos a fazer esta decisão para que possamos focar os nossos recursos noutras partes do Habitica sem interromper acesso a ninguém.",
|
||||
"sunsetFaqPara4": "Para celebrar os tempos que tivemos, a todos os jogadores será dado um Animal de Estimação Veterano enquanto avançamos para esta nova era. Para os nossos contribuidores incríveis, serão recompensados com o Esquipamento Especial para comemorar o todo o trabalho árduo nas comunidades do Habitica.",
|
||||
"sunsetFaqList1": "O principal objetivo do Habitica é fornecer motivação por meio de uma experiência de gerenciamento de tarefas gamificada. A Taverna e as Guildas são utilizadas por uma percentagem desproporcionalmente pequena da nossa base de jogadores. A maioria dos jogadores utiliza serviços externos que são voltados principalmente para interação social e são intencionalmente projetados e mantidos com esses casos de uso em mente.",
|
||||
"sunsetFaqList2": "As novas leis de segurança online exigem um nível de supervisão ativa do conteúdo para espaços públicos que o Habitica não oferecia previamente. Investir nos recursos exigidos por essas novas regulamentações resultaria no redireccionamento dos nossos limitados recursos para partes do Habitica que a grande maioria dos jogadores nunca utiliza."
|
||||
"sunsetFaqList2": "As novas leis de segurança online exigem um nível de supervisão ativa do conteúdo para espaços públicos que o Habitica não oferecia previamente. Investir nos recursos exigidos por essas novas regulamentações resultaria no redireccionamento dos nossos limitados recursos para partes do Habitica que a grande maioria dos jogadores nunca utiliza.",
|
||||
"subscriptionBenefitsAdjustments": "Ajustes nos benefícios do assinante",
|
||||
"subscriptionBenefitsFaqTitle": "Perguntas frequentes sobre ajustes de benefícios do assinante",
|
||||
"subscriptionHeading0": "Mudanças nas ampulhetas místicas",
|
||||
"subscriptionPara0": "Estamos tornando as assinaturas do Habitica melhores do que nunca com ainda mais Ampulhetas e Gemas Místicas! Essas alterações tornam muito mais fácil entender os benefícios da sua assinatura.",
|
||||
"subscriptionHeading1": "Mudanças nas joias do assinante",
|
||||
"subscriptionDetail11": "A quantidade de joias que você pode comprar mensalmente por Ouro não será mais redefinida se sua assinatura expirar.",
|
||||
"subscriptionDetail20": "Na estrutura atual, pode ser difícil entender quantas Ampulhetas Místicas você receberia e quando.",
|
||||
"subscriptionDetail23": "Dar uma Ampulheta Mística por mês permite que os assinantes aproveitem os itens rotativos na Loja dos Viajantes do Tempo.",
|
||||
"subscriptionDetail25": "Compreendemos que as finanças mudam e não queríamos punir os assinantes por assinaturas vencidas, retirando os benefícios que ganharam.",
|
||||
"subscriptionDetail33": "Para receber essas recompensas, sua conta deve ter uma assinatura recorrente ativa antes de 19 de novembro.",
|
||||
"subscriptionDetail40": "Sou assinante, quando receberei meu primeiro aumento regular no limite de Ampulheta Mística e Gemas com a nova programação?",
|
||||
"subscriptionDetail32": "Os jogadores com assinaturas recorrentes de 12 meses receberão o bônus Mystic Hourglass de 12 mencionados acima e 20 joias.",
|
||||
"subscriptionDetail43": "Se eu me inscrever para uma assinatura recorrente e cancelar, ainda terei benefícios?",
|
||||
"subscriptionPara3": "Esperamos que esta nova programação seja mais previsível, permita mais acesso ao incrível estoque de itens da Loja dos Viajantes do Tempo e dê ainda mais motivação para progredir em suas tarefas a cada mês!",
|
||||
"contentAnswer71": "Fique ligado para mais atualizações sobre melhorias planejadas para a experiência da Loja dos Viajantes do Tempo.",
|
||||
"subscriptionDetail00": "Todos os assinantes, incluindo aqueles com assinaturas de presente, receberão 1 Mystic Hourglass no início de cada mês em que tiverem benefícios de assinante.",
|
||||
"subscriptionDetail000": "Ao assinar por 12 meses, você ganhará 12 Ampulhetas Místicas em vez das 4 anteriores.",
|
||||
"subscriptionDetail001": "Todos os assinantes receberão Mystic Hourglasses no mesmo horário, correspondendo ao cronograma de lançamento dos Mystery Gear Sets mensais.",
|
||||
"subscriptionDetail002": "Os assinantes não precisarão mais esperar até o mês seguinte ao pagamento recorrente para receber qualquer Ampulheta Mística.",
|
||||
"subscriptionDetail003": "Todos os novos assinantes receberão 1 Mystic Hourglass imediatamente após a compra. Isso contará como a entrega da Ampulheta Mística do mês atual.",
|
||||
"subscriptionDetail01": "Novas assinaturas recorrentes de 12 meses receberão um bônus inicial único de 12 Ampulhetas Místicas extras na compra.",
|
||||
"subscriptionDetail010": "Isso será um acréscimo à Mystic Hourglass mensal que todos os novos assinantes recebem após a compra inicial.",
|
||||
"subscriptionDetail011": "Os jogadores atuais com uma assinatura recorrente ativa de 12 meses receberão este bônus no dia em que essas alterações entrarem em vigor.",
|
||||
"subscriptionDetail10": "A quantidade de Gemas que os assinantes podem comprar com Ouro no Mercado aumentará em 2 a cada mês que tiverem benefícios até atingir um máximo de 50.",
|
||||
"subscriptionDetail100": "Novas assinaturas de 1, 3 e 6 meses agora começarão com 24 joias por mês e esse valor aumentará a cada mês em que houver benefícios.",
|
||||
"subscriptionDetail101": "Todos os assinantes que atualmente possuem um número ímpar de Gemas por mês terão seu limite de Gemas arredondado para o número par mais próximo.",
|
||||
"subscriptionDetail102": "As novas assinaturas de 12 meses começarão imediatamente com a quantidade máxima de joias por mês, 50 joias em vez das 45 anteriores.",
|
||||
"subscriptionDetail110": "Se você aumentar a quantidade de joias que pode comprar a cada mês e cancelar sua assinatura, poderá recuperar a mesma quantia a qualquer momento no futuro, mesmo se adquirir um nível de assinatura inferior.",
|
||||
"subscriptionHeading2": "Por que estamos fazendo essas mudanças?",
|
||||
"subscriptionDetail21": "Os quatro níveis de assinatura eram conhecidos por causar complicações ao fazer upgrade ou downgrade para níveis diferentes.",
|
||||
"subscriptionDetail22": "Assinaturas de presente e recorrentes tinham experiências de benefícios e regras conflitantes que queríamos simplificar.",
|
||||
"subscriptionDetail24": "Queríamos que os assinantes tivessem mais de quatro chances por ano de coletar itens da Loja dos Viajantes do Tempo.",
|
||||
"subscriptionHeading3": "Recompensas do dia de lançamento",
|
||||
"subscriptionPara1": "Para ajudar a facilitar a transição para a nova programação, os assinantes existentes podem esperar algumas novidades extras no dia do lançamento. Queremos agradecer sinceramente por seu apoio contínuo durante essa mudança!",
|
||||
"subscriptionDetail30": "Jogadores com assinaturas recorrentes de 1 mês ou assinaturas de Plano de Grupo receberão 2 Ampulhetas Místicas e 20 Gemas.",
|
||||
"subscriptionDetail31": "Jogadores com assinaturas recorrentes de 3 ou 6 meses receberão 4 Mystic Hourglasses e 20 Gems.",
|
||||
"subscriptionDetail400": "Para assinantes atuais, você receberá sua primeira Ampulheta Mística e +2 joias adicionadas ao seu limite mensal no primeiro login do mês após o lançamento. Isso significa que se você já estiver logado",
|
||||
"subscriptionDetail41": "O preço das assinaturas mudará quando for lançado?",
|
||||
"subscriptionDetail410": "Estas alterações não afetarão o preço atual das assinaturas.",
|
||||
"subscriptionDetail42": "Se eu não fizer login por um mês enquanto estiver inscrito, perderei esses benefícios?",
|
||||
"subscriptionDetail44": "Sou assinante atual, quantas Gemas terei disponíveis no Mercado a cada mês após a mudança?",
|
||||
"subscriptionDetail45": "Comprar assinaturas extras de presentes me dará mais Ampulhetas Místicas ou um limite de Gemas maior mais rápido?",
|
||||
"subscriptionDetail450": "Como as Ampulhetas Místicas e o aumento do limite de Gemas agora são um benefício mensal, a compra de várias assinaturas de presentes não trará mais benefícios de uma vez.",
|
||||
"subscriptionDetail460": "Como costumávamos redefinir a quantidade de joias que você poderia comprar a cada mês quando seus benefícios acabassem, os jogadores com benefícios de assinatura expirados terão que começar do zero com este novo sistema.",
|
||||
"subscriptionDetail47": "Tenho uma assinatura do Plano de Grupo. Como isso me afeta?",
|
||||
"subscriptionDetail48": "Há alguma alteração em outros benefícios da assinatura, como conjuntos de equipamentos misteriosos?",
|
||||
"subscriptionDetail480": "Essas mudanças afetam apenas as Ampulhetas Místicas e as Gemas de assinante. Todos os outros benefícios permanecerão os mesmos.",
|
||||
"subscriptionPara2": "Se tiver alguma dúvida não contemplada nas respostas acima, pode sempre contactar a nossa equipa em <%= mailto %>.",
|
||||
"subscriptionDetail012": "Este bônus não se aplica a assinaturas de presente.",
|
||||
"subscriptionDetail4400": "Se você desbloqueou <%= inicialNumber %> joias por mês, você será definido como <%= roundNumber %>.",
|
||||
"subscriptionDetail46": "Se eu já tinha uma assinatura, posso desbloquear meu antigo limite de Gemas se assinar novamente agora?",
|
||||
"subscriptionDetail440": "No dia em que essas mudanças entrarem em vigor, os assinantes atuais com um número ímpar de Gemas por mês verão estes ajustes em seu limite de Gemas:",
|
||||
"sunsetFaqHeader3": "Ainda poderei falar com os membros do meu grupo ou plano de grupo?",
|
||||
"sunsetFaqPara7": "As festas e os planos de grupo permanecerão e manterão seus espaços de bate-papo. Você também poderá enviar mensagens privadas.",
|
||||
"sunsetFaqHeader4": "Onde posso pausar minhas Diárias?",
|
||||
"sunsetFaqPara8": "Este recurso foi realocado para Configurações. As funções de Pause Damage não serão afetadas pelo fim dos serviços da Taverna e da Guilda."
|
||||
}
|
||||
|
||||
@@ -1930,5 +1930,276 @@
|
||||
"weaponSpecialFall2023MageText": "Equipe Brilhante",
|
||||
"weaponSpecialFall2023MageNotes": "Com um cristal em seu núcleo, este cajado brilhante faz a magia emergir do mundano. Aumenta a Inteligência em <%= int %>. Edição Limitada Outono 2023.",
|
||||
"weaponSpecialFall2023HealerText": "Grande Martelo de Tronco",
|
||||
"weaponSpecialFall2023HealerNotes": "Com ataques lentos e pesados, este martelo retorcido desfere golpes de cura em vez de dano. Aumenta a Inteligência em <%= int %>. Equipamento de Edição Limitada Outono 2023."
|
||||
"weaponSpecialFall2023HealerNotes": "Com ataques lentos e pesados, este martelo retorcido desfere golpes de cura em vez de dano. Aumenta a Inteligência em <%= int %>. Equipamento de Edição Limitada Outono 2023.",
|
||||
"weaponArmoireNephriteBowText": "Arco de Nefrita",
|
||||
"weaponArmoireEnchantersStaffText": "Cajado do Encantador",
|
||||
"weaponSpecialSpring2025WarriorText": "Cimitarra do Sol",
|
||||
"weaponSpecialSpring2025WarriorNotes": "Com uma fatia, você pode cortar caules de flores para fazer um buquê ou passar por obstáculos para realizar suas tarefas. Aumenta a Força em <%= str %>. Equipamento de edição limitada da primavera de 2025.",
|
||||
"weaponSpecialSpring2025RogueText": "Mangual de Ponta de Cristal",
|
||||
"weaponSpecialSpring2025HealerNotes": "Com um aceno, você pode convocar polinizadores ao seu lado para ajudá-lo em suas aventuras. Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada da primavera de 2025.",
|
||||
"weaponSpecialSpring2025MageText": "Cajado do Louva-a-deus",
|
||||
"weaponSpecialWinter2025WarriorText": "Machado Guerreiro Alce",
|
||||
"weaponSpecialWinter2025WarriorNotes": "Um machado poderoso para um alce poderoso! Você será imparável! Aumenta a Força em <%= str %>. Equipamento de edição limitada para inverno 2024-2025.",
|
||||
"weaponSpecialWinter2025RogueText": "Explosão de floco de neve",
|
||||
"weaponSpecialWinter2025RogueNotes": "Bata e deslumbre essas tarefas difíceis até a submissão! Você será imparável! Aumenta a Força em <%= str %>. Equipamento de edição limitada para inverno 2024-2025.",
|
||||
"weaponSpecialWinter2025HealerText": "Varinha Estelar",
|
||||
"weaponSpecialWinter2025HealerNotes": "O que você precisa agora é de mais luzes e uma estrela brilhante para subir! Você será imparável! Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada para inverno 2024-2025.",
|
||||
"weaponSpecialWinter2025MageText": "Exibição da aurora boreal",
|
||||
"weaponSpecialWinter2025MageNotes": "Este show deslumbrante e colorido oferece o cenário perfeito! Você será imparável! Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Equipamento de edição limitada para inverno 2024-2025.",
|
||||
"weaponArmoireBuoyantBubblesText": "Bolhas flutuantes",
|
||||
"weaponSpecialSummer2025WarriorText": "Lança de Vieira",
|
||||
"weaponSpecialSummer2025WarriorNotes": "Não há como dizer quantos anos isso tem, mas ele irá acompanhá-lo em muitas tarefas difíceis. Aumenta a Força em <%= str %>. Equipamento de edição limitada do verão de 2025.",
|
||||
"weaponSpecialSummer2025RogueText": "Tentáculo de Lula",
|
||||
"weaponSpecialSummer2025RogueNotes": "Este tentáculo se agarrará firmemente aos seus objetivos para que você não perca o impulso ao concluir as tarefas. Aumenta a Força em <%= str %>. Equipamento de edição limitada do verão de 2025.",
|
||||
"weaponSpecialSummer2025HealerText": "Remo Asa de Anjo do Mar",
|
||||
"weaponSpecialSummer2025HealerNotes": "Desenhe um oito à medida que avança, progredindo muito em suas tarefas. Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada do verão de 2025.",
|
||||
"weaponSpecialSummer2025MageText": "Ramo Coral",
|
||||
"weaponSpecialFall2024WarriorText": "Espada de Chama",
|
||||
"weaponSpecialFall2024WarriorNotes": "Os obstáculos da tarefa são eliminados imediatamente onde estão por esta arma formidável. Aumenta a Força em <%= str %>. Equipamento de edição limitada do outono de 2024.",
|
||||
"weaponSpecialFall2024RogueText": "Varinha de fita",
|
||||
"weaponSpecialFall2024RogueNotes": "As tarefas irão congelar devido às voltas e reviravoltas desta arma hipnotizante. Aumenta a Força em <%= str %>. Equipamento de edição limitada do outono de 2024.",
|
||||
"weaponSpecialFall2024HealerText": "Foice Espacial",
|
||||
"weaponSpecialFall2024MageText": "Pessoal do Submundo",
|
||||
"weaponSpecialFall2024MageNotes": "As etapas das tarefas serão simplificadas instantaneamente com um toque nesta arma brilhante. Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Equipamento de edição limitada do outono de 2024.",
|
||||
"weaponSpecialSummer2024HealerNotes": "Você ficará surpreso ao descobrir o quão dura é a casca no final desta equipe. Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada para o verão de 2024.",
|
||||
"weaponSpecialSummer2025MageNotes": "Amplie seus talentos e habilidades para enfrentar uma variedade de tarefas. Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Equipamento de edição limitada do verão de 2025.",
|
||||
"weaponSpecialFall2025WarriorText": "Machado Sasquatch",
|
||||
"weaponSpecialFall2025WarriorNotes": "Uma arma poderosa para abrir um caminho seguro em uma floresta de outono cheia de complicações. Aumenta a Força em <%= str %>. Equipamento de edição limitada do outono de 2025.",
|
||||
"weaponSpecialFall2025RogueText": "Lâmina Esqueleto",
|
||||
"weaponSpecialFall2025HealerText": "Machado Kobold",
|
||||
"weaponSpecialFall2025HealerNotes": "Uma arma poderosa para abrir um caminho seguro através de uma floresta de outono cheia de obstáculos. Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada do outono de 2025.",
|
||||
"weaponSpecialFall2025MageText": "Machado Fantasma Mascarado",
|
||||
"weaponArmoireHattersShearsText": "Tesouras Afiadas",
|
||||
"weaponArmoireHeraldsBuisineText": "Negócios do Herald",
|
||||
"weaponArmoireSpookyCandyBucketText": "Balde de doces assustador",
|
||||
"weaponSpecialSpring2026WarriorText": "Poderoso Froggy Foil",
|
||||
"weaponSpecialSpring2026RogueText": "Ramo Primavera",
|
||||
"weaponSpecialSpring2026HealerText": "Cajado do floco de neve",
|
||||
"weaponSpecialSpring2026HealerNotes": "Uma oportunidade de começar de novo com um novo começo está logo à frente, e com esta equipe esplêndida, você estará pronto! Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada da primavera de 2026.",
|
||||
"weaponSpecialSpring2026MageText": "Guarda-sol de mastro",
|
||||
"weaponMystery202403Text": "Espada Esmeralda da Sorte",
|
||||
"weaponMystery202403Notes": "Carregar a maior espada é certamente uma forma de criar sua própria sorte! Não confere nenhum benefício. Item de assinante de março de 2024.",
|
||||
"weaponMystery202404Text": "Cajado do Mago Micélio",
|
||||
"weaponMystery202408Text": "Égide Arcana",
|
||||
"weaponMystery202408Notes": "Um escudo de bolha mágico que protege você de feitiços inimigos ou ajuda você a flutuar no ar ou na água. Não confere nenhum benefício. Item de assinante de agosto de 2024.",
|
||||
"weaponMystery202512Text": "Lâmina do Campeão de Biscoitos",
|
||||
"weaponMystery202601Text": "Égide do Inverno",
|
||||
"weaponMystery202601Notes": "Um escudo de bolha gelada que concede proteção mágica contra elementos opostos. Não confere nenhum benefício. Item de assinante de janeiro de 2026.",
|
||||
"weaponMystery202603Text": "Equipe do Mago das Glicínias",
|
||||
"weaponArmoireSlingshotText": "Estilingue",
|
||||
"weaponArmoireShadowMastersMaceText": "Maça do Mestre das Sombras",
|
||||
"weaponArmoireShadowMastersMaceNotes": "Creatures of darkness will obey your every command when you wave this glowing mace. Increases Perception by <%= per %>. Enchanted Armoire: Shadow Master Set (Item 3 of 4).",
|
||||
"weaponArmoireSkullLanternNotes": "Deixe seu brilho ser o seu guia nas noites mais sombrias de suas aventuras. Aumenta a Inteligência em <%= int %>. Armário Encantado: Item Independente.",
|
||||
"weaponArmoirePotionBaseText": "Poção Base Decorativa",
|
||||
"weaponArmoirePotionWhiteText": "Poção Branca Decorativa",
|
||||
"weaponArmoirePotionShadeText": "Poção de Sombra Decorativa",
|
||||
"weaponArmoirePotionSkeletonText": "Poção de Esqueleto Decorativo",
|
||||
"weaponArmoirePotionZombieText": "Poção Decorativa de Zumbi",
|
||||
"weaponArmoirePotionPinkText": "Poção Rosa Decorativa de Algodão Doce",
|
||||
"weaponArmoireMagicSpatulaText": "Espátula Mágica",
|
||||
"weaponArmoireScholarlyTextbooksText": "Livros didáticos acadêmicos",
|
||||
"weaponArmoireOptimistsCloverText": "Trevo de quatro folhas",
|
||||
"weaponArmoirePottersWheelText": "Roda de Oleiro",
|
||||
"weaponArmoireShadyBeachUmbrellaText": "Guarda-sol",
|
||||
"weaponArmoireCorsairsBladeText": "Lâmina do Corsário",
|
||||
"weaponArmoireDragonKnightsLanceText": "Lança do Cavaleiro Dragão",
|
||||
"weaponArmoireDragonKnightsLanceNotes": "Esta lança vermelha e prateada derrubou muitos oponentes de suas montarias. Aumenta a Constituição em <%= con %>. Armário Encantado: Conjunto Cavaleiro Dragão (Item 3 de 3).",
|
||||
"weaponArmoireFunnyFoolBatonText": "Bastão de tolo engraçado",
|
||||
"weaponArmoireStormKnightAxeText": "Machado do Cavaleiro da Tempestade",
|
||||
"weaponArmoireGildedKnightsSpearText": "Lança de Cavaleiro Dourado",
|
||||
"weaponArmoireBeekeepersSmokerText": "Fumante",
|
||||
"weaponArmoireBlacksmithsHammerText": "Martelo de Ferreiro",
|
||||
"weaponArmoirePrettyPinkParasolText": "Guarda-sol rosa lindo",
|
||||
"armorSpecialHeroicTunicText": "Túnica Heroica",
|
||||
"armorSpecialHeroicTunicNotes": "Dizem que os heróis não devem descansar sobre os louros, mas você pode descansar com esta roupa confortável e moderna. Aumenta todas as estatísticas em <%= attrs %>.",
|
||||
"armorSpecialBirthday2024Text": "Roupões de festa ridículos",
|
||||
"weaponMystery202201Notes": "Liberte uma nuvem de brilho dourado e prateado quando o relógio bater meia-noite. Feliz Ano Novo! Agora quem está limpando isso? Não confere nenhum benefício. Item de assinante de janeiro de 2022.",
|
||||
"weaponArmoirePotionGoldenText": "Poção Dourada Decorativa",
|
||||
"weaponSpecialFall2024HealerNotes": "Tarefas que antes eram cosmicamente complicadas são dizimadas por esta lâmina impressionante. Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada do outono de 2024.",
|
||||
"weaponSpecialSpring2025RogueNotes": "Com um golpe, você pode eliminar quaisquer obstáculos que atrapalhem seus objetivos. Aumenta a Força em <%= str %>. Equipamento de edição limitada da primavera de 2025.",
|
||||
"weaponSpecialSpring2025MageNotes": "Com uma barra, você pode usar magia elementar para controlar o ambiente ao seu redor. Aproveite e salte para frente! Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Edição Limitada",
|
||||
"weaponSpecialFall2025RogueNotes": "Uma arma poderosa para abrir um caminho seguro através de uma floresta de outono cheia de obstáculos. Aumenta a Força em <%= str %>. Equipamento de edição limitada do outono de 2025.",
|
||||
"weaponSpecialFall2025MageNotes": "Uma arma poderosa para abrir um caminho seguro através de uma floresta de outono cheia de sustos. Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Equipamento de edição limitada do outono de 2025.",
|
||||
"weaponSpecialSpring2026WarriorNotes": "Uma oportunidade de duelar pode surgir a qualquer momento e, com este formidável florete, você estará pronto! Aumenta a Força em <%= str %>. Equipamento de edição limitada da primavera de 2026.",
|
||||
"weaponSpecialSpring2026RogueNotes": "Uma oportunidade de crescer está quase chegando e, com esses ramos em flor, você estará pronto! Aumenta a Força em <%= str %>. Equipamento de edição limitada da primavera de 2026.",
|
||||
"weaponSpecialSpring2026MageNotes": "Uma oportunidade para comemorar as aproximações, e com este lindo guarda-sol você estará pronto! Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Equipamento de edição limitada da primavera de 2026.",
|
||||
"weaponMystery202404Notes": "Este cajado concederá a você uma sabedoria antiga tão eterna quanto as rochas e as árvores. Não confere nenhum benefício. Item de assinante de abril de 2024.",
|
||||
"weaponMystery202511Notes": "O brilho gelado desta espada tornará o trabalho rápido até mesmo em tarefas vermelhas escuras. Não confere nenhum benefício. Item de assinante de novembro de 2025.",
|
||||
"weaponMystery202512Notes": "Uma espada brilhante feita de açúcar, hortelã e encantamentos arcanos. Não confere nenhum benefício. Item de assinante de dezembro de 2025.",
|
||||
"weaponMystery202603Notes": "Lance feitiços para aquecer o ar da primavera e estimular o florescimento! Não confere nenhum benefício. Item de assinante de março de 2026.",
|
||||
"weaponArmoireGuardiansCrookNotes": "Este cajado de pastor pode ser útil na próxima vez que você levar seus animais de estimação para passear no campo... Aumenta a Força em <%= str %>. Armário Encantado: Conjunto Guardião dos Grazers (Item 2 de 3).",
|
||||
"weaponArmoireHeraldsBuisineNotes": "Qualquer anúncio soará muito melhor após a fanfarra desta trombeta. Aumenta a Força em <%= str %>. Armário Encantado: Conjunto Arauto (Item 3 de 4).",
|
||||
"weaponArmoirePotionBaseNotes": "Os animais de estimação que você choca com esta poção são tudo menos básicos! Aumenta Força, Inteligência, Constituição e Percepção em <%= attrs %> cada. Armário Encantado: Conjunto de Poções (Item 1 de 10)",
|
||||
"weaponArmoirePotionRedText": "Poção Vermelha Decorativa",
|
||||
"weaponArmoirePotionBlueText": "Poção Azul Decorativa de Algodão Doce",
|
||||
"weaponArmoireRegalSceptreNotes": "Mostre sua autoridade real pegando este cajado adornado com joias. Aumenta a Percepção em <%= por %>. Armário Encantado: Conjunto Regal (Item 2 de 2).",
|
||||
"weaponArmoireFunnyFoolBatonNotes": "Com um aceno de bastão, você pode dar uma piada, redirecionar a atenção ou convocar aplausos. Aumenta a Constituição e a Força em <%= attrs %> cada. Armário Encantado: Conjunto Engraçado (Item 3 de 3).",
|
||||
"weaponArmoireStormKnightAxeNotes": "Reúna sua fúria e dê um golpe como um trovão! Aumenta a Força em <%= str %>. Armário Encantado: Conjunto de Cavaleiro da Tempestade (Item 3 de 3).",
|
||||
"weaponArmoireBambooFluteText": "Flauta de Bambu",
|
||||
"armorSpecialBirthday2024Notes": "Feliz aniversário, Habitica! Use estes roupões de festa ridículos para celebrar este dia maravilhoso. Não confere nenhum benefício.",
|
||||
"weaponArmoireGildedKnightsSpearNotes": "Com esta arma, você pode garantir que todos sempre paguem suas dívidas. Aumenta a Força em <%= str %>. Armário Encantado: Conjunto de Cavaleiro Dourado (Item 3 de 3).",
|
||||
"armorSpecialSummer2019WarriorText": "Armadura de Carapaça",
|
||||
"armorSpecialFall2020WarriorText": "Vestes Revenantes",
|
||||
"armorSpecialFall2020HealerText": "Asas de Falcão",
|
||||
"weaponMystery202508Text": "Lâmina Carmesim Brilhante",
|
||||
"weaponMystery202508Notes": "Esta lâmina giratória irá aterrorizar qualquer monstro ou Diário vermelho que cruzar seu caminho! Não confere nenhum benefício. Item de assinante de agosto de 2025.",
|
||||
"weaponArmoireBeachFlagNotes": "Reúna as tropas ao redor do seu castelo de areia e diga a todos onde pedir ajuda! Aumenta a Percepção em <%= por %>. Armário Encantado: Conjunto de Salva-vidas (Item 1 de 3).",
|
||||
"weaponArmoireEnchantersStaffNotes": "As pedras verdes deste cajado estão repletas do poder da mudança que flui forte através do vento do outono. Aumenta a Percepção em <%= por %>. Armário Encantado: Conjunto Encantador de Outono (Item 3 de 4).",
|
||||
"weaponArmoireClubOfClubsNotes": "Este elegante clube não revelará suas intenções muito cedo sobre suas intenções em relação a essas velhas tarefas sorrateiras. Aumenta a Força em <%= str %>. Armário Encantado: Conjunto de Valete de Paus (Item 2 de 3).",
|
||||
"weaponArmoireBlueMoonSaiNotes": "Este sai é uma arma tradicional, imbuída dos poderes do lado negro da lua. Aumenta a Força em <%= str %>. Armário Encantado: Conjunto Blue Moon Rogue (item 1 de 4).",
|
||||
"weaponArmoirePotionShadeNotes": "É hora de jogar um pouco de sombra (poção) em um ovo para chocar um animal de estimação sombrio! Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Armário Encantado: Conjunto de Poções (Item 5 de 10)",
|
||||
"armorSpecialSpring2019WarriorText": "Armadura de Orquídea",
|
||||
"weaponArmoireChefsSpoonNotes": "Levante-o ao soltar seu grito de guerra: “SPOOOON!!” Aumenta a Inteligência em <%= int %>. Armário Encantado: Conjunto do Chef (Item 3 de 4).",
|
||||
"weaponSpecialWinter2026WarriorNotes": "As foices ajudam a cortar, colher e cobrir grandes áreas – tudo o que você precisa para refinar uma lista de tarefas. Aumenta a Força em <%= str %>. Equipamento de edição limitada para o inverno 2025-2026.",
|
||||
"weaponSpecialWinter2026RogueText": "Pólo de esqui",
|
||||
"weaponSpecialWinter2026RogueNotes": "Os bastões de esqui ajudam você a manter o equilíbrio, a estabilidade e o timing – tudo o que você precisa para ser verdadeiramente produtivo. Aumenta a Força em <%= str %>. Equipamento de edição limitada para o inverno 2025-2026.",
|
||||
"weaponSpecialWinter2026HealerText": "Cajado Polar",
|
||||
"weaponSpecialWinter2026HealerNotes": "As equipes ajudam com suporte, estabilidade e direção – todas as coisas que ajudam você a realmente conquistar uma lista de tarefas. Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada para o inverno 2025-2026.",
|
||||
"weaponSpecialWinter2026MageText": "Cajado de Candelabros",
|
||||
"weaponArmoireShootingStarSpellText": "Brilhos de poeira estelar",
|
||||
"weaponMystery202111Text": "Cajado do Cronomante",
|
||||
"headSpecialNye2021Notes": "Você recebeu um chapéu de festa absurdo! Use-o com orgulho ao comemorar o Ano Novo! Não confere nenhum benefício.",
|
||||
"weaponSpecialSummer2022MageNotes": "Limpe magicamente as águas à sua frente com um giro deste cajado. Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Equipamento de verão de edição limitada de 2022.",
|
||||
"weaponSpecialSummer2022RogueNotes": "Se você estiver em apuros, não hesite em mostrar essas garras temíveis! Aumenta a Força em <%= str %>. Equipamento de verão de edição limitada de 2022.",
|
||||
"weaponSpecialSummer2022HealerNotes": "Essas bolhas liberam magia de cura na água com um estalo satisfatório! Aumenta a Inteligência em <%= int %>. Equipamento de verão de edição limitada de 2022.",
|
||||
"weaponArmoireHuntingHornText": "Chifre de caça",
|
||||
"weaponArmoireOrangeKiteNotes": "Com cores como o nascer e o pôr do sol, vamos ver quão alto sua pipa consegue chegar! Aumenta todas as estatísticas em <%= attrs %> cada. Armário Encantado: Kite Set (Item 3 de 5)",
|
||||
"weaponArmoirePinkKiteText": "pipa rosa",
|
||||
"weaponArmoirePinkKiteNotes": "Mergulhando, girando, voando alto, sua pipa se destaca no céu. Aumenta todas as estatísticas em <%= attrs %> cada. Armário Encantado: Kite Set (Item 4 de 5)",
|
||||
"weaponArmoireYellowKiteText": "pipa amarela",
|
||||
"weaponSpecialSpring2022MageNotes": "Estes sinos amarelos brilhantes estão prontos para canalizar sua poderosa magia da primavera. Aumenta a Inteligência em <%= int %> e a Percepção em <%= per %>. Equipamento de primavera de edição limitada 2022.",
|
||||
"weaponMystery202104Text": "Cajado de Cardo Espinhoso",
|
||||
"weaponArmoireHandyHookNotes": "Quem precisa de polegares opositores? Este gancho é “útil” o suficiente para qualquer pessoa. Aumenta a Força em <%= str %>. Armário Encantado: Conjunto Pirata (Item 1 de 3).",
|
||||
"weaponArmoireSlingshotNotes": "Mire nas suas Diárias vermelhas! Aumenta a Força em <%= str %>. Armário Encantado: Item Independente.",
|
||||
"weaponArmoireFloridFanText": "Fã da Flórida",
|
||||
"weaponArmoireEveningTeaText": "xícara de chá",
|
||||
"weaponMystery202211Text": "Cajado Eletromante",
|
||||
"weaponMystery202211Notes": "Aproveite o incrível poder de uma tempestade com raios com este cajado. Não confere nenhum benefício. Item de assinante de novembro de 2022.",
|
||||
"weaponSpecialSpring2022HealerNotes": "Use esta varinha para explorar as propriedades curativas do peridoto, seja para trazer calma, positividade ou bondade. Aumenta a Inteligência em <%= int %>. Equipamento de primavera de edição limitada 2022.",
|
||||
"weaponArmoirePotionRedNotes": "É um dia marcante porque esta poção de incubação não é uma pista falsa! Aumenta a Força e a Constituição em <%= attrs %> cada. Armário Encantado: Conjunto de Poções (Item 4 de 10)",
|
||||
"weaponArmoireVernalTaperNotes": "Os dias estão ficando mais longos, mas esta vela irá ajudá-lo a encontrar o caminho antes do nascer do sol. Aumenta a Constituição em <%= con %>. Armário Encantado: Conjunto de Vestimentas Vernais (Item 3 de 3).",
|
||||
"weaponArmoireRegalSceptreText": "Cetro Real",
|
||||
"weaponArmoireJugglingBallsNotes": "Os habiticanos são mestres em multitarefas, então você não deverá ter problemas para manter todas essas bolas no ar! Aumenta a Inteligência em <%= int %>. Armário Encantado: Item Independente.",
|
||||
"weaponArmoireJugglingBallsText": "Malabarismo com bolas",
|
||||
"weaponArmoireMedievalWashboardNotes": "Scrub-a-dub-dub! É hora de aplicar um pouco de graxa nos cotovelos e limpar a roupa. Aumenta a Força em <%= str %>. Armário Encantado: Conjunto de Lavadeiras Medievais (Item 5 de 6).",
|
||||
"weaponArmoireMagnifyingGlassText": "Lupa",
|
||||
"weaponArmoireBambooCaneText": "Cana de Bambu",
|
||||
"weaponArmoireAstronomersTelescopeText": "Telescópio do Astrônomo",
|
||||
"weaponArmoireChefsSpoonText": "Colher do Chef",
|
||||
"weaponArmoireResplendentRapierText": "Florete Resplandecente",
|
||||
"weaponArmoireGuardiansCrookText": "Bandido do Guardião",
|
||||
"weaponArmoireBlueMoonSaiText": "Sai Lunar Sombrio",
|
||||
"weaponArmoireAlchemistsDistillerText": "Destilador do Alquimista",
|
||||
"weaponArmoireEveningTeaNotes": "Esta panaceia irá ajudá-lo a relaxar para que essas grandes tarefas não pareçam tão ameaçadoras. Aumenta a Inteligência em <%= int %>. Armário Encantado: Conjunto de Roupão (Item 3 de 3).",
|
||||
"weaponArmoireClubOfClubsText": "Clube de... Clubes",
|
||||
"weaponMystery202102Text": "Varinha Encantadora",
|
||||
"weaponArmoireVernalTaperText": "Cônico Vernal",
|
||||
"weaponArmoirePotionWhiteNotes": "Você quase poderia perder um animal de estimação nascido com esta poção em uma tempestade de neve! Aumenta a Constituição em <%= con %> e a Percepção em <%= per %>. Armário Encantado: Conjunto de Poções (Item 2 de 10)",
|
||||
"weaponArmoirePotionDesertText": "Poção Decorativa do Deserto",
|
||||
"weaponMystery201911Text": "Cajado de Cristal Encantado",
|
||||
"weaponMystery202311Text": "Equipe que tudo vê",
|
||||
"weaponMystery202311Notes": "Veja além dos limites do espaço e do tempo! Não confere nenhum benefício. Item de assinante de novembro de 2023.",
|
||||
"weaponArmoireAstronomersTelescopeNotes": "Um instrumento que lhe permitirá observar a antiga dança das estrelas. Aumenta a Percepção em <%= por %>. Armário Encantado: Conjunto de Mago Astrônomo (Item 3 de 3).",
|
||||
"weaponArmoireMagnifyingGlassNotes": "Ah! Uma prova! Examine-o de perto com esta lupa fina. Aumenta a Percepção em <%= por %>. Armário Encantado: Conjunto de Detetive (Item 3 de 4).",
|
||||
"weaponArmoireResplendentRapierNotes": "Demonstre sua esgrima com esta arma pontiaguda. Aumenta a Percepção em <%= por %>. Armário Encantado: Item Independente.",
|
||||
"weaponArmoireShootingStarSpellNotes": "Envolva-se num feitiço de magia estelar para ajudá-lo a realizar todos os seus desejos. Aumenta a Força e a Inteligência em <%= attrs %> cada. Armário Encantado: Conjunto Stardust (Item 3 de 3).",
|
||||
"weaponArmoirePinkLongbowNotes": "Seja um cupido em treinamento, dominando tanto o tiro com arco quanto os assuntos do coração com este lindo arco. Aumenta a Percepção em <%= per %> e a Força em <%= str %>. Armário Encantado: Item Independente.",
|
||||
"weaponArmoireGardenersWateringCanText": "Regador",
|
||||
"weaponArmoireGardenersWateringCanNotes": "Você não pode ir longe sem água! Tenha um suprimento infinito em mãos com este regador mágico de recarga. Aumenta a Inteligência em <%= int %>. Armário Encantado: Conjunto de Jardineiro (Item 4 de 4).",
|
||||
"weaponArmoireBlueKiteText": "pipa azul",
|
||||
"weaponArmoireBlueKiteNotes": "Navegando alto no azul, que truques você pode fazer com sua pipa? Aumenta todas as estatísticas em <%= attrs %> cada. Armário Encantado: Kite Set (Item 1 de 5)",
|
||||
"weaponArmoireGreenKiteText": "Papagaio Verde",
|
||||
"weaponArmoireGreenKiteNotes": "Uma pipa mais deslumbrante que você nunca viu, com seus tons de amarelo e verde. Aumenta todas as estatísticas em <%= attrs %> cada. Armário Encantado: Kite Set (Item 2 de 5)",
|
||||
"weaponArmoireRollingPinText": "Rolo",
|
||||
"armorSpecialSpring2019RogueText": "Armadura de Nuvem",
|
||||
"armorSpecialSpring2019RogueNotes": "Um pouco de tufo fofo. Aumenta a Percepção em <%= por %>. Equipamento de primavera de edição limitada 2019.",
|
||||
"armorSpecialSpring2019WarriorNotes": "A armadura de aço com pétalas reforçadas protege seu coração e também parece muito elegante. Aumenta a Constituição em <%= con %>. Equipamento de primavera de edição limitada 2019.",
|
||||
"armorSpecialSpring2019MageText": "Vestes Âmbar",
|
||||
"armorSpecialSummer2019RogueNotes": "Esta cauda sinuosa é perfeita para fazer curvas fechadas durante ousadas escapadas aquáticas. Aumenta a Percepção em <%= por %>. Equipamento de verão de edição limitada de 2019.",
|
||||
"armorSpecialSummer2019MageNotes": "Os lírios conhecerão você como um deles e não temerão sua abordagem. Aumenta a Inteligência em <%= int %>. Equipamento de verão de edição limitada de 2019.",
|
||||
"armorSpecialSummer2019HealerNotes": "Deslize suavemente pelas águas costeiras quentes com esta cauda elegante. Aumenta a Constituição em <%= con %>. Equipamento de verão de edição limitada de 2019.",
|
||||
"armorSpecialFall2019RogueText": "Casaco de ópera com capa",
|
||||
"armorSpecialFall2019WarriorNotes": "Essas vestes de penas concedem o poder de voar, permitindo que você sobrevoe qualquer batalha. Aumenta a Constituição em <%= con %>. Edição limitada de equipamento de outono de 2019.",
|
||||
"armorSpecialFall2019MageText": "Bata de Polifemo",
|
||||
"armorSpecialFall2019HealerNotes": "Dizem que estas vestes são feitas de pura noite. Use o poder das trevas com sabedoria! Aumenta a Constituição em <%= con %>. Edição limitada de equipamento de outono de 2019.",
|
||||
"armorSpecialWinter2020WarriorNotes": "Ó poderoso pinheiro, ó abeto imponente, empreste sua força. Ou melhor, a sua Constituição! Aumenta a Constituição em <%= con %>. Equipamento de inverno de edição limitada 2019-2020.",
|
||||
"armorSpecialWinter2020MageText": "Casaco curvilíneo",
|
||||
"armorSpecialSummer2020MageNotes": "Aproveite o poder das profundezas do mar com esta armadura de labirinto de remos. Aumenta a Inteligência em <%= int %>. Equipamento de verão de edição limitada 2020.",
|
||||
"armorSpecialSummer2020HealerText": "Regalia das Ondas Tumbling",
|
||||
"armorSpecialWinter2020HealerText": "Vestido Casca de Laranja",
|
||||
"armorSpecialSpring2020RogueNotes": "A cor do crepúsculo, de uma infinidade de pedras preciosas, do mar mais profundo! Aumenta a Percepção em <%= por %>. Equipamento de primavera de edição limitada 2020.",
|
||||
"armorSpecialSpring2020WarriorNotes": "Esta carapaça rígida pode mantê-lo protegido até mesmo dos ataques mais esmagadores. Aumenta a Constituição em <%= con %>. Equipamento de primavera de edição limitada 2020.",
|
||||
"armorSpecialSpring2020MageText": "Vestido Whirlpuddle",
|
||||
"armorSpecialSpring2020HealerText": "Pétalas Protetoras",
|
||||
"armorSpecialSpring2020HealerNotes": "Envolva-se em folhas e pétalas macias de íris para enganar os inimigos e fazê-los subestimar seu poder de cura. Aumenta a Constituição em <%= con %>. Equipamento de primavera de edição limitada 2020.",
|
||||
"armorSpecialSummer2020WarriorText": "Rabo de truta arco-íris",
|
||||
"armorSpecialSummer2020WarriorNotes": "Você será o peixe brilhante de um riacho sem graça, com essas escamas deslumbrantes! Aumenta a Constituição em <%= con %>. Equipamento de verão de edição limitada 2020.",
|
||||
"armorSpecialSummer2020HealerNotes": "Você é tão paciente quanto o oceano, tão forte quanto as correntes, tão confiável quanto as marés. Aumenta a Constituição em <%= con %>. Equipamento de verão de edição limitada 2020.",
|
||||
"armorSpecialFall2020RogueNotes": "Assuma a força da pedra com esta armadura, que garante repelir os ataques mais ferozes. Aumenta a Percepção em <%= por %>. Equipamento de outono de edição limitada de 2020.",
|
||||
"armorSpecialFall2020MageText": "No Alto Após a Iluminação",
|
||||
"armorSpecialFall2020HealerNotes": "Seu esplendor se desenrola à noite, e aqueles que o testemunham em vôo se perguntam o que esse presságio pode significar. Aumenta a Constituição em <%= con %>. Equipamento de outono de edição limitada de 2020.",
|
||||
"armorSpecialWinter2021RogueText": "Traje Verde-Ivy",
|
||||
"armorSpecialWinter2021RogueNotes": "Derreta-se nas sombras da madeira perene! Aumenta a Percepção em <%= por %>. Equipamento de inverno de edição limitada 2020-2021.",
|
||||
"weaponArmoireSkullLanternText": "Lanterna Caveira",
|
||||
"armorSpecialFall2019WarriorText": "Asas da Noite",
|
||||
"weaponMystery202212Text": "Varinha Glacial",
|
||||
"weaponSpecialWinter2023HealerNotes": "Observe esta guirlanda festiva e espinhosa girar no ar em direção ao seu inimigo ou obstáculos e retornar para você como um bumerangue para outro lançamento. Aumenta a Inteligência em <%= int %>. Edição Limitada 2022-Equipamento de inverno 2023.",
|
||||
"weaponMystery202306Text": "Guarda-chuva arco-íris",
|
||||
"weaponMystery202306Notes": "Brilhe com orgulho e leve um prisma de cor cintilante onde quer que você vá! Não confere nenhum benefício. Item de assinante de junho de 2023.",
|
||||
"weaponArmoireOrangeKiteText": "pipa laranja",
|
||||
"weaponArmoireYellowKiteNotes": "Mergulhando e desviando para frente e para trás, observe sua pipa alegre voar. Aumenta todas as estatísticas em <%= attrs %> cada. Armário Encantado: Kite Set (Item 5 de 5)",
|
||||
"weaponArmoirePushBroomText": "Empurre a vassoura",
|
||||
"weaponArmoirePushBroomNotes": "Leve esta ferramenta de arrumação em suas aventuras e sempre seja capaz de varrer uma varanda com fuligem ou limpar teias de aranha dos cantos. Aumenta a Força e a Inteligência em <%= attrs %> cada. Armário Encantado: Limpeza",
|
||||
"weaponArmoireFeatherDusterText": "Espanador",
|
||||
"weaponArmoireFinelyCutGemText": "Gema Finamente Cortada",
|
||||
"armorSpecialSummer2019MageText": "Vestido floral",
|
||||
"armorSpecialSummer2020MageText": "Armadura de Peixe-Remo",
|
||||
"weaponSpecialSpring2023RogueNotes": "Corte! Golpe! Lanche! Fique forte e pronto para a metamorfose que se aproxima. Aumenta a Força em <%= str %>. Edição limitada de equipamento de primavera 2023.",
|
||||
"weaponArmoireBeachFlagText": "Bandeira de praia",
|
||||
"weaponArmoirePotionZombieNotes": "Use isso para chocar um animal de estimação zumbi, mas fique atento caso ele comece a mordiscar você! Aumenta a Constituição em <%= con %> e a Percepção em <%= per %>. Armário Encantado: Conjunto de Poções (Item 7 de 10)",
|
||||
"weaponMystery201911Notes": "A bola de cristal no topo deste cajado pode lhe mostrar o futuro, mas cuidado! Usar esse conhecimento perigoso pode mudar uma pessoa de maneiras inesperadas. Não confere nenhum benefício. Item de assinante de novembro de 2019.",
|
||||
"weaponMystery202209Text": "Manual Mágico",
|
||||
"weaponArmoireHandyHookText": "Gancho prático",
|
||||
"weaponArmoireMedievalWashboardText": "Tábua de lavar",
|
||||
"armorSpecialBirthday2020Text": "Robes de festa ultrajantes",
|
||||
"armorSpecialBirthday2021Text": "Robes de festa extravagantes",
|
||||
"armorSpecialSummer2019HealerText": "Cauda das Marés Tropicais",
|
||||
"armorSpecialSummer2020RogueText": "Disfarce de crocodilo",
|
||||
"weaponArmoirePaintbrushText": "Pincel",
|
||||
"armorSpecialSpring2019MageNotes": "Essas vestes reúnem poder da resina mágica incrustada nas fibras da antiga casca que compõem o tecido. Aumenta a Inteligência em <%= int %>. Equipamento de primavera de edição limitada 2019.",
|
||||
"armorSpecialSpring2019HealerText": "Fantasia de Robin",
|
||||
"armorSpecialSpring2019HealerNotes": "Suas penas brilhantes farão com que todos saibam que o frio e a escuridão do inverno já passaram. Aumenta a Constituição em <%= con %>. Equipamento de primavera de edição limitada 2019.",
|
||||
"armorSpecialSummer2019RogueText": "Cauda de martelo",
|
||||
"armorSpecialFall2020RogueText": "Armadura Escultórica",
|
||||
"armorSpecialBirthday2022Text": "Robes de festa absurdos",
|
||||
"armorSpecialBirthday2023Text": "Robes de festa fabulosos",
|
||||
"armorSpecialWinter2020MageNotes": "Comece o ano novo quente, confortável e protegido contra vibrações excessivas. Aumenta a Inteligência em <%= int %>. Equipamento de inverno de edição limitada 2019-2020.",
|
||||
"weaponArmoireMopText": "Esfregar",
|
||||
"weaponArmoireCleaningClothText": "Pano de limpeza",
|
||||
"armorSpecialKS2019Text": "Armadura Grifo Mítica",
|
||||
"armorSpecialKS2019Notes": "Brilhando por dentro como o nobre coração de um grifo, esta armadura resplandecente encoraja você a se orgulhar de suas realizações. Aumenta a Constituição em <%= con %>.",
|
||||
"armorSpecialBirthday2020Notes": "Feliz aniversário, Habitica! Use estes roupões de festa ultrajantes para celebrar este dia maravilhoso. Não confere nenhum benefício.",
|
||||
"armorSpecialBirthday2021Notes": "Feliz aniversário, Habitica! Use estes Robes de Festa Extravagantes para celebrar este dia maravilhoso. Não confere nenhum benefício.",
|
||||
"armorSpecialBirthday2022Notes": "Feliz aniversário, Habitica! Use estes roupões de festa Proposterous para celebrar este dia maravilhoso. Não confere nenhum benefício.",
|
||||
"armorSpecialBirthday2023Notes": "Feliz aniversário, Habitica! Use estes fabulosos roupões de festa para celebrar este dia maravilhoso. Não confere nenhum benefício.",
|
||||
"armorSpecialFall2019HealerText": "Vestes da Escuridão",
|
||||
"armorSpecialWinter2020RogueText": "Parka fofa",
|
||||
"armorSpecialWinter2020WarriorText": "Armadura de Casca",
|
||||
"armorSpecialWinter2020HealerNotes": "Um vestido opulento para quem tem um toque festivo! Aumenta a Constituição em <%= con %>. Equipamento de inverno de edição limitada 2019-2020.",
|
||||
"armorSpecialSpring2020RogueText": "Armadura Ultramarina",
|
||||
"armorSpecialSpring2020WarriorText": "Armadura de Exoesqueleto",
|
||||
"weaponMystery202104Notes": "É melhor que seus inimigos tomem cuidado - você tem defesas poderosas e espinhosas! Não confere nenhum benefício. Item de assinante de abril de 2021.",
|
||||
"weaponMystery202111Notes": "Molde o fluxo do tempo com este cajado misterioso e poderoso. Não confere nenhum benefício. Item de assinante de novembro de 2021.",
|
||||
"weaponMystery202002Text": "Guarda-sol elegante",
|
||||
"weaponMystery202002Notes": "Um acessório que lhe confere um ar de mistério e romance. A proteção solar é um bônus! Não confere nenhum benefício. Item de assinante de fevereiro de 2020.",
|
||||
"weaponMystery202102Notes": "A joia rosa brilhante nesta varinha tem o poder de espalhar alegria e amizade por toda parte! Não confere nenhum benefício. Item de assinante de fevereiro de 2021.",
|
||||
"weaponMystery202201Text": "Canhão de confete da meia-noite",
|
||||
"weaponMystery202209Notes": "Este livro irá guiá-lo em sua jornada na criação de magia. Não confere nenhum benefício. Item de assinante de setembro de 2022.",
|
||||
"weaponMystery202212Notes": "O floco de neve brilhante nesta varinha tem o poder de aquecer corações até mesmo nas noites mais frias de inverno! Não confere nenhum benefício. Item de assinante de dezembro de 2022.",
|
||||
"weaponArmoireFloridFanNotes": "Este lindo leque de seda dobra quando não está em uso. Aumenta a Constituição em <%= con %>. Armário Encantado: Item Independente.",
|
||||
"weaponArmoireJadeGlaiveNotes": "O alcance desta glaive irá mantê-lo longe de seus inimigos! Além disso, você pode derrubar coisas de prateleiras altas. Aumenta a Força em <%= str %>. Armário Encantado: Conjunto Guerreiro de Jade (Item 3 de 3).",
|
||||
"weaponArmoireRidingBroomText": "Vassoura de equitação",
|
||||
"weaponSpecialSpring2025HealerText": "Plumeria Crook",
|
||||
"weaponMystery202511Text": "Espada Gélida",
|
||||
"weaponSpecialWinter2026WarriorText": "Foice Rime"
|
||||
}
|
||||
|
||||
@@ -342,5 +342,96 @@
|
||||
"leaveGuild": "Abandonar Guilda",
|
||||
"features": "Funcionalidades",
|
||||
"taskClaimed": "<%= userName %> aceitou a tarefa: <span class=\"notification-bold\"><%= taskText %></span>.",
|
||||
"youHaveBeenAssignedTask": "<%= managerName %> atribuiu-te a tarefa: <span class=\"notification-bold\"><%= taskText %></span>."
|
||||
"youHaveBeenAssignedTask": "<%= managerName %> atribuiu-te a tarefa: <span class=\"notification-bold\"><%= taskText %></span>.",
|
||||
"upgradeExistingGroup": "Atualizar um grupo existente",
|
||||
"createNewGroup": "Crie um novo grupo",
|
||||
"yourParty": "Sua festa",
|
||||
"inviteOthersForAdditional": "Convide outras pessoas para o seu grupo por um período adicional",
|
||||
"perMember": "por membro",
|
||||
"oneMember": "1 membro",
|
||||
"membersCount": "<%= contagem %> membros",
|
||||
"pendingCount": "(<%= contagem %> pendente)",
|
||||
"upgradeCancelsPendingInvites": "Atualizar seu grupo cancelará todos os convites pendentes",
|
||||
"invitedToPartyBy": "<a href=\"/profile/<%= userId %>\" target=\"_blank\">@<%= userName %></a> convidou você para participar da festa <span class=\"notification-bold\"><%= party %></span>",
|
||||
"chatTemporarilyUnavailable": "O bate-papo está temporariamente indisponível. Por favor, tente novamente mais tarde.",
|
||||
"thisTaskApproved": "Esta tarefa foi aprovada",
|
||||
"challengeBannedWords": "Seu Desafio contém um ou mais palavrões ou referências a um tópico adulto. Edite seu desafio para poder salvá-lo. Você deve remover a palavra, não apenas censurá-la.",
|
||||
"challengeBannedSlursPrivate": "Seu Desafio contém um insulto que viola as diretrizes da comunidade do Habitica. Remova-o para salvar seu desafio.",
|
||||
"blockedUser": "<strong>Você bloqueou este jogador.</strong> Ele não pode enviar mensagens privadas para você, mas você ainda verá as postagens dele.",
|
||||
"bannedUser": "<strong>Este jogador foi banido.</strong>",
|
||||
"upgradeYourCrew": "Pronto para atualizar sua tripulação?",
|
||||
"createGroupToday": "Crie seu grupo hoje!",
|
||||
"createGroupTitle": "Criar grupo",
|
||||
"groupUse": "O que melhor descreve o uso do seu grupo?*",
|
||||
"suggestedGroup": "Sugerido porque você é novo no Habitica.",
|
||||
"newGroupsBullet09": "Uma tarefa compartilhada pode ser desmarcada para mostrar que ainda precisa de trabalho",
|
||||
"newGroupsVisitFAQ": "Visite as <a href='/static/faq#group-plans' target='_blank'>Perguntas frequentes</a> no menu suspenso Ajuda para obter mais orientações.",
|
||||
"interestedLearningMore": "Interessado em aprender mais?",
|
||||
"checkGroupPlanFAQ": "Confira as <a href='/static/faq#what-is-group-plan'>Perguntas frequentes sobre planos de grupo</a> para saber como aproveitar ao máximo sua experiência de tarefas compartilhadas.",
|
||||
"lookForParty": "Procure uma festa",
|
||||
"readyToUpgrade": "Pronto para atualizar?",
|
||||
"messageCopiedToClipboard": "Mensagem copiada para a área de transferência.",
|
||||
"onlyPrivateGuildsCanUpgrade": "Somente guildas privadas podem ser atualizadas para um plano de grupo.",
|
||||
"createGroup": "Crie um grupo",
|
||||
"newGroupsWhatsNew": "Confira as novidades:",
|
||||
"newGroupsBullet06": "A visualização do status da tarefa permite ver rapidamente qual responsável concluiu uma tarefa",
|
||||
"newGroupsBullet08": "O líder do grupo e os gerentes podem adicionar tarefas rapidamente no topo das colunas de tarefas",
|
||||
"challengeBannedSlurs": "Seu Desafio contém um insulto que viola as diretrizes da comunidade do Habitica e seus privilégios de chat e criação de Desafio foram revogados. Contate admin@habitica.com para mais informações.",
|
||||
"dayStart": "<strong>Início do dia</strong>: <%= startTime %>",
|
||||
"newGroupsBullet01": "Interaja com tarefas diretamente do quadro de tarefas compartilhado",
|
||||
"newGroupsBullet07": "Alterne a capacidade de exibir as tarefas compartilhadas em seu quadro de tarefas pessoais",
|
||||
"chooseAnOption": "Escolha uma opção",
|
||||
"previouslyUpgradedGroup": "Grupo atualizado anteriormente",
|
||||
"additionalMembersProrated": "Membros adicionais convidados durante o mês serão adicionados ao total do próximo ciclo de faturamento como uma cobrança proporcional.",
|
||||
"youEmphasized": "<strong>Você</strong>",
|
||||
"newGroupsWelcome": "Bem-vindo ao novo quadro de tarefas compartilhadas!",
|
||||
"newGroupsBullet05": "As tarefas compartilhadas perderão a cor se forem deixadas incompletas para ajudar a monitorar o progresso",
|
||||
"newGroupsBullet10": "O status da atribuição determina a condição de conclusão:",
|
||||
"newGroupsBullet10a": "<strong>Deixe uma tarefa não atribuída</strong> se algum membro puder concluí-la",
|
||||
"newGroupsBullet10b": "<strong>Atribua uma tarefa a um membro</strong> para que somente ele possa concluí-la",
|
||||
"newGroupsBullet10c": "<strong>Atribua uma tarefa a vários membros</strong> se todos precisarem concluí-la",
|
||||
"chatSunsetWarning": "⚠️ <strong>O bate-papo do Habitica Guilds e Tavern será descontinuado em 08/08/2023.</strong> <a href='/static/faq/tavern-and-guilds'>Clique aqui</a> para ler mais sobre essa mudança.",
|
||||
"currentlyLookingForParty": "Você está procurando uma festa!",
|
||||
"partyFinderDescription": "Quer participar de um grupo com outras pessoas, mas não conhece nenhum outro jogador? Deixe os líderes do Partido saberem que você está procurando um convite!",
|
||||
"upgradeToGroup": "Atualizar para grupo",
|
||||
"viewDetails": "Ver detalhes",
|
||||
"invitedToThisQuest": "Você foi convidado para esta missão!",
|
||||
"groupUseDefault": "Escolha uma resposta",
|
||||
"groupParentChildren": "Usando com minha família",
|
||||
"groupFriends": "Usando com amigos",
|
||||
"groupManager": "Usando para o trabalho",
|
||||
"groupTeacher": "Usando para educação",
|
||||
"nameStar": "Nome*",
|
||||
"nameStarText": "Adicione um título",
|
||||
"descriptionOptional": "Descrição",
|
||||
"descriptionOptionalText": "Adicione uma descrição",
|
||||
"nextPaymentMethod": "Próximo: Pagamento",
|
||||
"lastCompleted": "Concluído pela última vez",
|
||||
"newGroupsBullet02": "Qualquer pessoa pode concluir uma tarefa não atribuída",
|
||||
"newGroupsBullet03": "As tarefas compartilhadas são redefinidas ao mesmo tempo para todos, facilitando a colaboração",
|
||||
"newGroupsBullet04": "‘As Diárias Compartilhadas não causarão danos quando perdidas ou aparecerão no prompt Gravar Atividade de Ontem.",
|
||||
"newGroupsEnjoy": "Esperamos que você aproveite a nova experiência dos Planos de Grupo!",
|
||||
"classLabel": "Aula:",
|
||||
"languageLabel": "Linguagem:",
|
||||
"invitedToYourParty": "<strong>Convidado para sua festa!</strong> Clique para desfazer",
|
||||
"lookingForPartyTitle": "Encontre membros",
|
||||
"findMorePartyMembers": "Encontre mais membros",
|
||||
"tavernDiscontinuedLinks": "Leia mais sobre a <a href='/static/faq/tavern-and-guilds'>Descontinuação dos Serviços de Taverna e Guilda</a> ou volte para a <a href='/'>página inicial</a>.",
|
||||
"sendGiftLabel": "Gostaria de enviar uma mensagem de presente?",
|
||||
"assignTo": "Atribuir a",
|
||||
"bannedWordsAllowedDetail": "Com esta opção selecionada, será permitido o uso de palavras banidas neste Grupo.",
|
||||
"assignedDateOnly": "Atribuído em <strong><%= data %></strong>",
|
||||
"assignedDateAndUser": "Atribuído por @<%= nome de usuário %> em <%= data %>",
|
||||
"claimRewards": "Reivindicar recompensas",
|
||||
"newPartyPlaceholder": "Digite o nome do seu grupo.",
|
||||
"languageSettings": "Configurações de idioma",
|
||||
"bannedWordsAllowed": "Permitir palavras banidas",
|
||||
"groupActivityNotificationTitle": "<%= usuário %> postou em <%= grupo %>",
|
||||
"managerNotes": "Notas do gerente",
|
||||
"findPartyMembers": "Encontre membros do grupo",
|
||||
"noOneLooking": "Não há ninguém procurando por uma festa no momento.<br>Você pode voltar mais tarde!",
|
||||
"questWithOthers": "Participe de missões com outras pessoas",
|
||||
"startPartyDetail": "Comece seu próprio grupo ou participe de um já existente <br/>para realizar missões e aumentar sua motivação!",
|
||||
"partyExceedsInvitesLimit": "Um grupo só pode ter até <%= maxInvites %> convites pendentes.",
|
||||
"messagePartyLeader": "Líder do grupo de mensagens"
|
||||
}
|
||||
|
||||
@@ -166,5 +166,124 @@
|
||||
"spring2019CloudRogueSet": "Nuvem (Ladino)",
|
||||
"fall2019OperaticSpecterSet": "Fantasma da Ópera (Ladino)",
|
||||
"royalPurpleJackolantern": "Abóbora de Hallowen de Púrpura Real",
|
||||
"winter2020CarolOfTheMageSet": "O Conto do Feiticeiro (Mago)"
|
||||
"winter2020CarolOfTheMageSet": "O Conto do Feiticeiro (Mago)",
|
||||
"winter2020WinterSpiceSet": "Tempero de Inverno (Curandeiro)",
|
||||
"fall2020DeathsHeadMothHealerSet": "Mariposa da Cabeça da Morte (Curandeiro)",
|
||||
"fall2020TwoHeadedRogueSet": "Duas Cabeças (Vampira)",
|
||||
"summer2020OarfishMageSet": "Peixe Remo (Mago)",
|
||||
"spring2020PuddleMageSet": "Poça (Mago)",
|
||||
"summer2020SeaGlassHealerSet": "Vidro do Mar (Curandeiro)",
|
||||
"fall2020WraithWarriorSet": "Espectro (Guerreiro)",
|
||||
"summer2021ClownfishRogueSet": "Peixe-palhaço (ladino)",
|
||||
"summer2021FlyingFishWarriorSet": "Peixe Voador (Guerreiro)",
|
||||
"winter2021IceFishingWarriorSet": "Pescador de Gelo (Guerreiro)",
|
||||
"summer2021ParrotHealerSet": "Papagaio (curandeiro)",
|
||||
"winter2020LanternSet": "Lanterna (Vampira)",
|
||||
"summer2021NautilusMageSet": "Nautilus (Mago)",
|
||||
"spring2020BeetleWarriorSet": "Besouro Rinoceronte (Guerreiro)",
|
||||
"summer2020CrocodileRogueSet": "Crocodilo (Vampira)",
|
||||
"winter2021HollyIvyRogueSet": "Holly e Ivy (Vampira)",
|
||||
"spring2020LapisLazuliRogueSet": "Lápis-lazúli (Vampira)",
|
||||
"spring2022MagpieRogueSet": "Pega (Vampira)",
|
||||
"spring2022RainstormWarriorSet": "Tempestade (Guerreiro)",
|
||||
"spring2022ForsythiaMageSet": "Forsítia (Mago)",
|
||||
"spring2022PeridotHealerSet": "Peridoto (curandeiro)",
|
||||
"summer2022CrabRogueSet": "Caranguejo (Vampira)",
|
||||
"summer2022WaterspoutWarriorSet": "Tromba D'água (Guerreiro)",
|
||||
"summer2022MantaRayMageSet": "Manta Ray (Mago)",
|
||||
"summer2022AngelfishHealerSet": "Peixe-anjo (curandeiro)",
|
||||
"fall2022KappaRogueSet": "Kappa (Vampira)",
|
||||
"fall2022OrcWarriorSet": "Orc (Guerreiro)",
|
||||
"fall2022HarpyMageSet": "Harpia (Mago)",
|
||||
"fall2021OozeRogueSet": "Ooze (Vampira)",
|
||||
"fall2021HeadlessWarriorSet": "Sem Cabeça (Guerreiro)",
|
||||
"fall2021BrainEaterMageSet": "Devorador de Cérebros (Mago)",
|
||||
"fall2021FlameSummonerHealerSet": "Invocador de Chamas (Curandeiro)",
|
||||
"winter2022FireworksRogueSet": "Fogos de artifício (Rogue)",
|
||||
"winter2022StockingWarriorSet": "Meia (Guerreiro)",
|
||||
"winter2022PomegranateMageSet": "Romã (Mago)",
|
||||
"winter2022IceCrystalHealerSet": "Cristal de Gelo (Curandeiro)",
|
||||
"spring2024FluoriteWarriorSet": "Conjunto Fluorita (Guerreiro)",
|
||||
"spring2024HibiscusMageSet": "Conjunto Fluorita (Guerreiro)",
|
||||
"spring2024BluebirdHealerSet": "Conjunto Bluebird (Curandeiro)",
|
||||
"wantToPayWithGemsText": "Quer pagar com Gems?",
|
||||
"spring2024MeltingSnowRogueSet": "Conjunto de neve derretida (Rogue)",
|
||||
"buyNowMoneyButton": "Compre agora por $ 9,99",
|
||||
"buyNowGemsButton": "Compre agora por 60 joias",
|
||||
"ownJubilantGryphatrice": "<strong>Você é o dono do Jubilant Gryphatrice!</strong> Visite Pets e Montarias para equipar!",
|
||||
"summer2024SeaSnailHealerSet": "Conjunto de Caracol Marinho (Curandeiro)",
|
||||
"summer2024NudibranchRogueSet": "Conjunto Nudibrânquios (Rogue)",
|
||||
"summer2024SeaAnemoneMageSet": "Conjunto de Anêmona do Mar (Mago)",
|
||||
"dayTen": "Dia 10",
|
||||
"partyRobes": "Robes de festa",
|
||||
"fall2025SasquatchWarriorSet": "Conjunto Sasquatch (Guerreiro)",
|
||||
"fall2025SkeletonRogueSet": "Conjunto de Esqueleto (Rogue)",
|
||||
"jubilantGryphatricePromo": "Animal de estimação animado Jubilant Gryphatrice",
|
||||
"winter2025MooseWarriorSet": "Conjunto Alce (Guerreiro)",
|
||||
"winter2025AuroraMageSet": "Conjunto Aurora (Mago)",
|
||||
"winter2025StringLightsHealerSet": "Conjunto de luzes de corda (curandeiro)",
|
||||
"winter2025SnowRogueSet": "Conjunto de neve (Rogue)",
|
||||
"anniversaryGryphatricePrice": "Adquira-o hoje mesmo por <strong>US$ 9,99</strong> ou <strong>60 joias</strong>",
|
||||
"dayOne": "Dia 1",
|
||||
"spring2025SunshineWarriorSet": "Conjunto Luz do Sol (Guerreiro)",
|
||||
"spring2025CrystalPointRogueSet": "Conjunto de Pontas de Cristal (Rogue)",
|
||||
"spring2025PlumeriaHealerSet": "Conjunto Plumeria (Curandeiro)",
|
||||
"spring2025MantisMageSet": "Conjunto Louva-a-deus (Mago)",
|
||||
"fourForFreeText": "Para continuar a festa, vamos oferecer Robes de Festa, 20 Gemas e um fundo de aniversário de edição limitada e um conjunto de itens que inclui uma Capa, Brafoneiras e uma Máscara de Olhos.",
|
||||
"birthdaySet": "Conjunto de aniversário",
|
||||
"winter2024SnowyOwlRogueSet": "Coruja das Neves (Vampira)",
|
||||
"winter2024FrozenHealerSet": "Congelado (Curandeiro)",
|
||||
"winter2024PeppermintBarkWarriorSet": "Conjunto Casca de Hortelã (Guerreiro)",
|
||||
"winter2024NarwhalWizardMageSet": "Conjunto de Mago de Narval (Mago)",
|
||||
"fourForFree": "Quatro de graça",
|
||||
"fall2024FieryImpWarriorSet": "Conjunto Diabrete Ardente (Guerreiro)",
|
||||
"fall2024UnderworldSorcerorMageSet": "Conjunto Feiticeiro do Submundo (Mago)",
|
||||
"fall2024SpaceInvaderHealerSet": "Conjunto Invasor do Espaço (Curandeiro)",
|
||||
"fall2024BlackCatRogueSet": "Conjunto Gato Preto (Rogue)",
|
||||
"anniversaryGryphatriceText": "O raro Jubilant Gryphatrice junta-se às comemorações do aniversário! Não perca a chance de possuir este animal de estimação animado exclusivo.",
|
||||
"stableVisit": "Visite Pets e Montarias para equipar!",
|
||||
"winter2023FairyLightsMageSet": "Luzes de Fada (Mago)",
|
||||
"winter2023CardinalHealerSet": "Cardeal (curandeiro)",
|
||||
"summer2025ScallopWarriorSet": "Conjunto Vieira (Guerreiro)",
|
||||
"summer2025SquidRogueSet": "Conjunto de Lulas (Rogue)",
|
||||
"summer2025SeaAngelHealerSet": "Conjunto Anjo do Mar (Curandeiro)",
|
||||
"summer2025FairyWrasseMageSet": "Conjunto de Bodião Fada (Mago)",
|
||||
"spring2023CaterpillarRogueSet": "Lagarta (Vampira)",
|
||||
"spring2023HummingbirdWarriorSet": "Beija-flor (Guerreiro)",
|
||||
"spring2023MoonstoneMageSet": "Pedra da Lua (Mago)",
|
||||
"spring2023LilyHealerSet": "Lírio (curandeira)",
|
||||
"limitedEdition": "Edição Limitada",
|
||||
"fall2025KoboldHealerSet": "Conjunto Kobold (Curandeiro)",
|
||||
"fall2025MaskedGhostMageSet": "Conjunto Fantasma Mascarado (Mago)",
|
||||
"spring2026FrogWarriorSet": "Conjunto Sapo (Guerreiro)",
|
||||
"spring2026BranchRogueSet": "Conjunto de galhos de primavera (Rogue)",
|
||||
"spring2026SnowdropHealerSet": "Conjunto de galhos de primavera (Rogue)",
|
||||
"spring2026MaypoleMageSet": "Conjunto Maypole (Mago)",
|
||||
"wantToPayWithMoneyText": "Quer pagar com Stripe, Paypal ou Amazon?",
|
||||
"jubilantSuccess": "You've successfully purchased the <strong>Jubilant Gryphatrice!</strong>",
|
||||
"takeMeToStable": "Leve-me para animais de estimação e montarias",
|
||||
"plentyOfPotions": "Muitas Poções",
|
||||
"winter2023WalrusWarriorSet": "Morsa (Guerreiro)",
|
||||
"summer2024WhaleSharkWarriorSet": "Conjunto Tubarão Baleia (Guerreiro)",
|
||||
"winter2026RimeReaperWarriorSet": "Conjunto Rime Reaper (Guerreiro)",
|
||||
"winter2026SkiRogueSet": "Conjunto de esqui (Rogue)",
|
||||
"winter2026PolarBearHealerSet": "Conjunto Urso Polar (Curandeiro)",
|
||||
"winter2026MidwinterCandleMageSet": "Conjunto de Velas de Solstício de Inverno (Mago)",
|
||||
"plentyOfPotionsText": "Estamos trazendo de volta 10 das poções Magic Hatching favoritas da comunidade. Vá até o The Market para preencher sua coleção!",
|
||||
"dayFive": "Dia 5",
|
||||
"twentyGems": "20 joias",
|
||||
"anniversaryLimitedDates": "30 de janeiro a 8 de fevereiro",
|
||||
"limitedEvent": "Evento Limitado",
|
||||
"visitTheMarketButton": "Visite o Mercado",
|
||||
"fall2022WatcherHealerSet": "Espiador (curandeiro)",
|
||||
"winter2023RibbonRogueSet": "Fita (Vampira)",
|
||||
"summer2023GoldfishWarriorSet": "Peixe Dourado (Guerreiro)",
|
||||
"summer2023GuppyRogueSet": "Guppy (Vampira)",
|
||||
"summer2023KelpHealerSet": "Kelp (curandeiro)",
|
||||
"summer2023CoralMageSet": "Coral (Mago)",
|
||||
"celebrateAnniversary": "Comemore o 10º aniversário do Habitica com presentes e itens exclusivos abaixo!",
|
||||
"fall2023ScarletWarlockMageSet": "Feiticeiro Escarlate (Mago)",
|
||||
"fall2023WitchsBrewRogueSet": "Cerveja da Bruxa (Rogue)",
|
||||
"fall2023BogCreatureHealerSet": "Criatura do Pântano (Curandeiro)",
|
||||
"fall2023ScaryMovieWarriorSet": "Filme de terror (Guerreiro)"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"resetAccPop": "Comece de novo, removendo todos níveis, ouro, equipamentos, histórico, e tarefas.",
|
||||
"deleteAccount": "Eliminar Conta",
|
||||
"deleteAccPop": "Cancela e remove sua conta do Habitica.",
|
||||
"feedback": "Se tu queres nos dar um feedback, por favor insira-o abaixo - adoraríamos saber o que você gostou ou não gostou sobre o Habitica! Não fala bem o inglês? Sem problemas! Use o idioma que preferir.",
|
||||
"feedback": "Se você quer nos dar um feedback, por favor insira-o abaixo - adoraríamos ouvir seu feedback! Será anônimo, a menos que opte por enviar com suas informações de contato. Não fala bem o inglês? Sem problemas! Use o idioma que preferir.",
|
||||
"dataExport": "Exportação de Dados",
|
||||
"saveData": "Aqui estão algumas opções para guardar os seus dados.",
|
||||
"habitHistory": "Histórico de Hábitos",
|
||||
@@ -199,5 +199,13 @@
|
||||
"showLevelUpModal": "Quando Subir um Nível",
|
||||
"showHatchPetModal": "Quando chocar um Pet",
|
||||
"showRaisePetModal": "Quando criar um Pet para ser uma montaria",
|
||||
"showStreakModal": "Quando alcançar uma sequencia de Proezas"
|
||||
"showStreakModal": "Quando alcançar uma sequencia de Proezas",
|
||||
"feedbackPlaceholder": "Adicione seu feedback",
|
||||
"yourUserData": "Seus dados de usuário",
|
||||
"taskHistory": "Histórico de tarefas",
|
||||
"yourUserDataDisclaimer": "Aqui você pode baixar uma cópia do seu histórico de tarefas ou seus dados de usuário completo.",
|
||||
"useridCopied": "ID de usuário copiado para área de transferência.",
|
||||
"userIdTooltip": "O ID de usuário é um número único que o Habitica gera automaticamente quando um jogador se cadastra, semelhante a um nome de usuário. No entanto, diferentemente do nome de usuário, o ID de usuário não pode ser alterado.",
|
||||
"developerMode": "Modo Desenvolvedor",
|
||||
"developerModeTooltip": "O Habitica oferece um modo desenvolvedor para habilitar recursos adicionais que interagem com a API do Habitica."
|
||||
}
|
||||
|
||||
@@ -930,5 +930,14 @@
|
||||
"backgroundWinterDesertWithSaguarosText": "Deserto de Inverno com Cactos",
|
||||
"backgrounds022026": "Conjunto 141: Lançado em Fevereiro de 2026",
|
||||
"backgroundElegantPalaceText": "Palácio Elegante",
|
||||
"backgroundElegantPalaceNotes": "Admire os corredores coloridos de um Palácio Elegante."
|
||||
"backgroundElegantPalaceNotes": "Admire os corredores coloridos de um Palácio Elegante.",
|
||||
"backgrounds032026": "Conjunto 142: Lançado em Março de 2026",
|
||||
"backgroundWaterfallWithRainbowText": "Cachoeira com Arco-íris",
|
||||
"backgroundWaterfallWithRainbowNotes": "Admire a beleza de tirar o fôlego de uma Cachoeira com Arco-Íris.",
|
||||
"backgrounds042026": "Conjunto 143: Lançado em Abril de 2026",
|
||||
"backgroundRidingACometText": "Montando um Cometa",
|
||||
"backgroundRidingACometNotes": "Viaje pelo espaço Montando um Cometa!",
|
||||
"backgrounds052026": "Conjunto 144: Lançado em Maio de 2026",
|
||||
"backgroundElvenCitadelText": "Cidadela Élfica",
|
||||
"backgroundElvenCitadelNotes": "Faça uma jornada cênica até uma Cidadela Élfica."
|
||||
}
|
||||
|
||||
@@ -428,5 +428,13 @@
|
||||
"groupParentChildren": "Usando com meus familiares",
|
||||
"groupManager": "Usando para trabalho",
|
||||
"groupPlanBillingFYI": "As assinaturas do Plano de Grupo são renovadas automaticamente, a menos que você cancele pelo menos 24 horas antes do término do período atual. Você pode cancelar na aba \"Faturamento do Grupo\" do seu Plano de Grupo. A cobrança será feita em até 24 horas antes da renovação da sua assinatura, com base no número de membros do seu Plano de Grupo naquele momento. Se você adicionar membros entre os períodos de pagamento, verá uma cobrança proporcional adicional pelos benefícios deles no seu próximo ciclo de faturamento.",
|
||||
"groupPlanBillingFYIShort": "As assinaturas do Plano de Grupo são renovadas automaticamente, a menos que você cancele pelo menos 24 horas antes do término do período atual. A cobrança será feita em até 24 horas antes da renovação da sua assinatura, com base no número de membros do seu Plano de Grupo naquele momento. Se você adicionar membros entre os períodos de pagamento, será cobrada uma taxa adicional proporcional pelos benefícios deles no seu próximo ciclo de faturamento."
|
||||
"groupPlanBillingFYIShort": "As assinaturas do Plano de Grupo são renovadas automaticamente, a menos que você cancele pelo menos 24 horas antes do término do período atual. A cobrança será feita em até 24 horas antes da renovação da sua assinatura, com base no número de membros do seu Plano de Grupo naquele momento. Se você adicionar membros entre os períodos de pagamento, será cobrada uma taxa adicional proporcional pelos benefícios deles no seu próximo ciclo de faturamento.",
|
||||
"yourParty": "Seu Grupo",
|
||||
"previouslyUpgradedGroup": "Grupo previamente aprimorado",
|
||||
"oneMember": "1 membro",
|
||||
"membersCount": "<%= count %> membros",
|
||||
"pendingCount": "(<%= count %> pendentes)",
|
||||
"chooseAnOption": "Escolha uma Opção",
|
||||
"upgradeExistingGroup": "Aprimorar um Grupo Existente",
|
||||
"createNewGroup": "Criar um Novo Grupo"
|
||||
}
|
||||
|
||||
@@ -286,5 +286,6 @@
|
||||
"winter2026RimeReaperWarriorSet": "Conjunto Guerreiro Ceifador de Gelo",
|
||||
"winter2026SkiRogueSet": "Conjunto Ski Rogue",
|
||||
"winter2026PolarBearHealerSet": "Conjunto de Curandeiro Urso Polar",
|
||||
"winter2026MidwinterCandleMageSet": "Conjunto de Mago Velas do Inverno"
|
||||
"winter2026MidwinterCandleMageSet": "Conjunto de Mago Velas do Inverno",
|
||||
"spring2026FrogWarriorSet": "Conjunto de Guerreiro Sapo"
|
||||
}
|
||||
|
||||
@@ -921,5 +921,13 @@
|
||||
"backgroundInsideForestWitchsCottageText": "Домик лесной ведьмы",
|
||||
"backgroundAutumnSwampNotes": "Окунитесь в атмосферу осеннего болота.",
|
||||
"backgrounds102025": "Набор 137: Выпущен в октябре 2025 года",
|
||||
"backgroundInsideForestWitchsCottageNotes": "Сплетите заклинания в домике лесной ведьмы."
|
||||
"backgroundInsideForestWitchsCottageNotes": "Сплетите заклинания в домике лесной ведьмы.",
|
||||
"backgroundNighttimeStreetWithShopsText": "Ночная улица с магазинами",
|
||||
"backgrounds122025": "Набор 139: Выпущен в декабре 2025",
|
||||
"backgroundNighttimeStreetWithShopsNotes": "Наслаждайтесь теплым сиянием ночных торговых улиц.",
|
||||
"backgroundWinterDesertWithSaguarosNotes": "Вдохните свежий воздух зимней пустыни полной кактусов сагуаро.",
|
||||
"backgroundElegantPalaceText": "Прекрасный Дворец",
|
||||
"backgrounds012026": "Набор 140: Выпущен в январе 2026",
|
||||
"backgrounds022026": "Набор 141: Выпущен в феврале 2026",
|
||||
"backgroundWinterDesertWithSaguarosText": "Зимняя пустыня с кактусами сагуаро"
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@
|
||||
"backgroundBlossomingDesertText": "Çiçek Açan Çöl",
|
||||
"backgroundBlossomingDesertNotes": "Çiçek Açan Çölde nadir bir süperçiçeklenmeye tanık ol.",
|
||||
"backgrounds052019": "SET 60: Mayıs 2019'da yayınlandı",
|
||||
"backgroundDojoText": "Dojo",
|
||||
"backgroundDojoText": "Dojо",
|
||||
"backgroundDojoNotes": "Dojo'da yeni hareketler öğren.",
|
||||
"backgroundParkWithStatueText": "Heykelli Park",
|
||||
"backgroundParkWithStatueNotes": "Heykelli Park'ın içinden çiçek kaplı bir yolu takip edin.",
|
||||
@@ -930,5 +930,14 @@
|
||||
"backgroundMonstrousCaveText": "Canavar Mağarası",
|
||||
"backgroundMonstrousCaveNotes": "Canavar Mağarası'nın girişinden bak.",
|
||||
"backgroundJackOLanternStacksText": "Balkabağı Feneri Yığınları",
|
||||
"backgroundJackOLanternStacksNotes": "Balkabağı fenerleri tarlasını hayranlıkla izle."
|
||||
"backgroundJackOLanternStacksNotes": "Balkabağı fenerleri tarlasını hayranlıkla izle.",
|
||||
"backgrounds032026": "SET 142: Mart 2026'da yayımlandı",
|
||||
"backgroundWaterfallWithRainbowText": "Gökkuşağı ile Çağlayan",
|
||||
"backgroundWaterfallWithRainbowNotes": "Gökkuşağı ile Çağlayan'ın nefes kesici güzelliğine hayran ol.",
|
||||
"backgrounds042026": "SET 143: Nisan 2026'da yayımlandı",
|
||||
"backgroundRidingACometText": "Kuyruklu Yıldıza Binmek",
|
||||
"backgroundRidingACometNotes": "Bir kuyruklu yıldızla uzay boyu gez!",
|
||||
"backgrounds052026": "SET 144: Mayıs 2026'da yayınlandı",
|
||||
"backgroundElvenCitadelText": "Elf Kalesi",
|
||||
"backgroundElvenCitadelNotes": "Elflerin kalesine doğru manzaralı bir yolculuğa çık."
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"weaponRogue3Notes": "Özgün bir çalı bıçağı, hem hayatta kalma aracı hem de silah. Gücü <%= str %> puan arttırır.",
|
||||
"weaponRogue4Text": "Nunçaku",
|
||||
"weaponRogue4Notes": "Birbirine zincirle bağlı ağır sopalar. Gücü <%= str %> puan arttırır.",
|
||||
"weaponRogue5Text": "Ninja-to",
|
||||
"weaponRogue5Text": "Ninja-tо",
|
||||
"weaponRogue5Notes": "Ninjaların kendileri kadar zarif ve ölümcül. Gücü <%= str %> puan arttırır.",
|
||||
"weaponRogue6Text": "Kancalı Kılıç",
|
||||
"weaponRogue6Notes": "Düşmanı kapana kıstırıp silahsız bırakma konusunda çok kullanışlı, karmaşık bir silah. Gücü <%= str %> puan arttırır.",
|
||||
@@ -82,7 +82,7 @@
|
||||
"weaponSpecial1Text": "Kristal Kılıç",
|
||||
"weaponSpecial1Notes": "Parıldayan yüzeyleri, bir kahramanın masalını anlatır. Tüm özellikleri <%= attrs %> puan arttırır.",
|
||||
"weaponSpecial2Text": "Stephen Weber'in Ejderha Mili",
|
||||
"weaponSpecial2Notes": "İçindeki ejder gücünün dalga dalga akışını hisset! Gücü ve Sezgiyi <%= attrs %> puan arttırır.",
|
||||
"weaponSpecial2Notes": "İçindeki ejder gücünün dаlga dalga akışını hisset! Gücü ve Sezgiyi <%= attrs %> puan arttırır.",
|
||||
"weaponSpecial3Text": "Mustaine'in Milat Mahveden Seher Yıldızı",
|
||||
"weaponSpecial3Notes": "Mitingler, mahlukatlar, mutsuzluk: mazide kaldı! Mahvet! Gücü, Zekayı ve Dayanıklılığı <%= attrs %> puan arttırır.",
|
||||
"weaponSpecialCriticalText": "Yazılım Hatası Ezici Kritik Tokmak",
|
||||
@@ -109,7 +109,7 @@
|
||||
"weaponSpecialNomadsScimitarNotes": "Bu Palanın kıvrımlı bıçağı, bir bineğin üstünden İşlere saldırmak için mükemmeldir! Zekayı <%= int %> puan arttırır.",
|
||||
"weaponSpecialFencingFoilText": "Eskrim Folyosu",
|
||||
"weaponSpecialFencingFoilNotes": "Şerefine itiraz etmeye cesaret eden olursa, bu güzel folyo ile hazır olacaksın! Gücü <%= str %> puan arttırır.",
|
||||
"weaponSpecialTachiText": "Tachi",
|
||||
"weaponSpecialTachiText": "Taçi",
|
||||
"weaponSpecialTachiNotes": "Bu hafif ve kıvrımlı kılıç, işlerini şerit parçalarına ayıracak! Gücü <%= str %> puan arttırır.",
|
||||
"weaponSpecialAetherCrystalsText": "Tinsel Kristaller",
|
||||
"weaponSpecialAetherCrystalsNotes": "Bu bileklikler ve kristaller bir zamanlar Kayıp Uzmaneleman'ın kendine aitti. Tüm Nitelikleri <%= attrs %> puan arttırır.",
|
||||
@@ -146,7 +146,7 @@
|
||||
"weaponSpecialFallHealerText": "Bokböceği Asası",
|
||||
"weaponSpecialFallHealerNotes": "Bu asanın üzerindeki bokböceği, kendisini taşıyanı korur ve iyileştirir. Zekayı <%= int %> puan kadar arttırır. Sınırlı Sürüm 2014 Güz Ekipmanı.",
|
||||
"weaponSpecialWinter2015RogueText": "Buz Çivisi",
|
||||
"weaponSpecialWinter2015RogueNotes": "Bunu tam anlamıyla, kesinlikle, bayağı bayağı yerden aldın. Gücü <%= str %> puan arttırır. Sınırlı Sürüm 2014-2015 Kış Ekipmanı.",
|
||||
"weaponSpecialWinter2015RogueNotes": "Bunu tam anlamıyla, kesinlikle, bаyağı bayağı yerden aldın. Gücü <%= str %> puan arttırır. Sınırlı Sürüm 2014-2015 Kış Ekipmanı.",
|
||||
"weaponSpecialWinter2015WarriorText": "Jelibon Kılıç",
|
||||
"weaponSpecialWinter2015WarriorNotes": "Bu leziz kılıç büyük ihtimalle canavarları kendine çekecektir... neyse ki mücadeleye varsın! Gücü <%= str %> puan arttırır. Sınırlı Sürüm 2014-2015 Kış Ekipmanı.",
|
||||
"weaponSpecialWinter2015MageText": "Kış-ıltılı Değnek",
|
||||
@@ -264,7 +264,7 @@
|
||||
"weaponSpecialSummer2018MageText": "Aslan Balığı İğneleri",
|
||||
"weaponSpecialSummer2018MageNotes": "Suyun altında ateş, buz ve elektrik büyüeri, bunu yapan Büyücüye tehlike yaratabilir. Sihirle zehirli iğneler yaratmak ise hiçbir sorun teşkil etmez! Zekayı <%= int %> ve Sezgiyi <%= per %> puan arttırır. Sınırlı Sürüm 2018 Yaz Ekipmanı.",
|
||||
"weaponSpecialSummer2018HealerText": "Denizhalkı Hükümdarının Üç Dişli Mızrağı",
|
||||
"weaponSpecialSummer2018HealerNotes": "İyilikçi bir hamle ile şifalı suların ülkene dalga dalga akmasını sağlarsın. Zekayı <%= int %> puan arttırır. Sınırlı Sürüm 2018 Yaz Ekipmanı.",
|
||||
"weaponSpecialSummer2018HealerNotes": "İyilikçi bir hamle ile şifalı suların ülkene dаlga dalga akmasını sağlarsın. Zekayı <%= int %> puan arttırır. Sınırlı Sürüm 2018 Yaz Ekipmanı.",
|
||||
"weaponSpecialFall2018RogueText": "Duruluk Şişesi",
|
||||
"weaponSpecialFall2018RogueNotes": "Kendine gelmen gerektiğinde, doğru kararı vermek için itici bir güce ihtiyacın olduğunda derin bir nefes ve bir yudum al. Her şey yoluna girecek! Gücü <%= str %> artırır. Sınırlı Sürüm 2018 Sonbahar Eşyası.",
|
||||
"weaponSpecialFall2018WarriorText": "Minos'un Kırbacı",
|
||||
@@ -371,8 +371,8 @@
|
||||
"weaponArmoireNeedleOfBookbindingNotes": "Kitapların ne kadar sağlam olabildiklerini gördüğünde şaşıracaksın. Bu iğne ise işlerinin tam kalbine kadar delebilir. Gücü <%= str %> puan arttırır. Efsunlu Gardırop: Ciltçi Seti (4 Eşyadan 3'üncüsü).",
|
||||
"weaponArmoireSpearOfSpadesText": "Maça Mızrağı",
|
||||
"weaponArmoireSpearOfSpadesNotes": "Bu şövalye mızrağı en kırmızı Alışkanlıklarına ve Günlük İşlerine saldırmak için idealdir. Bünyeyi <%= con %> puan arttırır. Efsunlu Gardırop: Maça Ası Seti (3 Eşyadan 3'üncüsü).",
|
||||
"weaponArmoireArcaneScrollText": "Arcane Scroll",
|
||||
"weaponArmoireArcaneScrollNotes": "Bu antik Yapılacaklar Listesi, unutulmuş çağlara ait garip simgeler ve büyülerle dolduruldu. Zekayı <%= int %> artırır. Büyülü Gardrop: Yazman Seti (3 Eşyadan 3.sü)",
|
||||
"weaponArmoireArcaneScrollText": "Gizemli Parşömen",
|
||||
"weaponArmoireArcaneScrollNotes": "Bu antik Yapılacaklar Listesi, unutulmuş çağlara ait garip simgeler ve büyülerle dolduruldu. Zekayı <%= int %> artırır. Büyülü Gardrop: Yazman Seti (3 Eşyadan 3.sü).",
|
||||
"armor": "zırh",
|
||||
"armorCapitalized": "Zırh",
|
||||
"armorBase0Text": "Sade Giysi",
|
||||
@@ -452,7 +452,7 @@
|
||||
"armorSpecialTurkeyArmorBaseText": "Hindi Zırhı",
|
||||
"armorSpecialTurkeyArmorBaseNotes": "Butlarını bu tüylü zırhın içinde sıcak ve rahat tut! Bir fayda sağlamaz.",
|
||||
"armorSpecialTurkeyArmorGildedText": "Yaldızlı Hindi Zırhı",
|
||||
"armorSpecialTurkeyArmorGildedNotes": "Bu mevsimsel ve ışıltılı zırhın içinde salına salına yürü! Bir fayda sağlamaz.",
|
||||
"armorSpecialTurkeyArmorGildedNotes": "Bu mevsimsel ve ışıltılı zırhın içinde salınа salına yürü! Bir yarar sağlamaz.",
|
||||
"armorSpecialYetiText": "Yeti Terbiyecisi Cübbesi",
|
||||
"armorSpecialYetiNotes": "Kabarık ve vahşi. Bünyeyi <%= con %> puan arttırır. Sınırlı Sürüm 2013-2014 Kış Ekipmanı.",
|
||||
"armorSpecialSkiText": "Sui-kay-st Parkası",
|
||||
@@ -471,8 +471,8 @@
|
||||
"armorSpecialBirthday2017Notes": "İyi ki doğdun Habitica! Bu mükemmel günü kutlamak için bu Acayip Parti Cübbesi'ni giy. Bir fayda sağlamaz.",
|
||||
"armorSpecialBirthday2018Text": "Hayalci Parti Cübbesi",
|
||||
"armorSpecialBirthday2018Notes": "İyi ki doğdun Habitica! Bu mükemmel günü kutlamak için bu Hayalci Parti Cübbesi'ni giy. Bir fayda sağlamaz.",
|
||||
"armorSpecialBirthday2019Text": "Outlandish Party Robes",
|
||||
"armorSpecialBirthday2019Notes": "Happy Birthday, Habitica! Wear these Outlandish Party Robes to celebrate this wonderful day. Confers no benefit.",
|
||||
"armorSpecialBirthday2019Text": "Sıradışı Parti Giysileri",
|
||||
"armorSpecialBirthday2019Notes": "Doğum günün kutlu olsun Habitica! Bu görkemli günü kutlamak için bu sıradışı parti giysilerini giy. Hiçbir yararları yoktur.",
|
||||
"armorSpecialGaymerxText": "Gökkuşağı Savaşçısı Zırhı",
|
||||
"armorSpecialGaymerxNotes": "GaymerX konferansının şerefine tasarlanan bu zırh ışıltılı, rengarenk gökkuşağı desenleri ile bezenmiştir. GaymerX, LGBTQ'yu ve oyunculuğu kutlayan bir fuardır ve herkese açıktır.",
|
||||
"armorSpecialSpringRogueText": "Kaypak Kedi Kostümü",
|
||||
@@ -546,7 +546,7 @@
|
||||
"armorSpecialSpring2016MageText": "Görkemli Pisirbaz Kaftanı",
|
||||
"armorSpecialSpring2016MageNotes": "Parlak renkleri sayesinde kimse seni bir nekrofare sanmayacak. Zekayı <%= int %> puan arttırır. Sınırlı Sürüm 2016 Bahar Ekipmanı.",
|
||||
"armorSpecialSpring2016HealerText": "Kabarık Tavşan Pantolonu",
|
||||
"armorSpecialSpring2016HealerNotes": "Hoop-pa! Tepeden tepeye, zıplaya zıplaya giderek ihtiyacı olanlara şifa götürür. Bünyeyi <%= con %> puan arttırır. Sınırlı Sürüm 2016 Bahar Ekipmanı.",
|
||||
"armorSpecialSpring2016HealerNotes": "Hoop-pa! Tepeden tepeye, zıplayа zıplaya giderek ihtiyacı olanlara şifa götürür. Bünyeyi <%= con %> puan arttırır. Sınırlı Sürüm 2016 Bahar Ekipmanı.",
|
||||
"armorSpecialSummer2016RogueText": "Yılan Balığı Kuyruğu",
|
||||
"armorSpecialSummer2016RogueNotes": "Bu elektrikli kumaş, giyeni gerçek bir Yılan Balığı Düzenbazına çevirir! Sezgiyi <%= per %> puan arttırır. Sınırlı Sürüm 2016 Yaz Ekipmanı.",
|
||||
"armorSpecialSummer2016WarriorText": "Köpek Balığı Kuyruğu",
|
||||
@@ -628,11 +628,11 @@
|
||||
"armorSpecialFall2018HealerText": "Etoburluk Kaftanı",
|
||||
"armorSpecialFall2018HealerNotes": "Bitkilerden yapılmış olsa da bu vejetaryen olduğu anlamına gelmez. Kötü alışkanlıklar bu kaftanın kilometrelerce yakınına varmaya bile çekinirler. Bünyeyi <%= con %> puan arttırır. Sınırlı Sürüm 2018 Güz Ekipmanı.",
|
||||
"armorSpecialWinter2019RogueText": "Atatürk Çiçeği Zırhı",
|
||||
"armorSpecialWinter2019RogueNotes": "With holiday greenery all about, no one will notice an extra shrubbery! You can move through seasonal gatherings with ease and stealth. Increases Perception by <%= per %>. Limited Edition 2018-2019 Winter Gear.",
|
||||
"armorSpecialWinter2019RogueNotes": "Her yer bayram yeşillikleriyle kaplıyken, fazladan bir çalıyı kimse fark etmeyecek! Mevsimsel toplantılarda kolayca ve sessizce hareket edebilirsin. Sezgiyi <%= per %> artırır. Sınırlı Üretim 2018-2019 Kış Ekipmanı.",
|
||||
"armorSpecialWinter2019WarriorText": "Buzul Zırh",
|
||||
"armorSpecialWinter2019WarriorNotes": "In the heat of battle, this armor will keep you ice cool and ready for action. Increases Constitution by <%= con %>. Limited Edition 2018-2019 Winter Gear.",
|
||||
"armorSpecialWinter2019MageText": "Robes of Burning Inspiration",
|
||||
"armorSpecialWinter2019MageNotes": "This fireproof garb will help protect you if any of your flashes of brilliance should happen to backfire! Increases Intelligence by <%= int %>. Limited Edition 2018-2019 Winter Gear.",
|
||||
"armorSpecialWinter2019WarriorNotes": "Savaşın kızgınlığında, bu zırh seni buz gibi serin tutacak ve harekete hazır hale getirecek. Dayanıklılığı <%= con %> artırır. Sınırlı Üretim 2018-2019 Kış Ekipmanı.",
|
||||
"armorSpecialWinter2019MageText": "Esin Dolu Yanan Giysiler",
|
||||
"armorSpecialWinter2019MageNotes": "Bu yanmaz giysi, parlak düşüncelerinin ters tepmesi durumunda seni koruyacaktır! Zekayı <%= int %> artırır. Sınırlı Üretim 2018-2019 Kış Ekipmanı",
|
||||
"armorSpecialWinter2019HealerText": "Gece Yarısı Bornozu",
|
||||
"armorSpecialWinter2019HealerNotes": "Without darkness, there wouldn't be any light. These dark robes help bring peace and rest to promote healing. Increases Constitution by <%= con %>. Limited Edition 2018-2019 Winter Gear.",
|
||||
"armorMystery201402Text": "Haberci Kaftanı",
|
||||
|
||||
@@ -423,5 +423,22 @@
|
||||
"classLabel": "Sınıf:",
|
||||
"invitedToYourParty": "<strong> senin takımına davetli!</strong> Geri Almak İçin Tıkla",
|
||||
"noOneLooking": "Şu anda Takım arayan kimse yok. <br> Daha sonra tekrar bakabilirsin!",
|
||||
"tavernDiscontinuedDetail": "Oyuncu tabanımızın Habitica ile etkileşim biçimindeki değişiklikler de dahil olmak üzere bir dizi etken nedeniyle, bu alanları sürdürmek için gereken kaynaklar, bunlara katılan kişi sayısıyla orantısız hale geldi ve uzun vadede sürdürülemez oldu."
|
||||
"tavernDiscontinuedDetail": "Oyuncu tabanımızın Habitica ile etkileşim biçimindeki değişiklikler de dahil olmak üzere bir dizi etken nedeniyle, bu alanları sürdürmek için gereken kaynaklar, bunlara katılan kişi sayısıyla orantısız hale geldi ve uzun vadede sürdürülemez oldu.",
|
||||
"chooseAnOption": "Bir Seçenek Seç",
|
||||
"upgradeExistingGroup": "Var Olan Bir Grubu Yükselt",
|
||||
"createNewGroup": "Yeni Grup Oluştur",
|
||||
"yourParty": "Senin Partin",
|
||||
"previouslyUpgradedGroup": "Daha Önce Yükseltilmiş Grup",
|
||||
"inviteOthersForAdditional": "Ek ayrıcalıklar için başkalarını da grubuna çağır",
|
||||
"perMember": "üye başı",
|
||||
"additionalMembersProrated": "Ay içinde çağrılan ek üyeler, bir sonraki fatura döneminin toplamına orantılı bir ücret olarak eklenecek.",
|
||||
"oneMember": "1 üye",
|
||||
"membersCount": "<%= count %> üye",
|
||||
"pendingCount": "(<%= count %> beklemede)",
|
||||
"upgradeCancelsPendingInvites": "Partini yükseltmek, bekleyen tüm çağrıları iptal edecek",
|
||||
"interestedLearningMore": "Daha fazlasını öğrenmek ister misin?",
|
||||
"checkGroupPlanFAQ": "Paylaşımlı görev deneyiminizden en iyi biçimde nasıl yararlanacağınızı öğrenmek için <a href='/static/faq#what-is-group-plan'>Grup Planları SSS</a> bölümüne göz at.",
|
||||
"groupPlanBillingFYI": "Grup Planı abonelikleri, mevcut döneminizin bitiminden en az 24 saat önce iptal etmediğin sürece otomatik olarak yenilenir. İptal işlemini Grup Planınızın Grup Faturalandırma sekmesinden yapabilirsin. Aboneliğinin yenilenmesinden 24 saat önce, o anki Grup Planındaki üye sayısına göre ücretlendirileceksin. Ödeme dönemleri arasında üye eklersen, bir sonraki faturalama döneminde bu üyelerin ayrıcalıkları için ek bir orantılı ücret göreceksin.",
|
||||
"groupPlanBillingFYIShort": "Grup Planı abonelikleri, mevcut döneminin bitiminden en az 24 saat önce iptal etmediğin sürece otomatik olarak yenilenir. Aboneliğinin yenilenmesinden 24 saat önce, o anki Grup Planındaki üye sayısına göre ücretlendirileceksin. Ödeme dönemleri arasında üye eklersen, bir sonraki fatura döneminde bu üyelerin ayrıcalıkları için orantılı ek bir ücret göreceksin.",
|
||||
"chatSunsetWarning": "⚠️ <strong>Habitica Loncaları ve Meyhane sohbeti 8/8/2023 tarihinde kullanımdan kaldırılacak.</strong> Bu değişiklikle ilgili daha fazla bilgi edinmek için <a href='/static/faq/tavern-and-guilds'>buraya tıkla</a>."
|
||||
}
|
||||
|
||||
@@ -142,16 +142,16 @@
|
||||
"spring2019AmberMageSet": "Kehribar (Sihirbaz)",
|
||||
"royalPurpleJackolantern": "Soylu Mor Balkabağı Feneri",
|
||||
"gemSaleLimitationsText": "Bu promosyon yalnızca sınırlı süreli etkinlik boyunca geçerlidir. Bu etkinlik <%= eventStartMonth %> <%= eventStartOrdinal %> tarihinde <%= eventStartTime %> <%= timeZone %> saatinde başlayacak ve <%= eventEndMonth %> <%= eventEndOrdinal %> tarihinde <%= eventEndTime %> <%= timeZone %> saatinde sona erecek. Promosyon teklifi yalnızca kendin için Elmas satın aldığında geçerlidir.",
|
||||
"winter2025StringLightsHealerSet": "Süs Işıkları Şifacı Seti",
|
||||
"winter2025AuroraMageSet": "Kuzey Işıkları Büyücü Seti",
|
||||
"winter2025MooseWarriorSet": "Geyik Savaşçı Seti",
|
||||
"winter2025SnowRogueSet": "Karlı Düzenbaz Seti",
|
||||
"spring2025CrystalPointRogueSet": "Kristal Nokta Düzenbaz Seti",
|
||||
"spring2025SunshineWarriorSet": "Gün Işığı Savaşçı Seti",
|
||||
"spring2025PlumeriaHealerSet": "Plumeria Şifacı Seti",
|
||||
"spring2025MantisMageSet": "Mantis Büyücü Seti",
|
||||
"winter2025StringLightsHealerSet": "Süs Işıkları Seti (Şifacı)",
|
||||
"winter2025AuroraMageSet": "Kuzey Işıkları Seti (Büyücü)",
|
||||
"winter2025MooseWarriorSet": "Geyik Seti (Savaşçı)",
|
||||
"winter2025SnowRogueSet": "Karlı Set (Düzenbaz",
|
||||
"spring2025CrystalPointRogueSet": "Kristal Parçası Seti (Düzenbaz)",
|
||||
"spring2025SunshineWarriorSet": "Gün Işığı Seti (Savaşçı)",
|
||||
"spring2025PlumeriaHealerSet": "Plumeria Seti (Şifacı)",
|
||||
"spring2025MantisMageSet": "Peygamberdevesi Seti (Büyücü)",
|
||||
"fall2024BlackCatRogueSet": "Kara Kedi Seti (Düzenbaz)",
|
||||
"summer2025ScallopWarriorSet": "Deniz Tarağı Savaşçısı Seti",
|
||||
"summer2025ScallopWarriorSet": "Deniz Tarağı Seti (Savaşçı)",
|
||||
"fall2019LichSet": "Lich (Şifacı)",
|
||||
"fall2019CyclopsSet": "Tepegöz (Büyücü)",
|
||||
"spring2019RobinHealerSet": "Robin (Şifacı)",
|
||||
@@ -171,18 +171,18 @@
|
||||
"summer2024SeaSnailHealerSet": "Deniz Salyangozu Seti (Şifacı)",
|
||||
"summer2024SeaAnemoneMageSet": "Denizşakayığı Seti (Büyücü)",
|
||||
"summer2024WhaleSharkWarriorSet": "Balina Köpekbalığı Seti (Savaşçı)",
|
||||
"fall2025KoboldHealerSet": "Kobold Şifacı Seti",
|
||||
"fall2025SkeletonRogueSet": "İskelet Düzenbaz Seti",
|
||||
"fall2025SasquatchWarriorSet": "Kocaayak Savaşçı Seti",
|
||||
"fall2025MaskedGhostMageSet": "Maskeli Hayalet Büyücü Seti",
|
||||
"fall2025KoboldHealerSet": "Kobold Seti (Şifacı)",
|
||||
"fall2025SkeletonRogueSet": "İskelet Seti (Düzenbaz)",
|
||||
"fall2025SasquatchWarriorSet": "Kocaayak Seti (Savaşçı)",
|
||||
"fall2025MaskedGhostMageSet": "Maskeli Hayalet Seti (Büyücü)",
|
||||
"fall2024SpaceInvaderHealerSet": "Uzay İstilacısı Seti (Şifacı)",
|
||||
"fall2024UnderworldSorcerorMageSet": "Yeraltı Büyücüsü Seti (Büyücü)",
|
||||
"fall2024FieryImpWarriorSet": "Alevli Cin Seti (Savaşçı)",
|
||||
"summer2025FairyWrasseMageSet": "Peri Levreği Büyücü Seti",
|
||||
"summer2025SeaAngelHealerSet": "Deniz Meleği Şifacı Seti",
|
||||
"summer2025SquidRogueSet": "Kalamar Düzenbaz Seti",
|
||||
"winter2026SkiRogueSet": "Kayak Düzenbaz Seti",
|
||||
"winter2026RimeReaperWarriorSet": "Kırağı Biçer Savaşçı Seti",
|
||||
"summer2025FairyWrasseMageSet": "Peri Levreği Seti (Büyücü)",
|
||||
"summer2025SeaAngelHealerSet": "Deniz Meleği Seti (Şifacı)",
|
||||
"summer2025SquidRogueSet": "Kalamar Seti (Düzenbaz)",
|
||||
"winter2026SkiRogueSet": "Kayak Seti (Düzenbaz)",
|
||||
"winter2026RimeReaperWarriorSet": "Kırağı Biçer Seti (Savaşçı)",
|
||||
"winter2020LanternSet": "Fener (Düzenbaz)",
|
||||
"spring2020BeetleWarriorSet": "Gergedan Böceği (Savaşçı)",
|
||||
"winter2020WinterSpiceSet": "Kış Baharatı (Şifacı)",
|
||||
@@ -208,8 +208,8 @@
|
||||
"spring2023LilyHealerSet": "Zambak (Şifacı)",
|
||||
"anniversaryLimitedDates": "30 Ocak - 8 Şubat",
|
||||
"jubilantGryphatricePromo": "Sevinçli Gripatris Hareketli Evcil Hayvanı",
|
||||
"winter2026PolarBearHealerSet": "Kutup Ayısı Şifacı Seti",
|
||||
"winter2026MidwinterCandleMageSet": "Kış Ortası Mumu Büyücü Seti",
|
||||
"winter2026PolarBearHealerSet": "Kutup Ayısı Seti (Şifacı)",
|
||||
"winter2026MidwinterCandleMageSet": "Kış Ortası Mumu Seti (Büyücü)",
|
||||
"fourForFree": "Dört tanesi ücretsiz",
|
||||
"anniversaryGryphatriceText": "Nadir bulunan Sevinçli Grifatris doğum günü kutlamalarına katılıyor! Bu hareketli evcil hayvana sahip olma şansını kaçırma.",
|
||||
"fall2020TwoHeadedRogueSet": "Çift Baş (Düzenbaz)",
|
||||
@@ -286,5 +286,9 @@
|
||||
"dayOne": "1. Gün",
|
||||
"dayFive": "5. Gün",
|
||||
"dayTen": "10. Gün",
|
||||
"partyRobes": "Parti Giysileri"
|
||||
"partyRobes": "Parti Giysileri",
|
||||
"spring2026FrogWarriorSet": "Kurbağa Seti (Savaşçı)",
|
||||
"spring2026BranchRogueSet": "Bahar Dalı Seti (Düzenbaz)",
|
||||
"spring2026SnowdropHealerSet": "Kar Düşüşü Seti (Şifacı)",
|
||||
"spring2026MaypoleMageSet": "Bahar Bayramı Seti (Büyücü)"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rebirthNew": "Переродження: Нова пригода чекає на вас!",
|
||||
"rebirthUnlock": "Ви розблокували Переродження! Цей особливий предмет дозволить вам розпочати нову гру з 1-го рівня, зберігши ваші завдання, досягнення, улюбленців тощо. Використайте його, щоб вдихнути нове життя у Habitica, якщо ви відчуваєте, що досягли всього, або хочете пройти через нові пригоди з новими враженнями, наче початківець!",
|
||||
"rebirthAchievement": "Ви розпочали нову пригоду! Це ваше <%= number %> переродження, і найвищий рівень, якого ви досягали - <%= level %>. Аби отримати це досягнення ще раз, почніть вашу нову пригоду після того, як досягнете ще вищого рівня!",
|
||||
"rebirthAchievement100": "Ви розпочали нову пригоду! Це ваше <%= number %> переродження, і найвищий рівень, який ви отримали - 100 чи більше. Аби отримати це досягнення ще раз, почніть вашу нову пригоду після того, як отримаєте щонайменше рівень 100!",
|
||||
"rebirthAchievement": "Ви використовували Кулю Переродження <strong><%= number %></strong> разів, а ваш найвищий досягнутий рівень — <strong><%= level %></strong>.",
|
||||
"rebirthAchievement100": "Ви використовували Кулю Переродження <strong><%= number %></strong> разів, а ваш найвищий досягнутий рівень — <strong>100</strong> або вище.",
|
||||
"rebirthBegan": "Нова пригода починається",
|
||||
"rebirthText": "Розпочато <%= rebirths %> нові(их) пригоди",
|
||||
"rebirthOrb": "Після досягнення <%= level %> рівня використано Кулю Переродження, щоб розпочати заново.",
|
||||
@@ -11,5 +11,12 @@
|
||||
"rebirthPop": "Миттєво переродить вашого персонажа як воїна 1-го рівня, зберігши ваші досягнення, предмети колекціонування та спорядження. Ваші завдання та їхня історія залишаться, проте вони будуть скинуті до жовтого кольору. Ваші серії будуть видалені, окрім завдань з активних Випробувань та Спільнот. Ваше золото, досвід, мана та ефекти всіх навичок буде видалено. Все вищеперераховане буде застосовано негайно.",
|
||||
"rebirthName": "Куля Переродження",
|
||||
"rebirthComplete": "Ви переродилися!",
|
||||
"nextFreeRebirth": "<strong><%= days %> дн.</strong> до <strong>БЕЗКОШТОВНОЇ</strong> Кулі Переродження"
|
||||
"nextFreeRebirth": "<strong><%= days %> дн.</strong> до <strong>БЕЗКОШТОВНОЇ</strong> Кулі Переродження",
|
||||
"rebirthUnlockedOrb": "Нова пригода доступна!",
|
||||
"rebirthNewAchievement": "Нове Досягнення",
|
||||
"rebirthNewAdventure": "Нова пригода починається зараз!",
|
||||
"rebirthAchievementPlural": "Ви використовували Кулю Переродження <strong><%= number %></strong> разів, а ваш найвищий досягнутий рівень — <strong><%= level %></strong>.",
|
||||
"rebirthStackInfo": "Це досягнення буде накопичуватися щоразу, коли ви використовуєте Кулю Переродження.",
|
||||
"rebirthUnlockedNewItem": "Куля Переродження Розблокована",
|
||||
"rebirthUnlockedDesc": "Використовуйте Кулю Переродження, щоб вдихнути нове життя у свої пригоди в Habitica, коли відчуєте, що досягли всього! Почніть заново з 1-го рівня, зберігши свої завдання, досягнення та улюбленців за допомогою цього особливого предмета, який можна знайти на Ринку."
|
||||
}
|
||||
|
||||
@@ -257,5 +257,21 @@
|
||||
"contentRelease": "Релізи контенту + Події",
|
||||
"resetTextLocal": "Якщо ви абсолютно впевнені, введіть свій пароль у полі нижче.",
|
||||
"resetTextSocial": "Якщо ви абсолютно впевнені, введіть <b>\"<%= magicWord %>\"</b> у полі нижче.",
|
||||
"transaction_subscription_bonus": "Бонус <b>Підписки</b>"
|
||||
"transaction_subscription_bonus": "Бонус <b>Підписки</b>",
|
||||
"acceptAllCookies": "Прийняти всі файли cookie",
|
||||
"denyNonEssentialCookies": "Заборонити несуттєві файли cookie",
|
||||
"managePrivacyPreferences": "Керуйте налаштуваннями конфіденційності",
|
||||
"yourPrivacyPreferences": "Ваші налаштування конфіденційності",
|
||||
"learnMorePrivacy": "Щоб дізнатися більше, перегляньте нашу <a href='/static/privacy' target='_blank'>Політику конфіденційності</a>.",
|
||||
"strictlyNecessary": "Строго необхідні",
|
||||
"alwaysActive": "Завжди активні",
|
||||
"requiredToRun": "Вони потрібні нашому веб-сайту та додаткам для найкращої роботи.",
|
||||
"performanceAnalytics": "Ефективність і аналітика",
|
||||
"savePreferences": "Зберегти налаштування",
|
||||
"habiticaPrivacyPolicy": "Політика конфіденційності Habitica",
|
||||
"privacyOverview": "У сучасному світі здається, ніби кожна компанія прагне отримати прибуток з ваших даних. Через це буває важко знайти правильний додаток для вдосконалення своїх звичок. Habitica використовує файли cookie, які зберігають дані виключно для аналізу продуктивності, обробки запитів у службу підтримки та забезпечення найкращого ігрового досвіду. Ви можете змінити ці налаштування в будь-який час у налаштуваннях вашого акаунта.",
|
||||
"privacySettingsOverview": "Habitica використовує файли cookie для аналізу продуктивності, обробки запитів у службу підтримки та забезпечення найкращого ігрового досвіду. Для цього нам потрібно отримати наступні дозволи. Ви можете змінити їх у будь-який час у налаштуваннях вашого акаунта.",
|
||||
"usedForSupport": "Вони використовуються для покращення взаємодії з користувачем, продуктивності та послуг нашого веб-сайту та програм. Ці дані використовуються нашою командою підтримки під час обробки запитів і звітів про помилки.",
|
||||
"gpcWarning": "<a href='<%= url %>' target='_blank'>GPC</a> увімкнено. Увімкнення відстеження нижче скасує цей параметр і надішле дані нашим аналітичним партнерам.",
|
||||
"gpcPlusAnalytics": "<a href='<%= url %>' target='_blank'>GPC</a> увімкнено. Ви надали згоду на відстеження та надсилання даних нашим аналітичним партнерам."
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rebirthNew": "重生:开始新的冒险!",
|
||||
"rebirthUnlock": "你已经解锁了重生之球! 这个特殊的市场商品可使你从 1 级开始新的游戏,同时保留你的任务、成就、宠物等。 如果你觉得自己已经达成了所有目标,或者想要以新手的视角体验新功能,请使用它为Habitica注入新的活力!",
|
||||
"rebirthAchievement": "你开始了新的冒险! 这是你的第<%= number %>次重生,你达到的最高等级是<%= level %>。 要叠加此成就,请在达到更高等级时开始下一次新的冒险!",
|
||||
"rebirthAchievement100": "你开始了新的冒险! 这是你最高等级达到100级以上的第<%= number %>次重生。要叠加此成就,请在达到至少 100 级时开始下次新的冒险!",
|
||||
"rebirthAchievement": "这是你的第<strong><%= number %></strong>次使用重生之球,你达到的最高等级是<strong><%= level %></strong>。",
|
||||
"rebirthAchievement100": "这是你的第<strong><%= number %></strong>次使用重生之球,你达到的最高等级是<strong>100</strong> 或更高。",
|
||||
"rebirthBegan": "已开始新的冒险",
|
||||
"rebirthText": "已开始<%= rebirths %>次新的冒险",
|
||||
"rebirthOrb": "在达到<%= level %>级后,使用了重生之球重新开始。",
|
||||
@@ -11,5 +11,12 @@
|
||||
"rebirthPop": "立即将你的角色重置成为1级战士,同时保留成就、物品和装备。你的任务及其历史记录会保留,但会被重置为黄色;你的连击次数将被移除,挑战任务和团队计划任务除外;你的金币、经验值以及法力值和及所有技能的效果都将被移除。上述内容将立即生效。",
|
||||
"rebirthName": "重生之球",
|
||||
"rebirthComplete": "你已经重生!",
|
||||
"nextFreeRebirth": "下个<strong>免费</strong>的重生之球可在<strong><%= days %>天</strong>后获得"
|
||||
"nextFreeRebirth": "下个<strong>免费</strong>的重生之球可在<strong><%= days %>天</strong>后获得",
|
||||
"rebirthUnlockedNewItem": "已解锁重生之球",
|
||||
"rebirthUnlockedOrb": "全新冒险现已开启!",
|
||||
"rebirthUnlockedDesc": "当你感觉已征服Habitica的全部内容时,不妨使用重生之球为你的冒险注入新活力!通过在市场获取的这个特殊道具,你将重启旅程回到1级,同时保留所有任务、成就和宠物。",
|
||||
"rebirthNewAchievement": "新成就",
|
||||
"rebirthNewAdventure": "新的冒险现在开始!",
|
||||
"rebirthAchievementPlural": "这是你的第<strong><%= number %></strong>次使用重生之球,你达到的最高等级是<strong><%= level %></strong>。",
|
||||
"rebirthStackInfo": "每使用一次重生之球,本成就就会叠加一次。"
|
||||
}
|
||||
|
||||
@@ -274,5 +274,8 @@
|
||||
"subscriptionBillingFYI": "本订阅服务将自动续期。若您未在当前计费周期结束前至少24小时前取消订阅,则自动续订。您可在设置中的\"订阅\"选项卡中管理您的订阅。在续订日期起24小时内,系统将按照您最初支付的价格完成扣费。",
|
||||
"mysterySet202512": "饼干冠军套装",
|
||||
"mysterySet202601": "冬之神盾套装",
|
||||
"mysterySet202602": "樱之狐套装"
|
||||
"mysterySet202602": "樱之狐套装",
|
||||
"mysterySet202603": "紫藤魔法师套装",
|
||||
"mysterySet202605": "暮光云环套装",
|
||||
"mysterySet202604": "无畏宇航员套装"
|
||||
}
|
||||
|
||||
@@ -696,6 +696,9 @@ const backgrounds = {
|
||||
birthday_bash: {
|
||||
price: 0,
|
||||
},
|
||||
on_a_strange_planet: {
|
||||
price: 0,
|
||||
},
|
||||
},
|
||||
timeTravelBackgrounds: {
|
||||
airship: {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import eggs from '../eggs';
|
||||
import stable from '../stable';
|
||||
|
||||
const SWAPS = [
|
||||
'Veggie',
|
||||
'Dessert',
|
||||
'VirtualPet',
|
||||
'TeaShop',
|
||||
'Fungi',
|
||||
'Cryptid',
|
||||
'Alien',
|
||||
];
|
||||
|
||||
export function getMatchingSwap (date = new Date()) {
|
||||
const year = date.getFullYear();
|
||||
const diff = year - 2020;
|
||||
return SWAPS[diff % SWAPS.length];
|
||||
}
|
||||
|
||||
export function makeSubstitutionMap (swappedPotion) {
|
||||
const substitutions = {
|
||||
pets: {
|
||||
default: `Pet-Dragon-${swappedPotion}`,
|
||||
noPet: `Pet-Wolf-${swappedPotion}`,
|
||||
noPetIOS: `Pet-TigerCub-${swappedPotion}`,
|
||||
noPetAndroid: `Pet-Cactus-${swappedPotion}`,
|
||||
},
|
||||
};
|
||||
for (const pet of Object.keys(stable.specialPets)) {
|
||||
substitutions.pets[`Pet-${pet}`] = `Pet-Dragon-${swappedPotion}`;
|
||||
}
|
||||
for (const egg of Object.keys(eggs.drops)) {
|
||||
substitutions.pets[`Pet-${egg}-`] = `Pet-${egg}-${swappedPotion}`;
|
||||
}
|
||||
for (const egg of Object.keys(eggs.quests)) {
|
||||
substitutions.pets[`Pet-${egg}-`] = `Pet-BearCub-${swappedPotion}`;
|
||||
}
|
||||
return substitutions;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable key-spacing */
|
||||
import moment from 'moment';
|
||||
import { getMatchingSwap, makeSubstitutionMap } from './aprilFools';
|
||||
|
||||
// gem block: number of gems
|
||||
const gemsPromo = {
|
||||
@@ -53,7 +54,7 @@ export const REPEATING_EVENTS = {
|
||||
aprilFools: {
|
||||
start: new Date('1970-04-01T04:00-04:00'),
|
||||
end: new Date('1970-04-02T03:59-04:00'),
|
||||
aprilFools: 'Cryptid',
|
||||
spriteSubstitutions: makeSubstitutionMap(getMatchingSwap()),
|
||||
},
|
||||
aprilFoolsResale: {
|
||||
start: new Date('1970-04-03T04:00-04:00'),
|
||||
@@ -65,6 +66,7 @@ export const REPEATING_EVENTS = {
|
||||
'virtualpet',
|
||||
'waffle',
|
||||
'fungi',
|
||||
'alien',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ export default [
|
||||
'Mount_Body_Gryphon-Gryphatrice',
|
||||
'Mount_Head_Dragon-Hydra',
|
||||
'Mount_Head_Gryphon-Gryphatrice',
|
||||
'Pet_HatchingPotion_Alien',
|
||||
'Pet_HatchingPotion_Cryptid',
|
||||
'Pet_HatchingPotion_Dessert',
|
||||
'Pet_HatchingPotion_Fungi',
|
||||
@@ -24,6 +25,8 @@ export default [
|
||||
'Pet-Gryphatrice-Jubilant',
|
||||
'Pet-Gryphon-Gryphatrice',
|
||||
'Pet-Wolf-Cerberus',
|
||||
'quest_alien',
|
||||
'quest_lostMasterclasser4',
|
||||
'quest_solarSystem',
|
||||
'quest_virtualpet',
|
||||
'quest_windup',
|
||||
|
||||
@@ -53,4 +53,5 @@ export const HATCHING_POTIONS_RELEASE_DATES = {
|
||||
Cryptid: { year: 2025, month: 4, day: 3 },
|
||||
Balloon: { year: 2025, month: 4, day: 21 },
|
||||
Opal: { year: 2025, month: 5, day: 14 },
|
||||
Alien: { year: 2026, month: 4, day: 3 },
|
||||
};
|
||||
|
||||
@@ -155,6 +155,10 @@ const wacky = {
|
||||
canBuy: hasQuestAchievementFunction('fungi'),
|
||||
},
|
||||
Cryptid: {},
|
||||
Alien: {
|
||||
questPotion: true,
|
||||
canBuy: hasQuestAchievementFunction('alien'),
|
||||
},
|
||||
};
|
||||
|
||||
each(drops, (pot, key) => {
|
||||
|
||||
@@ -233,6 +233,45 @@ const QUEST_SEASONAL = {
|
||||
unlock: t('questFungiUnlockText'),
|
||||
},
|
||||
},
|
||||
alien: {
|
||||
text: t('questAlienText'),
|
||||
notes: t('questAlienNotes'),
|
||||
completion: t('questAlienCompletion'),
|
||||
value: 4,
|
||||
category: 'hatchingPotion',
|
||||
boss: {
|
||||
name: t('questAlienBoss'),
|
||||
hp: 500,
|
||||
str: 2,
|
||||
rage: {
|
||||
title: t('questAlienRageTitle'),
|
||||
description: t('questAlienRageDescription'),
|
||||
value: 50,
|
||||
healing: 0.3,
|
||||
effect: t('questAlienRageEffect'),
|
||||
},
|
||||
},
|
||||
drop: {
|
||||
items: [
|
||||
{
|
||||
type: 'hatchingPotions',
|
||||
key: 'Alien',
|
||||
text: t('questAlienDropAlienPotion'),
|
||||
}, {
|
||||
type: 'hatchingPotions',
|
||||
key: 'Alien',
|
||||
text: t('questAlienDropAlienPotion'),
|
||||
}, {
|
||||
type: 'hatchingPotions',
|
||||
key: 'Alien',
|
||||
text: t('questAlienDropAlienPotion'),
|
||||
},
|
||||
],
|
||||
gp: 40,
|
||||
exp: 500,
|
||||
unlock: t('questAlienUnlockText'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default QUEST_SEASONAL;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import each from 'lodash/each';
|
||||
import moment from 'moment';
|
||||
import { EVENTS } from './constants/events';
|
||||
import allEggs from './eggs';
|
||||
import allPotions from './hatching-potions';
|
||||
import t from './translation';
|
||||
@@ -193,7 +191,7 @@ function buildInfo () {
|
||||
|
||||
Object.assign(petInfo['Gryphatrice-Jubilant'], {
|
||||
canBuy () {
|
||||
return moment().isBetween(EVENTS.birthday10.start, EVENTS.birthday10.end);
|
||||
return false;
|
||||
},
|
||||
currency: 'gems',
|
||||
event: 'birthday10',
|
||||
|
||||
@@ -25,7 +25,8 @@ export default function cleanupPinnedItems (user) {
|
||||
if (type === 'background') {
|
||||
return matchers.backgrounds.match(pinnedItem.path.split('.')[1]);
|
||||
} if (type === 'premiumHatchingPotion') {
|
||||
return matchers.premiumHatchingPotions.match(key);
|
||||
const potion = getItemByPathAndType(type, pinnedItem.path);
|
||||
return potion.questPotion || matchers.premiumHatchingPotions.match(key);
|
||||
} if (type === 'mystery_set') {
|
||||
return matchers.timeTravelers.match(key);
|
||||
} if (type === 'seasonalQuest') {
|
||||
|
||||
@@ -2,7 +2,6 @@ import each from 'lodash/each';
|
||||
import pick from 'lodash/pick';
|
||||
import i18n from '../i18n';
|
||||
import { capByLevel } from '../statHelpers';
|
||||
import { MAX_LEVEL } from '../constants';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
@@ -103,9 +102,9 @@ export default async function rebirth (user, tasks = [], req = {}, analytics) {
|
||||
if (!user.achievements.rebirths) {
|
||||
user.achievements.rebirths = 1;
|
||||
user.achievements.rebirthLevel = lvl;
|
||||
} else if (lvl > user.achievements.rebirthLevel || lvl === MAX_LEVEL) {
|
||||
} else {
|
||||
user.achievements.rebirths += 1;
|
||||
user.achievements.rebirthLevel = lvl;
|
||||
user.achievements.rebirthLevel = Math.max(user.achievements.rebirthLevel || 0, lvl);
|
||||
}
|
||||
|
||||
if (!notFree) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import forEach from 'lodash/forEach';
|
||||
import isFunction from 'lodash/isFunction';
|
||||
import pick from 'lodash/pick';
|
||||
import nconf from 'nconf';
|
||||
import get from 'lodash/get';
|
||||
import { authWithHeaders } from '../../middlewares/auth';
|
||||
@@ -10,10 +9,6 @@ import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../../libs/errors';
|
||||
import {
|
||||
basicFields as basicGroupFields,
|
||||
model as Group,
|
||||
} from '../../models/group';
|
||||
import * as Tasks from '../../models/task';
|
||||
import * as passwordUtils from '../../libs/password';
|
||||
import {
|
||||
@@ -23,6 +18,7 @@ import {
|
||||
getUserInfo,
|
||||
sendTxn,
|
||||
} from '../../libs/email';
|
||||
import worker from '../../libs/worker';
|
||||
import * as inboxLib from '../../libs/inbox';
|
||||
import * as userLib from '../../libs/user';
|
||||
import { model as UserHistory } from '../../models/userHistory';
|
||||
@@ -299,21 +295,6 @@ api.deleteUser = {
|
||||
throw new NotAuthorized(res.t('cannotDeleteActiveAccount'));
|
||||
}
|
||||
|
||||
const types = ['party', 'guilds'];
|
||||
const groupFields = basicGroupFields.concat(' leader memberCount purchased');
|
||||
|
||||
const groupsUserIsMemberOf = await Group.getGroups({ user, types, groupFields });
|
||||
|
||||
const groupLeavePromises = groupsUserIsMemberOf.map(group => group.leave(user, 'remove-all'));
|
||||
|
||||
await Promise.all(groupLeavePromises);
|
||||
|
||||
await Tasks.Task.deleteMany({
|
||||
userId: user._id,
|
||||
}).exec();
|
||||
|
||||
await user.deleteOne();
|
||||
|
||||
if (feedback) {
|
||||
sendTxn({ email: TECH_ASSISTANCE_EMAIL }, 'admin-feedback', [
|
||||
{ name: 'PROFILE_NAME', content: user.profile.name },
|
||||
@@ -325,11 +306,13 @@ api.deleteUser = {
|
||||
]);
|
||||
}
|
||||
|
||||
res.analytics.track('account delete', {
|
||||
user: pick(user, ['preferences', 'registeredThrough']),
|
||||
uuid: user._id,
|
||||
hitType: 'event',
|
||||
category: 'behavior',
|
||||
worker.sendJob('deleteUser', {
|
||||
identifier: user._id,
|
||||
data: {
|
||||
userId: user._id,
|
||||
deleteAccount: true,
|
||||
deleteAmplitude: true,
|
||||
},
|
||||
});
|
||||
|
||||
res.respond(200, {});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { sendJob } from '../../libs/worker';
|
||||
import worker from '../../libs/worker';
|
||||
import { authWithHeaders } from '../../middlewares/auth';
|
||||
import { ensurePermission } from '../../middlewares/ensureAccessRight';
|
||||
import { TransactionModel as Transaction } from '../../models/transaction';
|
||||
@@ -48,7 +48,8 @@ api.deleteMember = {
|
||||
req.checkQuery('deleteAmplitude').optional().isIn(['true', 'false']);
|
||||
const validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
sendJob('delete-user', {
|
||||
await worker.sendJob('deleteUser', {
|
||||
identifier: req.params.memberId,
|
||||
data: {
|
||||
userId: req.params.memberId,
|
||||
deleteAccount: req.query.deleteAccount === 'true',
|
||||
|
||||
@@ -24,7 +24,10 @@ api.getReady = {
|
||||
middlewares: [disableCache],
|
||||
async handler (req, res) {
|
||||
// This allows kubernetes to determine if the server is ready to receive traffic
|
||||
if (!SERVER_STATUS.MONGODB || !SERVER_STATUS.REDIS || !SERVER_STATUS.EXPRESS) {
|
||||
if (!SERVER_STATUS.MONGODB
|
||||
|| !SERVER_STATUS.RATE_LIMITER
|
||||
|| !SERVER_STATUS.WORKER
|
||||
|| !SERVER_STATUS.EXPRESS) {
|
||||
res.respond(503, {
|
||||
status: 'not ready',
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import nconf from 'nconf';
|
||||
import { TAVERN_ID } from '../models/group'; // eslint-disable-line import/no-cycle
|
||||
import { encrypt } from './encryption';
|
||||
import common from '../../common';
|
||||
import { sendJob } from './worker';
|
||||
import worker from './worker';
|
||||
|
||||
const IS_PROD = nconf.get('IS_PROD');
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
@@ -148,7 +148,8 @@ export async function sendTxn (mailingInfoArray, emailType, variables, personalV
|
||||
}
|
||||
|
||||
if (IS_PROD && mailingInfoArray.length > 0) {
|
||||
return sendJob('email', {
|
||||
return worker.sendJob('email', {
|
||||
identifier: emailType,
|
||||
data: {
|
||||
emailType,
|
||||
to: mailingInfoArray,
|
||||
|
||||
@@ -1,26 +1,46 @@
|
||||
import {
|
||||
createCipheriv,
|
||||
createDecipheriv,
|
||||
randomBytes,
|
||||
} from 'crypto';
|
||||
import nconf from 'nconf';
|
||||
|
||||
const algorithm = 'aes-256-ctr';
|
||||
const ALGORITHM = 'aes-256-gcm';
|
||||
const IV_LENGTH_BYTES = 12; // 96-bit nonce per NIST guidance for GCM
|
||||
const AUTH_TAG_LENGTH_BYTES = 16; // 128-bit authentication tag
|
||||
const SESSION_SECRET_KEY = nconf.get('SESSION_SECRET_KEY');
|
||||
const SESSION_SECRET_IV = nconf.get('SESSION_SECRET_IV');
|
||||
|
||||
const key = Buffer.from(SESSION_SECRET_KEY, 'hex');
|
||||
const iv = Buffer.from(SESSION_SECRET_IV, 'hex');
|
||||
|
||||
/**
|
||||
* Encrypt a UTF-8 string using AES-256-GCM and return iv|ciphertext|tag as hex.
|
||||
* A fresh nonce is generated for every message to avoid keystream reuse, and
|
||||
* the auth tag ensures forged payloads are rejected at the trust boundary.
|
||||
*/
|
||||
export function encrypt (text) {
|
||||
const cipher = createCipheriv(algorithm, key, iv);
|
||||
let crypted = cipher.update(text, 'utf8', 'hex');
|
||||
crypted += cipher.final('hex');
|
||||
return crypted;
|
||||
const iv = randomBytes(IV_LENGTH_BYTES);
|
||||
const cipher = createCipheriv(ALGORITHM, key, iv);
|
||||
const ciphertext = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
|
||||
const authTag = cipher.getAuthTag();
|
||||
return Buffer.concat([iv, ciphertext, authTag]).toString('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt an AES-256-GCM payload previously produced by encrypt().
|
||||
* The layout is iv (12B) || ciphertext || authTag (16B), all hex encoded.
|
||||
*/
|
||||
export function decrypt (text) {
|
||||
const decipher = createDecipheriv(algorithm, key, iv);
|
||||
let dec = decipher.update(text, 'hex', 'utf8');
|
||||
dec += decipher.final('utf8');
|
||||
return dec;
|
||||
const payload = Buffer.from(text, 'hex');
|
||||
if (payload.length <= IV_LENGTH_BYTES + AUTH_TAG_LENGTH_BYTES) {
|
||||
throw new Error('Encrypted payload is malformed');
|
||||
}
|
||||
|
||||
const iv = payload.subarray(0, IV_LENGTH_BYTES);
|
||||
const authTag = payload.subarray(payload.length - AUTH_TAG_LENGTH_BYTES);
|
||||
const ciphertext = payload.subarray(IV_LENGTH_BYTES, payload.length - AUTH_TAG_LENGTH_BYTES);
|
||||
|
||||
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
||||
decipher.setAuthTag(authTag);
|
||||
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
||||
return decrypted.toString('utf8');
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ async function inviteByEmail (invite, group, inviter, req, res) {
|
||||
];
|
||||
|
||||
if (group.type === 'guild') {
|
||||
variables.push({ name: 'GUILD_NAME', content: group.name });
|
||||
variables.push({ name: 'GROUP_NAME', content: group.name });
|
||||
}
|
||||
|
||||
// Check for the email address not to be unsubscribed
|
||||
|
||||
@@ -316,6 +316,7 @@ api.subscribe = async function subscribe (options) {
|
||||
const group = await Group.getGroup({
|
||||
user, groupId, populateLeader: false, groupFields,
|
||||
});
|
||||
if (!group) throw new NotFound(i18n.t('groupNotFound'));
|
||||
const membersCount = await group.getMemberCount();
|
||||
amount = sub.price + (membersCount - leaderCount) * priceOfSingleMember;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import IORedis from 'ioredis';
|
||||
|
||||
export default function setupRedis (connectionOptions, config) {
|
||||
const redisConfig = { ...config };
|
||||
if (connectionOptions.username) {
|
||||
redisConfig.username = connectionOptions.username;
|
||||
}
|
||||
if (connectionOptions.password) {
|
||||
redisConfig.password = connectionOptions.password;
|
||||
}
|
||||
if (connectionOptions.db) {
|
||||
redisConfig.db = connectionOptions.db;
|
||||
}
|
||||
let connection;
|
||||
const redisUrl = connectionOptions.url;
|
||||
if (redisUrl) {
|
||||
connection = new IORedis(redisUrl, redisConfig);
|
||||
} else {
|
||||
connection = new IORedis(connectionOptions.port, connectionOptions.host, redisConfig);
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
const SERVER_STATUS = {
|
||||
MONGODB: false,
|
||||
REDIS: false,
|
||||
RATE_LIMITER: false,
|
||||
WORKER: false,
|
||||
EXPRESS: false,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,33 +1,49 @@
|
||||
import got from 'got';
|
||||
import nconf from 'nconf';
|
||||
import logger from './logger';
|
||||
import { Queue } from 'bullmq';
|
||||
import setupRedis from './redis';
|
||||
import SERVER_STATUS from './serverStatus';
|
||||
|
||||
const EMAIL_SERVER = {
|
||||
url: nconf.get('EMAIL_SERVER_URL'),
|
||||
auth: {
|
||||
user: nconf.get('EMAIL_SERVER_AUTH_USER'),
|
||||
password: nconf.get('EMAIL_SERVER_AUTH_PASSWORD'),
|
||||
},
|
||||
};
|
||||
let redisClient;
|
||||
const queues = {};
|
||||
|
||||
export function sendJob (type, config) {
|
||||
const { data, options } = config;
|
||||
const usedOptions = {
|
||||
backoff: { delay: 10 * 60 * 1000, type: 'exponential' },
|
||||
...options,
|
||||
if (nconf.get('WORKER_REDIS_URL')) {
|
||||
redisClient = setupRedis({
|
||||
url: nconf.get('WORKER_REDIS_URL'),
|
||||
username: nconf.get('WORKER_REDIS_USERNAME'),
|
||||
password: nconf.get('WORKER_REDIS_PASSWORD'),
|
||||
});
|
||||
|
||||
redisClient.on('ready', () => {
|
||||
SERVER_STATUS.WORKER = true;
|
||||
});
|
||||
|
||||
redisClient.on('reconnecting', () => {
|
||||
SERVER_STATUS.WORKER = false;
|
||||
});
|
||||
|
||||
const queueConfig = {
|
||||
connection: redisClient,
|
||||
};
|
||||
if (nconf.get('WORKER_REDIS_KEY_PREFIX')) {
|
||||
queueConfig.prefix = nconf.get('WORKER_REDIS_KEY_PREFIX');
|
||||
}
|
||||
|
||||
return got.post(`${EMAIL_SERVER.url}/job`, {
|
||||
retry: 5, // retry the http request to the email server 5 times
|
||||
timeout: 60000, // wait up to 60s before timing out
|
||||
username: EMAIL_SERVER.auth.user,
|
||||
password: EMAIL_SERVER.auth.password,
|
||||
json: {
|
||||
type,
|
||||
data,
|
||||
options: usedOptions,
|
||||
},
|
||||
}).json().catch(err => logger.error(err, {
|
||||
extraMessage: 'Error while sending an email.',
|
||||
}));
|
||||
queues.email = new Queue('emails', queueConfig);
|
||||
queues.deleteUser = new Queue('DeleteUsers', queueConfig);
|
||||
} else {
|
||||
SERVER_STATUS.WORKER = true;
|
||||
}
|
||||
|
||||
function sendJob (type, config) {
|
||||
if (!queues[type]) {
|
||||
return Promise.reject(new Error(`Queue ${type} does not exist`));
|
||||
}
|
||||
const { identifier, data } = config;
|
||||
return queues[type].add(identifier, data);
|
||||
}
|
||||
|
||||
export function getRedisClient () {
|
||||
return redisClient;
|
||||
}
|
||||
|
||||
export default { sendJob };
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import nconf from 'nconf';
|
||||
import redis from 'redis';
|
||||
import {
|
||||
RateLimiterRedis,
|
||||
RateLimiterMemory,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
import logger from '../libs/logger';
|
||||
import { apiError } from '../libs/apiError';
|
||||
import SERVER_STATUS from '../libs/serverStatus';
|
||||
import setupRedis from '../libs/redis';
|
||||
|
||||
// Middleware to rate limit requests to the API
|
||||
|
||||
@@ -19,9 +19,6 @@ import SERVER_STATUS from '../libs/serverStatus';
|
||||
|
||||
const IS_TEST = nconf.get('IS_TEST');
|
||||
const RATE_LIMITER_ENABLED = nconf.get('RATE_LIMITER_ENABLED') === 'true';
|
||||
const REDIS_HOST = nconf.get('REDIS_HOST');
|
||||
const REDIS_PASSWORD = nconf.get('REDIS_PASSWORD');
|
||||
const REDIS_PORT = nconf.get('REDIS_PORT');
|
||||
const LIVELINESS_PROBE_KEY = nconf.get('LIVELINESS_PROBE_KEY');
|
||||
const REGISTRATION_COST = nconf.get('RATE_LIMITER_REGISTRATION_COST') || 5;
|
||||
const IP_RATE_LIMIT_COST = nconf.get('RATE_LIMITER_IP_COST') || 5;
|
||||
@@ -41,19 +38,21 @@ if (RATE_LIMITER_ENABLED) {
|
||||
...rateLimiterOpts,
|
||||
});
|
||||
} else {
|
||||
redisClient = redis.createClient({
|
||||
host: REDIS_HOST,
|
||||
password: REDIS_PASSWORD,
|
||||
port: REDIS_PORT,
|
||||
enable_offline_queue: false,
|
||||
redisClient = setupRedis({
|
||||
host: nconf.get('REDIS_HOST'),
|
||||
username: nconf.get('REDIS_USERNAME'),
|
||||
password: nconf.get('REDIS_PASSWORD'),
|
||||
port: nconf.get('REDIS_PORT'),
|
||||
}, {
|
||||
enableOfflineQueue: false,
|
||||
});
|
||||
|
||||
redisClient.on('ready', () => {
|
||||
SERVER_STATUS.REDIS = true;
|
||||
SERVER_STATUS.RATE_LIMITER = true;
|
||||
});
|
||||
|
||||
redisClient.on('reconnecting', () => {
|
||||
SERVER_STATUS.REDIS = false;
|
||||
SERVER_STATUS.RATE_LIMITER = false;
|
||||
});
|
||||
|
||||
redisClient.on('error', error => {
|
||||
@@ -66,7 +65,7 @@ if (RATE_LIMITER_ENABLED) {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
SERVER_STATUS.REDIS = true;
|
||||
SERVER_STATUS.RATE_LIMITER = true;
|
||||
}
|
||||
|
||||
function setResponseHeaders (res, rateLimiterRes) {
|
||||
@@ -83,6 +82,10 @@ function setResponseHeaders (res, rateLimiterRes) {
|
||||
res.set(headers);
|
||||
}
|
||||
|
||||
export function getRedisClient () {
|
||||
return redisClient;
|
||||
}
|
||||
|
||||
export default function rateLimiterMiddleware (req, res, next) {
|
||||
if (!RATE_LIMITER_ENABLED) return next();
|
||||
if (LIVELINESS_PROBE_KEY && req.query.liveliness === LIVELINESS_PROBE_KEY) return next();
|
||||
|
||||
@@ -261,11 +261,11 @@ schema.statics.getGroup = async function getGroup (options = {}) {
|
||||
} else if (isTavern) {
|
||||
query = { _id: TAVERN_ID };
|
||||
} else if (optionalMembership === true) {
|
||||
query = { _id: groupId };
|
||||
query = { privacy: 'private', _id: groupId };
|
||||
} else if (isUserGuild) {
|
||||
query = { type: 'guild', _id: groupId };
|
||||
query = { type: 'guild', privacy: 'private', _id: groupId };
|
||||
} else {
|
||||
query = { type: 'guild', privacy: 'public', _id: groupId };
|
||||
return null;
|
||||
}
|
||||
|
||||
const mQuery = this.findOne(query);
|
||||
@@ -1431,6 +1431,7 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC
|
||||
|
||||
if (members.length === 0) {
|
||||
promises.push(group.deleteOne());
|
||||
promises.push(Chat.deleteMany({ groupId: group._id }));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,16 @@ schema.statics.cleanupCorruptData = function cleanupCorruptNotificationsData (no
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remove duplicate STREAK_ACHIEVEMENT notifications
|
||||
// Deduplicates by keeping only the first occurrence of STREAK_ACHIEVEMENT notification
|
||||
filteredNotifications = _.uniqWith(filteredNotifications, (val, otherVal) => {
|
||||
if (val.type === 'STREAK_ACHIEVEMENT' && val.type === otherVal.type) {
|
||||
// If both are streak achievements, they are duplicates
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return filteredNotifications;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ import nconf from 'nconf';
|
||||
import express from 'express';
|
||||
import http from 'http';
|
||||
import mongoose from 'mongoose';
|
||||
import redis from 'redis';
|
||||
import logger from './libs/logger';
|
||||
import { getRedisClient as getWorkerRedisClient } from './libs/worker';
|
||||
import { getRedisClient as getRateLimiterRedisClient } from './middlewares/rateLimiter';
|
||||
|
||||
// Setup translations
|
||||
// Must come before attach middlewares so Mongoose validations can use translations
|
||||
@@ -31,7 +32,10 @@ process.on('SIGTERM', async () => {
|
||||
console.log('SIGTERM signal received: closing HTTP server');
|
||||
server.close(async () => {
|
||||
await mongoose.disconnect();
|
||||
await redis.quit();
|
||||
const workerRedisClient = getWorkerRedisClient();
|
||||
if (workerRedisClient) await workerRedisClient.quit();
|
||||
const rateLimiterRedisClient = getRateLimiterRedisClient();
|
||||
if (rateLimiterRedisClient) await rateLimiterRedisClient.quit();
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user