@rpgjs/common 3.3.2 → 4.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/lib/AbstractObject.d.ts +3 -2
- package/lib/AbstractObject.js +296 -323
- package/lib/AbstractObject.js.map +1 -1
- package/lib/Color.js +1 -5
- package/lib/Color.js.map +1 -1
- package/lib/Event.js +2 -6
- package/lib/Event.js.map +1 -1
- package/lib/EventEmitter.js +3 -7
- package/lib/EventEmitter.js.map +1 -1
- package/lib/Game.js +73 -85
- package/lib/Game.js.map +1 -1
- package/lib/Hit.js +21 -28
- package/lib/Hit.js.map +1 -1
- package/lib/Logger.js +2 -7
- package/lib/Logger.js.map +1 -1
- package/lib/Map.d.ts +1 -0
- package/lib/Map.js +29 -53
- package/lib/Map.js.map +1 -1
- package/lib/Module.d.ts +2 -2
- package/lib/Module.js +84 -97
- package/lib/Module.js.map +1 -1
- package/lib/Player.d.ts +1 -0
- package/lib/Player.js +4 -7
- package/lib/Player.js.map +1 -1
- package/lib/Plugin.d.ts +4 -3
- package/lib/Plugin.js +14 -14
- package/lib/Plugin.js.map +1 -1
- package/lib/Scheduler.js +14 -21
- package/lib/Scheduler.js.map +1 -1
- package/lib/Shape.d.ts +1 -1
- package/lib/Shape.js +39 -52
- package/lib/Shape.js.map +1 -1
- package/lib/Utils.d.ts +4 -1
- package/lib/Utils.js +38 -53
- package/lib/Utils.js.map +1 -1
- package/lib/Vector2d.js +3 -8
- package/lib/Vector2d.js.map +1 -1
- package/lib/VirtualGrid.d.ts +1 -1
- package/lib/VirtualGrid.js +5 -12
- package/lib/VirtualGrid.js.map +1 -1
- package/lib/Worker.js +2 -10
- package/lib/Worker.js.map +1 -1
- package/lib/WorldMaps.d.ts +3 -1
- package/lib/WorldMaps.js +29 -15
- package/lib/WorldMaps.js.map +1 -1
- package/lib/gui/PrebuiltGui.js +2 -5
- package/lib/gui/PrebuiltGui.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +24 -69
- package/lib/index.js.map +1 -1
- package/lib/transports/io.js +5 -9
- package/lib/transports/io.js.map +1 -1
- package/lib/workers/move.js +26 -42
- package/lib/workers/move.js.map +1 -1
- package/package.json +9 -11
- package/src/AbstractObject.ts +915 -0
- package/src/Color.ts +29 -0
- package/src/DefaultInput.ts +26 -0
- package/src/Event.ts +3 -0
- package/src/EventEmitter.ts +52 -0
- package/src/Game.ts +150 -0
- package/src/Hit.ts +70 -0
- package/src/Logger.ts +7 -0
- package/src/Map.ts +335 -0
- package/src/Module.ts +108 -0
- package/src/Player.ts +30 -0
- package/src/Plugin.ts +91 -0
- package/src/Scheduler.ts +88 -0
- package/src/Shape.ts +300 -0
- package/src/Utils.ts +168 -0
- package/src/Vector2d.ts +70 -0
- package/src/VirtualGrid.ts +78 -0
- package/src/Worker.ts +17 -0
- package/src/WorldMaps.ts +204 -0
- package/src/gui/PrebuiltGui.ts +27 -0
- package/src/index.ts +24 -0
- package/src/transports/io.ts +91 -0
- package/src/workers/move.ts +61 -0
- package/tsconfig.json +25 -0
package/src/Module.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { RpgPlugin, HookServer, HookClient } from './Plugin'
|
|
2
|
+
import { isArray, isClass, isFunction, isPromise } from './Utils'
|
|
3
|
+
import { warning } from './Logger'
|
|
4
|
+
|
|
5
|
+
enum Side {
|
|
6
|
+
Server = 'server',
|
|
7
|
+
Client = 'client'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type ModuleSide = {
|
|
11
|
+
client?: any,
|
|
12
|
+
server?: any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ModuleType = ModuleSide | [ModuleSide, {
|
|
16
|
+
client?: any,
|
|
17
|
+
server?: any
|
|
18
|
+
}]
|
|
19
|
+
|
|
20
|
+
export function RpgModule<T>(options: T) {
|
|
21
|
+
return (target) => {
|
|
22
|
+
for (let key in options) {
|
|
23
|
+
target.prototype[key] = options[key]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function loadModules(modules, obj, middleware?: Function): Promise<{ playerProps: any }> {
|
|
29
|
+
const { side, relations } = obj
|
|
30
|
+
let playerProps = {}
|
|
31
|
+
for (let module of modules) {
|
|
32
|
+
if (!module) continue
|
|
33
|
+
let plug: any = []
|
|
34
|
+
if (!isArray(module)) {
|
|
35
|
+
plug = [module]
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
plug = module
|
|
39
|
+
}
|
|
40
|
+
const [moduleClassSides, options] = plug
|
|
41
|
+
const moduleClass = moduleClassSides[side]
|
|
42
|
+
if (!moduleClass) continue
|
|
43
|
+
let mod
|
|
44
|
+
if (options && side == Side.Client && options[Side.Server]) {
|
|
45
|
+
warning(`Data that may be sensitive (normally visible only on the server side) are made optional and visible on the client side.\nInstead, import the configuration with the server! flag into an import. Example: \n\nimport config from 'server!./config\n\n'`, options[Side.Server])
|
|
46
|
+
}
|
|
47
|
+
if (options && !isClass(moduleClass) && isFunction(moduleClass)) {
|
|
48
|
+
mod = new (moduleClass(options[side]))()
|
|
49
|
+
}
|
|
50
|
+
else if (isClass(moduleClass)) {
|
|
51
|
+
mod = new moduleClass()
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
mod = moduleClass
|
|
55
|
+
}
|
|
56
|
+
if (middleware) {
|
|
57
|
+
mod = middleware(mod)
|
|
58
|
+
if (isPromise(mod)) {
|
|
59
|
+
mod = await mod
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const { imports, maps, spritesheets, sounds, gui, scenes, engine, database, worldMaps, scalability, events } = mod
|
|
63
|
+
if (imports) {
|
|
64
|
+
await loadModules(imports, obj)
|
|
65
|
+
}
|
|
66
|
+
if (maps) {
|
|
67
|
+
RpgPlugin.on(HookServer.AddMap, () => maps)
|
|
68
|
+
}
|
|
69
|
+
if (events) {
|
|
70
|
+
RpgPlugin.on(HookServer.AddEvent, () => events)
|
|
71
|
+
}
|
|
72
|
+
if (worldMaps) {
|
|
73
|
+
RpgPlugin.on(HookServer.AddWorldMaps, () => worldMaps)
|
|
74
|
+
}
|
|
75
|
+
if (database) {
|
|
76
|
+
RpgPlugin.on(HookServer.AddDatabase, () => database)
|
|
77
|
+
}
|
|
78
|
+
if (spritesheets) {
|
|
79
|
+
RpgPlugin.on(HookClient.AddSpriteSheet, () => spritesheets)
|
|
80
|
+
}
|
|
81
|
+
if (sounds) {
|
|
82
|
+
RpgPlugin.on(HookClient.AddSound, () => sounds)
|
|
83
|
+
}
|
|
84
|
+
if (gui) {
|
|
85
|
+
RpgPlugin.on(HookClient.AddGui, () => gui)
|
|
86
|
+
}
|
|
87
|
+
const player = side == Side.Server ? mod.player : mod.sprite
|
|
88
|
+
const loadRelations = (hook, relatioName) => {
|
|
89
|
+
if (hook) {
|
|
90
|
+
for (let method in relations[relatioName]) {
|
|
91
|
+
const hookName = relations[relatioName][method]
|
|
92
|
+
if (hook[method]) RpgPlugin.on(hookName, hook[method])
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
loadRelations(player, 'player')
|
|
97
|
+
if (player && player.props) {
|
|
98
|
+
playerProps = Object.assign(playerProps, player.props)
|
|
99
|
+
}
|
|
100
|
+
loadRelations(engine, 'engine')
|
|
101
|
+
if (scalability) loadRelations(scalability._hooks, 'scalability')
|
|
102
|
+
if (scenes) loadRelations(scenes.map, 'sceneMap')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
playerProps
|
|
107
|
+
}
|
|
108
|
+
}
|
package/src/Player.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PendingMove } from '@rpgjs/types'
|
|
2
|
+
import { AbstractObject } from './AbstractObject'
|
|
3
|
+
|
|
4
|
+
export const LiteralDirection = {
|
|
5
|
+
1: 'up',
|
|
6
|
+
2: 'right',
|
|
7
|
+
3: 'down',
|
|
8
|
+
4: 'left'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class RpgCommonPlayer extends AbstractObject {
|
|
12
|
+
events: any[] = []
|
|
13
|
+
layerName: string = ''
|
|
14
|
+
data: any = {}
|
|
15
|
+
pendingMove: PendingMove = []
|
|
16
|
+
inputsTimestamp: {
|
|
17
|
+
[inputName: string]: number
|
|
18
|
+
} = {}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Display/Hide the GUI attached to this sprite
|
|
22
|
+
*
|
|
23
|
+
* @prop {boolean} guiDisplay
|
|
24
|
+
* @since 3.0.0-beta.5
|
|
25
|
+
* @memberof RpgSprite
|
|
26
|
+
* */
|
|
27
|
+
guiDisplay: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default AbstractObject
|
package/src/Plugin.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { EventEmitter } from './EventEmitter'
|
|
2
|
+
import { isArray } from './Utils'
|
|
3
|
+
|
|
4
|
+
type PluginFunction = (obj: any, options?: any) => void
|
|
5
|
+
type PluginSides = { client: null | PluginFunction, server: null | PluginFunction }
|
|
6
|
+
export type Plugin = PluginSides | [PluginSides, any]
|
|
7
|
+
|
|
8
|
+
export enum HookServer {
|
|
9
|
+
Start = 'Server.Start',
|
|
10
|
+
Step = "Server.Step",
|
|
11
|
+
PlayerConnected = 'Server.onConnected',
|
|
12
|
+
PlayerDisconnected = 'Server.onDisconnected',
|
|
13
|
+
AddMap = 'Server.AddMap',
|
|
14
|
+
AddEvent = 'Server.AddEvent',
|
|
15
|
+
AddWorldMaps = 'Server.AddWorldMaps',
|
|
16
|
+
AddDatabase = 'Server.AddDatabase',
|
|
17
|
+
PlayerInput = 'Server.onInput',
|
|
18
|
+
PlayerJoinMap = 'Server.onJoinMap',
|
|
19
|
+
PlayerLeaveMap = 'Server.onLeaveMap',
|
|
20
|
+
PlayerLevelUp = 'Server.onLevelUp',
|
|
21
|
+
PlayerDead = 'Server.onDead',
|
|
22
|
+
PlayerInShape = 'Server.onInShape',
|
|
23
|
+
PlayerOutShape = 'Server.onOutShape',
|
|
24
|
+
PlayerMove = 'Server.PlayerMove',
|
|
25
|
+
PlayerCanChangeMap = 'Server.PlayerCanChangeMap',
|
|
26
|
+
ScalabilityPlayerConnected = 'Server.ScalabilityPlayerConnected',
|
|
27
|
+
ScalabilityChangeServer = 'Server.ScalabilityChangeServer'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export enum HookClient {
|
|
31
|
+
Start = 'Client.Start',
|
|
32
|
+
Step = 'Client.Step',
|
|
33
|
+
Connected = 'Client.Connected',
|
|
34
|
+
Disconnect = 'Client.Disconnect',
|
|
35
|
+
ConnectedError = 'Client.ConnectedError',
|
|
36
|
+
|
|
37
|
+
AddSpriteSheet = 'Client.AddSpriteSheet',
|
|
38
|
+
AddGui = 'Client.AddGui',
|
|
39
|
+
AddSound = 'Client.AddSound',
|
|
40
|
+
SendInput = 'Client.SendInput',
|
|
41
|
+
|
|
42
|
+
BeforeSceneLoading = 'Client.BeforeSceneLoading',
|
|
43
|
+
AfterSceneLoading = 'Client.AfterSceneLoading',
|
|
44
|
+
SceneMapLoading = 'Client.SceneMapLoading',
|
|
45
|
+
SceneAddSprite = 'Client.SceneAddSprite',
|
|
46
|
+
SceneOnChanges = 'Client.SceneOnChanges',
|
|
47
|
+
SceneDraw = 'Client.SceneDraw',
|
|
48
|
+
SceneRemoveSprite = 'Client.SceneRemoveSprite',
|
|
49
|
+
|
|
50
|
+
AddSprite = 'Client.AddSprite',
|
|
51
|
+
RemoveSprite = 'Client.RemoveSprite',
|
|
52
|
+
UpdateSprite = 'Client.UpdateSprite',
|
|
53
|
+
ChangesSprite = 'Client.ChangesSprite',
|
|
54
|
+
WindowResize = 'Client.WindowResize',
|
|
55
|
+
SpriteMove = 'Client.SpriteMove'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @deprecated
|
|
60
|
+
*/
|
|
61
|
+
export class PluginSystem extends EventEmitter {
|
|
62
|
+
private loadPlugins(plugins: Plugin[], shared: any, type: string) {
|
|
63
|
+
if (!plugins) return
|
|
64
|
+
for (let plugin of plugins) {
|
|
65
|
+
if (!plugin) continue
|
|
66
|
+
let plug: any = []
|
|
67
|
+
if (!isArray(plugin)) {
|
|
68
|
+
plug = [plugin]
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
plug = plugin
|
|
72
|
+
}
|
|
73
|
+
const [side, options] = plug
|
|
74
|
+
if (!side[type]) continue
|
|
75
|
+
side[type]({
|
|
76
|
+
RpgPlugin,
|
|
77
|
+
...shared
|
|
78
|
+
}, options)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
loadServerPlugins(plugins, shared) {
|
|
83
|
+
this.loadPlugins(plugins, shared, 'server')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
loadClientPlugins(plugins, shared) {
|
|
87
|
+
this.loadPlugins(plugins, shared, 'client')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const RpgPlugin = new PluginSystem()
|
package/src/Scheduler.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Tick } from '@rpgjs/types';
|
|
2
|
+
import { BehaviorSubject, Observable } from 'rxjs';
|
|
3
|
+
import { EventEmitter } from './EventEmitter';
|
|
4
|
+
import Utils from './Utils';
|
|
5
|
+
|
|
6
|
+
export class Scheduler extends EventEmitter {
|
|
7
|
+
private maxFps?: number
|
|
8
|
+
private fps: number = 60
|
|
9
|
+
private deltaTime: number = 0
|
|
10
|
+
public frame: number = 0
|
|
11
|
+
private timestamp: number = 0
|
|
12
|
+
private requestedDelay: number = 0
|
|
13
|
+
private lastTimestamp: number = 0
|
|
14
|
+
private _tick: BehaviorSubject<Tick> = new BehaviorSubject({
|
|
15
|
+
timestamp: 0,
|
|
16
|
+
deltaTime: 0,
|
|
17
|
+
frame: 0,
|
|
18
|
+
deltaRatio: 0
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
get tick(): Observable<Tick> {
|
|
22
|
+
return this._tick.asObservable()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
nextTick(timestamp: number) {
|
|
26
|
+
this.lastTimestamp = this.lastTimestamp || this.timestamp // first
|
|
27
|
+
this.deltaTime = Utils.preciseNow() - this.timestamp
|
|
28
|
+
this.timestamp = timestamp
|
|
29
|
+
this._tick.next({
|
|
30
|
+
timestamp: this.timestamp,
|
|
31
|
+
deltaTime: this.deltaTime,
|
|
32
|
+
frame: this.frame,
|
|
33
|
+
deltaRatio: ~~this.deltaTime / ~~Utils.fps2ms(this.fps)
|
|
34
|
+
})
|
|
35
|
+
this.lastTimestamp = this.timestamp
|
|
36
|
+
this.frame++
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* start the schedule
|
|
41
|
+
* @return {Scheduler} returns this scheduler instance
|
|
42
|
+
*/
|
|
43
|
+
start(options: {
|
|
44
|
+
maxFps?: number
|
|
45
|
+
fps?: number,
|
|
46
|
+
delay?: number
|
|
47
|
+
}) {
|
|
48
|
+
if (options.maxFps) this.maxFps = options.maxFps
|
|
49
|
+
if (options.fps) this.fps = options.fps
|
|
50
|
+
if (options.delay) this.requestedDelay = options.delay
|
|
51
|
+
const requestAnimationFrame = (fn: (timestamp: number) => void) => {
|
|
52
|
+
if (Utils.isBrowser()) {
|
|
53
|
+
window.requestAnimationFrame(fn.bind(this))
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
this.requestedDelay = 0
|
|
58
|
+
fn(Utils.preciseNow())
|
|
59
|
+
}, Utils.fps2ms(this.fps) + this.requestedDelay)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!this.maxFps) {
|
|
64
|
+
const loop = (timestamp: number) => {
|
|
65
|
+
requestAnimationFrame(loop)
|
|
66
|
+
this.nextTick(timestamp)
|
|
67
|
+
}
|
|
68
|
+
requestAnimationFrame(loop)
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const msInterval = Utils.fps2ms(this.maxFps)
|
|
72
|
+
let now = Utils.preciseNow()
|
|
73
|
+
let then = Utils.preciseNow()
|
|
74
|
+
const loop = (timestamp: number) => {
|
|
75
|
+
requestAnimationFrame(loop)
|
|
76
|
+
now = Utils.preciseNow()
|
|
77
|
+
const elapsed = now - then
|
|
78
|
+
if (elapsed > msInterval) {
|
|
79
|
+
then = now - (elapsed % msInterval)
|
|
80
|
+
this.nextTick(timestamp)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
requestAnimationFrame(loop)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/Shape.ts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { RpgCommonPlayer } from './Player'
|
|
2
|
+
import { Hit, HitType } from './Hit'
|
|
3
|
+
import { TiledObjectClass } from '@rpgjs/tiled'
|
|
4
|
+
import { PlayerType } from '@rpgjs/types'
|
|
5
|
+
import { Vector2d } from './Vector2d'
|
|
6
|
+
import { AbstractObject } from './AbstractObject'
|
|
7
|
+
|
|
8
|
+
export enum ShapePositioning {
|
|
9
|
+
Default = 'default',
|
|
10
|
+
Center = 'center'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type ShapeObject = TiledObjectClass & {
|
|
14
|
+
onIn?(player: RpgCommonPlayer)
|
|
15
|
+
onOut?(player: RpgCommonPlayer)
|
|
16
|
+
fixEvent?: RpgCommonPlayer,
|
|
17
|
+
positioning?: ShapePositioning
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class RpgShape extends TiledObjectClass {
|
|
21
|
+
_hitbox: any
|
|
22
|
+
type: string = HitType.Box
|
|
23
|
+
class: string = ''
|
|
24
|
+
/**
|
|
25
|
+
* Get/Set name
|
|
26
|
+
* @title name
|
|
27
|
+
* @prop { string } name
|
|
28
|
+
* @memberof Shape
|
|
29
|
+
*/
|
|
30
|
+
name: string = ''
|
|
31
|
+
fixEvent?: RpgCommonPlayer
|
|
32
|
+
private playersIn: {
|
|
33
|
+
[playerid: string]: boolean
|
|
34
|
+
} = {}
|
|
35
|
+
private onIn: (player: RpgCommonPlayer) => void
|
|
36
|
+
private onOut: (player: RpgCommonPlayer) => void
|
|
37
|
+
clientContainer: any = null
|
|
38
|
+
/**
|
|
39
|
+
* Get/Set positioning
|
|
40
|
+
* @title positioning
|
|
41
|
+
* @prop { ShapePositioning } positioning
|
|
42
|
+
* @default default
|
|
43
|
+
* @memberof Shape
|
|
44
|
+
*/
|
|
45
|
+
positioning?: ShapePositioning = ShapePositioning.Default
|
|
46
|
+
components: any[] = []
|
|
47
|
+
|
|
48
|
+
constructor(obj: ShapeObject) {
|
|
49
|
+
super()
|
|
50
|
+
Reflect.deleteProperty(obj, 'id')
|
|
51
|
+
this.set(obj)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private setPos(type: string, val: number) {
|
|
55
|
+
if (!this.hitbox.pos) return
|
|
56
|
+
if (this.isShapePosition()) {
|
|
57
|
+
this.hitbox[type] = val
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.hitbox.pos[type] = val
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get hasCollision(): boolean {
|
|
65
|
+
return this.getProperty<boolean, boolean>('collision', false)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// alias
|
|
69
|
+
get id(): any {
|
|
70
|
+
return this.name
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get hitbox() {
|
|
74
|
+
if (this.fixEvent) {
|
|
75
|
+
this._hitbox.pos.x = this.fixEvent.position.x
|
|
76
|
+
this._hitbox.pos.y = this.fixEvent.position.y
|
|
77
|
+
switch (this.positioning) {
|
|
78
|
+
case ShapePositioning.Center:
|
|
79
|
+
this._hitbox.pos.x -= this._hitbox.w / 2 - this.fixEvent.hitbox.w / 2
|
|
80
|
+
this._hitbox.pos.y -= this._hitbox.h / 2 - this.fixEvent.hitbox.h / 2
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return this._hitbox
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
set hitbox(val) {
|
|
88
|
+
this._hitbox = val
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get/Set width
|
|
93
|
+
* @title width
|
|
94
|
+
* @prop { number } width
|
|
95
|
+
* @since 3.0.0-beta.5
|
|
96
|
+
* @memberof Shape
|
|
97
|
+
*/
|
|
98
|
+
get width(): number {
|
|
99
|
+
return this.hitbox.w || 0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
set width(val: number) {
|
|
103
|
+
this.setPos('w', val)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get/Set height
|
|
108
|
+
* @title height
|
|
109
|
+
* @prop { number } height
|
|
110
|
+
* @since 3.0.0-beta.5
|
|
111
|
+
* @memberof Shape
|
|
112
|
+
*/
|
|
113
|
+
get height(): number {
|
|
114
|
+
return this.hitbox.h || 0
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
set height(val: number) {
|
|
118
|
+
this.setPos('h', val)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get/Set x
|
|
123
|
+
* @title x
|
|
124
|
+
* @prop { number } x
|
|
125
|
+
* @memberof Shape
|
|
126
|
+
*/
|
|
127
|
+
get x(): number {
|
|
128
|
+
return this.hitbox.x || this.hitbox.pos.x
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
set x(val: number) {
|
|
132
|
+
this.setPos('x', val)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
get z(): number | undefined {
|
|
136
|
+
return this.getProperty<number>('z')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get/Set y
|
|
141
|
+
* @title y
|
|
142
|
+
* @prop { number } y
|
|
143
|
+
* @memberof Shape
|
|
144
|
+
*/
|
|
145
|
+
get y(): number {
|
|
146
|
+
return this.hitbox.y || this.hitbox.pos.y
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
set y(val: number) {
|
|
150
|
+
this.setPos('y', val)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
get position(): Vector2d {
|
|
154
|
+
return new Vector2d(this.x, this.y, this.z)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get/Set properties
|
|
159
|
+
|
|
160
|
+
* @title Properties
|
|
161
|
+
* @prop { object } Properties
|
|
162
|
+
* @memberof Shape
|
|
163
|
+
*/
|
|
164
|
+
|
|
165
|
+
isEvent(): boolean {
|
|
166
|
+
return this.type == PlayerType.Event
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
set(obj: ShapeObject) {
|
|
170
|
+
const hit = Hit.getHitbox(obj)
|
|
171
|
+
Object.assign(this, hit)
|
|
172
|
+
const objClone = { ...obj };
|
|
173
|
+
// Delete dimension and position because already managed and given by the hitbox above
|
|
174
|
+
['width', 'height', 'x', 'y'].forEach((prop) => Reflect.deleteProperty(objClone, prop))
|
|
175
|
+
Object.assign(this, objClone)
|
|
176
|
+
const findPoint = (prop: string, isMin: boolean) => {
|
|
177
|
+
return this.hitbox.points.sort((a, b) => isMin ? a[prop] - b[prop] : b[prop] - a[prop])[0][prop]
|
|
178
|
+
}
|
|
179
|
+
if (this.type == HitType.Polygon) {
|
|
180
|
+
this.hitbox.minX = findPoint('x', true)
|
|
181
|
+
this.hitbox.maxX = findPoint('x', false)
|
|
182
|
+
this.hitbox.minY = findPoint('y', true)
|
|
183
|
+
this.hitbox.maxY = findPoint('y', false)
|
|
184
|
+
}
|
|
185
|
+
this.positioning = obj.positioning
|
|
186
|
+
this.fixEvent = obj.fixEvent
|
|
187
|
+
this.setComponent()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
setComponent() {
|
|
191
|
+
const color = this.getProperty<string>('color')
|
|
192
|
+
const image = this.getProperty<string>('image')
|
|
193
|
+
if (color) {
|
|
194
|
+
this.components = [{
|
|
195
|
+
id: 'shape', value: {
|
|
196
|
+
fill: color
|
|
197
|
+
}
|
|
198
|
+
}]
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
if (image) {
|
|
202
|
+
this.components = [{ id: 'image', value: image }]
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
if (this.text) {
|
|
206
|
+
this.components = [{ id: 'text', value: this.text.text }]
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
if (this.gid) {
|
|
210
|
+
this.components = [{ id: 'tile', value: this.gid }]
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
getType() {
|
|
216
|
+
return this.class || this.type
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async in(player: AbstractObject): Promise<boolean> {
|
|
220
|
+
if (!this.playerIsIn(player)) {
|
|
221
|
+
this.playersIn[player.id] = true
|
|
222
|
+
player.inShapes[this.name] = this
|
|
223
|
+
await player.execMethod('onInShape', [this])
|
|
224
|
+
await player.execMethod('onIn', [player], this)
|
|
225
|
+
return true
|
|
226
|
+
}
|
|
227
|
+
return false
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async out(player: AbstractObject): Promise<boolean> {
|
|
231
|
+
if (this.playerIsIn(player)) {
|
|
232
|
+
delete this.playersIn[player.id]
|
|
233
|
+
delete player.inShapes[this.name]
|
|
234
|
+
await player.execMethod('onOutShape', [this])
|
|
235
|
+
await player.execMethod('onOut', [player], this)
|
|
236
|
+
return true
|
|
237
|
+
}
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Whether the player is in this shape
|
|
243
|
+
*
|
|
244
|
+
* @title Player is in this shape ?
|
|
245
|
+
* @method shape.playerIsIn(player)
|
|
246
|
+
* @returns {boolean}
|
|
247
|
+
* @memberof Shape
|
|
248
|
+
*/
|
|
249
|
+
playerIsIn(player: AbstractObject): boolean {
|
|
250
|
+
return !!this.playersIn[player.id]
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
isShapePosition(): boolean {
|
|
254
|
+
return this.type !== HitType.Box && this.type !== HitType.Circle && this.type !== HitType.Polygon
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Recover the player with the shape. You must have used the `attachShape()` method on the player
|
|
259
|
+
*
|
|
260
|
+
* @title Get Player Owner
|
|
261
|
+
* @method shape.getPlayerOwner()
|
|
262
|
+
* @returns {RpgPlayer | undefined}
|
|
263
|
+
* @memberof Shape
|
|
264
|
+
*/
|
|
265
|
+
getPlayerOwner(): RpgCommonPlayer | undefined {
|
|
266
|
+
return this.fixEvent
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* We get the rectangle of a shape (box, circle and polygon). We use in the grid system to recover a shape.
|
|
271
|
+
* Generally we add a margin (size of a tile) to detect if the player enters or leaves a shape
|
|
272
|
+
* @param margin
|
|
273
|
+
* @returns { minX: number, minY: number, maxX: number, maxY: number }
|
|
274
|
+
*/
|
|
275
|
+
getSizeBox(margin: number = 0): { minX: number, minY: number, maxX: number, maxY: number } {
|
|
276
|
+
if (this.type == HitType.Circle) {
|
|
277
|
+
const radius = this.hitbox.r
|
|
278
|
+
return {
|
|
279
|
+
minX: this.x - radius - margin,
|
|
280
|
+
maxX: this.x + radius + margin,
|
|
281
|
+
minY: this.y - radius - margin,
|
|
282
|
+
maxY: this.y + radius + margin
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (this.type == HitType.Polygon) {
|
|
286
|
+
return {
|
|
287
|
+
minX: this.x + this.hitbox.minX - margin,
|
|
288
|
+
maxX: this.x + this.hitbox.maxX + margin,
|
|
289
|
+
minY: this.y + this.hitbox.minY - margin,
|
|
290
|
+
maxY: this.y + this.hitbox.maxY + margin
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
minX: this.x - margin,
|
|
295
|
+
maxX: this.x + this.width + margin,
|
|
296
|
+
minY: this.y - margin,
|
|
297
|
+
maxY: this.y + this.height + margin
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|