Compare commits
278 Commits
feature/#2
...
main
Author | SHA1 | Date | |
---|---|---|---|
bf5fbfef99 | |||
cfbdb0ca3d | |||
fd18ff64eb | |||
da2f65dc05 | |||
29b10086f4 | |||
10e2094444 | |||
661abd030f | |||
632f2c9770 | |||
f1395340b8 | |||
7c78262982 | |||
9f42d1e59d | |||
36c9522e8a | |||
5ca41cfd38 | |||
c9e8b29f11 | |||
bfafd41c46 | |||
2605295542 | |||
12805e571a | |||
e8adb5c815 | |||
29ef089fce | |||
423dbd93f7 | |||
5b06386a39 | |||
c59b391a6a | |||
d6681f9af7 | |||
b673e7a176 | |||
39d793570d | |||
2cbc951816 | |||
b7dd0cbd75 | |||
c14ae36a94 | |||
78daac9d95 | |||
66fc6d8b43 | |||
258ebf97d1 | |||
0efa9fb1d5 | |||
4c7751db55 | |||
a77b35d55a | |||
4ac1e8824d | |||
bfba7197b7 | |||
58d7e02de2 | |||
b173a993f7 | |||
f3e0d6e03a | |||
4cf87536ce | |||
1191e6bf55 | |||
f2dd1a2ffe | |||
d68d307895 | |||
67984f3e89 | |||
562935c6e8 | |||
049456cc40 | |||
161a9795bc | |||
9f84247839 | |||
17fa2a8f6e | |||
cbd6e2c307 | |||
daeb232d3b | |||
5acebfe377 | |||
086c7cd6d6 | |||
47be8597bf | |||
2ce9bbdedd | |||
3f8d36db5a | |||
fc91eb9873 | |||
606328dbef | |||
b508370eec | |||
22b776ef0f | |||
f76bf3df1f | |||
7da0323153 | |||
26405433a8 | |||
4e7e13d6f4 | |||
785a232776 | |||
14d296e5a9 | |||
8cca44a3b4 | |||
d75ed7a44f | |||
e21c03ee3b | |||
deac2892fb | |||
e40a56825a | |||
c47339dfcd | |||
fef0ae6e28 | |||
bebd5876da | |||
fedb5c154b | |||
9e55ac7990 | |||
8b51f6e16a | |||
e2ded75017 | |||
0cead14e71 | |||
f2905247ff | |||
e735522d76 | |||
94c619192b | |||
a8a98d0083 | |||
646c40db27 | |||
cfaea6ec7c | |||
9030550c0f | |||
c71c393a51 | |||
4d192bd5ec | |||
f46fff5b69 | |||
f5cae0db9f | |||
c627ea2412 | |||
30145e1662 | |||
778e4402ba | |||
e2bc151881 | |||
2b022ee4e0 | |||
3802b2cf9d | |||
2ee6a72984 | |||
f50e4c75a9 | |||
51cbe87755 | |||
d6aa8da2de | |||
7b4674587a | |||
8e652f8dcb | |||
5349e2ffe5 | |||
66759a87f2 | |||
b7748c254f | |||
ee080b6987 | |||
67021f9ada | |||
3270ea8729 | |||
04710edb73 | |||
d398764b6d | |||
be479b11c5 | |||
45964ba7f3 | |||
e7e187da7c | |||
64c0d82d48 | |||
12292ea4f2 | |||
0c8df9d175 | |||
86cbe3fdcb | |||
62e31c10d7 | |||
35fe0e6faa | |||
bcc5c0bca3 | |||
2de92ca51c | |||
2cdcfa7e56 | |||
ee4eca6db3 | |||
daca3d306d | |||
47d38e36dd | |||
14b4726546 | |||
394ad13341 | |||
fe18f8b54e | |||
11f177c901 | |||
e1f9f8e523 | |||
10c8e493a7 | |||
9a5aa9a53d | |||
2d5a445af0 | |||
af43faf8f2 | |||
cbaadc0c26 | |||
1ed9c2a61f | |||
b46989d3af | |||
30310bf0cf | |||
a28b1b9bee | |||
ccdc39e74b | |||
df860cb7d7 | |||
5e46597e7d | |||
aebe21f140 | |||
d4c1098a5e | |||
8b9afdf956 | |||
5f02aca6e4 | |||
e824f0f558 | |||
13252e056f | |||
50f595f718 | |||
a7726387af | |||
cff5fed4f7 | |||
52b8a9b7ad | |||
f5e7d10fb4 | |||
fae428f239 | |||
37f2cd90e6 | |||
3265bf7823 | |||
209f474575 | |||
733a1c4956 | |||
7e2c5eb529 | |||
b2f7d45a1f | |||
8b0746958e | |||
0c607fe39d | |||
aae125c6c6 | |||
4ace8c1e84 | |||
cf3b274cd3 | |||
10fd2064ba | |||
9ba8be51ab | |||
1686a7a9a0 | |||
b82e2fd0fd | |||
71dd1f240d | |||
f2917e67e3 | |||
9ea12ee458 | |||
67a4c6763b | |||
f0c0456121 | |||
4992ef69d4 | |||
765a0468bc | |||
ba96ae7dd4 | |||
9baffd1327 | |||
a14074afcf | |||
6b03937c39 | |||
89f8137dc3 | |||
53c232d0a3 | |||
a5d8cf5ef9 | |||
90559e8388 | |||
925721be8a | |||
70aa7345e0 | |||
a5ca524bb4 | |||
3b6c11090f | |||
f6a4bd3369 | |||
ccf43556a5 | |||
1be4a70fed | |||
f0bfa0b983 | |||
60753cb2db | |||
c400e868af | |||
eaa7385acc | |||
da2df6ace6 | |||
6f87c3f3c5 | |||
876c96e2c6 | |||
7fd33aa36b | |||
e57c19defd | |||
caf0e5c2f4 | |||
c8728ba83a | |||
b33b9cc29f | |||
3b65cae631 | |||
9d7cee2334 | |||
dbdc8c9d6e | |||
d17408acd9 | |||
5680b324b4 | |||
9771f45e6d | |||
b3ac6d34b8 | |||
b115181756 | |||
bf4789b9a8 | |||
f004f059f6 | |||
e6adb959ba | |||
85161ab4f6 | |||
8f4d4fc482 | |||
a0584c2bb9 | |||
7f4a784915 | |||
71fdbd89a4 | |||
5c34ed7286 | |||
edb7836e55 | |||
112559055c | |||
1546deb811 | |||
d7ac70662a | |||
dae0d365d5 | |||
020f2bd3c5 | |||
189fd39377 | |||
0ba79c2299 | |||
74f5214ca3 | |||
410d5cb7a8 | |||
41e65fc3e9 | |||
4b6935c44a | |||
4232042a06 | |||
3869eefaaf | |||
a4437fadce | |||
458293a5fc | |||
849ef07297 | |||
39ec4daa06 | |||
010454914b | |||
f47023dc81 | |||
4397552a86 | |||
7b90fa5c13 | |||
7dabed7ff6 | |||
d91a70be09 | |||
514ed87818 | |||
24586855cb | |||
be9ee97385 | |||
a05cd97430 | |||
cc1dbe5179 | |||
d7982493e1 | |||
57b21f1499 | |||
33afef5466 | |||
813ddbd8b1 | |||
4a55f47c06 | |||
097773995f | |||
04e4170c2d | |||
46cfb61cf5 | |||
db7121a4fa | |||
1dd0e73c4a | |||
747d05a92a | |||
48784a437f | |||
6b12d8e7b1 | |||
50c2b249f4 | |||
82aaf8a015 | |||
0e0854d365 | |||
b2e0ac47e7 | |||
f2d0e87e26 | |||
9bdafd5026 | |||
ae269be196 | |||
21f4c5328f | |||
47ec425acf | |||
1f0db75806 | |||
9a448542d3 | |||
067976c54a | |||
0b4420f956 | |||
e843213b0a | |||
4d50edd5dd | |||
0cadbc33b9 |
@ -3,7 +3,7 @@ ENV=development
|
||||
HOST="0.0.0.0"
|
||||
PORT=4000
|
||||
JWT_SECRET="secret"
|
||||
CLIENT_URL="http://192.168.3.4:5173"
|
||||
CLIENT_URL="http://localhost:5173"
|
||||
|
||||
# Database configuration
|
||||
REDIS_URL="redis://@127.0.0.1:6379/4"
|
||||
@ -26,3 +26,7 @@ SMTP_HOST=my.directonline.io
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=no-reply@noxious.gg
|
||||
SMTP_PASSWORD=""
|
||||
|
||||
# SSL
|
||||
#PUBLIC_KEY_PATH=
|
||||
#PRIVATE_KEY_PATH=
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
||||
migrations
|
@ -3,6 +3,9 @@
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 300,
|
||||
"trailingComma": "none"
|
||||
"printWidth": 200,
|
||||
"trailingComma": "none",
|
||||
"plugins": ["@ianvs/prettier-plugin-sort-imports"],
|
||||
"importOrderParserPlugins": ["typescript", "jsx", "decorators-legacy", "classProperties"],
|
||||
"importOrderCaseSensitive": false
|
||||
}
|
41
Dockerfile
41
Dockerfile
@ -1,41 +0,0 @@
|
||||
# Use the official Node.js 22.4.1 image
|
||||
FROM node:22.4.1-alpine
|
||||
|
||||
# Install Redis and tmux
|
||||
RUN apk add --no-cache redis tmux
|
||||
|
||||
# Set the working directory in the container
|
||||
WORKDIR /usr/src/
|
||||
|
||||
# Copy package.json and package-lock.json (if available)
|
||||
COPY package*.json ./
|
||||
|
||||
# Install application dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy prisma schema
|
||||
COPY prisma ./prisma/
|
||||
|
||||
# Generate Prisma client
|
||||
RUN npx prisma generate
|
||||
|
||||
# Copy the rest of your application code to the container
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Expose the ports your Node.js application and Redis will listen on
|
||||
EXPOSE 80 6379
|
||||
|
||||
# Create a shell script to run Redis, run migrations, and start the application in a tmux session
|
||||
RUN echo '#!/bin/sh' > /usr/src/start.sh && \
|
||||
echo 'redis-server --daemonize yes' >> /usr/src/start.sh && \
|
||||
echo 'npx prisma migrate deploy' >> /usr/src/start.sh && \
|
||||
echo 'tmux new-session -d -s nodeapp "node dist/server.js"' >> /usr/src/start.sh && \
|
||||
echo 'echo "App is running in tmux session. Attach with: tmux attach-session -t nodeapp"' >> /usr/src/start.sh && \
|
||||
echo 'tail -f /dev/null' >> /usr/src/start.sh && \
|
||||
chmod +x /usr/src/start.sh
|
||||
|
||||
# Use the shell script as the entry point
|
||||
CMD ["/usr/src/start.sh"]
|
18
README.md
18
README.md
@ -2,12 +2,22 @@
|
||||
|
||||
This is the server for the Noxious game.
|
||||
|
||||
## Projects requirements
|
||||
|
||||
- NodeJS 20.x or higher
|
||||
- MariaDB 11.x or higher
|
||||
- Redis 7.x or higher
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone the repository
|
||||
2. Install dependencies with `npm install`
|
||||
3. Copy the `.env.example` file to `.env` and fill in the required variables
|
||||
4. Run the server with `npm run dev`
|
||||
4. Extract assets.zip to the `public` folder
|
||||
5. After MySQL and Redis are running, run `npm run mikro-orm migration:up` to create the database schema
|
||||
6. Run the server with `npm run dev`
|
||||
7. Write `init` in the console to import default data and restart the server
|
||||
8. Write `tiles` in the console to fix tile sizes
|
||||
|
||||
## Commands
|
||||
|
||||
@ -29,15 +39,15 @@ MikroORM is used as the ORM for the server.
|
||||
|
||||
### Create init. migrations
|
||||
|
||||
Run `npx mikro-orm migration:create --initial` to create a new initial migration.
|
||||
Run `npm run mikro-orm migration:create --initial` to create a new initial migration.
|
||||
|
||||
### Create migrations
|
||||
|
||||
Run `npx mikro-orm migration:create` to create a new migration. You do this when you want to add a new table or change an existing one.
|
||||
Run `npm run mikro-orm migration:create` to create a new migration. You do this when you want to add a new table or change an existing one.
|
||||
|
||||
### Apply migrations
|
||||
|
||||
Run `npx mikro-orm migration:up` to apply all pending migrations.
|
||||
Run `npm run mikro-orm migration:up` to apply all pending migrations.
|
||||
|
||||
### Import default data
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"dockerfilePath" :"./Dockerfile"
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from '@typescript-eslint/eslint-plugin';
|
||||
import tsparser from '@typescript-eslint/parser';
|
||||
import importPlugin from 'eslint-plugin-import';
|
||||
|
||||
export default [
|
||||
eslint.configs.recommended,
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
ecmaVersion: 2023,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
'@typescript-eslint': tseslint,
|
||||
'import': importPlugin,
|
||||
},
|
||||
rules: {
|
||||
...tseslint.configs['recommended'].rules,
|
||||
...tseslint.configs['recommended-requiring-type-checking'].rules,
|
||||
'import/order': ['error', {
|
||||
'groups': [
|
||||
'builtin',
|
||||
'external',
|
||||
'internal',
|
||||
['parent', 'sibling'],
|
||||
'index',
|
||||
'object',
|
||||
'type'
|
||||
],
|
||||
'newlines-between': 'always',
|
||||
'alphabetize': {
|
||||
'order': 'asc',
|
||||
'caseInsensitive': true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
];
|
3848
package-lock.json
generated
3848
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@ -1,50 +1,48 @@
|
||||
{
|
||||
"type": "module",
|
||||
"tsNode": true,
|
||||
"scripts": {
|
||||
"start": "node dist/server.js",
|
||||
"start": "tsx src/server.ts",
|
||||
"dev": "nodemon --exec tsx src/server.ts",
|
||||
"build": "tsc",
|
||||
"format": "prettier --write src/",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix"
|
||||
"mikro-orm": "tsx ./node_modules/.bin/mikro-orm-esm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mikro-orm/cli": "^6.4.2",
|
||||
"@mikro-orm/core": "^6.4.2",
|
||||
"@mikro-orm/mariadb": "^6.4.2",
|
||||
"@mikro-orm/migrations": "^6.4.2",
|
||||
"@mikro-orm/mysql": "^6.4.2",
|
||||
"@mikro-orm/reflection": "^6.4.2",
|
||||
"@prisma/client": "^6.1.0",
|
||||
"@types/ioredis": "^4.28.10",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bufferutil": "^4.0.9",
|
||||
"bullmq": "^5.13.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"https": "^1.0.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"nodemailer": "^6.9.15",
|
||||
"pino": "^9.3.2",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"sharp": "^0.33.4",
|
||||
"socket.io": "^4.7.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.5.3",
|
||||
"utf-8-validate": "^6.0.5",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mikro-orm/cli": "^6.4.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.0",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/node": "^20.14.11",
|
||||
"@types/nodemailer": "^6.4.16",
|
||||
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
||||
"@typescript-eslint/parser": "^8.18.2",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"nodemon": "^3.1.4",
|
||||
"prettier": "^3.3.3",
|
||||
"prisma": "^6.1.0",
|
||||
"tsx": "^4.19.2"
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -1,7 +1,6 @@
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
|
||||
export abstract class BaseCommand {
|
||||
protected readonly logger = Logger.type(LoggerType.COMMAND)
|
||||
constructor(readonly io: Server) {}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
import fs from 'fs'
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
import type { Response } from 'express'
|
||||
|
||||
export abstract class BaseController {
|
||||
protected readonly logger = Logger.type(LoggerType.HTTP)
|
||||
@ -19,4 +19,18 @@ export abstract class BaseController {
|
||||
message
|
||||
})
|
||||
}
|
||||
|
||||
protected sendFile(res: Response, filePath: string) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
this.logger.error(`File not found: ${filePath}`)
|
||||
return this.sendError(res, 'Asset not found', 404)
|
||||
}
|
||||
|
||||
res.sendFile(filePath, (error) => {
|
||||
if (error) {
|
||||
this.logger.error('Error sending file:' + error)
|
||||
this.sendError(res, 'Error downloading the asset', 500)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,35 @@
|
||||
import Database from '@/application/database'
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
import { EntityManager } from '@mikro-orm/core'
|
||||
|
||||
import Database from '#application/database'
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
|
||||
export abstract class BaseEntity {
|
||||
protected readonly logger = Logger.type(LoggerType.ENTITY)
|
||||
protected entityManager?: EntityManager
|
||||
|
||||
private getEntityManager(): EntityManager {
|
||||
return Database.getEntityManager()
|
||||
if (!this.entityManager) {
|
||||
this.entityManager = Database.getORM().em.fork()
|
||||
}
|
||||
return this.entityManager
|
||||
}
|
||||
|
||||
public setEntityManager(entityManager: EntityManager) {
|
||||
this.entityManager = entityManager
|
||||
return this
|
||||
}
|
||||
|
||||
async save(): Promise<this> {
|
||||
return this.execute('persist', 'save entity')
|
||||
}
|
||||
|
||||
async update(): Promise<this> {
|
||||
return this.execute('merge', 'update entity')
|
||||
}
|
||||
|
||||
async delete(): Promise<this> {
|
||||
return this.execute('remove', 'remove entity')
|
||||
}
|
||||
|
||||
async update(): Promise<this> {
|
||||
return this.execute('merge', 'update entity')
|
||||
}
|
||||
|
||||
private async execute(method: 'persist' | 'merge' | 'remove', actionDescription: string): Promise<this> {
|
||||
try {
|
||||
const em = this.getEntityManager()
|
||||
@ -37,7 +45,10 @@ export abstract class BaseEntity {
|
||||
throw error
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to ${actionDescription}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
const errorMessage = error instanceof Error ? error.message : error && typeof error === 'object' && 'toString' in error ? error.toString() : String(error)
|
||||
|
||||
console.log(errorMessage)
|
||||
this.logger.error(`Failed to ${actionDescription}: ${errorMessage}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,43 @@
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
import type { TSocket } from '@/application/types'
|
||||
import { Character } from '@/entities/character'
|
||||
import MapManager from '@/managers/mapManager'
|
||||
import type MapCharacter from '@/models/mapCharacter'
|
||||
import CharacterRepository from '@/repositories/characterRepository'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
import { TSocket } from '#application/types'
|
||||
import { Character } from '#entities/character'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
|
||||
export abstract class BaseEvent {
|
||||
protected readonly logger = Logger.type(LoggerType.GAME)
|
||||
private lastActionTimes: Map<string, number> = new Map()
|
||||
|
||||
constructor(
|
||||
readonly io: Server,
|
||||
readonly socket: TSocket
|
||||
) {}
|
||||
|
||||
protected isThrottled(actionId: string, throttleTime: number): boolean {
|
||||
const now = Date.now()
|
||||
const lastActionTime = this.lastActionTimes.get(actionId) || 0
|
||||
|
||||
if (now - lastActionTime < throttleTime) {
|
||||
return true
|
||||
}
|
||||
|
||||
this.lastActionTimes.set(actionId, now)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
protected getMapCharacter(): MapCharacter | null {
|
||||
if (!this.socket.characterId) return null
|
||||
return MapManager.getCharacterById(this.socket.characterId)
|
||||
}
|
||||
|
||||
protected async getCharacter(): Promise<Character | null> {
|
||||
return CharacterRepository.getById(this.socket.characterId!)
|
||||
if (!this.socket.characterId) return null
|
||||
const characterRepository = new CharacterRepository()
|
||||
return characterRepository.getById(this.socket.characterId)
|
||||
}
|
||||
|
||||
protected async isCharacterGM(): Promise<boolean> {
|
||||
@ -22,14 +45,16 @@ export abstract class BaseEvent {
|
||||
return character?.getRole() === 'gm'
|
||||
}
|
||||
|
||||
protected emitError(message: string): void {
|
||||
this.socket.emit('notification', { title: 'Server message', message })
|
||||
this.logger.error('character:connect error', `Player ${this.socket.userId}: ${message}`)
|
||||
protected sendNotificationAndLog(message: string): void {
|
||||
console.log(message)
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Server message', message })
|
||||
this.logger.error('Base event error' + `Player ${this.socket.userId}: ${message}`)
|
||||
}
|
||||
|
||||
protected handleError(context: string, error: unknown): void {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
this.emitError(`${context}: ${errorMessage}`)
|
||||
this.logger.error('character:connect error', errorMessage)
|
||||
console.log(error)
|
||||
const errorMessage = error instanceof Error ? error.message : error && typeof error === 'object' && 'toString' in error ? error.toString() : String(error)
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Server message', message: `Server error occured. Please contact the server administrator.` })
|
||||
this.logger.error('Base event error: ' + errorMessage)
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
import Database from '@/application/database'
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
import { EntityManager } from '@mikro-orm/core'
|
||||
|
||||
import Database from '../database'
|
||||
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
|
||||
export abstract class BaseRepository {
|
||||
protected readonly logger = Logger.type(LoggerType.REPOSITORY)
|
||||
private entityManager?: EntityManager
|
||||
|
||||
protected get em(): EntityManager {
|
||||
return Database.getEntityManager()
|
||||
getEntityManager(): EntityManager {
|
||||
if (!this.entityManager) {
|
||||
this.entityManager = Database.getORM().em.fork()
|
||||
}
|
||||
return this.entityManager
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
|
||||
export abstract class BaseService {
|
||||
protected readonly logger = Logger.type(LoggerType.GAME)
|
||||
|
@ -6,7 +6,7 @@ class config {
|
||||
// Server configuration
|
||||
static ENV: string = process.env.ENV || 'development'
|
||||
static HOST: string = process.env.HOST || '0.0.0.0'
|
||||
static PORT: number = process.env.PORT ? parseInt(process.env.PORT) : 6969
|
||||
static PORT: number = process.env.PORT ? parseInt(process.env.PORT) : 4000
|
||||
static JWT_SECRET: string = process.env.JWT_SECRET || 'secret'
|
||||
static CLIENT_URL: string = process.env.CLIENT_URL ? process.env.CLIENT_URL : 'https://noxious.gg'
|
||||
|
||||
@ -31,6 +31,10 @@ class config {
|
||||
static SMTP_PORT: number = process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT) : 587
|
||||
static SMTP_USER: string = process.env.SMTP_USER || 'no-reply@noxious.gg'
|
||||
static SMTP_PASSWORD: string = process.env.SMTP_PASSWORD || 'password'
|
||||
|
||||
// SSL
|
||||
static PUBLIC_KEY_PATH: string = process.env.PUBLIC_KEY_PATH || ''
|
||||
static PRIVATE_KEY_PATH: string = process.env.PRIVATE_KEY_PATH || ''
|
||||
}
|
||||
|
||||
export default config
|
||||
|
@ -1,10 +1,9 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import { pathToFileURL } from 'url'
|
||||
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
import Storage from '#application/storage'
|
||||
import { Command } from '#application/types'
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
import Storage from '@/application/storage'
|
||||
import type { Command } from '@/application/types'
|
||||
|
||||
export class CommandRegistry {
|
||||
private readonly commands: Map<string, Command> = new Map()
|
||||
|
@ -1,7 +1,6 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
|
||||
export class LogReader {
|
||||
private logger = Logger.type(LoggerType.CONSOLE)
|
||||
@ -61,8 +60,8 @@ export class LogReader {
|
||||
})
|
||||
|
||||
stream.on('data', (data) => {
|
||||
process.stdout.write('\r' + `[${filename}]\n${data}`)
|
||||
process.stdout.write('\n> ')
|
||||
console.log(`[${filename}]`)
|
||||
console.log(data.toString()) //
|
||||
})
|
||||
|
||||
currentPosition = newPosition
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { EntityManager } from '@mikro-orm/core'
|
||||
import { MikroORM } from '@mikro-orm/mysql'
|
||||
// import { MikroORM } from '@mikro-orm/mysql'
|
||||
|
||||
import Logger, { LoggerType } from './logger'
|
||||
import config from '../../mikro-orm.config'
|
||||
import Logger, { LoggerType } from '@/application/logger'
|
||||
import config from '@/root/mikro-orm.config'
|
||||
import { MikroORM } from '@mikro-orm/mariadb'
|
||||
|
||||
class Database {
|
||||
private static orm: MikroORM
|
||||
@ -10,7 +10,7 @@ class Database {
|
||||
|
||||
public static async initialize(): Promise<void> {
|
||||
try {
|
||||
Database.orm = await MikroORM.init(config)
|
||||
this.orm = await MikroORM.init(config)
|
||||
this.logger.info('Database connection initialized')
|
||||
} catch (error) {
|
||||
this.logger.error(`MikroORM connection failed: ${error}`)
|
||||
@ -18,8 +18,8 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
public static getEntityManager(): EntityManager {
|
||||
return Database.orm.em.fork()
|
||||
public static getORM(): MikroORM {
|
||||
return this.orm
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,59 @@
|
||||
export enum SocketEvent {
|
||||
CHARACTER_CONNECT = 1,
|
||||
CHARACTER_MOVE = 2,
|
||||
CHARACTER_MOVE_ERROR = 3,
|
||||
CHARACTER_TELEPORT = 4,
|
||||
ZONE_CHARACTER_LEAVE = 5,
|
||||
ZONE_CHARACTER_JOIN = 6,
|
||||
ZONE_CHARACTER_LIST = 7,
|
||||
ZONE_CHARACTER_DELETE = 8,
|
||||
ZONE_CHARACTER_CREATE = 9,
|
||||
ZONE_CHARACTER_UPDATE = 10,
|
||||
ZONE_CHARACTER_HAIR_UPDATE = 11,
|
||||
ZONE_CHARACTER_HAIR_LIST = 12,
|
||||
ZONE_CHARACTER_TELEPORT = 13
|
||||
CONNECT_ERROR = 'connect_error',
|
||||
RECONNECT_FAILED = 'reconnect_failed',
|
||||
CLOSE = '52',
|
||||
DATA = '51',
|
||||
CHARACTER_CONNECT = '50',
|
||||
CHARACTER_CREATE = '49',
|
||||
CHARACTER_DELETE = '48',
|
||||
CHARACTER_LIST = '47',
|
||||
GM_CHARACTERHAIR_CREATE = '46',
|
||||
GM_CHARACTERHAIR_REMOVE = '45',
|
||||
GM_CHARACTERHAIR_LIST = '44',
|
||||
GM_CHARACTERHAIR_UPDATE = '43',
|
||||
GM_CHARACTERTYPE_CREATE = '42',
|
||||
GM_CHARACTERTYPE_REMOVE = '41',
|
||||
GM_CHARACTERTYPE_LIST = '40',
|
||||
GM_CHARACTERTYPE_UPDATE = '39',
|
||||
GM_ITEM_CREATE = '38',
|
||||
GM_ITEM_REMOVE = '37',
|
||||
GM_ITEM_LIST = '36',
|
||||
GM_ITEM_UPDATE = '35',
|
||||
GM_MAPOBJECT_LIST = '34',
|
||||
GM_MAPOBJECT_REMOVE = '33',
|
||||
GM_MAPOBJECT_UPDATE = '32',
|
||||
GM_MAPOBJECT_UPLOAD = '31',
|
||||
GM_SPRITE_COPY = '30',
|
||||
GM_SPRITE_CREATE = '29',
|
||||
GM_SPRITE_DELETE = '28',
|
||||
GM_SPRITE_LIST = '27',
|
||||
GM_SPRITE_UPDATE = '26',
|
||||
GM_TILE_DELETE = '25',
|
||||
GM_TILE_LIST = '24',
|
||||
GM_TILE_UPDATE = '23',
|
||||
GM_TILE_UPLOAD = '22',
|
||||
GM_MAP_CREATE = '21',
|
||||
GM_MAP_DELETE = '20',
|
||||
GM_MAP_REQUEST = '19',
|
||||
GM_MAP_UPDATE = '18',
|
||||
MAP_CHARACTER_MOVEERROR = '17',
|
||||
DISCONNECT = 'disconnect',
|
||||
USER_DISCONNECT = '15',
|
||||
LOGIN = '14',
|
||||
LOGGED_IN = '13',
|
||||
NOTIFICATION = '12',
|
||||
DATE = '11',
|
||||
FAILED = '10',
|
||||
COMPLETED = '9',
|
||||
CONNECTION = 'connection',
|
||||
WEATHER = '7',
|
||||
CHARACTER_DISCONNECT = '6',
|
||||
MAP_CHARACTER_ATTACK = '5',
|
||||
MAP_CHARACTER_TELEPORT = '4',
|
||||
MAP_CHARACTER_JOIN = '3',
|
||||
MAP_CHARACTER_LEAVE = '2',
|
||||
MAP_CHARACTER_MOVE = '1',
|
||||
CHAT_MESSAGE = '0'
|
||||
}
|
||||
|
||||
export enum ItemType {
|
||||
|
@ -1,5 +1,7 @@
|
||||
import pino from 'pino'
|
||||
|
||||
const logger = pino.pino
|
||||
|
||||
export enum LoggerType {
|
||||
HTTP = 'http',
|
||||
GAME = 'game',
|
||||
@ -13,13 +15,13 @@ export enum LoggerType {
|
||||
}
|
||||
|
||||
class Logger {
|
||||
private instances: Map<LoggerType, ReturnType<typeof pino>> = new Map()
|
||||
private instances: Map<LoggerType, pino.Logger> = new Map()
|
||||
|
||||
private getLogger(type: LoggerType): ReturnType<typeof pino> {
|
||||
private getLogger(type: LoggerType): pino.Logger {
|
||||
if (!this.instances.has(type)) {
|
||||
this.instances.set(
|
||||
type,
|
||||
pino({
|
||||
logger({
|
||||
level: process.env.LOG_LEVEL || 'debug',
|
||||
transport: {
|
||||
target: 'pino/file',
|
||||
|
@ -1,7 +1,6 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
import config from '#application/config'
|
||||
import config from '@/application/config'
|
||||
|
||||
class Storage {
|
||||
private readonly baseDir: string
|
||||
@ -9,7 +8,7 @@ class Storage {
|
||||
|
||||
constructor() {
|
||||
this.rootDir = process.cwd()
|
||||
this.baseDir = config.ENV === 'development' ? 'src' : 'dist'
|
||||
this.baseDir = config.ENV === 'development' ? 'src' : 'src'
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { Character } from '@/entities/character'
|
||||
import { MapEventTile } from '@/entities/mapEventTile'
|
||||
import { MapEventTileTeleport } from '@/entities/mapEventTileTeleport'
|
||||
import { Server, Socket } from 'socket.io'
|
||||
|
||||
import { Character } from '#entities/character'
|
||||
import { MapEventTile } from '#entities/mapEventTile'
|
||||
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
|
||||
|
||||
export type UUID = `${string}-${string}-${string}-${string}-${string}`
|
||||
|
||||
export type TSocket = Socket & {
|
||||
@ -37,7 +36,6 @@ export type AssetData = {
|
||||
updatedAt: Date
|
||||
originX?: number
|
||||
originY?: number
|
||||
isAnimated?: boolean
|
||||
frameRate?: number
|
||||
frameWidth?: number
|
||||
frameHeight?: number
|
||||
@ -46,8 +44,11 @@ export type AssetData = {
|
||||
|
||||
export type WorldSettings = {
|
||||
date: Date
|
||||
isRainEnabled: boolean
|
||||
isFogEnabled: boolean
|
||||
weatherState: WeatherState
|
||||
}
|
||||
|
||||
export type WeatherState = {
|
||||
rainPercentage: number
|
||||
fogDensity: number
|
||||
}
|
||||
|
||||
|
@ -58,3 +58,16 @@ export const ZCharacterCreate = z.object({
|
||||
.max(255, { message: 'Name must be at most 255 characters long' })
|
||||
.regex(/^[A-Za-z][A-Za-z0-9_-]*$/, { message: 'Name must start with a letter and can only contain letters, numbers, underscores, or dashes' })
|
||||
})
|
||||
|
||||
export const ZCharacterConnect = z.object({
|
||||
characterId: z.string(),
|
||||
characterHairId: z.string().optional().nullable(),
|
||||
newNickname: z
|
||||
.string()
|
||||
.min(3, { message: 'Name must be at least 3 characters long' })
|
||||
.max(255, { message: 'Name must be at most 255 characters long' })
|
||||
.regex(/^[A-Za-z][A-Za-z0-9_-]*$/, {
|
||||
message: 'Name must start with a letter and can only contain letters, numbers, underscores, or dashes'
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { BaseCommand } from '@/application/base/baseCommand'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { BaseCommand } from '#application/base/baseCommand'
|
||||
|
||||
type CommandInput = string[]
|
||||
|
||||
export default class AlertCommand extends BaseCommand {
|
||||
public execute(input: CommandInput): void {
|
||||
const message: string = input.join(' ') ?? null
|
||||
if (!message) return console.log('message is required')
|
||||
this.io.emit('notification', { message: message })
|
||||
this.io.emit(SocketEvent.NOTIFICATION, { message: message })
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,37 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import { BaseCommand } from '@/application/base/baseCommand'
|
||||
import { CharacterGender, CharacterRace } from '@/application/enums'
|
||||
import Storage from '@/application/storage'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { Character } from '@/entities/character'
|
||||
import { CharacterHair } from '@/entities/characterHair'
|
||||
import { CharacterType } from '@/entities/characterType'
|
||||
import { Map } from '@/entities/map'
|
||||
import { MapEffect } from '@/entities/mapEffect'
|
||||
import { MapObject } from '@/entities/mapObject'
|
||||
import { Sprite } from '@/entities/sprite'
|
||||
import { SpriteAction } from '@/entities/spriteAction'
|
||||
import { Tile } from '@/entities/tile'
|
||||
import { User } from '@/entities/user'
|
||||
import CharacterHairRepository from '@/repositories/characterHairRepository'
|
||||
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
|
||||
import MapRepository from '@/repositories/mapRepository'
|
||||
import sharp from 'sharp'
|
||||
|
||||
import { BaseCommand } from '#application/base/baseCommand'
|
||||
import { CharacterGender, CharacterRace } from '#application/enums'
|
||||
import Storage from '#application/storage'
|
||||
import { UUID } from '#application/types'
|
||||
import { Character } from '#entities/character'
|
||||
import { CharacterHair } from '#entities/characterHair'
|
||||
import { CharacterType } from '#entities/characterType'
|
||||
import { MapObject } from '#entities/mapObject'
|
||||
import { Sprite } from '#entities/sprite'
|
||||
import { SpriteAction } from '#entities/spriteAction'
|
||||
import { Tile } from '#entities/tile'
|
||||
import { User } from '#entities/user'
|
||||
import { Map } from '#entities/map'
|
||||
import { MapEffect } from '#entities/mapEffect'
|
||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
|
||||
// @TODO : Replace this with seeding
|
||||
// https://mikro-orm.io/docs/seeding
|
||||
|
||||
export default class InitCommand extends BaseCommand {
|
||||
private readonly mapRepository = new MapRepository()
|
||||
private readonly characterTypeRepository = new CharacterTypeRepository()
|
||||
private readonly characterHairRepository = new CharacterHairRepository()
|
||||
|
||||
public async execute(): Promise<void> {
|
||||
// Assets
|
||||
await this.importTiles()
|
||||
await this.importMapObjects()
|
||||
await this.createCharacterType()
|
||||
await this.createMaleCharacterType()
|
||||
// await this.createFemaleCharacterType()
|
||||
await this.createCharacterHair()
|
||||
// await this.createCharacterEquipment()
|
||||
|
||||
@ -60,34 +63,38 @@ export default class InitCommand extends BaseCommand {
|
||||
.setFrameWidth(
|
||||
(await sharp(Storage.getPublicPath('map_objects', mapObject))
|
||||
.metadata()
|
||||
.then((metadata) => metadata.height)) ?? 0
|
||||
.then((metadata) => metadata.width)) ?? 0
|
||||
)
|
||||
.setFrameHeight(
|
||||
(await sharp(Storage.getPublicPath('map_objects', mapObject))
|
||||
.metadata()
|
||||
.then((metadata) => metadata.width)) ?? 0
|
||||
.then((metadata) => metadata.height)) ?? 0
|
||||
)
|
||||
|
||||
await newMapObject.save()
|
||||
}
|
||||
}
|
||||
|
||||
private async createCharacterType(): Promise<void> {
|
||||
private async createMaleCharacterType(): Promise<void> {
|
||||
const characterSprite = new Sprite()
|
||||
characterSprite.setId('023d1e9d-f57f-4faa-8412-86c07107cf85').setName('Character')
|
||||
characterSprite.setId('023d1e9d-f57f-4faa-8412-86c07107cf85').setName('Male character')
|
||||
await characterSprite.save()
|
||||
|
||||
const idleRightDownAction = new SpriteAction()
|
||||
await idleRightDownAction
|
||||
.setAction('idle_right_down')
|
||||
.setSprites([
|
||||
'data:image/png;base64,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'
|
||||
{
|
||||
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAABeCAYAAAAwnXTzAAAHNklEQVR4nNVaTWgbRxT+LBtF2AiD5EoNBNbQiL1IIF9iXKgvqt1L8CE51gSfYhMKpjE1FIoPviuQW9OTMLmk0B5MTzaGYAhGJznIUIR8kCAQVthLhLBwDJZ7WL3RzOzs7qziSz8QaGdH882beX/zRiOFX19BF4nd7RtVu720NaI7xlgYopX8NGYK867360XnvQ5xIGFid/tmJT+NRCoNI2cilslifGpC6PPq9ywu6ydYL27fBJFGdMhmCvMC2WgyI3zGpyYQy2TxcuOJ57JrEQJAIpUGAMQyWc8+YUg9CUk6I2cGzUmA38R8Cb3QPbvA9Xndt4+flFpaqiIdR93VpoOhCMMQyPBd0rm1Z2hWawCAXrujPeh6cWc4wsv6CcxHj1E5OMRVqym8W13bxOrapus3q2ubOMzeH46QpJtbe4bKwaFr0DfvGy7SN+8bfkP6E9otC5f1EwCOPdKy0qB3f/w+kECb0F7aGikdN5iURs7EVauJXruDT0d/sn78d3qu/PCtJ2GglpKUzWqNkUZh4N+fngr9eP966WOm/kvKSWm3LNZOCjQ+NcE+hCBzCfQ09tLWyIu9MgAIitNrd9A9u3B9eu0O1os7nqFK27WVjhsCKe2n/JHNR4a2p1nJTzNSgjoYe0sXipBIgYG08gSA4KgfipAkminMM0WiTCBIMi1CPpehQEwwciYMDGLl88VZvOBCkhe5kpAn4gkAsDSje3bBvFAsk4WZyeJlzmSSl3bV+c2InCZSpAfAlksF3jZV/SoHhygdN1ySChLySRMAJOZmmSRy6mBmsoIHIkkBJ8o4W9BwTdS1pLRXiblZJzlCnbmquJln/Zw0Iwuj/8xndE6/11hRLC0zfD5pimWyGE1mADgZWTRlcCRg7TKojfrJiqaUMJoyBN/Ik6hyGR7Ul/ypkTOBvlt0SUgzikzGhZl2zy6YG6PBeL9JuKyfsHZgELxX8tNCBicQypomD+rlN2nwy/oJeu0OLusnsFuWMkd1LSktJy3PVauJysEhZgrzbC+pHRg4c/tgEL5Kxw283HiC8akJl7Z6ehqSjuxppjAvRAIikhWDj5sqxRoDRGMHgE7tmElROm5gJT8txEIALompPy+pCp4SknQE3ptEUwYik3EAYG4OAKIwXOPImsoIE6k0oimDLSUvHfshR0ZGDgDxZF+jOaJmtYavHroFYVoqayhpHg9eMnl/VPulOvQIZkGDXbWawubLkE/Aqna7ZaF7doFYJivYIiOUPYwurs/rLg/jB0FC1bIEkZF3Ic3mzYTP2pWEughzVJNXjRHS/hEoeKoSJR1i+t1oMiMopEvCXruj1FD+PSBqIC2dDNU4jHAYhfEbmHB9XhecuMvTBJkE0E/zuWfz0WPWHgRGGFZDdQanifOrp62lzWot8NzgBzL+iBwpvGC3LEbqRcxvB6/h9lGZ2aJrD5vVmpBPnqdNFD/2X378BLwvY+PuZwBiBKE8tfjxDpA2QWla5Y8dGDnT2ee9cj8ecp5ApTALCwvCc3F/X5jAAHewsLCAfXpP41WdPJZJqFNPS6cHk1peXvbsZ1nihAu//QL7qIza338NCPnIrcqYebL7950azL1791xkHz58cLXZR2Uk5madh72yW0v9pPUjo3bqQ6gcHKJ7dsFIGSG5Kjl+fQlIUylnFQgJcTOPubVnvgPJS/f27Vv2/d27dy7S9eIOVtc2YS9tjYwBjv10J+OIJ4G9n5+6jlmWZSGdTuP09JQtGZGenp4yUsuysL+/j6RVgy2R0vcxwLEhUluZLGnVQFoumwc/ITKFjbufUfJxxWOAYyu9dsez0kuGXuTsS9UnkUoHOv4xe2lrpLS7fTNTaALffO3ZMZFK43VhoMGUs1IqSYlx/WAPqoMogSmNnFnLZEbORDRlsA9fo6GcVgcRYKC+fikDDRiZjCMyGRcIwlT+BbPwShVY537GHTfziEzGlSdcbUKyF1Wpg5dgNJnxVC45EfMlJNIQkw1N5iLUQdAlya0TAsPfWQxFGEQWZPihCOUCwzAIfRXkRzQ+NaGsr/HQklBOEVUayZ+I/RBaQjL+LsT9so/KiGWClSkUIX+2l0tgzWoN8DljsDHCEFKqQAdRVdEhCKGXlNI9lfqrinkyQtuhLElYBx5KQgpJRg6uilOoeBgECr68OfCSye++mBAYaKjuwF9MeFu4NULdGsH/V0LdGkEoQr9BdTOBW5NQNwu4FUK6ZtCpcmj9gYfgt2y6JRVtCemSOSi1eL44i8Tu9o3XgVb7H0NEGgQjZ+L54qznKVrLees4Zr5PpmDgqtVEIpXGC+l2LVS0kK9+ZPAZQacWdw65fpddQaCjmpEzByUuriRG+3x9XkfczLNgzUNLwqtWU7yvePAdFh8uY+af1/0/6zQ8I73vlazyB/3SlZEDYpNOHeD6vC54nVv7qyAdx1fyAKoAuPteoOxbCfaC65ZbBb8iUdgj3n82KCKUHgxupgAAAABJRU5ErkJggg==',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setIsAnimated(false)
|
||||
.setIsLooping(false)
|
||||
.setFrameWidth(64)
|
||||
.setFrameWidth(28)
|
||||
.setFrameHeight(94)
|
||||
.setFrameRate(0)
|
||||
.setSprite(characterSprite)
|
||||
@ -97,14 +104,18 @@ export default class InitCommand extends BaseCommand {
|
||||
await idleLeftUpAction
|
||||
.setAction('idle_left_up')
|
||||
.setSprites([
|
||||
'data:image/png;base64,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'
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setIsAnimated(false)
|
||||
.setIsLooping(false)
|
||||
.setFrameWidth(64)
|
||||
.setFrameHeight(94)
|
||||
.setFrameWidth(26)
|
||||
.setFrameHeight(93)
|
||||
.setFrameRate(0)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
@ -113,17 +124,39 @@ export default class InitCommand extends BaseCommand {
|
||||
await walkRightDownAction
|
||||
.setAction('walk_right_down')
|
||||
.setSprites([
|
||||
'data:image/png;base64,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',
|
||||
'data:image/png;base64,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',
|
||||
'data:image/png;base64,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',
|
||||
'data:image/png;base64,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'
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 7,
|
||||
y: 8
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 7,
|
||||
y: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 2,
|
||||
y: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setIsAnimated(true)
|
||||
.setIsLooping(false)
|
||||
.setFrameWidth(64)
|
||||
.setFrameHeight(94)
|
||||
.setFrameWidth(36)
|
||||
.setFrameHeight(102)
|
||||
.setFrameRate(7)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
@ -132,41 +165,348 @@ export default class InitCommand extends BaseCommand {
|
||||
await walkLeftUpAction
|
||||
.setAction('walk_left_up')
|
||||
.setSprites([
|
||||
'data:image/png;base64,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',
|
||||
'data:image/png;base64,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',
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAABZCAYAAADVeL+8AAAFcElEQVR4nLVZzUtbWRT/JUqURAkkIWlBeIUmuDHQrqQI3Ygu/Qtk6LLMUldDqYtAF10ozKrMLjDC7LNsEMSdm7GQboIRfBDQ99BHxQ/aQM0sknM9971z33upmQOB5H78zu/ee+75uEks//EXoiTXqPWldm9tKxE2bzIO6MbqIqzqvNZ3tHeA+rDfpMQInmvU+huri6gsr2Lm+ROt7+bkHC+XXyNXLMFzHdQbtb6kQATPNWr9Pzd/w3RlAelCBhP5itY/A+AGgFUFLAxWJClImhinipYIrCZmZ5EqWoM5xZI8RmqsLK8imZ0VJ0hiVefx5sWzwMFr4MQ6TH5eHuPu4nYwOYJAYM+t6ryadHdxizSOtX4CjiOhphgFdn91DQCwW22xX9xzmhRXPNfBq7e/R4ObWJgIHO0diMAiuH9yWN/hP38r4O/HXwPjAnvuuU4sBYBu39KKReY91w4FpTHkb74ff9VIkWjMvbWtRL1R6+eKJVhVfWCqaAWU2q02LANrI3PPdSIPlvrtVltkLYJ7a1uJ+pfTgAJ+G6ndcx0jsJn5UAEHkoC51L+cBvy60RS9ta3EzudDIysOKgGHgo8ipkgUG3xUlwAACX+A5j6Z3K8/fpIc7R0AkPc7AJ5r1PpvXjxTN8+qzgfsm6IPtZEpkgFwJZMSMDFNFa1BOMNAAf1OFzK4Gc6jOCoFa+2GSsDpQgZ3AOAOxqQLGQADu7+/ulYrkYJ1krPmwCQT+YoC5G1cZp4/Qe7VoiJIoqyFGqVrz8Ek4Il8BRP5CqYrC1qwVuDcIvgB/rzUY6i/7ebkXP1OFzLaqjU7t1tt7Hw+DLDnYH5lPdfGzcm5WlUyO6t2QYFzjaMIEZFWOJbr7xfa4kjwuHnK3cVtgH0s5mEKRvbnJCZnZVLmb49kblLA200BfeQDlVhHBuieaxv3z88+rm8fmymSQu6HjOCmfZRYe66DnmurSoQu5KOYR2VmY9kWikIjgcfJGcPEmM5xBaSEK+u5dmTKZ2RumsiVRUkA/LI0j+2zqchkNCwBJRELrpWVFWw3m8DZN3xAUAGBbp9NAQDyQ2UUR2llxmpufX0djuPgXbMp9E4pEgDw7+4DAe52RfBSqYRyuYxyuYylpSUAQKfTEUk4jnlrRPByuSwO9rd3Oh2USnLdD7AD9R/e3Nyc+pgUmkgQlmiKBOj/7VcYJQo8yqyiCJBwf58EHsoUk48g6Xa7xj7Pdcxhjqe++/v7AVCylm63qz4mCyIJWMvm0x/Y3t0F8HBgBCKB+U2RF7xifv6hCLwbKiChC0OmR6DNZhN5BmxkzvPz3apeNYgXdbjSOiNvt9qqjNHA+dvW/dX1oERvAcApNp/+UAQAPSvmNSu3Os3OqZKg78CDiXJL4kUCT2BplWQcGjgFWH8lAehlOwEns7NIZmfx6eN7VezyV7rINy7OxP+qMZ1dUKv89PE9i06nD9tClsJLElPiQ2W73Wor6+BjxbKFREriRSXD7SFgKiUBBCsLXoXRNTaFOdp/u9VWMZUXw6RYYx6XtZ89MfcTCmwLdz6jekoSMgINnIDjZrH+1+fQS3R/da0dUFwxVYKhT3+mpw5ezoeVmGIF/RgxXn8uUXmgsuWQN/SxpNB+JxcA/5Xy3D8nVyxpjivgcklGtXGJ3CSdPAGTZzNZCpeeayMF/c1LA6cv6UJmpP8jPNcZRqm23uYH99+0qC0hv05+W+pX4L9i41FbBvxP7y2h4Ka/w8YCPi4RwcflZ4zMpT/yHgWeLmQwXVlAqmjh5fJrbKwuPkqBAh/lAsUVdUPvr64HD8LjBrdbbVhVaH4iToUcCe6tbSV2GrX+BgDyE6bH9lFFvfz7D+4xoCT/AaNCPNJBtKgMAAAAAElFTkSuQmCC',
|
||||
'data:image/png;base64,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'
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 3,
|
||||
y: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAABZCAYAAADVeL+8AAAFcElEQVR4nLVZzUtbWRT/JUqURAkkIWlBeIUmuDHQrqQI3Ygu/Qtk6LLMUldDqYtAF10ozKrMLjDC7LNsEMSdm7GQboIRfBDQ99BHxQ/aQM0sknM9971z33upmQOB5H78zu/ee+75uEks//EXoiTXqPWldm9tKxE2bzIO6MbqIqzqvNZ3tHeA+rDfpMQInmvU+huri6gsr2Lm+ROt7+bkHC+XXyNXLMFzHdQbtb6kQATPNWr9Pzd/w3RlAelCBhP5itY/A+AGgFUFLAxWJClImhinipYIrCZmZ5EqWoM5xZI8RmqsLK8imZ0VJ0hiVefx5sWzwMFr4MQ6TH5eHuPu4nYwOYJAYM+t6ryadHdxizSOtX4CjiOhphgFdn91DQCwW22xX9xzmhRXPNfBq7e/R4ObWJgIHO0diMAiuH9yWN/hP38r4O/HXwPjAnvuuU4sBYBu39KKReY91w4FpTHkb74ff9VIkWjMvbWtRL1R6+eKJVhVfWCqaAWU2q02LANrI3PPdSIPlvrtVltkLYJ7a1uJ+pfTgAJ+G6ndcx0jsJn5UAEHkoC51L+cBvy60RS9ta3EzudDIysOKgGHgo8ipkgUG3xUlwAACX+A5j6Z3K8/fpIc7R0AkPc7AJ5r1PpvXjxTN8+qzgfsm6IPtZEpkgFwJZMSMDFNFa1BOMNAAf1OFzK4Gc6jOCoFa+2GSsDpQgZ3AOAOxqQLGQADu7+/ulYrkYJ1krPmwCQT+YoC5G1cZp4/Qe7VoiJIoqyFGqVrz8Ek4Il8BRP5CqYrC1qwVuDcIvgB/rzUY6i/7ebkXP1OFzLaqjU7t1tt7Hw+DLDnYH5lPdfGzcm5WlUyO6t2QYFzjaMIEZFWOJbr7xfa4kjwuHnK3cVtgH0s5mEKRvbnJCZnZVLmb49kblLA200BfeQDlVhHBuieaxv3z88+rm8fmymSQu6HjOCmfZRYe66DnmurSoQu5KOYR2VmY9kWikIjgcfJGcPEmM5xBaSEK+u5dmTKZ2RumsiVRUkA/LI0j+2zqchkNCwBJRELrpWVFWw3m8DZN3xAUAGBbp9NAQDyQ2UUR2llxmpufX0djuPgXbMp9E4pEgDw7+4DAe52RfBSqYRyuYxyuYylpSUAQKfTEUk4jnlrRPByuSwO9rd3Oh2USnLdD7AD9R/e3Nyc+pgUmkgQlmiKBOj/7VcYJQo8yqyiCJBwf58EHsoUk48g6Xa7xj7Pdcxhjqe++/v7AVCylm63qz4mCyIJWMvm0x/Y3t0F8HBgBCKB+U2RF7xifv6hCLwbKiChC0OmR6DNZhN5BmxkzvPz3apeNYgXdbjSOiNvt9qqjNHA+dvW/dX1oERvAcApNp/+UAQAPSvmNSu3Os3OqZKg78CDiXJL4kUCT2BplWQcGjgFWH8lAehlOwEns7NIZmfx6eN7VezyV7rINy7OxP+qMZ1dUKv89PE9i06nD9tClsJLElPiQ2W73Wor6+BjxbKFREriRSXD7SFgKiUBBCsLXoXRNTaFOdp/u9VWMZUXw6RYYx6XtZ89MfcTCmwLdz6jekoSMgINnIDjZrH+1+fQS3R/da0dUFwxVYKhT3+mpw5ezoeVmGIF/RgxXn8uUXmgsuWQN/SxpNB+JxcA/5Xy3D8nVyxpjivgcklGtXGJ3CSdPAGTZzNZCpeeayMF/c1LA6cv6UJmpP8jPNcZRqm23uYH99+0qC0hv05+W+pX4L9i41FbBvxP7y2h4Ka/w8YCPi4RwcflZ4zMpT/yHgWeLmQwXVlAqmjh5fJrbKwuPkqBAh/lAsUVdUPvr64HD8LjBrdbbVhVaH4iToUcCe6tbSV2GrX+BgDyE6bH9lFFvfz7D+4xoCT/AaNCPNJBtKgMAAAAAElFTkSuQmCC',
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 6
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 2,
|
||||
y: 6
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setIsAnimated(true)
|
||||
.setIsLooping(false)
|
||||
.setFrameWidth(64)
|
||||
.setFrameHeight(94)
|
||||
.setFrameWidth(34)
|
||||
.setFrameHeight(101)
|
||||
.setFrameRate(7)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const attackRightDownAction = new SpriteAction()
|
||||
await attackRightDownAction
|
||||
.setAction('attack_right_down')
|
||||
.setSprites([
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 20,
|
||||
y: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 19,
|
||||
y: 8
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 17,
|
||||
y: 3
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setFrameWidth(69)
|
||||
.setFrameHeight(111)
|
||||
.setFrameRate(5)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const attackLeftUpAction = new SpriteAction()
|
||||
await attackLeftUpAction
|
||||
.setAction('attack_left_up')
|
||||
.setSprites([
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 2,
|
||||
y: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 6,
|
||||
y: 1
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setFrameWidth(34)
|
||||
.setFrameHeight(100)
|
||||
.setFrameRate(5)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const characterType = new CharacterType()
|
||||
await characterType.setId('75b70c78-17f0-44c0-a4fa-15043cb95be0').setName('New character type').setGender(CharacterGender.MALE).setRace(CharacterRace.HUMAN).setIsSelectable(true).setSprite(characterSprite).save()
|
||||
await characterType
|
||||
.setId('75b70c78-17f0-44c0-a4fa-15043cb95be0')
|
||||
.setName('Male character')
|
||||
.setGender(CharacterGender.MALE)
|
||||
.setRace(CharacterRace.HUMAN)
|
||||
.setIsSelectable(true)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
}
|
||||
|
||||
private async createFemaleCharacterType(): Promise<void> {
|
||||
const characterSprite = new Sprite()
|
||||
characterSprite.setId('023d1e9d-f57f-4faa-8412-86c07107cf85').setName('Male character')
|
||||
await characterSprite.save()
|
||||
|
||||
const idleRightDownAction = new SpriteAction()
|
||||
await idleRightDownAction
|
||||
.setAction('idle_right_down')
|
||||
.setSprites([
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setFrameWidth(28)
|
||||
.setFrameHeight(94)
|
||||
.setFrameRate(0)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const idleLeftUpAction = new SpriteAction()
|
||||
await idleLeftUpAction
|
||||
.setAction('idle_left_up')
|
||||
.setSprites([
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setFrameWidth(26)
|
||||
.setFrameHeight(93)
|
||||
.setFrameRate(0)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const walkRightDownAction = new SpriteAction()
|
||||
await walkRightDownAction
|
||||
.setAction('walk_right_down')
|
||||
.setSprites([
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 7,
|
||||
y: 8
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 7,
|
||||
y: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 2,
|
||||
y: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setFrameWidth(36)
|
||||
.setFrameHeight(102)
|
||||
.setFrameRate(7)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const walkLeftUpAction = new SpriteAction()
|
||||
await walkLeftUpAction
|
||||
.setAction('walk_left_up')
|
||||
.setSprites([
|
||||
{
|
||||
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAABdCAYAAABZy21jAAAGdElEQVR4nK2aMWgbSRSGf8lBMXKMQRJSAoY1RIsaC6RKGIMbEZUurwqHq+O40q6OAxeCK2248jrBBQ6uFKks3BhCcGWB0uikgBbMJbvnLGdkC0cQ64r1G8/uzuzOrvQqaWY037yZ92bevFGi/vPvUJVMuzkTldu7hwnlTgA8iQLbb9SglUuuuovTM7Qe6lXhodBMuznbb9Sg1xt49vK5q+7m42dU6zvI5AuwLROtdnOmAg6EZtrN2W8H32NZ30Q6t4KlrO6qfwbgBoBWBjQ4M6ACTgYB9xs1pPKaEMg6WFtFKq85v8kXgljhUADQ6w0k11aVOgIArVzCXmVDanCBUNIySL59GWBydet0EmFgQMCaauUS62xydYs0Bq56AsYRJZcJg9xfjwEARq+v1FfgmlJnqmJbJrZ+/Ck+VHXUNLCL0zMlYCDU22lQ3fmffzDg3eBDKFS6prZlKoEBt3+qzFCgplPLCO1gahlsP74bfHANViZCTe3dw0Sr3Zxl8gVoZXddKq/5BmP0+tCwIOu1LTO0I6o3en0lLQOh9u5hotUd+cD87kPltmUqAwOhPJgHiIC8tLqj0HM11GXs3cPE8cl5WDO0uiMloBI0iqhGDpGhUbdGkSRkgRl/JtIx542PSC5OzwA4UwyEayyEZtrN2V5lg+00Wrnk80+KFqiMd5mwtfVtDgSs1ndcgOTaKlJwwPQ9nVvBzUMbipOMXh97leBYyQWliIGmkbQhwAQALKdtOrfC6u6vx6xt9Yca7PeOtcvAPkMi4LK+6SpfyuoMxJfx8uzlcyxldWS2aqjWd6TxEoPStBJQZKU8RBQd3nz8zOqW9U1pdCh0mfvrMQanJy7D+fZl4GvHl3kPgXRuBXq9IdTWBc3kC1jWNzG1DByfnPu2Ph4iGgRfvpTVkVxbFWor1FT1iPL+ZmoZvgBO5NtJbwOvscQR0lbWl09T2fVBJc41en3cDT6Etk0Cj5ZLbiI7G8M643837nfZZ68xuTaH1VIF/7594+vs/nosvDp4B/H2738AnKEKv5/z8uinD1Zm9Pps4/aCRULlpKUDdsonV7dI5TWfBTOoVi5J3UAksqm+mkzZqcP37YLy6zm5ug2MdbzairSnEIc2C9GyLDRyuJpM2eeL0zMW7We2ai5jcqz3Yc5FVwJZwB0lgljK6q51ZZrOsymo3AT4dU1SAW0KqhFdkPChKwnvQkprqqKJSIxeX7gMDCpyF96Sp5bB4PwgppahdEDwyxeqqaxDfhAysS1T2IZBvc7+pVDC0aenoZco1YsTf5AEJjpevXqFo04H+PQffoUfTLCjT0+dgkIJKhbogoo0ev36NUzTxC+djuDnT9ngAKDDtaE7brW+w+xlr7KBVrs5C9S0UCigWCyiWCxie3sbw+EQxWKR1Q+HQ/bZNIOnmF++QEPiAbLvVFYoiCM/o9d3kl+5FbYrMU1VsiIAsL6+7vp+eXmJYrHo0tor6dyKXFORFa6vrzOQFygrCxMGjRMBqohtmT53TBIwSs5gXnli7x4mjrmgid/s3717h+3tbdb48vJSuKYkWbMPWwLic0xPvCC+g07n0SpN05Ra6HA4DHQZr5EG+unBi684euOODmkj4AcDOBtDNqAvCvjs3cNEaL73r/3vMLUM5ymkO4JwY3oYYEuirNdmQqF0867Wd9DqjnDw4iuc9N1jJBBmiLZluoKD0IwZuRL/IqHXG1jWN7GsbyKV16QJEOrDG42EakpnIkEpt0+7THJtlaUEZGBvmVKajq6BpDG90/DRgOqbDKD4cGBbJtB7GGXE5xCRKOUGW90Rjk/OsVfZ8L1GUeBF9WEPQUpQAnvLRIHcQp694nQa+9lLVej0ILeSuc5CoEFXSpXZiAwl7fgzMuqxqPzWRnJ/PXZyhIif+40MnVoGUtBc30lUg4G5DImAre4IlFdUkYXexGkAc79WhEmcgG5hmkbZ8Bc6vXM9xEcRr7VW6zvYb9QCwZGg9Gox7/Gm/N8VSnBRxEDXfnIX/swN89VIm4Ms7fOYSRmxskjvMjIRWSdpFDUFpLymqsfWQqGLlLmgUTaEhUHjylzQuOs8t6a07amEnnND+bvNXmVDOeYFYkQOS1kdaQwwAZCC5vz5ohctToqk6Tx/fIoNvb8eu/7mFVeUp9fo9aGVwZ6jgcdcr/fSuxAoZWD2AYDLhhKM2iiOX/6XA5GIrDNOvv9/P1uP8Q0WHzUAAAAASUVORK5CYII=',
|
||||
offset: {
|
||||
x: 3,
|
||||
y: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAABZCAYAAADVeL+8AAAFcElEQVR4nLVZzUtbWRT/JUqURAkkIWlBeIUmuDHQrqQI3Ygu/Qtk6LLMUldDqYtAF10ozKrMLjDC7LNsEMSdm7GQboIRfBDQ99BHxQ/aQM0sknM9971z33upmQOB5H78zu/ee+75uEks//EXoiTXqPWldm9tKxE2bzIO6MbqIqzqvNZ3tHeA+rDfpMQInmvU+huri6gsr2Lm+ROt7+bkHC+XXyNXLMFzHdQbtb6kQATPNWr9Pzd/w3RlAelCBhP5itY/A+AGgFUFLAxWJClImhinipYIrCZmZ5EqWoM5xZI8RmqsLK8imZ0VJ0hiVefx5sWzwMFr4MQ6TH5eHuPu4nYwOYJAYM+t6ryadHdxizSOtX4CjiOhphgFdn91DQCwW22xX9xzmhRXPNfBq7e/R4ObWJgIHO0diMAiuH9yWN/hP38r4O/HXwPjAnvuuU4sBYBu39KKReY91w4FpTHkb74ff9VIkWjMvbWtRL1R6+eKJVhVfWCqaAWU2q02LANrI3PPdSIPlvrtVltkLYJ7a1uJ+pfTgAJ+G6ndcx0jsJn5UAEHkoC51L+cBvy60RS9ta3EzudDIysOKgGHgo8ipkgUG3xUlwAACX+A5j6Z3K8/fpIc7R0AkPc7AJ5r1PpvXjxTN8+qzgfsm6IPtZEpkgFwJZMSMDFNFa1BOMNAAf1OFzK4Gc6jOCoFa+2GSsDpQgZ3AOAOxqQLGQADu7+/ulYrkYJ1krPmwCQT+YoC5G1cZp4/Qe7VoiJIoqyFGqVrz8Ek4Il8BRP5CqYrC1qwVuDcIvgB/rzUY6i/7ebkXP1OFzLaqjU7t1tt7Hw+DLDnYH5lPdfGzcm5WlUyO6t2QYFzjaMIEZFWOJbr7xfa4kjwuHnK3cVtgH0s5mEKRvbnJCZnZVLmb49kblLA200BfeQDlVhHBuieaxv3z88+rm8fmymSQu6HjOCmfZRYe66DnmurSoQu5KOYR2VmY9kWikIjgcfJGcPEmM5xBaSEK+u5dmTKZ2RumsiVRUkA/LI0j+2zqchkNCwBJRELrpWVFWw3m8DZN3xAUAGBbp9NAQDyQ2UUR2llxmpufX0djuPgXbMp9E4pEgDw7+4DAe52RfBSqYRyuYxyuYylpSUAQKfTEUk4jnlrRPByuSwO9rd3Oh2USnLdD7AD9R/e3Nyc+pgUmkgQlmiKBOj/7VcYJQo8yqyiCJBwf58EHsoUk48g6Xa7xj7Pdcxhjqe++/v7AVCylm63qz4mCyIJWMvm0x/Y3t0F8HBgBCKB+U2RF7xifv6hCLwbKiChC0OmR6DNZhN5BmxkzvPz3apeNYgXdbjSOiNvt9qqjNHA+dvW/dX1oERvAcApNp/+UAQAPSvmNSu3Os3OqZKg78CDiXJL4kUCT2BplWQcGjgFWH8lAehlOwEns7NIZmfx6eN7VezyV7rINy7OxP+qMZ1dUKv89PE9i06nD9tClsJLElPiQ2W73Wor6+BjxbKFREriRSXD7SFgKiUBBCsLXoXRNTaFOdp/u9VWMZUXw6RYYx6XtZ89MfcTCmwLdz6jekoSMgINnIDjZrH+1+fQS3R/da0dUFwxVYKhT3+mpw5ezoeVmGIF/RgxXn8uUXmgsuWQN/SxpNB+JxcA/5Xy3D8nVyxpjivgcklGtXGJ3CSdPAGTZzNZCpeeayMF/c1LA6cv6UJmpP8jPNcZRqm23uYH99+0qC0hv05+W+pX4L9i41FbBvxP7y2h4Ka/w8YCPi4RwcflZ4zMpT/yHgWeLmQwXVlAqmjh5fJrbKwuPkqBAh/lAsUVdUPvr64HD8LjBrdbbVhVaH4iToUcCe6tbSV2GrX+BgDyE6bH9lFFvfz7D+4xoCT/AaNCPNJBtKgMAAAAAElFTkSuQmCC',
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 6
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 2,
|
||||
y: 6
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setFrameWidth(34)
|
||||
.setFrameHeight(101)
|
||||
.setFrameRate(7)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const attackRightDownAction = new SpriteAction()
|
||||
await attackRightDownAction
|
||||
.setAction('attack_right_down')
|
||||
.setSprites([
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 20,
|
||||
y: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 19,
|
||||
y: 8
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 17,
|
||||
y: 3
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setFrameWidth(69)
|
||||
.setFrameHeight(111)
|
||||
.setFrameRate(5)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const attackLeftUpAction = new SpriteAction()
|
||||
await attackLeftUpAction
|
||||
.setAction('attack_left_up')
|
||||
.setSprites([
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 2,
|
||||
y: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'data:image/png;base64,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',
|
||||
offset: {
|
||||
x: 6,
|
||||
y: 1
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0)
|
||||
.setOriginY(0)
|
||||
.setFrameWidth(34)
|
||||
.setFrameHeight(100)
|
||||
.setFrameRate(5)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
|
||||
const characterType = new CharacterType()
|
||||
await characterType
|
||||
.setId('75b70c78-17f0-44c0-a4fa-15043cb95be0')
|
||||
.setName('Male character')
|
||||
.setGender(CharacterGender.MALE)
|
||||
.setRace(CharacterRace.HUMAN)
|
||||
.setIsSelectable(true)
|
||||
.setSprite(characterSprite)
|
||||
.save()
|
||||
}
|
||||
|
||||
private async createCharacterHair(): Promise<void> {
|
||||
const hairSprite = new Sprite()
|
||||
hairSprite.setId('922ee95f-1500-49c0-8ead-f8cc46dad136').setName('Hair 1')
|
||||
hairSprite.setId('922ee95f-1500-49c0-8ead-f8cc46dad136').setName('Hair 1').setWidth(30).setHeight(40)
|
||||
await hairSprite.save()
|
||||
|
||||
const frontAction = new SpriteAction()
|
||||
await frontAction
|
||||
.setAction('front')
|
||||
.setSprites([
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAASCAYAAABB7B6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5ElEQVR4nK2VMQ7DIAxFc4AulViysTBUldgqReoGp+pReloqIhx9LOy4IoOVYBw//DFkyZ/vUs3db2Vkuc1rMTwOrRvUwPjwZXs9S0rv/d1BkuDXYx4N41SABAl+7ZJXPzcEqQALJApmroAnC22smbQPw5WPkjoAE7w++X6JAJSF9JRWS3EWiEnzwCrBWPJLkA4gdU1sPvoQIdjGGE+xp8mTULr1IO5Orjlt9NkGaif4kIgqwC6hSarMkkgFoLZ8hVvTeQoglXwJQNMV5bsUUJNKnZJnAHhVWO76vwCjH8hs8gr4ARPESmbfQUg4AAAAAElFTkSuQmCC'
|
||||
{
|
||||
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAASCAYAAABB7B6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5ElEQVR4nK2VMQ7DIAxFc4AulViysTBUldgqReoGp+pReloqIhx9LOy4IoOVYBw//DFkyZ/vUs3db2Vkuc1rMTwOrRvUwPjwZXs9S0rv/d1BkuDXYx4N41SABAl+7ZJXPzcEqQALJApmroAnC22smbQPw5WPkjoAE7w++X6JAJSF9JRWS3EWiEnzwCrBWPJLkA4gdU1sPvoQIdjGGE+xp8mTULr1IO5Orjlt9NkGaif4kIgqwC6hSarMkkgFoLZ8hVvTeQoglXwJQNMV5bsUUJNKnZJnAHhVWO76vwCjH8hs8gr4ARPESmbfQUg4AAAAAElFTkSuQmCC',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0.5)
|
||||
.setOriginY(5.34)
|
||||
.setIsAnimated(false)
|
||||
.setIsLooping(false)
|
||||
.setFrameWidth(64)
|
||||
.setFrameWidth(24)
|
||||
.setFrameHeight(18)
|
||||
.setFrameRate(0)
|
||||
.setSprite(hairSprite)
|
||||
@ -176,52 +516,24 @@ export default class InitCommand extends BaseCommand {
|
||||
await backAction
|
||||
.setAction('back')
|
||||
.setSprites([
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAWCAYAAADafVyIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABHUlEQVR4nJ1VuwrDMAzMB3QpZOmWxUMpdCsYsrlflU/p16YIrCJfTrLTQZCHfSflLufpvX0mVvP1sns1sk7fu8DP+7Ln16MpeZaWWwMkz0pZf++xgQO4AssmLEsi1QN3J5DFXqUT4JRASRQoBYWkpybQ7lIFssCsASUb0gDBM3x7Bm6NQF3EwKWYuBa4p8nBPbgpB+CeqxoC9pPoBgau99ixR+I6yAPPRhtLwCZ3CbzPkquQVh9Zp3uty8IJLHgpa9OV3ishyyYbOeFPhnZFh0UEeu0SoIC5dm8JoojoRgXqUCq4JcB4GIoKpkMiE2EGDUUFngnoILQiOx+6Bw46IZFYQMJIB1ccL7pn+BH/EtkjsZONgA8TMIeMgAvBF10K1sbmijzuAAAAAElFTkSuQmCC'
|
||||
{
|
||||
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAWCAYAAADafVyIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABHUlEQVR4nJ1VuwrDMAzMB3QpZOmWxUMpdCsYsrlflU/p16YIrCJfTrLTQZCHfSflLufpvX0mVvP1sns1sk7fu8DP+7Ln16MpeZaWWwMkz0pZf++xgQO4AssmLEsi1QN3J5DFXqUT4JRASRQoBYWkpybQ7lIFssCsASUb0gDBM3x7Bm6NQF3EwKWYuBa4p8nBPbgpB+CeqxoC9pPoBgau99ixR+I6yAPPRhtLwCZ3CbzPkquQVh9Zp3uty8IJLHgpa9OV3ishyyYbOeFPhnZFh0UEeu0SoIC5dm8JoojoRgXqUCq4JcB4GIoKpkMiE2EGDUUFngnoILQiOx+6Bw46IZFYQMJIB1ccL7pn+BH/EtkjsZONgA8TMIeMgAvBF10K1sbmijzuAAAAAElFTkSuQmCC',
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
])
|
||||
.setOriginX(0.5)
|
||||
.setOriginY(4.34)
|
||||
.setIsAnimated(false)
|
||||
.setIsLooping(false)
|
||||
.setFrameWidth(64)
|
||||
.setFrameWidth(24)
|
||||
.setFrameHeight(22)
|
||||
.setFrameRate(0)
|
||||
.setSprite(hairSprite)
|
||||
.save()
|
||||
|
||||
const characterHair = new CharacterHair()
|
||||
await characterHair.setId('a2471230-d238-4ffb-9eca-9eab869f1b67').setName('Hair 1').setGender(CharacterGender.MALE).setIsSelectable(true).setSprite(hairSprite).save()
|
||||
}
|
||||
|
||||
private async createCharacterEquipment(): Promise<void> {
|
||||
const equipmentSprite = new Sprite()
|
||||
equipmentSprite.id = '5b3932dd-0791-4bb7-bb1e-da9833c3cc50'
|
||||
equipmentSprite.name = 'Male shirt'
|
||||
|
||||
// Create actions similar to createCharacterSprite()
|
||||
// with appropriate sprite data and parameters
|
||||
const actions = [
|
||||
{
|
||||
action: 'idle_right_down',
|
||||
sprites: ['data:image/png;base64,...'],
|
||||
originX: 0,
|
||||
originY: 0,
|
||||
isAnimated: false,
|
||||
isLooping: false,
|
||||
frameWidth: 64,
|
||||
frameHeight: 94,
|
||||
frameRate: 0
|
||||
}
|
||||
// Add other actions...
|
||||
]
|
||||
|
||||
for (const actionData of actions) {
|
||||
const action = new SpriteAction()
|
||||
Object.assign(action, actionData)
|
||||
action.sprite = equipmentSprite
|
||||
await action.save()
|
||||
}
|
||||
|
||||
await equipmentSprite.save()
|
||||
await characterHair.setId('a2471230-d238-4ffb-9eca-9eab869f1b67').setName('Hair 1').setGender(CharacterGender.MALE).setColor('#1B1212').setIsSelectable(true).setSprite(hairSprite).save()
|
||||
}
|
||||
|
||||
private async createMap(): Promise<void> {
|
||||
@ -240,6 +552,8 @@ export default class InitCommand extends BaseCommand {
|
||||
private async createUser(): Promise<void> {
|
||||
const user = new User()
|
||||
await user.setId('6f9a58b4-172d-425e-b9ea-71e1d13d81ee').setUsername('root').setEmail('local@host').setPassword('password').setOnline(false).save()
|
||||
const map = await this.mapRepository.getFirst()
|
||||
if (!map) return
|
||||
|
||||
const character = new Character()
|
||||
await character
|
||||
@ -247,9 +561,9 @@ export default class InitCommand extends BaseCommand {
|
||||
.setUser(user)
|
||||
.setName('root')
|
||||
.setRole('gm')
|
||||
.setMap((await MapRepository.getFirst())!)
|
||||
.setCharacterType((await CharacterTypeRepository.getFirst()) ?? undefined)
|
||||
.setCharacterHair((await CharacterHairRepository.getFirst()) ?? undefined)
|
||||
.setMap(map)
|
||||
.setCharacterType(await this.characterTypeRepository.getFirst())
|
||||
.setCharacterHair(await this.characterHairRepository.getFirst())
|
||||
.save()
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { BaseCommand } from '#application/base/baseCommand'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import { BaseCommand } from '@/application/base/baseCommand'
|
||||
import MapManager from '@/managers/mapManager'
|
||||
|
||||
type CommandInput = string[]
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import { BaseCommand } from '@/application/base/baseCommand'
|
||||
import Storage from '@/application/storage'
|
||||
import sharp from 'sharp'
|
||||
|
||||
import { BaseCommand } from '#application/base/baseCommand'
|
||||
import Storage from '#application/storage'
|
||||
|
||||
export default class TilesCommand extends BaseCommand {
|
||||
public async execute(): Promise<void> {
|
||||
// Get all tiles
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { BaseController } from '@/application/base/baseController'
|
||||
import config from '@/application/config'
|
||||
import { loginAccountSchema, newPasswordSchema, registerAccountSchema, resetPasswordSchema } from '@/application/zodTypes'
|
||||
import UserService from '@/services/userService'
|
||||
import type { Request, Response } from 'express'
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
import { BaseController } from '#application/base/baseController'
|
||||
import config from '#application/config'
|
||||
import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPasswordSchema } from '#application/zodTypes'
|
||||
import UserService from '#services/userService'
|
||||
|
||||
export class AuthController extends BaseController {
|
||||
/**
|
||||
* Login user
|
@ -1,28 +1,30 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import { Request, Response } from 'express'
|
||||
import { BaseController } from '@/application/base/baseController'
|
||||
import Storage from '@/application/storage'
|
||||
import type { UUID } from '@/application/types'
|
||||
import CharacterHairRepository from '@/repositories/characterHairRepository'
|
||||
import CharacterRepository from '@/repositories/characterRepository'
|
||||
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
|
||||
import type { Request, Response } from 'express'
|
||||
import sharp from 'sharp'
|
||||
|
||||
import { BaseController } from '#application/base/baseController'
|
||||
import Storage from '#application/storage'
|
||||
import { UUID } from '#application/types'
|
||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||
|
||||
interface AvatarOptions {
|
||||
characterTypeId: UUID
|
||||
characterHairId?: UUID
|
||||
}
|
||||
|
||||
export class AvatarController extends BaseController {
|
||||
private readonly characterTypeRepository = new CharacterTypeRepository()
|
||||
private readonly characterHairRepository = new CharacterHairRepository()
|
||||
private readonly characterRepository = new CharacterRepository()
|
||||
|
||||
/**
|
||||
* Get avatar by character
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async getByName(req: Request, res: Response) {
|
||||
const character = await CharacterRepository.getByName(req.params.characterName)
|
||||
const character = await this.characterRepository.getByName(req.params.characterName!)
|
||||
if (!character?.characterType) {
|
||||
return this.sendError(res, 'Character or character type not found', 404)
|
||||
}
|
||||
@ -53,7 +55,7 @@ export class AvatarController extends BaseController {
|
||||
*/
|
||||
private async generateAvatar(res: Response, options: AvatarOptions) {
|
||||
try {
|
||||
const characterType = await CharacterTypeRepository.getById(options.characterTypeId)
|
||||
const characterType = await this.characterTypeRepository.getById(options.characterTypeId)
|
||||
if (!characterType?.sprite?.id) {
|
||||
return this.sendError(res, 'Character type not found', 404)
|
||||
}
|
||||
@ -63,18 +65,35 @@ export class AvatarController extends BaseController {
|
||||
return this.sendError(res, 'Body sprite file not found', 404)
|
||||
}
|
||||
|
||||
// Get body sprite metadata
|
||||
const bodyMetadata = await sharp(bodySpritePath).metadata()
|
||||
|
||||
let avatar = sharp(bodySpritePath).extend({
|
||||
top: 2,
|
||||
bottom: 2,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
})
|
||||
|
||||
if (options.characterHairId) {
|
||||
const characterHair = await CharacterHairRepository.getById(options.characterHairId)
|
||||
const characterHair = await this.characterHairRepository.getById(options.characterHairId)
|
||||
if (characterHair?.sprite?.id) {
|
||||
const hairSpritePath = Storage.getPublicPath('sprites', characterHair.sprite.id, 'front.png')
|
||||
if (fs.existsSync(hairSpritePath)) {
|
||||
avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }])
|
||||
// Resize hair sprite to match body dimensions
|
||||
const resizedHair = await sharp(hairSpritePath)
|
||||
.resize(bodyMetadata.width, bodyMetadata.height, {
|
||||
fit: 'contain',
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
})
|
||||
.toBuffer()
|
||||
|
||||
avatar = avatar.composite([
|
||||
{
|
||||
input: resizedHair,
|
||||
left: 0,
|
||||
top: -27 // Apply vertical offset
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,6 +101,7 @@ export class AvatarController extends BaseController {
|
||||
res.setHeader('Content-Type', 'image/png')
|
||||
return avatar.pipe(res)
|
||||
} catch (error) {
|
||||
console.error('Avatar generation error:', error)
|
||||
return this.sendError(res, 'Error generating avatar', 500)
|
||||
}
|
||||
}
|
118
src/controllers/cache.ts
Normal file
118
src/controllers/cache.ts
Normal file
@ -0,0 +1,118 @@
|
||||
import { BaseController } from '@/application/base/baseController'
|
||||
import CharacterHairRepository from '@/repositories/characterHairRepository'
|
||||
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
|
||||
import MapObjectRepository from '@/repositories/mapObjectRepository'
|
||||
import MapRepository from '@/repositories/mapRepository'
|
||||
import SpriteRepository from '@/repositories/spriteRepository'
|
||||
import TileRepository from '@/repositories/tileRepository'
|
||||
import type { Request, Response } from 'express'
|
||||
|
||||
export class CacheController extends BaseController {
|
||||
/**
|
||||
* Serve a list of tiles and send as JSON
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async tiles(req: Request, res: Response) {
|
||||
const items: any[] = []
|
||||
|
||||
const tileRepository = new TileRepository()
|
||||
const tiles = await tileRepository.getAll()
|
||||
|
||||
for (const tile of tiles) {
|
||||
items.push(await tile.cache())
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, items)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a list of maps and send as JSON
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async maps(req: Request, res: Response) {
|
||||
const items: any[] = []
|
||||
|
||||
const mapRepository = new MapRepository()
|
||||
const maps = await mapRepository.getAll()
|
||||
|
||||
for (const map of maps) {
|
||||
items.push(await map.cache())
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, items)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a list of map objects and send as JSON
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async mapObjects(req: Request, res: Response) {
|
||||
const items: any[] = []
|
||||
|
||||
const mapObjectRepository = new MapObjectRepository()
|
||||
const mapObjects = await mapObjectRepository.getAll()
|
||||
|
||||
for (const mapObject of mapObjects) {
|
||||
items.push(await mapObject.cache())
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, items)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a list of character hairs and send as JSON
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async characterHair(req: Request, res: Response) {
|
||||
const items: any[] = []
|
||||
|
||||
const characterHairRepository = new CharacterHairRepository()
|
||||
const characterHairs = await characterHairRepository.getAll()
|
||||
|
||||
for (const characterHair of characterHairs) {
|
||||
items.push(await characterHair.cache())
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, items)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a list of character types and send as JSON
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async characterTypes(req: Request, res: Response) {
|
||||
const items: any[] = []
|
||||
|
||||
const characterTypeRepository = new CharacterTypeRepository()
|
||||
const characterTypes = await characterTypeRepository.getAll()
|
||||
|
||||
for (const characterType of characterTypes) {
|
||||
items.push(await characterType.cache())
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, items)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a list of sprites and send as JSON
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async sprites(req: Request, res: Response) {
|
||||
const items: any[] = []
|
||||
|
||||
const spriteRepository = new SpriteRepository()
|
||||
const sprites = await spriteRepository.getAll()
|
||||
|
||||
for (const sprite of sprites) {
|
||||
items.push(await sprite.cache())
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, items)
|
||||
}
|
||||
}
|
22
src/controllers/textures.ts
Normal file
22
src/controllers/textures.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { BaseController } from '@/application/base/baseController'
|
||||
import Storage from '@/application/storage'
|
||||
import type { Request, Response } from 'express'
|
||||
|
||||
export class TexturesController extends BaseController {
|
||||
/**
|
||||
* Download texture
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async download(req: Request, res: Response) {
|
||||
const { type, spriteId, file } = req.params
|
||||
|
||||
if (!type || !file) {
|
||||
return this.sendError(res, 'Invalid request', 400)
|
||||
}
|
||||
|
||||
const texture = type === 'sprites' && spriteId ? Storage.getPublicPath(type, spriteId, file) : Storage.getPublicPath(type, file)
|
||||
|
||||
this.sendFile(res, texture)
|
||||
}
|
||||
}
|
293
src/entities/base/character.ts
Normal file
293
src/entities/base/character.ts
Normal file
@ -0,0 +1,293 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { CharacterEquipment } from '@/entities/characterEquipment'
|
||||
import type { CharacterHair } from '@/entities/characterHair'
|
||||
import type { CharacterItem } from '@/entities/characterItem'
|
||||
import type { CharacterType } from '@/entities/characterType'
|
||||
import type { Chat } from '@/entities/chat'
|
||||
import type { Map } from '@/entities/map'
|
||||
import type { User } from '@/entities/user'
|
||||
import { Collection, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseCharacter extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
user!: User
|
||||
|
||||
@Property({ unique: true })
|
||||
name!: string
|
||||
|
||||
@Property()
|
||||
online = false
|
||||
|
||||
@Property()
|
||||
role = 'player'
|
||||
|
||||
@OneToMany({ mappedBy: 'character' })
|
||||
chats = new Collection<Chat>(this)
|
||||
|
||||
// Position - @TODO: Update to spawn point when current map is not found
|
||||
@ManyToOne()
|
||||
map!: Map
|
||||
|
||||
@Property()
|
||||
positionX = 0
|
||||
|
||||
@Property()
|
||||
positionY = 0
|
||||
|
||||
@Property()
|
||||
rotation = 0
|
||||
|
||||
// Customization
|
||||
@ManyToOne({ deleteRule: 'set null' })
|
||||
characterType: CharacterType | null = null
|
||||
|
||||
@ManyToOne({ deleteRule: 'set null' })
|
||||
characterHair: CharacterHair | null = null
|
||||
|
||||
// Inventory
|
||||
@OneToMany({ mappedBy: 'character' })
|
||||
items = new Collection<CharacterItem>(this)
|
||||
|
||||
@OneToMany({ mappedBy: 'character' })
|
||||
equipment = new Collection<CharacterEquipment>(this)
|
||||
|
||||
// Stats
|
||||
@Property()
|
||||
alignment = 50
|
||||
|
||||
@Property()
|
||||
hitpoints = 100
|
||||
|
||||
@Property()
|
||||
mana = 100
|
||||
|
||||
@Property()
|
||||
level = 1
|
||||
|
||||
@Property()
|
||||
experience = 0
|
||||
|
||||
@Property()
|
||||
strength = 10
|
||||
|
||||
@Property()
|
||||
dexterity = 10
|
||||
|
||||
@Property()
|
||||
intelligence = 10
|
||||
|
||||
@Property()
|
||||
wisdom = 10
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setUser(user: User) {
|
||||
this.user = user
|
||||
return this
|
||||
}
|
||||
|
||||
getUser() {
|
||||
return this.user
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setOnline(online: boolean) {
|
||||
this.online = online
|
||||
return this
|
||||
}
|
||||
|
||||
getOnline() {
|
||||
return this.online
|
||||
}
|
||||
|
||||
setRole(role: string) {
|
||||
this.role = role
|
||||
return this
|
||||
}
|
||||
|
||||
getRole() {
|
||||
return this.role
|
||||
}
|
||||
|
||||
setChats(chats: Collection<Chat>) {
|
||||
this.chats = chats
|
||||
return this
|
||||
}
|
||||
|
||||
getChats() {
|
||||
return this.chats
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setPositionX(positionX: number) {
|
||||
this.positionX = positionX
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionX() {
|
||||
return this.positionX
|
||||
}
|
||||
|
||||
setPositionY(positionY: number) {
|
||||
this.positionY = positionY
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionY() {
|
||||
return this.positionY
|
||||
}
|
||||
|
||||
setRotation(rotation: number) {
|
||||
this.rotation = rotation
|
||||
return this
|
||||
}
|
||||
|
||||
getRotation() {
|
||||
return this.rotation
|
||||
}
|
||||
|
||||
setCharacterType(characterType: CharacterType | null) {
|
||||
this.characterType = characterType
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacterType() {
|
||||
return this.characterType
|
||||
}
|
||||
|
||||
setCharacterHair(characterHair: CharacterHair | null) {
|
||||
this.characterHair = characterHair
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacterHair() {
|
||||
return this.characterHair
|
||||
}
|
||||
|
||||
setItems(items: Collection<CharacterItem>) {
|
||||
this.items = items
|
||||
return this
|
||||
}
|
||||
|
||||
getItems() {
|
||||
return this.items
|
||||
}
|
||||
|
||||
setEquipment(equipment: Collection<CharacterEquipment>) {
|
||||
this.equipment = equipment
|
||||
return this
|
||||
}
|
||||
|
||||
getEquipment() {
|
||||
return this.equipment
|
||||
}
|
||||
|
||||
setAlignment(alignment: number) {
|
||||
this.alignment = alignment
|
||||
return this
|
||||
}
|
||||
|
||||
getAlignment() {
|
||||
return this.alignment
|
||||
}
|
||||
|
||||
setHitpoints(hitpoints: number) {
|
||||
this.hitpoints = hitpoints
|
||||
return this
|
||||
}
|
||||
|
||||
getHitpoints() {
|
||||
return this.hitpoints
|
||||
}
|
||||
|
||||
setMana(mana: number) {
|
||||
this.mana = mana
|
||||
return this
|
||||
}
|
||||
|
||||
getMana() {
|
||||
return this.mana
|
||||
}
|
||||
|
||||
setLevel(level: number) {
|
||||
this.level = level
|
||||
return this
|
||||
}
|
||||
|
||||
getLevel() {
|
||||
return this.level
|
||||
}
|
||||
|
||||
setExperience(experience: number) {
|
||||
this.experience = experience
|
||||
return this
|
||||
}
|
||||
|
||||
getExperience() {
|
||||
return this.experience
|
||||
}
|
||||
|
||||
setStrength(strength: number) {
|
||||
this.strength = strength
|
||||
return this
|
||||
}
|
||||
|
||||
getStrength() {
|
||||
return this.strength
|
||||
}
|
||||
|
||||
setDexterity(dexterity: number) {
|
||||
this.dexterity = dexterity
|
||||
return this
|
||||
}
|
||||
|
||||
getDexterity() {
|
||||
return this.dexterity
|
||||
}
|
||||
|
||||
setIntelligence(intelligence: number) {
|
||||
this.intelligence = intelligence
|
||||
return this
|
||||
}
|
||||
|
||||
getIntelligence() {
|
||||
return this.intelligence
|
||||
}
|
||||
|
||||
setWisdom(wisdom: number) {
|
||||
this.wisdom = wisdom
|
||||
return this
|
||||
}
|
||||
|
||||
getWisdom() {
|
||||
return this.wisdom
|
||||
}
|
||||
}
|
57
src/entities/base/characterEquipment.ts
Normal file
57
src/entities/base/characterEquipment.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import { CharacterEquipmentSlotType } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { Character } from '@/entities/character'
|
||||
import type { CharacterItem } from '@/entities/characterItem'
|
||||
import { Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core'
|
||||
|
||||
export class BaseCharacterEquipment extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Enum(() => CharacterEquipmentSlotType)
|
||||
slot!: CharacterEquipmentSlotType
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
character!: Character
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
characterItem!: CharacterItem
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setSlot(slot: CharacterEquipmentSlotType) {
|
||||
this.slot = slot
|
||||
return this
|
||||
}
|
||||
|
||||
getSlot() {
|
||||
return this.slot
|
||||
}
|
||||
|
||||
setCharacter(character: Character) {
|
||||
this.character = character
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacter() {
|
||||
return this.character
|
||||
}
|
||||
|
||||
setCharacterItem(characterItem: CharacterItem) {
|
||||
this.characterItem = characterItem
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacterItem() {
|
||||
return this.characterItem
|
||||
}
|
||||
}
|
105
src/entities/base/characterHair.ts
Normal file
105
src/entities/base/characterHair.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import { CharacterGender } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { Character } from '@/entities/character'
|
||||
import { Sprite } from '@/entities/sprite'
|
||||
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseCharacterHair extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property()
|
||||
gender: CharacterGender = CharacterGender.MALE
|
||||
|
||||
@Property()
|
||||
color: string = '#000000'
|
||||
|
||||
@Property()
|
||||
isSelectable = false
|
||||
|
||||
@ManyToOne()
|
||||
sprite!: Sprite
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setGender(gender: CharacterGender) {
|
||||
this.gender = gender
|
||||
return this
|
||||
}
|
||||
|
||||
getGender() {
|
||||
return this.gender
|
||||
}
|
||||
|
||||
setColor(color: string) {
|
||||
this.color = color
|
||||
return this
|
||||
}
|
||||
|
||||
getColor() {
|
||||
return this.color
|
||||
}
|
||||
|
||||
setIsSelectable(isSelectable: boolean) {
|
||||
this.isSelectable = isSelectable
|
||||
return this
|
||||
}
|
||||
|
||||
getIsSelectable() {
|
||||
return this.isSelectable
|
||||
}
|
||||
|
||||
setSprite(sprite: Sprite) {
|
||||
this.sprite = sprite
|
||||
return this
|
||||
}
|
||||
|
||||
getSprite() {
|
||||
return this.sprite
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
}
|
56
src/entities/base/characterItem.ts
Normal file
56
src/entities/base/characterItem.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { Character } from '@/entities/character'
|
||||
import type { Item } from '@/entities/item'
|
||||
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseCharacterItem extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
character!: Character
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
item!: Item
|
||||
|
||||
@Property()
|
||||
quantity!: number
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setCharacter(character: Character) {
|
||||
this.character = character
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacter() {
|
||||
return this.character
|
||||
}
|
||||
|
||||
setItem(item: Item) {
|
||||
this.item = item
|
||||
return this
|
||||
}
|
||||
|
||||
getItem() {
|
||||
return this.item
|
||||
}
|
||||
|
||||
setQuantity(quantity: number) {
|
||||
this.quantity = quantity
|
||||
return this
|
||||
}
|
||||
|
||||
getQuantity() {
|
||||
return this.quantity
|
||||
}
|
||||
}
|
105
src/entities/base/characterType.ts
Normal file
105
src/entities/base/characterType.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import { CharacterGender, CharacterRace } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { Character } from '@/entities/character'
|
||||
import { Sprite } from '@/entities/sprite'
|
||||
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseCharacterType extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Enum(() => CharacterGender)
|
||||
gender!: CharacterGender
|
||||
|
||||
@Enum(() => CharacterRace)
|
||||
race!: CharacterRace
|
||||
|
||||
@Property()
|
||||
isSelectable = false
|
||||
|
||||
@ManyToOne()
|
||||
sprite?: Sprite
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setGender(gender: CharacterGender) {
|
||||
this.gender = gender
|
||||
return this
|
||||
}
|
||||
|
||||
getGender() {
|
||||
return this.gender
|
||||
}
|
||||
|
||||
setRace(race: CharacterRace) {
|
||||
this.race = race
|
||||
return this
|
||||
}
|
||||
|
||||
getRace() {
|
||||
return this.race
|
||||
}
|
||||
|
||||
setIsSelectable(isSelectable: boolean) {
|
||||
this.isSelectable = isSelectable
|
||||
return this
|
||||
}
|
||||
|
||||
getIsSelectable() {
|
||||
return this.isSelectable
|
||||
}
|
||||
|
||||
setSprite(sprite: Sprite) {
|
||||
this.sprite = sprite
|
||||
return this
|
||||
}
|
||||
|
||||
getSprite() {
|
||||
return this.sprite
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
}
|
68
src/entities/base/chat.ts
Normal file
68
src/entities/base/chat.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { Character } from '@/entities/character'
|
||||
import type { Map } from '@/entities/map'
|
||||
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseChat extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
character!: Character
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
map!: Map
|
||||
|
||||
@Property()
|
||||
message!: string
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setCharacter(character: Character) {
|
||||
this.character = character
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacter() {
|
||||
return this.character
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setMessage(message: string) {
|
||||
this.message = message
|
||||
return this
|
||||
}
|
||||
|
||||
getMessage() {
|
||||
return this.message
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
}
|
117
src/entities/base/item.ts
Normal file
117
src/entities/base/item.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import { ItemRarity, ItemType } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { CharacterItem } from '@/entities/characterItem'
|
||||
import { Sprite } from '@/entities/sprite'
|
||||
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseItem extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property()
|
||||
description: string = ''
|
||||
|
||||
@Enum(() => ItemType)
|
||||
itemType!: ItemType
|
||||
|
||||
@Property()
|
||||
stackable = false
|
||||
|
||||
@Enum(() => ItemRarity)
|
||||
rarity: ItemRarity = ItemRarity.COMMON
|
||||
|
||||
@ManyToOne()
|
||||
sprite!: Sprite
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setDescription(description: string) {
|
||||
this.description = description
|
||||
return this
|
||||
}
|
||||
|
||||
getDescription() {
|
||||
return this.description
|
||||
}
|
||||
|
||||
setItemType(itemType: ItemType) {
|
||||
this.itemType = itemType
|
||||
return this
|
||||
}
|
||||
|
||||
getItemType() {
|
||||
return this.itemType
|
||||
}
|
||||
|
||||
setStackable(stackable: boolean) {
|
||||
this.stackable = stackable
|
||||
return this
|
||||
}
|
||||
|
||||
getStackable() {
|
||||
return this.stackable
|
||||
}
|
||||
|
||||
setRarity(rarity: ItemRarity) {
|
||||
this.rarity = rarity
|
||||
return this
|
||||
}
|
||||
|
||||
getRarity() {
|
||||
return this.rarity
|
||||
}
|
||||
|
||||
setSprite(sprite: Sprite) {
|
||||
this.sprite = sprite
|
||||
return this
|
||||
}
|
||||
|
||||
getSprite() {
|
||||
return this.sprite
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
}
|
141
src/entities/base/map.ts
Normal file
141
src/entities/base/map.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { MapEffect } from '@/entities/mapEffect'
|
||||
import type { MapEventTile } from '@/entities/mapEventTile'
|
||||
import type { PlacedMapObject } from '@/entities/placedMapObject'
|
||||
import { Collection, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseMap extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name: string = ''
|
||||
|
||||
@Property()
|
||||
width = 10
|
||||
|
||||
@Property()
|
||||
height = 10
|
||||
|
||||
@Property({ type: 'json' })
|
||||
tiles: Array<Array<string>> = []
|
||||
|
||||
@Property()
|
||||
pvp = false
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
@OneToMany({ mappedBy: 'map', orphanRemoval: true })
|
||||
mapEffects = new Collection<MapEffect>(this)
|
||||
|
||||
@OneToMany({ mappedBy: 'map', orphanRemoval: true })
|
||||
mapEventTiles = new Collection<MapEventTile>(this)
|
||||
|
||||
@OneToMany({ mappedBy: 'map', orphanRemoval: true })
|
||||
placedMapObjects = new Collection<PlacedMapObject>(this)
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setWidth(width: number) {
|
||||
this.width = width
|
||||
return this
|
||||
}
|
||||
|
||||
getWidth() {
|
||||
return this.width
|
||||
}
|
||||
|
||||
setHeight(height: number) {
|
||||
this.height = height
|
||||
return this
|
||||
}
|
||||
|
||||
getHeight() {
|
||||
return this.height
|
||||
}
|
||||
|
||||
setTiles(tiles: any) {
|
||||
this.tiles = tiles
|
||||
return this
|
||||
}
|
||||
|
||||
getTiles() {
|
||||
return this.tiles
|
||||
}
|
||||
|
||||
setPvp(pvp: boolean) {
|
||||
this.pvp = pvp
|
||||
return this
|
||||
}
|
||||
|
||||
getPvp() {
|
||||
return this.pvp
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
|
||||
setMapEffects(mapEffects: Collection<MapEffect>) {
|
||||
this.mapEffects = mapEffects
|
||||
return this
|
||||
}
|
||||
|
||||
getMapEffects() {
|
||||
return this.mapEffects
|
||||
}
|
||||
|
||||
setMapEventTiles(mapEventTiles: Collection<MapEventTile>) {
|
||||
this.mapEventTiles = mapEventTiles
|
||||
return this
|
||||
}
|
||||
|
||||
getMapEventTiles() {
|
||||
return this.mapEventTiles
|
||||
}
|
||||
|
||||
setPlacedMapObjects(placedMapObjects: Collection<PlacedMapObject>) {
|
||||
this.placedMapObjects = placedMapObjects
|
||||
return this
|
||||
}
|
||||
|
||||
getPlacedMapObjects() {
|
||||
return this.placedMapObjects
|
||||
}
|
||||
}
|
55
src/entities/base/mapEffect.ts
Normal file
55
src/entities/base/mapEffect.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { Map } from '@/entities/map'
|
||||
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseMapEffect extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
map!: Map
|
||||
|
||||
@Property()
|
||||
effect!: string
|
||||
|
||||
@Property()
|
||||
strength!: number
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setEffect(effect: string) {
|
||||
this.effect = effect
|
||||
return this
|
||||
}
|
||||
|
||||
getEffect() {
|
||||
return this.effect
|
||||
}
|
||||
|
||||
setStrength(strength: number) {
|
||||
this.strength = strength
|
||||
return this
|
||||
}
|
||||
|
||||
getStrength() {
|
||||
return this.strength
|
||||
}
|
||||
}
|
81
src/entities/base/mapEventTile.ts
Normal file
81
src/entities/base/mapEventTile.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import { MapEventTileType } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { Map } from '@/entities/map'
|
||||
import type { MapEventTileTeleport } from '@/entities/mapEventTileTeleport'
|
||||
import { Enum, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseMapEventTile extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
map!: Map
|
||||
|
||||
@Enum(() => MapEventTileType)
|
||||
type!: MapEventTileType
|
||||
|
||||
@Property()
|
||||
positionX: number = 0
|
||||
|
||||
@Property()
|
||||
positionY: number = 0
|
||||
|
||||
@OneToOne({ eager: true, deleteRule: 'cascade', orphanRemoval: true })
|
||||
teleport?: MapEventTileTeleport
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setType(type: MapEventTileType) {
|
||||
this.type = type
|
||||
return this
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this.type
|
||||
}
|
||||
|
||||
setPositionX(positionX: number) {
|
||||
this.positionX = positionX
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionX() {
|
||||
return this.positionX
|
||||
}
|
||||
|
||||
setPositionY(positionY: number) {
|
||||
this.positionY = positionY
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionY() {
|
||||
return this.positionY
|
||||
}
|
||||
|
||||
setTeleport(teleport: MapEventTileTeleport) {
|
||||
this.teleport = teleport
|
||||
return this
|
||||
}
|
||||
|
||||
getTeleport() {
|
||||
return this.teleport
|
||||
}
|
||||
}
|
80
src/entities/base/mapEventTileTeleport.ts
Normal file
80
src/entities/base/mapEventTileTeleport.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { Map } from '@/entities/map'
|
||||
import type { MapEventTile } from '@/entities/mapEventTile'
|
||||
import { ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseMapEventTileTeleport extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@OneToOne({ deleteRule: 'cascade', orphanRemoval: true })
|
||||
mapEventTile!: MapEventTile
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade', eager: true })
|
||||
toMap!: Map
|
||||
|
||||
@Property()
|
||||
toRotation!: number
|
||||
|
||||
@Property()
|
||||
toPositionX!: number
|
||||
|
||||
@Property()
|
||||
toPositionY!: number
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setMapEventTile(mapEventTile: MapEventTile) {
|
||||
this.mapEventTile = mapEventTile
|
||||
return this
|
||||
}
|
||||
|
||||
getMapEventTile() {
|
||||
return this.mapEventTile
|
||||
}
|
||||
|
||||
setToMap(toMap: Map) {
|
||||
this.toMap = toMap
|
||||
return this
|
||||
}
|
||||
|
||||
getToMap() {
|
||||
return this.toMap
|
||||
}
|
||||
|
||||
setToRotation(toRotation: number) {
|
||||
this.toRotation = toRotation
|
||||
return this
|
||||
}
|
||||
|
||||
getToRotation() {
|
||||
return this.toRotation
|
||||
}
|
||||
|
||||
setToPositionX(toPositionX: number) {
|
||||
this.toPositionX = toPositionX
|
||||
return this
|
||||
}
|
||||
|
||||
getToPositionX() {
|
||||
return this.toPositionX
|
||||
}
|
||||
|
||||
setToPositionY(toPositionY: number) {
|
||||
this.toPositionY = toPositionY
|
||||
return this
|
||||
}
|
||||
|
||||
getToPositionY() {
|
||||
return this.toPositionY
|
||||
}
|
||||
}
|
137
src/entities/base/mapObject.ts
Normal file
137
src/entities/base/mapObject.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseMapObject extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property({ type: 'json' })
|
||||
tags: string[] = []
|
||||
|
||||
@Property({ type: 'json' })
|
||||
depthOffsets: number[] = [0]
|
||||
|
||||
@Property({ type: 'decimal', precision: 10, scale: 2 })
|
||||
originX = 0
|
||||
|
||||
@Property({ type: 'decimal', precision: 10, scale: 2 })
|
||||
originY = 0
|
||||
|
||||
@Property()
|
||||
frameRate = 0
|
||||
|
||||
@Property()
|
||||
frameWidth = 0
|
||||
|
||||
@Property()
|
||||
frameHeight = 0
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setTags(tags: any) {
|
||||
this.tags = tags
|
||||
return this
|
||||
}
|
||||
|
||||
getTags() {
|
||||
return this.tags
|
||||
}
|
||||
|
||||
setDepthOffsets(offsets: number[]) {
|
||||
this.depthOffsets = offsets
|
||||
}
|
||||
|
||||
getDepthOffsets() {
|
||||
return this.depthOffsets
|
||||
}
|
||||
|
||||
setOriginX(originX: number) {
|
||||
this.originX = originX
|
||||
return this
|
||||
}
|
||||
|
||||
getOriginX() {
|
||||
return this.originX
|
||||
}
|
||||
|
||||
setOriginY(originY: number) {
|
||||
this.originY = originY
|
||||
return this
|
||||
}
|
||||
|
||||
getOriginY() {
|
||||
return this.originY
|
||||
}
|
||||
|
||||
setFrameRate(frameRate: number) {
|
||||
this.frameRate = frameRate
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameRate() {
|
||||
return this.frameRate
|
||||
}
|
||||
|
||||
setFrameWidth(frameWidth: number) {
|
||||
this.frameWidth = frameWidth
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameWidth() {
|
||||
return this.frameWidth
|
||||
}
|
||||
|
||||
setFrameHeight(frameHeight: number) {
|
||||
this.frameHeight = frameHeight
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameHeight() {
|
||||
return this.frameHeight
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
}
|
55
src/entities/base/passwordResetToken.ts
Normal file
55
src/entities/base/passwordResetToken.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { User } from '@/entities/user'
|
||||
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BasePasswordResetToken extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
user!: User
|
||||
|
||||
@Property({ unique: true })
|
||||
token!: string
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setUser(user: User) {
|
||||
this.user = user
|
||||
return this
|
||||
}
|
||||
|
||||
getUser() {
|
||||
return this.user
|
||||
}
|
||||
|
||||
setToken(token: string) {
|
||||
this.token = token
|
||||
return this
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this.token
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
}
|
80
src/entities/base/placedMapObject.ts
Normal file
80
src/entities/base/placedMapObject.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { Map } from '@/entities/map'
|
||||
import type { MapObject } from '@/entities/mapObject'
|
||||
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BasePlacedMapObject extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
map!: Map
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade', eager: true })
|
||||
mapObject!: MapObject
|
||||
|
||||
@Property()
|
||||
isRotated = false
|
||||
|
||||
@Property()
|
||||
positionX = 0
|
||||
|
||||
@Property()
|
||||
positionY = 0
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setMapObject(mapObject: MapObject) {
|
||||
this.mapObject = mapObject
|
||||
return this
|
||||
}
|
||||
|
||||
getMapObject() {
|
||||
return this.mapObject
|
||||
}
|
||||
|
||||
setIsRotated(isRotated: boolean) {
|
||||
this.isRotated = isRotated
|
||||
return this
|
||||
}
|
||||
|
||||
getIsRotated() {
|
||||
return this.isRotated
|
||||
}
|
||||
|
||||
setPositionX(positionX: number) {
|
||||
this.positionX = positionX
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionX() {
|
||||
return this.positionX
|
||||
}
|
||||
|
||||
setPositionY(positionY: number) {
|
||||
this.positionY = positionY
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionY() {
|
||||
return this.positionY
|
||||
}
|
||||
}
|
91
src/entities/base/sprite.ts
Normal file
91
src/entities/base/sprite.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { SpriteAction } from '@/entities/spriteAction'
|
||||
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseSprite extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@OneToMany({ mappedBy: 'sprite', orphanRemoval: true })
|
||||
spriteActions = new Collection<SpriteAction>(this)
|
||||
|
||||
@Property({ nullable: true })
|
||||
width: number | null = null
|
||||
|
||||
@Property({ nullable: true })
|
||||
height: number | null = null
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setSpriteActions(spriteActions: Collection<SpriteAction>) {
|
||||
this.spriteActions = spriteActions
|
||||
return this
|
||||
}
|
||||
|
||||
getSpriteActions() {
|
||||
return this.spriteActions
|
||||
}
|
||||
|
||||
setWidth(width: number | null) {
|
||||
this.width = width
|
||||
return this
|
||||
}
|
||||
|
||||
getWidth() {
|
||||
return this.width
|
||||
}
|
||||
|
||||
setHeight(height: number | null) {
|
||||
this.height = height
|
||||
return this
|
||||
}
|
||||
|
||||
getHeight() {
|
||||
return this.height
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
}
|
123
src/entities/base/spriteAction.ts
Normal file
123
src/entities/base/spriteAction.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import type { Sprite } from '@/entities/sprite'
|
||||
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export interface SpriteImage {
|
||||
url: string
|
||||
offset: {
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseSpriteAction extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
sprite!: Sprite
|
||||
|
||||
@Property()
|
||||
action!: string
|
||||
|
||||
@Property({ type: 'json', nullable: true })
|
||||
sprites?: SpriteImage[]
|
||||
|
||||
@Property({ type: 'decimal', precision: 5, scale: 2 })
|
||||
originX = 0.0
|
||||
|
||||
@Property({ type: 'decimal', precision: 5, scale: 2 })
|
||||
originY = 0.0
|
||||
|
||||
@Property()
|
||||
frameWidth = 0
|
||||
|
||||
@Property()
|
||||
frameHeight = 0
|
||||
|
||||
@Property()
|
||||
frameRate = 0
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setSprite(sprite: Sprite) {
|
||||
this.sprite = sprite
|
||||
return this
|
||||
}
|
||||
|
||||
getSprite() {
|
||||
return this.sprite
|
||||
}
|
||||
|
||||
setAction(action: string) {
|
||||
this.action = action
|
||||
return this
|
||||
}
|
||||
|
||||
getAction() {
|
||||
return this.action
|
||||
}
|
||||
|
||||
setSprites(sprites: SpriteImage[]) {
|
||||
this.sprites = sprites
|
||||
return this
|
||||
}
|
||||
|
||||
getSprites() {
|
||||
return this.sprites
|
||||
}
|
||||
|
||||
setOriginX(originX: number) {
|
||||
this.originX = originX
|
||||
return this
|
||||
}
|
||||
|
||||
getOriginX() {
|
||||
return this.originX
|
||||
}
|
||||
|
||||
setOriginY(originY: number) {
|
||||
this.originY = originY
|
||||
return this
|
||||
}
|
||||
|
||||
getOriginY() {
|
||||
return this.originY
|
||||
}
|
||||
|
||||
setFrameWidth(frameWidth: number) {
|
||||
this.frameWidth = frameWidth
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameWidth() {
|
||||
return this.frameWidth
|
||||
}
|
||||
|
||||
setFrameHeight(frameHeight: number) {
|
||||
this.frameHeight = frameHeight
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameHeight() {
|
||||
return this.frameHeight
|
||||
}
|
||||
|
||||
setFrameRate(frameRate: number) {
|
||||
this.frameRate = frameRate
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameRate() {
|
||||
return this.frameRate
|
||||
}
|
||||
}
|
66
src/entities/base/tile.ts
Normal file
66
src/entities/base/tile.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseTile extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property({ type: 'json', nullable: true })
|
||||
tags?: any
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setTags(tags: any) {
|
||||
this.tags = tags
|
||||
return this
|
||||
}
|
||||
|
||||
getTags() {
|
||||
return this.tags
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
}
|
94
src/entities/base/user.ts
Normal file
94
src/entities/base/user.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { Character } from '@/entities/character'
|
||||
import { PasswordResetToken } from '@/entities/passwordResetToken'
|
||||
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
import bcrypt from 'bcryptjs'
|
||||
|
||||
export class BaseUser extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property({ unique: true })
|
||||
username!: string
|
||||
|
||||
@Property({ unique: true })
|
||||
email!: string
|
||||
|
||||
@Property()
|
||||
password!: string
|
||||
|
||||
@Property()
|
||||
online = false
|
||||
|
||||
@OneToMany(() => Character, (character) => character.user)
|
||||
characters = new Collection<Character>(this)
|
||||
|
||||
@OneToMany(() => PasswordResetToken, (token) => token.user)
|
||||
passwordResetTokens = new Collection<PasswordResetToken>(this)
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setUsername(username: string) {
|
||||
this.username = username
|
||||
return this
|
||||
}
|
||||
|
||||
getUsername() {
|
||||
return this.username
|
||||
}
|
||||
|
||||
setEmail(email: string) {
|
||||
this.email = email
|
||||
return this
|
||||
}
|
||||
|
||||
getEmail() {
|
||||
return this.email
|
||||
}
|
||||
|
||||
setPassword(password: string) {
|
||||
this.password = bcrypt.hashSync(password, 10)
|
||||
return this
|
||||
}
|
||||
|
||||
getPassword() {
|
||||
return this.password
|
||||
}
|
||||
|
||||
setOnline(online: boolean) {
|
||||
this.online = online
|
||||
return this
|
||||
}
|
||||
|
||||
getOnline() {
|
||||
return this.online
|
||||
}
|
||||
|
||||
setCharacters(characters: Collection<Character>) {
|
||||
this.characters = characters
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacters() {
|
||||
return this.characters
|
||||
}
|
||||
|
||||
setPasswordResetTokens(passwordResetTokens: Collection<PasswordResetToken>) {
|
||||
this.passwordResetTokens = passwordResetTokens
|
||||
return this
|
||||
}
|
||||
|
||||
getPasswordResetTokens() {
|
||||
return this.passwordResetTokens
|
||||
return this
|
||||
}
|
||||
}
|
40
src/entities/base/world.ts
Normal file
40
src/entities/base/world.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { BaseEntity } from '@/application/base/baseEntity'
|
||||
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
export class BaseWorld extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
date = new Date()
|
||||
|
||||
@Property()
|
||||
rainPercentage = 0
|
||||
|
||||
@Property()
|
||||
fogDensity = 0
|
||||
|
||||
setDate(date: Date) {
|
||||
this.date = date
|
||||
return this
|
||||
}
|
||||
|
||||
getDate() {
|
||||
return this.date
|
||||
}
|
||||
|
||||
setRainPercentage(rainPercentage: number) {
|
||||
this.rainPercentage = rainPercentage
|
||||
return this
|
||||
}
|
||||
|
||||
getRainPercentage() {
|
||||
return this.rainPercentage
|
||||
}
|
||||
|
||||
setFogDensity(fogDensity: number) {
|
||||
this.fogDensity = fogDensity
|
||||
return this
|
||||
}
|
||||
|
||||
getFogDensity() {
|
||||
return this.fogDensity
|
||||
}
|
||||
}
|
@ -1,297 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { CharacterEquipment } from './characterEquipment'
|
||||
import { CharacterHair } from './characterHair'
|
||||
import { CharacterItem } from './characterItem'
|
||||
import { CharacterType } from './characterType'
|
||||
import { Chat } from './chat'
|
||||
import { User } from './user'
|
||||
import { Map } from './map'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseCharacter } from '@/entities/base/character'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class Character extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
user!: User
|
||||
|
||||
@Property({ unique: true })
|
||||
name!: string
|
||||
|
||||
@Property()
|
||||
online = false
|
||||
|
||||
@Property()
|
||||
role = 'player'
|
||||
|
||||
@OneToMany(() => Chat, (chat) => chat.character)
|
||||
chats = new Collection<Chat>(this)
|
||||
|
||||
// Position
|
||||
@ManyToOne()
|
||||
map!: Map // @TODO: Update to spawn point when current map is not found
|
||||
|
||||
@Property()
|
||||
positionX = 0
|
||||
|
||||
@Property()
|
||||
positionY = 0
|
||||
|
||||
@Property()
|
||||
rotation = 0
|
||||
|
||||
// Customization
|
||||
@ManyToOne({ deleteRule: 'set null' })
|
||||
characterType?: CharacterType | null | undefined
|
||||
|
||||
@ManyToOne({ deleteRule: 'set null' })
|
||||
characterHair?: CharacterHair | null | undefined
|
||||
|
||||
// Inventory
|
||||
@OneToMany({ mappedBy: 'character' })
|
||||
items = new Collection<CharacterItem>(this)
|
||||
|
||||
@OneToMany({ mappedBy: 'character' })
|
||||
equipment = new Collection<CharacterEquipment>(this)
|
||||
|
||||
// Stats
|
||||
@Property()
|
||||
alignment = 50
|
||||
|
||||
@Property()
|
||||
hitpoints = 100
|
||||
|
||||
@Property()
|
||||
mana = 100
|
||||
|
||||
@Property()
|
||||
level = 1
|
||||
|
||||
@Property()
|
||||
experience = 0
|
||||
|
||||
@Property()
|
||||
strength = 10
|
||||
|
||||
@Property()
|
||||
dexterity = 10
|
||||
|
||||
@Property()
|
||||
intelligence = 10
|
||||
|
||||
@Property()
|
||||
wisdom = 10
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setUser(user: User) {
|
||||
this.user = user
|
||||
return this
|
||||
}
|
||||
|
||||
getUser() {
|
||||
return this.user
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setOnline(online: boolean) {
|
||||
this.online = online
|
||||
return this
|
||||
}
|
||||
|
||||
getOnline() {
|
||||
return this.online
|
||||
}
|
||||
|
||||
setRole(role: string) {
|
||||
this.role = role
|
||||
return this
|
||||
}
|
||||
|
||||
getRole() {
|
||||
return this.role
|
||||
}
|
||||
|
||||
setChats(chats: Collection<Chat>) {
|
||||
this.chats = chats
|
||||
return this
|
||||
}
|
||||
|
||||
getChats() {
|
||||
return this.chats
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setPositionX(positionX: number) {
|
||||
this.positionX = positionX
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionX() {
|
||||
return this.positionX
|
||||
}
|
||||
|
||||
setPositionY(positionY: number) {
|
||||
this.positionY = positionY
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionY() {
|
||||
return this.positionY
|
||||
}
|
||||
|
||||
setRotation(rotation: number) {
|
||||
this.rotation = rotation
|
||||
return this
|
||||
}
|
||||
|
||||
getRotation() {
|
||||
return this.rotation
|
||||
}
|
||||
|
||||
setCharacterType(characterType: CharacterType | null | undefined) {
|
||||
this.characterType = characterType
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacterType() {
|
||||
return this.characterType
|
||||
}
|
||||
|
||||
setCharacterHair(characterHair: CharacterHair | null | undefined) {
|
||||
this.characterHair = characterHair
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacterHair() {
|
||||
return this.characterHair
|
||||
}
|
||||
|
||||
setItems(items: Collection<CharacterItem>) {
|
||||
this.items = items
|
||||
return this
|
||||
}
|
||||
|
||||
getItems() {
|
||||
return this.items
|
||||
}
|
||||
|
||||
setEquipment(equipment: Collection<CharacterEquipment>) {
|
||||
this.equipment = equipment
|
||||
return this
|
||||
}
|
||||
|
||||
getEquipment() {
|
||||
return this.equipment
|
||||
}
|
||||
|
||||
setAlignment(alignment: number) {
|
||||
this.alignment = alignment
|
||||
return this
|
||||
}
|
||||
|
||||
getAlignment() {
|
||||
return this.alignment
|
||||
}
|
||||
|
||||
setHitpoints(hitpoints: number) {
|
||||
this.hitpoints = hitpoints
|
||||
return this
|
||||
}
|
||||
|
||||
getHitpoints() {
|
||||
return this.hitpoints
|
||||
}
|
||||
|
||||
setMana(mana: number) {
|
||||
this.mana = mana
|
||||
return this
|
||||
}
|
||||
|
||||
getMana() {
|
||||
return this.mana
|
||||
}
|
||||
|
||||
setLevel(level: number) {
|
||||
this.level = level
|
||||
return this
|
||||
}
|
||||
|
||||
getLevel() {
|
||||
return this.level
|
||||
}
|
||||
|
||||
setExperience(experience: number) {
|
||||
this.experience = experience
|
||||
return this
|
||||
}
|
||||
|
||||
getExperience() {
|
||||
return this.experience
|
||||
}
|
||||
|
||||
setStrength(strength: number) {
|
||||
this.strength = strength
|
||||
return this
|
||||
}
|
||||
|
||||
getStrength() {
|
||||
return this.strength
|
||||
}
|
||||
|
||||
setDexterity(dexterity: number) {
|
||||
this.dexterity = dexterity
|
||||
return this
|
||||
}
|
||||
|
||||
getDexterity() {
|
||||
return this.dexterity
|
||||
}
|
||||
|
||||
setIntelligence(intelligence: number) {
|
||||
this.intelligence = intelligence
|
||||
return this
|
||||
}
|
||||
|
||||
getIntelligence() {
|
||||
return this.intelligence
|
||||
}
|
||||
|
||||
setWisdom(wisdom: number) {
|
||||
this.wisdom = wisdom
|
||||
return this
|
||||
}
|
||||
|
||||
getWisdom() {
|
||||
return this.wisdom
|
||||
}
|
||||
}
|
||||
export class Character extends BaseCharacter {}
|
||||
|
@ -1,61 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core'
|
||||
|
||||
import { Character } from './character'
|
||||
import { CharacterItem } from './characterItem'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { CharacterEquipmentSlotType } from '#application/enums'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseCharacterEquipment } from '@/entities/base/characterEquipment'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class CharacterEquipment extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Enum(() => CharacterEquipmentSlotType)
|
||||
slot!: CharacterEquipmentSlotType
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
character!: Character
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
characterItem!: CharacterItem
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setSlot(slot: CharacterEquipmentSlotType) {
|
||||
this.slot = slot
|
||||
return this
|
||||
}
|
||||
|
||||
getSlot() {
|
||||
return this.slot
|
||||
}
|
||||
|
||||
setCharacter(character: Character) {
|
||||
this.character = character
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacter() {
|
||||
return this.character
|
||||
}
|
||||
|
||||
setCharacterItem(characterItem: CharacterItem) {
|
||||
this.characterItem = characterItem
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacterItem() {
|
||||
return this.characterItem
|
||||
}
|
||||
}
|
||||
export class CharacterEquipment extends BaseCharacterEquipment {}
|
||||
|
@ -1,73 +1,14 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Character } from './character'
|
||||
import { Sprite } from './sprite'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { CharacterGender } from '#application/enums'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseCharacterHair } from '@/entities/base/characterHair'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class CharacterHair extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property()
|
||||
gender: CharacterGender = CharacterGender.MALE
|
||||
|
||||
@Property()
|
||||
isSelectable = false
|
||||
|
||||
@ManyToOne({ nullable: true })
|
||||
sprite?: Sprite
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setGender(gender: CharacterGender) {
|
||||
this.gender = gender
|
||||
return this
|
||||
}
|
||||
|
||||
getGender() {
|
||||
return this.gender
|
||||
}
|
||||
|
||||
setIsSelectable(isSelectable: boolean) {
|
||||
this.isSelectable = isSelectable
|
||||
return this
|
||||
}
|
||||
|
||||
getIsSelectable() {
|
||||
return this.isSelectable
|
||||
}
|
||||
|
||||
setSprite(sprite: Sprite) {
|
||||
this.sprite = sprite
|
||||
return this
|
||||
}
|
||||
|
||||
getSprite() {
|
||||
return this.sprite
|
||||
export class CharacterHair extends BaseCharacterHair {
|
||||
public async cache() {
|
||||
try {
|
||||
return this
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,61 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Character } from './character'
|
||||
import { CharacterEquipment } from './characterEquipment'
|
||||
import { Item } from './item'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseCharacterItem } from '@/entities/base/characterItem'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class CharacterItem extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
character!: Character
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
item!: Item
|
||||
|
||||
@Property()
|
||||
quantity!: number
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setCharacter(character: Character) {
|
||||
this.character = character
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacter() {
|
||||
return this.character
|
||||
}
|
||||
|
||||
setItem(item: Item) {
|
||||
this.item = item
|
||||
return this
|
||||
}
|
||||
|
||||
getItem() {
|
||||
return this.item
|
||||
}
|
||||
|
||||
setQuantity(quantity: number) {
|
||||
this.quantity = quantity
|
||||
return this
|
||||
}
|
||||
|
||||
getQuantity() {
|
||||
return this.quantity
|
||||
}
|
||||
}
|
||||
export class CharacterItem extends BaseCharacterItem {}
|
||||
|
@ -1,109 +1,14 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Character } from './character'
|
||||
import { Sprite } from './sprite'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { CharacterGender, CharacterRace } from '#application/enums'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseCharacterType } from '@/entities/base/characterType'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class CharacterType extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Enum(() => CharacterGender)
|
||||
gender!: CharacterGender
|
||||
|
||||
@Enum(() => CharacterRace)
|
||||
race!: CharacterRace
|
||||
|
||||
@Property()
|
||||
isSelectable = false
|
||||
|
||||
@ManyToOne({ nullable: true })
|
||||
sprite?: Sprite
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setGender(gender: CharacterGender) {
|
||||
this.gender = gender
|
||||
return this
|
||||
}
|
||||
|
||||
getGender() {
|
||||
return this.gender
|
||||
}
|
||||
|
||||
setRace(race: CharacterRace) {
|
||||
this.race = race
|
||||
return this
|
||||
}
|
||||
|
||||
getRace() {
|
||||
return this.race
|
||||
}
|
||||
|
||||
setIsSelectable(isSelectable: boolean) {
|
||||
this.isSelectable = isSelectable
|
||||
return this
|
||||
}
|
||||
|
||||
getIsSelectable() {
|
||||
return this.isSelectable
|
||||
}
|
||||
|
||||
setSprite(sprite: Sprite) {
|
||||
this.sprite = sprite
|
||||
return this
|
||||
}
|
||||
|
||||
getSprite() {
|
||||
return this.sprite
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
export class CharacterType extends BaseCharacterType {
|
||||
public async cache() {
|
||||
try {
|
||||
return this
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,72 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Character } from './character'
|
||||
import { Map } from './map'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseChat } from '@/entities/base/chat'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class Chat extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
character!: Character
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
map!: Map
|
||||
|
||||
@Property()
|
||||
message!: string
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setCharacter(character: Character) {
|
||||
this.character = character
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacter() {
|
||||
return this.character
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setMessage(message: string) {
|
||||
this.message = message
|
||||
return this
|
||||
}
|
||||
|
||||
getMessage() {
|
||||
return this.message
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
}
|
||||
export class Chat extends BaseChat {}
|
||||
|
@ -1,121 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { CharacterItem } from './characterItem'
|
||||
import { Sprite } from './sprite'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { ItemType, ItemRarity } from '#application/enums'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseItem } from '@/entities/base/item'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class Item extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property({ nullable: true })
|
||||
description?: string
|
||||
|
||||
@Enum(() => ItemType)
|
||||
itemType!: ItemType
|
||||
|
||||
@Property()
|
||||
stackable = false
|
||||
|
||||
@Enum(() => ItemRarity)
|
||||
rarity: ItemRarity = ItemRarity.COMMON
|
||||
|
||||
@ManyToOne(() => Sprite, { nullable: true })
|
||||
sprite?: Sprite
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setDescription(description: string) {
|
||||
this.description = description
|
||||
return this
|
||||
}
|
||||
|
||||
getDescription() {
|
||||
return this.description
|
||||
}
|
||||
|
||||
setItemType(itemType: ItemType) {
|
||||
this.itemType = itemType
|
||||
return this
|
||||
}
|
||||
|
||||
getItemType() {
|
||||
return this.itemType
|
||||
}
|
||||
|
||||
setStackable(stackable: boolean) {
|
||||
this.stackable = stackable
|
||||
return this
|
||||
}
|
||||
|
||||
getStackable() {
|
||||
return this.stackable
|
||||
}
|
||||
|
||||
setRarity(rarity: ItemRarity) {
|
||||
this.rarity = rarity
|
||||
return this
|
||||
}
|
||||
|
||||
getRarity() {
|
||||
return this.rarity
|
||||
}
|
||||
|
||||
setSprite(sprite: Sprite) {
|
||||
this.sprite = sprite
|
||||
return this
|
||||
}
|
||||
|
||||
getSprite() {
|
||||
return this.sprite
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
}
|
||||
export class Item extends BaseItem {}
|
||||
|
@ -1,184 +1,77 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BaseMap } from '@/entities/base/map'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Character } from './character'
|
||||
import { Chat } from './chat'
|
||||
import { MapEffect } from './mapEffect'
|
||||
import { MapEventTile } from './mapEventTile'
|
||||
import { MapEventTileTeleport } from './mapEventTileTeleport'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { PlacedMapObject } from '#entities/placedMapObject'
|
||||
export type MapCacheT = ReturnType<Map['cache']> | {}
|
||||
export type MapEditorMapT = ReturnType<Map['mapEditorObject']> | {}
|
||||
|
||||
@Entity()
|
||||
export class Map extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
export class Map extends BaseMap {
|
||||
public async cache() {
|
||||
try {
|
||||
await this.getPlacedMapObjects().load()
|
||||
await this.getMapEffects().load()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property()
|
||||
width = 10
|
||||
|
||||
@Property()
|
||||
height = 10
|
||||
|
||||
@Property({ type: 'json', nullable: true })
|
||||
tiles?: any
|
||||
|
||||
@Property()
|
||||
pvp = false
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
@OneToMany(() => MapEffect, (effect) => effect.map)
|
||||
mapEffects = new Collection<MapEffect>(this)
|
||||
|
||||
@OneToMany(() => MapEventTile, (tile) => tile.map)
|
||||
mapEventTiles = new Collection<MapEventTile>(this)
|
||||
|
||||
@OneToMany(() => MapEventTileTeleport, (teleport) => teleport.toMap)
|
||||
mapEventTileTeleports = new Collection<MapEventTileTeleport>(this)
|
||||
|
||||
@OneToMany(() => PlacedMapObject, (object) => object.map)
|
||||
placedMapObjects = new Collection<PlacedMapObject>(this)
|
||||
|
||||
@OneToMany(() => Character, (character) => character.map)
|
||||
characters = new Collection<Character>(this)
|
||||
|
||||
@OneToMany(() => Chat, (chat) => chat.map)
|
||||
chats = new Collection<Chat>(this)
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
return {
|
||||
id: this.getId(),
|
||||
name: this.getName(),
|
||||
width: this.getWidth(),
|
||||
height: this.getHeight(),
|
||||
tiles: this.getTiles(),
|
||||
pvp: this.getPvp(),
|
||||
updatedAt: this.getUpdatedAt(),
|
||||
placedMapObjects: this.getPlacedMapObjects().map((placedMapObject) => ({
|
||||
id: placedMapObject.getId(),
|
||||
mapObject: placedMapObject.getMapObject().getId(),
|
||||
isRotated: placedMapObject.getIsRotated(),
|
||||
positionX: placedMapObject.getPositionX(),
|
||||
positionY: placedMapObject.getPositionY()
|
||||
})),
|
||||
mapEffects: this.getMapEffects().map((mapEffect) => ({
|
||||
effect: mapEffect.getEffect(),
|
||||
strength: mapEffect.getStrength()
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
public async mapEditorObject() {
|
||||
try {
|
||||
await this.getPlacedMapObjects().load()
|
||||
await this.getMapEffects().load()
|
||||
await this.getMapEventTiles().load()
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setWidth(width: number) {
|
||||
this.width = width
|
||||
return this
|
||||
}
|
||||
|
||||
getWidth() {
|
||||
return this.width
|
||||
}
|
||||
|
||||
setHeight(height: number) {
|
||||
this.height = height
|
||||
return this
|
||||
}
|
||||
|
||||
getHeight() {
|
||||
return this.height
|
||||
}
|
||||
|
||||
setTiles(tiles: any) {
|
||||
this.tiles = tiles
|
||||
return this
|
||||
}
|
||||
|
||||
getTiles() {
|
||||
return this.tiles
|
||||
}
|
||||
|
||||
setPvp(pvp: boolean) {
|
||||
this.pvp = pvp
|
||||
return this
|
||||
}
|
||||
|
||||
getPvp() {
|
||||
return this.pvp
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
}
|
||||
|
||||
setMapEffects(mapEffects: Collection<MapEffect>) {
|
||||
this.mapEffects = mapEffects
|
||||
return this
|
||||
}
|
||||
|
||||
getMapEffects() {
|
||||
return this.mapEffects
|
||||
}
|
||||
|
||||
setMapEventTiles(mapEventTiles: Collection<MapEventTile>) {
|
||||
this.mapEventTiles = mapEventTiles
|
||||
return this
|
||||
}
|
||||
|
||||
getMapEventTiles() {
|
||||
return this.mapEventTiles
|
||||
}
|
||||
|
||||
setMapEventTileTeleports(mapEventTileTeleports: Collection<MapEventTileTeleport>) {
|
||||
this.mapEventTileTeleports = mapEventTileTeleports
|
||||
return this
|
||||
}
|
||||
|
||||
getMapEventTileTeleports() {
|
||||
return this.mapEventTileTeleports
|
||||
}
|
||||
|
||||
setPlacedMapObjects(placedMapObjects: Collection<PlacedMapObject>) {
|
||||
this.placedMapObjects = placedMapObjects
|
||||
return this
|
||||
}
|
||||
|
||||
getPlacedMapObjects() {
|
||||
return this.placedMapObjects
|
||||
}
|
||||
|
||||
setCharacters(characters: Collection<Character>) {
|
||||
this.characters = characters
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacters() {
|
||||
return this.characters
|
||||
}
|
||||
|
||||
setChats(chats: Collection<Chat>) {
|
||||
this.chats = chats
|
||||
return this
|
||||
}
|
||||
|
||||
getChats() {
|
||||
return this.chats
|
||||
return {
|
||||
id: this.getId(),
|
||||
name: this.getName(),
|
||||
width: this.getWidth(),
|
||||
height: this.getHeight(),
|
||||
tiles: this.getTiles(),
|
||||
pvp: this.getPvp(),
|
||||
createdAt: this.getCreatedAt(),
|
||||
updatedAt: this.getUpdatedAt(),
|
||||
placedMapObjects: this.getPlacedMapObjects(),
|
||||
mapEffects: this.getMapEffects(),
|
||||
mapEventTiles: this.getMapEventTiles().map((mapEventTile) => ({
|
||||
id: mapEventTile.getId(),
|
||||
type: mapEventTile.getType(),
|
||||
positionX: mapEventTile.getPositionX(),
|
||||
positionY: mapEventTile.getPositionY(),
|
||||
teleport: mapEventTile.getTeleport()
|
||||
? {
|
||||
toMap: mapEventTile.getTeleport()?.getToMap().getId(),
|
||||
toPositionX: mapEventTile.getTeleport()?.getToPositionX(),
|
||||
toPositionY: mapEventTile.getTeleport()?.getToPositionY(),
|
||||
toRotation: mapEventTile.getTeleport()?.getToRotation()
|
||||
}
|
||||
: undefined
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Map } from './map'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseMapEffect } from '@/entities/base/mapEffect'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class MapEffect extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
map!: Map
|
||||
|
||||
@Property()
|
||||
effect!: string
|
||||
|
||||
@Property()
|
||||
strength!: number
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setEffect(effect: string) {
|
||||
this.effect = effect
|
||||
return this
|
||||
}
|
||||
|
||||
getEffect() {
|
||||
return this.effect
|
||||
}
|
||||
|
||||
setStrength(strength: number) {
|
||||
this.strength = strength
|
||||
return this
|
||||
}
|
||||
|
||||
getStrength() {
|
||||
return this.strength
|
||||
}
|
||||
}
|
||||
export class MapEffect extends BaseMapEffect {}
|
||||
|
@ -1,85 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, Enum, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Map } from './map'
|
||||
import { MapEventTileTeleport } from './mapEventTileTeleport'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { MapEventTileType } from '#application/enums'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseMapEventTile } from '@/entities/base/mapEventTile'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class MapEventTile extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
map!: Map
|
||||
|
||||
@Enum(() => MapEventTileType)
|
||||
type!: MapEventTileType
|
||||
|
||||
@Property()
|
||||
positionX!: number
|
||||
|
||||
@Property()
|
||||
positionY!: number
|
||||
|
||||
@OneToOne(() => MapEventTileTeleport, (teleport) => teleport.mapEventTile)
|
||||
teleport?: MapEventTileTeleport
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setType(type: MapEventTileType) {
|
||||
this.type = type
|
||||
return this
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this.type
|
||||
}
|
||||
|
||||
setPositionX(positionX: number) {
|
||||
this.positionX = positionX
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionX() {
|
||||
return this.positionX
|
||||
}
|
||||
|
||||
setPositionY(positionY: number) {
|
||||
this.positionY = positionY
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionY() {
|
||||
return this.positionY
|
||||
}
|
||||
|
||||
setTeleport(teleport: MapEventTileTeleport) {
|
||||
this.teleport = teleport
|
||||
return this
|
||||
}
|
||||
|
||||
getTeleport() {
|
||||
return this.teleport
|
||||
}
|
||||
}
|
||||
export class MapEventTile extends BaseMapEventTile {}
|
||||
|
@ -1,84 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Map } from './map'
|
||||
import { MapEventTile } from './mapEventTile'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseMapEventTileTeleport } from '@/entities/base/mapEventTileTeleport'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class MapEventTileTeleport extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@OneToOne({ deleteRule: 'cascade' })
|
||||
mapEventTile!: MapEventTile
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
toMap!: Map
|
||||
|
||||
@Property()
|
||||
toRotation!: number
|
||||
|
||||
@Property()
|
||||
toPositionX!: number
|
||||
|
||||
@Property()
|
||||
toPositionY!: number
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setMapEventTile(mapEventTile: MapEventTile) {
|
||||
this.mapEventTile = mapEventTile
|
||||
return this
|
||||
}
|
||||
|
||||
getMapEventTile() {
|
||||
return this.mapEventTile
|
||||
}
|
||||
|
||||
setToMap(toMap: Map) {
|
||||
this.toMap = toMap
|
||||
return this
|
||||
}
|
||||
|
||||
getToMap() {
|
||||
return this.toMap
|
||||
}
|
||||
|
||||
setToRotation(toRotation: number) {
|
||||
this.toRotation = toRotation
|
||||
return this
|
||||
}
|
||||
|
||||
getToRotation() {
|
||||
return this.toRotation
|
||||
}
|
||||
|
||||
setToPositionX(toPositionX: number) {
|
||||
this.toPositionX = toPositionX
|
||||
return this
|
||||
}
|
||||
|
||||
getToPositionX() {
|
||||
return this.toPositionX
|
||||
}
|
||||
|
||||
setToPositionY(toPositionY: number) {
|
||||
this.toPositionY = toPositionY
|
||||
return this
|
||||
}
|
||||
|
||||
getToPositionY() {
|
||||
return this.toPositionY
|
||||
}
|
||||
}
|
||||
export class MapEventTileTeleport extends BaseMapEventTileTeleport {}
|
||||
|
@ -1,141 +1,14 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseMapObject } from '@/entities/base/mapObject'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class MapObject extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property({ type: 'json', nullable: true })
|
||||
tags?: any
|
||||
|
||||
@Property({ type: 'decimal', precision: 10, scale: 2 })
|
||||
originX = 0
|
||||
|
||||
@Property({ type: 'decimal', precision: 10, scale: 2 })
|
||||
originY = 0
|
||||
|
||||
@Property()
|
||||
isAnimated = false
|
||||
|
||||
@Property()
|
||||
frameRate = 0
|
||||
|
||||
@Property()
|
||||
frameWidth = 0
|
||||
|
||||
@Property()
|
||||
frameHeight = 0
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setTags(tags: any) {
|
||||
this.tags = tags
|
||||
return this
|
||||
}
|
||||
|
||||
getTags() {
|
||||
return this.tags
|
||||
}
|
||||
|
||||
setOriginX(originX: number) {
|
||||
this.originX = originX
|
||||
return this
|
||||
}
|
||||
|
||||
getOriginX() {
|
||||
return this.originX
|
||||
}
|
||||
|
||||
setOriginY(originY: number) {
|
||||
this.originY = originY
|
||||
return this
|
||||
}
|
||||
|
||||
getOriginY() {
|
||||
return this.originY
|
||||
}
|
||||
|
||||
setIsAnimated(isAnimated: boolean) {
|
||||
this.isAnimated = isAnimated
|
||||
return this
|
||||
}
|
||||
|
||||
getIsAnimated() {
|
||||
return this.isAnimated
|
||||
}
|
||||
|
||||
setFrameRate(frameRate: number) {
|
||||
this.frameRate = frameRate
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameRate() {
|
||||
return this.frameRate
|
||||
}
|
||||
|
||||
setFrameWidth(frameWidth: number) {
|
||||
this.frameWidth = frameWidth
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameWidth() {
|
||||
return this.frameWidth
|
||||
}
|
||||
|
||||
setFrameHeight(frameHeight: number) {
|
||||
this.frameHeight = frameHeight
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameHeight() {
|
||||
return this.frameHeight
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
export class MapObject extends BaseMapObject {
|
||||
public async cache() {
|
||||
try {
|
||||
return this
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { User } from './user'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BasePasswordResetToken } from '@/entities/base/passwordResetToken'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class PasswordResetToken extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
user!: User
|
||||
|
||||
@Property({ unique: true })
|
||||
token!: string
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setUser(user: User) {
|
||||
this.user = user
|
||||
return this
|
||||
}
|
||||
|
||||
getUser() {
|
||||
return this.user
|
||||
}
|
||||
|
||||
setToken(token: string) {
|
||||
this.token = token
|
||||
return this
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this.token
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
}
|
||||
export class PasswordResetToken extends BasePasswordResetToken {}
|
||||
|
@ -1,97 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { BasePlacedMapObject } from '@/entities/base/placedMapObject'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Map } from './map'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { MapObject } from '#entities/mapObject'
|
||||
|
||||
//@TODO : Rename mapObject
|
||||
@Entity()
|
||||
export class PlacedMapObject extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
map!: Map
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
mapObject!: MapObject
|
||||
|
||||
@Property()
|
||||
depth = 0
|
||||
|
||||
@Property()
|
||||
isRotated = false
|
||||
|
||||
@Property()
|
||||
positionX = 0
|
||||
|
||||
@Property()
|
||||
positionY = 0
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setMap(map: Map) {
|
||||
this.map = map
|
||||
return this
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
setMapObject(mapObject: MapObject) {
|
||||
this.mapObject = mapObject
|
||||
return this
|
||||
}
|
||||
|
||||
getMapObject() {
|
||||
return this.mapObject
|
||||
}
|
||||
|
||||
setDepth(depth: number) {
|
||||
this.depth = depth
|
||||
return this
|
||||
}
|
||||
|
||||
getDepth() {
|
||||
return this.depth
|
||||
}
|
||||
|
||||
setIsRotated(isRotated: boolean) {
|
||||
this.isRotated = isRotated
|
||||
return this
|
||||
}
|
||||
|
||||
getIsRotated() {
|
||||
return this.isRotated
|
||||
}
|
||||
|
||||
setPositionX(positionX: number) {
|
||||
this.positionX = positionX
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionX() {
|
||||
return this.positionX
|
||||
}
|
||||
|
||||
setPositionY(positionY: number) {
|
||||
this.positionY = positionY
|
||||
return this
|
||||
}
|
||||
|
||||
getPositionY() {
|
||||
return this.positionY
|
||||
}
|
||||
}
|
||||
export class PlacedMapObject extends BasePlacedMapObject {}
|
||||
|
@ -1,71 +1,31 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { SpriteAction } from './spriteAction'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseSprite } from '@/entities/base/sprite'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class Sprite extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
export class Sprite extends BaseSprite {
|
||||
public async cache() {
|
||||
await this.getSpriteActions().load()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@OneToMany(() => SpriteAction, (action) => action.sprite)
|
||||
spriteActions = new Collection<SpriteAction>(this)
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setSpriteActions(spriteActions: Collection<SpriteAction>) {
|
||||
this.spriteActions = spriteActions
|
||||
return this
|
||||
}
|
||||
|
||||
getSpriteActions() {
|
||||
return this.spriteActions
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
try {
|
||||
return {
|
||||
id: this.getId(),
|
||||
name: this.getName(),
|
||||
createdAt: this.getCreatedAt(),
|
||||
updatedAt: this.getUpdatedAt(),
|
||||
spriteActions: this.getSpriteActions().map((spriteAction) => ({
|
||||
id: spriteAction.getId(),
|
||||
action: spriteAction.getAction(),
|
||||
originX: spriteAction.getOriginX(),
|
||||
originY: spriteAction.getOriginY(),
|
||||
frameWidth: spriteAction.getFrameWidth(),
|
||||
frameHeight: spriteAction.getFrameHeight(),
|
||||
frameRate: spriteAction.getFrameRate(),
|
||||
frameCount: spriteAction.getSprites()?.length
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,143 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { Sprite } from './sprite'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseSpriteAction } from '@/entities/base/spriteAction'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class SpriteAction extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@ManyToOne({ deleteRule: 'cascade' })
|
||||
sprite!: Sprite
|
||||
|
||||
@Property()
|
||||
action!: string
|
||||
|
||||
@Property({ type: 'json', nullable: true })
|
||||
sprites?: string[]
|
||||
|
||||
@Property()
|
||||
originX = 0
|
||||
|
||||
@Property()
|
||||
originY = 0
|
||||
|
||||
@Property()
|
||||
isAnimated = false
|
||||
|
||||
@Property()
|
||||
isLooping = false
|
||||
|
||||
@Property()
|
||||
frameWidth = 0
|
||||
|
||||
@Property()
|
||||
frameHeight = 0
|
||||
|
||||
@Property()
|
||||
frameRate = 0
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setSprite(sprite: Sprite) {
|
||||
this.sprite = sprite
|
||||
return this
|
||||
}
|
||||
|
||||
getSprite() {
|
||||
return this.sprite
|
||||
}
|
||||
|
||||
setAction(action: string) {
|
||||
this.action = action
|
||||
return this
|
||||
}
|
||||
|
||||
getAction() {
|
||||
return this.action
|
||||
}
|
||||
|
||||
setSprites(sprites: string[]) {
|
||||
this.sprites = sprites
|
||||
return this
|
||||
}
|
||||
|
||||
getSprites() {
|
||||
return this.sprites
|
||||
}
|
||||
|
||||
setOriginX(originX: number) {
|
||||
this.originX = originX
|
||||
return this
|
||||
}
|
||||
|
||||
getOriginX() {
|
||||
return this.originX
|
||||
}
|
||||
|
||||
setOriginY(originY: number) {
|
||||
this.originY = originY
|
||||
return this
|
||||
}
|
||||
|
||||
getOriginY() {
|
||||
return this.originY
|
||||
}
|
||||
|
||||
setIsAnimated(isAnimated: boolean) {
|
||||
this.isAnimated = isAnimated
|
||||
return this
|
||||
}
|
||||
|
||||
getIsAnimated() {
|
||||
return this.isAnimated
|
||||
}
|
||||
|
||||
setIsLooping(isLooping: boolean) {
|
||||
this.isLooping = isLooping
|
||||
return this
|
||||
}
|
||||
|
||||
getIsLooping() {
|
||||
return this.isLooping
|
||||
}
|
||||
|
||||
setFrameWidth(frameWidth: number) {
|
||||
this.frameWidth = frameWidth
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameWidth() {
|
||||
return this.frameWidth
|
||||
}
|
||||
|
||||
setFrameHeight(frameHeight: number) {
|
||||
this.frameHeight = frameHeight
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameHeight() {
|
||||
return this.frameHeight
|
||||
}
|
||||
|
||||
setFrameRate(frameRate: number) {
|
||||
this.frameRate = frameRate
|
||||
return this
|
||||
}
|
||||
|
||||
getFrameRate() {
|
||||
return this.frameRate
|
||||
}
|
||||
}
|
||||
export class SpriteAction extends BaseSpriteAction {}
|
||||
|
@ -1,69 +1,14 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseTile } from '@/entities/base/tile'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class Tile extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property()
|
||||
name!: string
|
||||
|
||||
@Property({ type: 'json', nullable: true })
|
||||
tags?: any
|
||||
|
||||
@Property()
|
||||
createdAt = new Date()
|
||||
|
||||
@Property()
|
||||
updatedAt = new Date()
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name
|
||||
}
|
||||
|
||||
setTags(tags: any) {
|
||||
this.tags = tags
|
||||
return this
|
||||
}
|
||||
|
||||
getTags() {
|
||||
return this.tags
|
||||
}
|
||||
|
||||
setCreatedAt(createdAt: Date) {
|
||||
this.createdAt = createdAt
|
||||
return this
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
setUpdatedAt(updatedAt: Date) {
|
||||
this.updatedAt = updatedAt
|
||||
return this
|
||||
}
|
||||
|
||||
getUpdatedAt() {
|
||||
return this.updatedAt
|
||||
export class Tile extends BaseTile {
|
||||
public async cache() {
|
||||
try {
|
||||
return this
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,98 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
import bcrypt from 'bcryptjs'
|
||||
|
||||
import { Character } from './character'
|
||||
import { PasswordResetToken } from './passwordResetToken'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseUser } from '@/entities/base/user'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class User extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
id = randomUUID()
|
||||
|
||||
@Property({ unique: true })
|
||||
username!: string
|
||||
|
||||
@Property({ unique: true })
|
||||
email!: string
|
||||
|
||||
@Property()
|
||||
password!: string
|
||||
|
||||
@Property()
|
||||
online = false
|
||||
|
||||
@OneToMany(() => Character, (character) => character.user)
|
||||
characters = new Collection<Character>(this)
|
||||
|
||||
@OneToMany(() => PasswordResetToken, (token) => token.user)
|
||||
passwordResetTokens = new Collection<PasswordResetToken>(this)
|
||||
|
||||
setId(id: UUID) {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
setUsername(username: string) {
|
||||
this.username = username
|
||||
return this
|
||||
}
|
||||
|
||||
getUsername() {
|
||||
return this.username
|
||||
}
|
||||
|
||||
setEmail(email: string) {
|
||||
this.email = email
|
||||
return this
|
||||
}
|
||||
|
||||
getEmail() {
|
||||
return this.email
|
||||
}
|
||||
|
||||
setPassword(password: string) {
|
||||
this.password = bcrypt.hashSync(password, 10)
|
||||
return this
|
||||
}
|
||||
|
||||
getPassword() {
|
||||
return this.password
|
||||
}
|
||||
|
||||
setOnline(online: boolean) {
|
||||
this.online = online
|
||||
return this
|
||||
}
|
||||
|
||||
getOnline() {
|
||||
return this.online
|
||||
}
|
||||
|
||||
setCharacters(characters: Collection<Character>) {
|
||||
this.characters = characters
|
||||
return this
|
||||
}
|
||||
|
||||
getCharacters() {
|
||||
return this.characters
|
||||
}
|
||||
|
||||
setPasswordResetTokens(passwordResetTokens: Collection<PasswordResetToken>) {
|
||||
this.passwordResetTokens = passwordResetTokens
|
||||
return this
|
||||
}
|
||||
|
||||
getPasswordResetTokens() {
|
||||
return this.passwordResetTokens
|
||||
return this
|
||||
}
|
||||
}
|
||||
export class User extends BaseUser {}
|
||||
|
@ -1,66 +1,5 @@
|
||||
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||
|
||||
import { BaseEntity } from '#application/base/baseEntity'
|
||||
import { BaseWorld } from '@/entities/base/world'
|
||||
import { Entity } from '@mikro-orm/core'
|
||||
|
||||
@Entity()
|
||||
export class World extends BaseEntity {
|
||||
@PrimaryKey()
|
||||
date = new Date()
|
||||
|
||||
@Property()
|
||||
isRainEnabled = false
|
||||
|
||||
@Property()
|
||||
rainPercentage = 0
|
||||
|
||||
@Property()
|
||||
isFogEnabled = false
|
||||
|
||||
@Property()
|
||||
fogDensity = 0
|
||||
|
||||
setDate(date: Date) {
|
||||
this.date = date
|
||||
return this
|
||||
}
|
||||
|
||||
getDate() {
|
||||
return this.date
|
||||
}
|
||||
|
||||
setIsRainEnabled(isRainEnabled: boolean) {
|
||||
this.isRainEnabled = isRainEnabled
|
||||
return this
|
||||
}
|
||||
|
||||
getIsRainEnabled() {
|
||||
return this.isRainEnabled
|
||||
}
|
||||
|
||||
setRainPercentage(rainPercentage: number) {
|
||||
this.rainPercentage = rainPercentage
|
||||
return this
|
||||
}
|
||||
|
||||
getRainPercentage() {
|
||||
return this.rainPercentage
|
||||
}
|
||||
|
||||
setIsFogEnabled(isFogEnabled: boolean) {
|
||||
this.isFogEnabled = isFogEnabled
|
||||
return this
|
||||
}
|
||||
|
||||
getIsFogEnabled() {
|
||||
return this.isFogEnabled
|
||||
}
|
||||
|
||||
setFogDensity(fogDensity: number) {
|
||||
this.fogDensity = fogDensity
|
||||
return this
|
||||
}
|
||||
|
||||
getFogDensity() {
|
||||
return this.fogDensity
|
||||
}
|
||||
}
|
||||
export class World extends BaseWorld {}
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import Database from '#application/database'
|
||||
import { CharacterHair } from '#entities/characterHair'
|
||||
import characterHairRepository from '#repositories/characterHairRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
export default class characterHairListEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('character:hair:list', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
||||
try {
|
||||
const items: CharacterHair[] = await characterHairRepository.getAllSelectable()
|
||||
await Database.getEntityManager().populate(items, ['sprite'])
|
||||
return callback(items)
|
||||
} catch (error) {
|
||||
this.logger.error('character:hair:list error', error)
|
||||
return callback([])
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +1,66 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import TeleportService from '#services/teleportService'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { ZCharacterConnect } from '@/application/zodTypes'
|
||||
import MapManager from '@/managers/mapManager'
|
||||
import CharacterHairRepository from '@/repositories/characterHairRepository'
|
||||
import CharacterRepository from '@/repositories/characterRepository'
|
||||
import TeleportService from '@/services/characterTeleportService'
|
||||
|
||||
interface CharacterConnectPayload {
|
||||
characterId: UUID
|
||||
characterHairId?: UUID
|
||||
characterHairId: UUID | null
|
||||
newNickname?: string
|
||||
}
|
||||
|
||||
export default class CharacterConnectEvent extends BaseEvent {
|
||||
private readonly characterHairRepository = new CharacterHairRepository()
|
||||
private readonly characterRepository = new CharacterRepository()
|
||||
|
||||
public listen(): void {
|
||||
this.socket.on('character:connect', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHARACTER_CONNECT, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: CharacterConnectPayload, callback: (response: any) => void): Promise<void> {
|
||||
try {
|
||||
if (await this.checkForActiveCharacters()) {
|
||||
this.emitError('You are already connected to another character')
|
||||
if (data.newNickname === '') data.newNickname = undefined
|
||||
const result = ZCharacterConnect.safeParse(data)
|
||||
if (!result.success) {
|
||||
this.sendNotificationAndLog(result.error?.errors[0]?.message ?? 'Invalid data')
|
||||
return
|
||||
}
|
||||
|
||||
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, data.characterId)
|
||||
if (await this.checkForActiveCharacters()) {
|
||||
this.sendNotificationAndLog('You are already connected to another character')
|
||||
return
|
||||
}
|
||||
|
||||
let character = await this.characterRepository.getByUserAndId(this.socket.userId!, data.characterId)
|
||||
|
||||
if (!character) {
|
||||
this.emitError('Character not found or does not belong to this user')
|
||||
this.sendNotificationAndLog('Character not found or does not belong to this user')
|
||||
return
|
||||
}
|
||||
|
||||
if (data.newNickname) {
|
||||
const existingCharacter = await this.characterRepository.getByName(data.newNickname)
|
||||
if (existingCharacter) {
|
||||
this.sendNotificationAndLog('Nickname already in use: ' + data.newNickname)
|
||||
return
|
||||
}
|
||||
|
||||
await character.setName(data.newNickname).save()
|
||||
}
|
||||
|
||||
// Set character id
|
||||
this.socket.characterId = character.id
|
||||
|
||||
// Set character hair
|
||||
if (data.characterHairId !== undefined && data.characterHairId !== null) {
|
||||
const characterHair = await CharacterHairRepository.getById(data.characterHairId)
|
||||
await character.setCharacterHair(characterHair).update()
|
||||
const characterHair = await this.characterHairRepository.getById(data.characterHairId)
|
||||
await character.setCharacterHair(characterHair).save()
|
||||
} else {
|
||||
await character.setCharacterHair(null).save()
|
||||
}
|
||||
|
||||
// Emit character connect event
|
||||
@ -58,7 +83,7 @@ export default class CharacterConnectEvent extends BaseEvent {
|
||||
}
|
||||
|
||||
private async checkForActiveCharacters(): Promise<boolean> {
|
||||
const characters = await CharacterRepository.getByUserId(this.socket.userId!)
|
||||
const characters = await this.characterRepository.getByUserId(this.socket.userId!)
|
||||
return characters?.some((char) => MapManager.getCharacterById(char.id)) ?? false
|
||||
}
|
||||
}
|
||||
|
@ -1,63 +1,84 @@
|
||||
import { ZodError } from 'zod'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import { ZCharacterCreate } from '@/application/zodTypes'
|
||||
import { Character } from '@/entities/character'
|
||||
import CharacterRepository from '@/repositories/characterRepository'
|
||||
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
|
||||
import MapRepository from '@/repositories/mapRepository'
|
||||
import UserRepository from '@/repositories/userRepository'
|
||||
import { z, ZodError } from 'zod'
|
||||
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { ZCharacterCreate } from '#application/zodTypes'
|
||||
import { Character } from '#entities/character'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import UserRepository from '#repositories/userRepository'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
const MAX_CHARACTERS = 4
|
||||
|
||||
export default class CharacterCreateEvent extends BaseEvent {
|
||||
private readonly userRepository: UserRepository = new UserRepository()
|
||||
private readonly characterRepository: CharacterRepository = new CharacterRepository()
|
||||
private readonly characterTypeRepository: CharacterTypeRepository = new CharacterTypeRepository()
|
||||
private readonly mapRepository: MapRepository = new MapRepository()
|
||||
|
||||
public listen(): void {
|
||||
this.socket.on('character:create', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHARACTER_CREATE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: any): Promise<any> {
|
||||
// zod validate
|
||||
private async handleEvent(data: z.infer<typeof ZCharacterCreate>, callback: (success: boolean) => void): Promise<void> {
|
||||
try {
|
||||
data = ZCharacterCreate.parse(data)
|
||||
|
||||
const user = await UserRepository.getById(this.socket.userId!)
|
||||
|
||||
if (!user) {
|
||||
return this.socket.emit('notification', { message: 'User not found' })
|
||||
}
|
||||
|
||||
// Check if character name already exists
|
||||
const characterExists = await CharacterRepository.getByName(data.name)
|
||||
|
||||
if (characterExists) {
|
||||
return this.socket.emit('notification', { message: 'Character name already exists' })
|
||||
}
|
||||
|
||||
let characters: Character[] = await CharacterRepository.getByUserId(user.getId())
|
||||
|
||||
if (characters.length >= 4) {
|
||||
return this.socket.emit('notification', { message: 'You can only have 4 characters' })
|
||||
}
|
||||
|
||||
// @TODO: Change to default location
|
||||
const map = await MapRepository.getFirst()
|
||||
|
||||
const newCharacter = new Character()
|
||||
await newCharacter.setName(data.name).setUser(user).setMap(map!).save()
|
||||
|
||||
if (!newCharacter) {
|
||||
return this.socket.emit('notification', { message: 'Failed to create character. Please try again (later).' })
|
||||
}
|
||||
|
||||
characters = [...characters, newCharacter]
|
||||
|
||||
this.socket.emit('character:create:success')
|
||||
this.socket.emit('character:list', characters)
|
||||
|
||||
this.logger.info('character:create success')
|
||||
} catch (error: any) {
|
||||
this.logger.error(`character:create error: ${error.message}`)
|
||||
if (error instanceof ZodError) {
|
||||
return this.socket.emit('notification', { message: error.issues[0].message })
|
||||
}
|
||||
return this.socket.emit('notification', { message: 'Could not create character. Please try again (later).' })
|
||||
const validatedData = ZCharacterCreate.parse(data)
|
||||
await this.createCharacter(validatedData)
|
||||
callback(true)
|
||||
} catch (error: unknown) {
|
||||
this.returnError(error)
|
||||
callback(false)
|
||||
}
|
||||
}
|
||||
|
||||
private async createCharacter(data: z.infer<typeof ZCharacterCreate>): Promise<void> {
|
||||
const user = await this.userRepository.getById(this.socket.userId!)
|
||||
if (!user) {
|
||||
throw new Error('You are not logged in')
|
||||
}
|
||||
|
||||
const characterExists = await this.characterRepository.getByName(data.name)
|
||||
if (characterExists) {
|
||||
throw new Error('Character name already exists')
|
||||
}
|
||||
|
||||
let characters = await this.characterRepository.getByUserId(user.getId())
|
||||
if (characters.length >= MAX_CHARACTERS) {
|
||||
throw new Error(`You can only create ${MAX_CHARACTERS} characters`)
|
||||
}
|
||||
|
||||
const map = await this.mapRepository.getFirst()
|
||||
if (!map) {
|
||||
throw new Error('No default map found')
|
||||
}
|
||||
|
||||
const characterType = await this.characterTypeRepository.getFirst()
|
||||
if (!characterType) {
|
||||
throw new Error('No character type found')
|
||||
}
|
||||
|
||||
const newCharacter = new Character()
|
||||
await newCharacter.setName(data.name).setUser(user).setMap(map).setCharacterType(characterType).save()
|
||||
characters = await this.characterRepository.getByUserId(user.getId())
|
||||
|
||||
this.socket.emit(SocketEvent.CHARACTER_LIST, characters)
|
||||
this.logger.info('character:create success')
|
||||
}
|
||||
|
||||
private returnError(error: unknown): void {
|
||||
this.logger.error(`character:create error: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||
|
||||
let errorMessage = 'Could not create character. Please try again later.'
|
||||
|
||||
if (error instanceof ZodError) {
|
||||
errorMessage = error.issues[0]?.message || errorMessage
|
||||
} else if (error instanceof Error) {
|
||||
errorMessage = error.message
|
||||
}
|
||||
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||
title: 'Error',
|
||||
message: errorMessage
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import { Character } from '#entities/character'
|
||||
import { Map } from '#entities/map'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { Character } from '@/entities/character'
|
||||
import CharacterRepository from '@/repositories/characterRepository'
|
||||
|
||||
type TypePayload = {
|
||||
characterId: UUID
|
||||
@ -14,17 +14,18 @@ type TypeResponse = {
|
||||
|
||||
export default class CharacterDeleteEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('character:delete', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHARACTER_DELETE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> {
|
||||
try {
|
||||
await (await CharacterRepository.getByUserAndId(this.socket.userId!, data.characterId))?.delete()
|
||||
const characters: Character[] = await CharacterRepository.getByUserId(this.socket.userId!)
|
||||
const characterRepository = new CharacterRepository()
|
||||
await (await characterRepository.getByUserAndId(this.socket.userId, data.characterId))?.delete()
|
||||
const characters: Character[] = await characterRepository.getByUserId(this.socket.userId)
|
||||
|
||||
this.socket.emit('character:list', characters)
|
||||
this.socket.emit(SocketEvent.CHARACTER_LIST, characters)
|
||||
} catch (error: any) {
|
||||
return this.socket.emit('notification', { message: 'Character delete failed. Please try again.' })
|
||||
return this.socket.emit(SocketEvent.NOTIFICATION, { message: 'Character delete failed. Please try again.' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,19 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { Character } from '#entities/character'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import { Character } from '@/entities/character'
|
||||
import CharacterRepository from '@/repositories/characterRepository'
|
||||
|
||||
export default class CharacterListEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('character:list', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHARACTER_LIST, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: any): Promise<void> {
|
||||
try {
|
||||
let characters: Character[] = await CharacterRepository.getByUserId(this.socket.userId!, ['characterType', 'characterHair'])
|
||||
this.socket.emit('character:list', characters)
|
||||
const characterRepository = new CharacterRepository()
|
||||
let characters: Character[] = await characterRepository.getByUserId(this.socket.userId)
|
||||
|
||||
this.socket.emit(SocketEvent.CHARACTER_LIST, characters)
|
||||
} catch (error: any) {
|
||||
this.logger.error('character:list error', error.message)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import ChatService from '#services/chatService'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import CharacterRepository from '@/repositories/characterRepository'
|
||||
import ChatService from '@/services/chatService'
|
||||
|
||||
type TypePayload = {
|
||||
message: string
|
||||
@ -8,27 +9,16 @@ type TypePayload = {
|
||||
|
||||
export default class AlertCommandEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!ChatService.isCommand(data.message, 'alert')) {
|
||||
return
|
||||
}
|
||||
// Check if command is alert
|
||||
if (!ChatService.isCommand(data.message, 'alert')) return
|
||||
|
||||
// Check if character exists
|
||||
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, this.socket.characterId!)
|
||||
if (!character) {
|
||||
this.logger.error('chat:alert_command error', 'Character not found')
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
// Check if the user is the GM
|
||||
if (character.role !== 'gm') {
|
||||
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
|
||||
return callback(false)
|
||||
}
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const args = ChatService.getArgs('alert', data.message)
|
||||
|
||||
@ -36,7 +26,7 @@ export default class AlertCommandEvent extends BaseEvent {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
this.io.emit('notification', { title: 'Message from GM', message: args.join(' ') })
|
||||
this.io.emit(SocketEvent.NOTIFICATION, { title: 'Message from GM', message: args.join(' ') })
|
||||
return callback(true)
|
||||
} catch (error: any) {
|
||||
this.logger.error('chat:alert_command error', error.message)
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import DateManager from '#managers/dateManager'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import ChatService from '#services/chatService'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import DateManager from '@/managers/dateManager'
|
||||
import CharacterRepository from '@/repositories/characterRepository'
|
||||
import ChatService from '@/services/chatService'
|
||||
|
||||
type TypePayload = {
|
||||
message: string
|
||||
@ -9,27 +10,16 @@ type TypePayload = {
|
||||
|
||||
export default class SetTimeCommand extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!ChatService.isCommand(data.message, 'time')) {
|
||||
return
|
||||
}
|
||||
// Check if command is time
|
||||
if (!ChatService.isCommand(data.message, 'time')) return
|
||||
|
||||
// Check if character exists
|
||||
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, this.socket.characterId!)
|
||||
if (!character) {
|
||||
this.logger.error('chat:alert_command error', 'Character not found')
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user is the GM
|
||||
if (character.role !== 'gm') {
|
||||
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
|
||||
return
|
||||
}
|
||||
// Check if character exists and is GM
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
// Get arguments
|
||||
const args = ChatService.getArgs('time', data.message)
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
import ChatService from '#services/chatService'
|
||||
import TeleportService from '#services/teleportService'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import MapManager from '@/managers/mapManager'
|
||||
import MapRepository from '@/repositories/mapRepository'
|
||||
import TeleportService from '@/services/characterTeleportService'
|
||||
import ChatService from '@/services/chatService'
|
||||
|
||||
type TypePayload = {
|
||||
message: string
|
||||
@ -11,30 +12,24 @@ type TypePayload = {
|
||||
|
||||
export default class TeleportCommandEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void) {
|
||||
try {
|
||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||
if (!mapCharacter) {
|
||||
this.logger.error('chat:message error', 'Character not found')
|
||||
return
|
||||
}
|
||||
|
||||
const character = mapCharacter.character
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if command is teleport
|
||||
if (!ChatService.isCommand(data.message, 'teleport')) return
|
||||
|
||||
// Check if character exists and is GM
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const character = await this.getCharacter()
|
||||
if (!character) return
|
||||
|
||||
const args = ChatService.getArgs('teleport', data.message)
|
||||
|
||||
if (!args || args.length === 0 || args.length > 3) {
|
||||
this.socket.emit('notification', {
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||
title: 'Server message',
|
||||
message: 'Usage: /teleport <mapId> [x] [y]'
|
||||
})
|
||||
@ -46,16 +41,17 @@ export default class TeleportCommandEvent extends BaseEvent {
|
||||
const targetY = args[2] ? parseInt(args[2], 10) : 0
|
||||
|
||||
if (!mapId || isNaN(targetX) || isNaN(targetY)) {
|
||||
this.socket.emit('notification', {
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||
title: 'Server message',
|
||||
message: 'Invalid parameters. X and Y coordinates must be numbers.'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const map = await MapRepository.getById(mapId)
|
||||
const mapRepository = new MapRepository()
|
||||
const map = await mapRepository.getById(mapId)
|
||||
if (!map) {
|
||||
this.socket.emit('notification', {
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||
title: 'Server message',
|
||||
message: 'Map not found'
|
||||
})
|
||||
@ -63,7 +59,7 @@ export default class TeleportCommandEvent extends BaseEvent {
|
||||
}
|
||||
|
||||
if (character.map.id === map.id && targetX === character.positionX && targetY === character.positionY) {
|
||||
this.socket.emit('notification', {
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||
title: 'Server message',
|
||||
message: 'You are already at that location'
|
||||
})
|
||||
@ -78,20 +74,20 @@ export default class TeleportCommandEvent extends BaseEvent {
|
||||
})
|
||||
|
||||
if (!success) {
|
||||
return this.socket.emit('notification', {
|
||||
return this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||
title: 'Server message',
|
||||
message: 'Failed to teleport'
|
||||
})
|
||||
}
|
||||
|
||||
this.socket.emit('notification', {
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||
title: 'Server message',
|
||||
message: `Teleported to ${map.name} (${targetX}, ${targetY})`
|
||||
})
|
||||
this.logger.info('teleport', `Character ${character.id} teleported to map ${map.id} at position (${targetX}, ${targetY})`)
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error in teleport command: ${error.message}`)
|
||||
this.socket.emit('notification', {
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||
title: 'Server message',
|
||||
message: 'An error occurred while teleporting'
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import WeatherManager from '#managers/weatherManager'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import ChatService from '#services/chatService'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import WeatherManager from '@/managers/weatherManager'
|
||||
import ChatService from '@/services/chatService'
|
||||
|
||||
type TypePayload = {
|
||||
message: string
|
||||
@ -9,29 +9,21 @@ type TypePayload = {
|
||||
|
||||
export default class ToggleFogCommand extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!ChatService.isCommand(data.message, 'fog')) {
|
||||
return
|
||||
}
|
||||
// Check if command is fog
|
||||
if (!ChatService.isCommand(data.message, 'fog')) return
|
||||
|
||||
// Check if character exists
|
||||
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, this.socket.characterId!)
|
||||
if (!character) {
|
||||
this.logger.error('chat:alert_command error', 'Character not found')
|
||||
return
|
||||
}
|
||||
// Check if character exists and is GM
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
// Check if the user is the GM
|
||||
if (character.role !== 'gm') {
|
||||
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
|
||||
return
|
||||
}
|
||||
const args = ChatService.getArgs('fog', data.message)
|
||||
|
||||
await WeatherManager.toggleFog()
|
||||
await WeatherManager.setFogValue(args![0] ? Number(args![0]) : null)
|
||||
callback(true)
|
||||
} catch (error: any) {
|
||||
this.logger.error('command error', error.message)
|
||||
callback(false)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import WeatherManager from '#managers/weatherManager'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import ChatService from '#services/chatService'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import WeatherManager from '@/managers/weatherManager'
|
||||
import ChatService from '@/services/chatService'
|
||||
|
||||
type TypePayload = {
|
||||
message: string
|
||||
@ -9,29 +9,21 @@ type TypePayload = {
|
||||
|
||||
export default class ToggleRainCommand extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!ChatService.isCommand(data.message, 'rain')) {
|
||||
return
|
||||
}
|
||||
// Check if command is rain
|
||||
if (!ChatService.isCommand(data.message, 'rain')) return
|
||||
|
||||
// Check if character exists
|
||||
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, this.socket.characterId!)
|
||||
if (!character) {
|
||||
this.logger.error('chat:alert_command error', 'Character not found')
|
||||
return
|
||||
}
|
||||
// Check if character exists and is GM
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
// Check if the user is the GM
|
||||
if (character.role !== 'gm') {
|
||||
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
|
||||
return
|
||||
}
|
||||
let args = ChatService.getArgs('rain', data.message)
|
||||
|
||||
await WeatherManager.toggleRain()
|
||||
await WeatherManager.setRainValue(args![0] ? Number(args![0]) : null)
|
||||
callback(true)
|
||||
} catch (error: any) {
|
||||
this.logger.error('command error', error.message)
|
||||
callback(false)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
import ChatService from '#services/chatService'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import MapManager from '@/managers/mapManager'
|
||||
import ChatService from '@/services/chatService'
|
||||
|
||||
type TypePayload = {
|
||||
message: string
|
||||
@ -9,30 +9,22 @@ type TypePayload = {
|
||||
|
||||
export default class ChatMessageEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!data.message || ChatService.isCommand(data.message)) {
|
||||
return callback(false)
|
||||
}
|
||||
if (!data.message || ChatService.isCommand(data.message)) return
|
||||
|
||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||
if (!mapCharacter) {
|
||||
this.logger.error('chat:message error', 'Character not found')
|
||||
return callback(false)
|
||||
return
|
||||
}
|
||||
|
||||
const character = mapCharacter.character
|
||||
|
||||
const map = await MapRepository.getById(character.map.id)
|
||||
if (!map) {
|
||||
this.logger.error('chat:message error', 'Map not found')
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
if (await ChatService.sendMapMessage(character.getId(), map.getId(), data.message)) {
|
||||
if (await ChatService.sendMapMessage(character.getId(), data.message)) {
|
||||
return callback(true)
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import MapManager from '@/managers/mapManager'
|
||||
|
||||
export default class DisconnectEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('disconnect', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.DISCONNECT, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(): Promise<void> {
|
||||
@ -13,7 +14,7 @@ export default class DisconnectEvent extends BaseEvent {
|
||||
return
|
||||
}
|
||||
|
||||
this.io.emit('user:disconnect', this.socket.userId)
|
||||
this.io.emit(SocketEvent.USER_DISCONNECT, this.socket.userId)
|
||||
|
||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||
if (!mapCharacter) {
|
||||
|
@ -1,28 +1,33 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { CharacterHair } from '#entities/characterHair'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { CharacterGender, SocketEvent } from '@/application/enums'
|
||||
import { CharacterHair } from '@/entities/characterHair'
|
||||
import SpriteRepository from '@/repositories/spriteRepository'
|
||||
|
||||
export default class CharacterHairCreateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:characterHair:create', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_CHARACTERHAIR_CREATE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> {
|
||||
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
const character = await characterRepository.getById(this.socket.characterId!)
|
||||
if (!character) return callback(false)
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
// Get first sprite
|
||||
const spriteRepository = new SpriteRepository()
|
||||
const firstSprite = await spriteRepository.getFirst()
|
||||
|
||||
if (!firstSprite) {
|
||||
this.sendNotificationAndLog('No sprites found')
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
const newCharacterHair = new CharacterHair()
|
||||
await newCharacterHair.setName('New hair').save()
|
||||
await newCharacterHair.setName('New hair').setGender(CharacterGender.MALE).setSprite(firstSprite).save()
|
||||
|
||||
callback(true, newCharacterHair)
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
console.error('Error creating character hair:', error)
|
||||
callback(false)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,31 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import CharacterHairRepository from '@/repositories/characterHairRepository'
|
||||
|
||||
interface IPayload {
|
||||
id: UUID
|
||||
}
|
||||
|
||||
export default class characterHairDeleteEvent extends BaseEvent {
|
||||
export default class CharacterHairDeleteEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:characterHair:remove', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_CHARACTERHAIR_REMOVE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const characterHair = await CharacterHairRepository.getById(data.id)
|
||||
await (await CharacterHairRepository.getById(data.id))?.delete()
|
||||
const characterHairRepository = new CharacterHairRepository()
|
||||
const characterHair = await characterHairRepository.getById(data.id)
|
||||
if (!characterHair) return callback(false)
|
||||
|
||||
await characterHair.delete()
|
||||
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
this.logger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
callback(false)
|
||||
this.logger.error(`Error deleting character hair ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,23 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { CharacterHair } from '#entities/characterHair'
|
||||
import characterHairRepository from '#repositories/characterHairRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import { CharacterHair } from '@/entities/characterHair'
|
||||
import CharacterHairRepository from '@/repositories/characterHairRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
export default class characterHairListEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:characterHair:list', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_CHARACTERHAIR_LIST, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const characterHairRepository = new CharacterHairRepository()
|
||||
const items = await characterHairRepository.getAll()
|
||||
await characterHairRepository.getEntityManager().populate(items, ['sprite'])
|
||||
|
||||
return callback(items)
|
||||
} catch (error) {
|
||||
this.logger.error('gm:characterHair:list error', error)
|
||||
|
@ -1,35 +1,37 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { CharacterGender } from '#application/enums'
|
||||
import { UUID } from '#application/types'
|
||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
import SpriteRepository from '#repositories/spriteRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { CharacterGender, SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import CharacterHairRepository from '@/repositories/characterHairRepository'
|
||||
import SpriteRepository from '@/repositories/spriteRepository'
|
||||
|
||||
type Payload = {
|
||||
id: UUID
|
||||
name: string
|
||||
gender: CharacterGender
|
||||
color: string
|
||||
isSelectable: boolean
|
||||
spriteId: UUID
|
||||
}
|
||||
|
||||
export default class CharacterHairUpdateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:characterHair:update', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_CHARACTERHAIR_UPDATE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const sprite = await SpriteRepository.getById(data.spriteId)
|
||||
const characterHair = await CharacterHairRepository.getById(data.id)
|
||||
const spriteRepository = new SpriteRepository()
|
||||
const sprite = await spriteRepository.getById(data.spriteId)
|
||||
if (!sprite) return callback(false)
|
||||
|
||||
if (!characterHair) {
|
||||
return callback(false)
|
||||
}
|
||||
const characterHairRepository = new CharacterHairRepository()
|
||||
const characterHair = await characterHairRepository.getById(data.id)
|
||||
if (!characterHair) return callback(false)
|
||||
|
||||
await characterHair.setName(data.name).setGender(data.gender).setColor(data.color).setIsSelectable(data.isSelectable).setSprite(sprite).setUpdatedAt(new Date()).save()
|
||||
|
||||
await characterHair.setName(data.name).setGender(data.gender).setIsSelectable(data.isSelectable).setSprite(sprite!).update()
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
this.logger.error(`Error updating character hair: ${error instanceof Error ? error.message : String(error)}`)
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { CharacterType } from '#entities/characterType'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import { CharacterType } from '@/entities/characterType'
|
||||
|
||||
export default class CharacterTypeCreateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:characterType:create', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_CHARACTERTYPE_CREATE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { UUID } from '#application/types'
|
||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
|
||||
|
||||
interface IPayload {
|
||||
id: UUID
|
||||
@ -8,14 +9,15 @@ interface IPayload {
|
||||
|
||||
export default class CharacterTypeDeleteEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:characterType:remove', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_CHARACTERTYPE_REMOVE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const characterType = await CharacterTypeRepository.getById(data.id)
|
||||
const characterTypeRepository = new CharacterTypeRepository()
|
||||
const characterType = await characterTypeRepository.getById(data.id)
|
||||
if (!characterType) return callback(false)
|
||||
|
||||
await characterType.delete()
|
||||
|
@ -1,19 +1,23 @@
|
||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { CharacterType } from '#entities/characterType'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import { CharacterType } from '@/entities/characterType'
|
||||
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
export default class CharacterTypeListEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:characterType:list', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_CHARACTERTYPE_LIST, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: CharacterType[]) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const items = await CharacterTypeRepository.getAll()
|
||||
const characterTypeRepository = new CharacterTypeRepository()
|
||||
const items = await characterTypeRepository.getAll()
|
||||
await characterTypeRepository.getEntityManager().populate(items, ['sprite'])
|
||||
|
||||
return callback(items)
|
||||
} catch (error) {
|
||||
this.logger.error('gm:characterType:list error', error)
|
||||
|
@ -1,53 +1,41 @@
|
||||
import { CharacterGender, CharacterRace } from '@prisma/client'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import prisma from '#application/prisma'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { CharacterGender, CharacterRace, SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
|
||||
import SpriteRepository from '@/repositories/spriteRepository'
|
||||
|
||||
type Payload = {
|
||||
id: number
|
||||
id: UUID
|
||||
name: string
|
||||
gender: CharacterGender
|
||||
race: CharacterRace
|
||||
isSelectable: boolean
|
||||
spriteId: string
|
||||
spriteId: UUID
|
||||
}
|
||||
|
||||
export default class CharacterTypeUpdateEvent {
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {}
|
||||
|
||||
export default class CharacterTypeUpdateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:characterType:update', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_CHARACTERTYPE_UPDATE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||
const character = await characterRepository.getById(this.socket.characterId!)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.characterType.update({
|
||||
where: { id: data.id },
|
||||
data: {
|
||||
name: data.name,
|
||||
gender: data.gender,
|
||||
race: data.race,
|
||||
isSelectable: data.isSelectable,
|
||||
spriteId: data.spriteId
|
||||
}
|
||||
})
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
callback(true)
|
||||
const characterTypeRepository = new CharacterTypeRepository()
|
||||
const characterType = await characterTypeRepository.getById(data.id)
|
||||
if (!characterType) return callback(false)
|
||||
|
||||
const spriteRepository = new SpriteRepository()
|
||||
const sprite = await spriteRepository.getById(data.spriteId)
|
||||
if (!sprite) return callback(false)
|
||||
|
||||
await characterType.setName(data.name).setGender(data.gender).setRace(data.race).setIsSelectable(data.isSelectable).setSprite(sprite).save()
|
||||
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
callback(false)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +1,31 @@
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import prisma from '#application/prisma'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
|
||||
export default class ItemCreateEvent {
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {}
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { ItemRarity, ItemType, SocketEvent } from '@/application/enums'
|
||||
import { Item } from '@/entities/item'
|
||||
import SpriteRepository from '@/repositories/spriteRepository'
|
||||
|
||||
export default class ItemCreateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:item:create', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_ITEM_CREATE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: undefined, callback: (response: boolean, item?: any) => void): Promise<void> {
|
||||
try {
|
||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
const spriteRepository = new SpriteRepository()
|
||||
const sprite = await spriteRepository.getFirst()
|
||||
if (!sprite) {
|
||||
this.sendNotificationAndLog('No sprites found')
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
const newItem = await prisma.item.create({
|
||||
data: {
|
||||
name: 'New Item',
|
||||
itemType: 'WEAPON',
|
||||
stackable: false,
|
||||
rarity: 'COMMON',
|
||||
spriteId: null
|
||||
}
|
||||
})
|
||||
const newItem = new Item()
|
||||
await newItem.setName('New Item').setItemType(ItemType.WEAPON).setStackable(false).setRarity(ItemRarity.COMMON).setSprite(sprite).save()
|
||||
|
||||
callback(true, newItem)
|
||||
return callback(true, newItem)
|
||||
} catch (error) {
|
||||
console.error('Error creating item:', error)
|
||||
callback(false)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +1,31 @@
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { gameMasterLogger } from '#application/logger'
|
||||
import prisma from '#application/prisma'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import ItemRepository from '@/repositories/itemRepository'
|
||||
|
||||
interface IPayload {
|
||||
id: string
|
||||
id: UUID
|
||||
}
|
||||
|
||||
export default class ItemDeleteEvent {
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {}
|
||||
|
||||
export default class ItemDeleteEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:item:remove', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_ITEM_REMOVE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.item.delete({
|
||||
where: { id: data.id }
|
||||
})
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
callback(true)
|
||||
const itemRepository = new ItemRepository()
|
||||
const item = await itemRepository.getById(data.id)
|
||||
if (!item) return callback(false)
|
||||
|
||||
await item.delete()
|
||||
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
gameMasterLogger.error(`Error deleting item ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
callback(false)
|
||||
this.logger.error(`Error deleting item ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,26 @@
|
||||
import { Item } from '@prisma/client'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { gameMasterLogger } from '#application/logger'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
import itemRepository from '#repositories/itemRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import { Item } from '@/entities/item'
|
||||
import ItemRepository from '@/repositories/itemRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
export default class ItemListEvent {
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {}
|
||||
|
||||
export default class ItemListEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:item:list', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_ITEM_LIST, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: Item[]) => void): Promise<void> {
|
||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
||||
if (!character) {
|
||||
gameMasterLogger.error('gm:item:list error', 'Character not found')
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const itemRepository = new ItemRepository()
|
||||
const items = await itemRepository.getAll()
|
||||
|
||||
return callback(items)
|
||||
} catch (error) {
|
||||
this.logger.error('gm:item:list error', error)
|
||||
return callback([])
|
||||
}
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
gameMasterLogger.info(`User ${character.id} tried to list items but is not a game master.`)
|
||||
return callback([])
|
||||
}
|
||||
|
||||
// get all items
|
||||
const items = await itemRepository.getAll()
|
||||
callback(items)
|
||||
}
|
||||
}
|
||||
|
@ -1,55 +1,41 @@
|
||||
import { ItemType, ItemRarity } from '@prisma/client'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { gameMasterLogger } from '#application/logger'
|
||||
import prisma from '#application/prisma'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { ItemRarity, ItemType, SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import ItemRepository from '@/repositories/itemRepository'
|
||||
import SpriteRepository from '@/repositories/spriteRepository'
|
||||
|
||||
type Payload = {
|
||||
id: string
|
||||
id: UUID
|
||||
name: string
|
||||
description: string | null
|
||||
description: string
|
||||
itemType: ItemType
|
||||
stackable: boolean
|
||||
rarity: ItemRarity
|
||||
spriteId: string | null
|
||||
spriteId: UUID
|
||||
}
|
||||
|
||||
export default class ItemUpdateEvent {
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {}
|
||||
|
||||
export default class ItemUpdateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:item:update', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_ITEM_UPDATE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.item.update({
|
||||
where: { id: data.id },
|
||||
data: {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
itemType: data.itemType,
|
||||
stackable: data.stackable,
|
||||
rarity: data.rarity,
|
||||
spriteId: data.spriteId
|
||||
}
|
||||
})
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const itemRepository = new ItemRepository()
|
||||
const item = await itemRepository.getById(data.id)
|
||||
if (!item) return callback(false)
|
||||
|
||||
const spriteRepository = new SpriteRepository()
|
||||
const sprite = await spriteRepository.getById(data.spriteId)
|
||||
if (!sprite) return callback(false)
|
||||
|
||||
await item.setName(data.name).setDescription(data.description).setItemType(data.itemType).setStackable(data.stackable).setRarity(data.rarity).setSprite(sprite).save()
|
||||
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
gameMasterLogger.error(`Error updating item: ${error instanceof Error ? error.message : String(error)}`)
|
||||
console.error(error)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,27 @@
|
||||
import ObjectRepository from '#repositories/mapObjectRepository'
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { MapObject } from '#entities/mapObject'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import { MapObject } from '@/entities/mapObject'
|
||||
import MapObjectRepository from '@/repositories/mapObjectRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
export default class MapObjectListEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:mapObject:list', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_MAPOBJECT_LIST, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: MapObject[]) => void): Promise<void> {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
// get all objects
|
||||
const objects = await ObjectRepository.getAll()
|
||||
return callback(objects)
|
||||
// Get all map objects
|
||||
const mapObjectRepository = new MapObjectRepository()
|
||||
const mapObjects = await mapObjectRepository.getAll()
|
||||
|
||||
return callback(mapObjects)
|
||||
} catch (error) {
|
||||
this.logger.error('gm:mapObject:list error', error)
|
||||
return callback([])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import fs from 'fs'
|
||||
import Storage from '#application/storage'
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import MapObjectRepository from '#repositories/mapObjectRepository'
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import Storage from '@/application/storage'
|
||||
import type { UUID } from '@/application/types'
|
||||
import MapObjectRepository from '@/repositories/mapObjectRepository'
|
||||
|
||||
interface IPayload {
|
||||
mapObjectId: UUID
|
||||
@ -10,13 +11,12 @@ interface IPayload {
|
||||
|
||||
export default class MapObjectRemoveEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:mapObject:remove', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_MAPOBJECT_REMOVE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
// remove the tile from the disk
|
||||
const finalFilePath = Storage.getPublicPath('map_objects', data.mapObjectId + '.png')
|
||||
fs.unlink(finalFilePath, async (err) => {
|
||||
@ -26,7 +26,8 @@ export default class MapObjectRemoveEvent extends BaseEvent {
|
||||
return
|
||||
}
|
||||
|
||||
await (await MapObjectRepository.getById(data.mapObjectId))?.delete()
|
||||
const mapObjectRepository = new MapObjectRepository()
|
||||
await (await mapObjectRepository.getById(data.mapObjectId))?.delete()
|
||||
|
||||
return callback(true)
|
||||
})
|
||||
|
@ -1,14 +1,15 @@
|
||||
import { UUID } from '#application/types'
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import MapObjectRepository from '#repositories/mapObjectRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import MapObjectRepository from '@/repositories/mapObjectRepository'
|
||||
|
||||
type Payload = {
|
||||
id: UUID
|
||||
name: string
|
||||
tags: string[]
|
||||
depthOffsets: number[]
|
||||
originX: number
|
||||
originY: number
|
||||
isAnimated: boolean
|
||||
frameRate: number
|
||||
frameWidth: number
|
||||
frameHeight: number
|
||||
@ -16,30 +17,32 @@ type Payload = {
|
||||
|
||||
export default class MapObjectUpdateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:mapObject:update', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_MAPOBJECT_UPDATE, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const mapObject = await MapObjectRepository.getById(data.id)
|
||||
const mapObjectRepository = new MapObjectRepository()
|
||||
|
||||
const mapObject = await mapObjectRepository.getById(data.id)
|
||||
if (!mapObject) return callback(false)
|
||||
|
||||
await mapObject
|
||||
.setName(data.name)
|
||||
.setTags(data.tags)
|
||||
.setOriginX(data.originX)
|
||||
.setOriginY(data.originY)
|
||||
.setIsAnimated(data.isAnimated)
|
||||
.setFrameRate(data.frameRate)
|
||||
.setFrameWidth(data.frameWidth)
|
||||
.setFrameHeight(data.frameHeight)
|
||||
.update()
|
||||
if (data.name !== undefined) mapObject.name = data.name
|
||||
if (data.tags !== undefined) mapObject.tags = data.tags
|
||||
if (data.depthOffsets !== undefined) mapObject.depthOffsets = data.depthOffsets
|
||||
if (data.originX !== undefined) mapObject.originX = data.originX
|
||||
if (data.originY !== undefined) mapObject.originY = data.originY
|
||||
if (data.frameRate !== undefined) mapObject.frameRate = data.frameRate
|
||||
if (data.frameWidth !== undefined) mapObject.frameWidth = data.frameWidth
|
||||
if (data.frameHeight !== undefined) mapObject.frameHeight = data.frameHeight
|
||||
|
||||
await mapObject.save()
|
||||
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Error', message: 'Failed to update mapObject.' })
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import fs from 'fs/promises'
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import Storage from '@/application/storage'
|
||||
import { MapObject } from '@/entities/mapObject'
|
||||
import sharp from 'sharp'
|
||||
import Storage from '#application/storage'
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { MapObject } from '#entities/mapObject'
|
||||
|
||||
interface IObjectData {
|
||||
[key: string]: Buffer
|
||||
@ -12,7 +12,7 @@ interface IObjectData {
|
||||
|
||||
export default class MapObjectUploadEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:mapObject:upload', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_MAPOBJECT_UPLOAD, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IObjectData, callback: (response: boolean) => void): Promise<void> {
|
||||
@ -32,7 +32,7 @@ export default class MapObjectUploadEvent extends BaseEvent {
|
||||
|
||||
// Create new map object and save it to database
|
||||
const mapObject = new MapObject()
|
||||
await mapObject.setName(key).setTags([]).setOriginX(0).setOriginY(0).setFrameWidth(width).setFrameHeight(height).save()
|
||||
await mapObject.setName('New map object').setTags([]).setOriginX(0).setOriginY(0).setFrameWidth(width).setFrameHeight(height).save()
|
||||
|
||||
// Save image to disk
|
||||
const uuid = mapObject.getId()
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import { Sprite } from '#entities/sprite'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import SpriteRepository from '#repositories/spriteRepository'
|
||||
import { BaseEvent } from '@/application/base/baseEvent'
|
||||
import { SocketEvent } from '@/application/enums'
|
||||
import type { UUID } from '@/application/types'
|
||||
import { Sprite } from '@/entities/sprite'
|
||||
import { SpriteAction } from '@/entities/spriteAction'
|
||||
import SpriteRepository from '@/repositories/spriteRepository'
|
||||
|
||||
interface CopyPayload {
|
||||
id: UUID
|
||||
@ -10,26 +11,45 @@ interface CopyPayload {
|
||||
|
||||
export default class SpriteCopyEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:sprite:copy', this.handleEvent.bind(this))
|
||||
this.socket.on(SocketEvent.GM_SPRITE_COPY, this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(payload: CopyPayload, callback: (success: boolean) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const sourceSprite = await SpriteRepository.getById(payload.id)
|
||||
const spriteRepository = new SpriteRepository()
|
||||
const sourceSprite = await spriteRepository.getById(payload.id)
|
||||
|
||||
if (!sourceSprite) {
|
||||
throw new Error('Source sprite not found')
|
||||
this.logger.error('gm:sprite:copy error', 'Source sprite not found')
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
const newSprite = new Sprite()
|
||||
await newSprite.setName(`${sourceSprite.getName()} (Copy)`).setSpriteActions(sourceSprite.getSpriteActions()).save()
|
||||
// Populate source sprite with spriteActions
|
||||
await spriteRepository.getEntityManager().populate(sourceSprite, ['spriteActions'])
|
||||
|
||||
callback(true)
|
||||
const newSprite = new Sprite()
|
||||
await newSprite.setName(`${sourceSprite.getName()} (Copy)`).save()
|
||||
|
||||
for (const spriteAction of sourceSprite.getSpriteActions()) {
|
||||
const newSpriteAction = new SpriteAction()
|
||||
await newSpriteAction
|
||||
.setSprite(newSprite)
|
||||
.setAction(spriteAction.getAction())
|
||||
.setSprites(spriteAction.getSprites() ?? [])
|
||||
.setOriginX(spriteAction.getOriginX())
|
||||
.setOriginY(spriteAction.getOriginY())
|
||||
.setFrameWidth(spriteAction.getFrameWidth())
|
||||
.setFrameHeight(spriteAction.getFrameHeight())
|
||||
.setFrameRate(spriteAction.getFrameRate())
|
||||
.save()
|
||||
}
|
||||
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
this.logger.error(`Error copying sprite:`, String(error))
|
||||
callback(false)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user