diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index d9a89bd1..442661bf 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -173,6 +173,37 @@ Example: WA.player.state.toto //will retrieve the variable ``` +### Move player to position +```typescript +WA.player.moveTo(x: number, y: number, speed?: number): Promise<{ x: number, y: number, cancelled: boolean }>; +``` +Player will try to find shortest path to the destination point and proceed to move there. +```typescript +// Let's move player to x: 250 y: 250 with speed of 10 +WA.player.moveTo(250, 250, 10); +``` +You can also chain movement like this: +```typescript +// Player will move to the next point after reaching first one +await WA.player.moveTo(250, 250, 10); +await WA.player.moveTo(500, 0, 10); +``` +Or like this: +```typescript +// Player will move to the next point after reaching first one or stop if the movement was cancelled +WA.player.moveTo(250, 250, 10).then((result) => { + if (!result.cancelled) { + WA.player.moveTo(500, 0, 10); + } +}); +``` +It is possible to get the information about current player's position on stop and if the movement was interrupted +```typescript +// Result will store x and y of Player at the moment of movement's end and information if the movement was interrupted +const result = await WA.player.moveTo(250, 250, 10); +// result: { x: number, y: number, cancelled: boolean } +``` + ### Set the outline color of the player ``` WA.player.setOutlineColor(red: number, green: number, blue: number): Promise; diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 93d0735c..e56699a7 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -34,6 +34,8 @@ import type { ChangeZoneEvent } from "./ChangeZoneEvent"; import type { CameraSetEvent } from "./CameraSetEvent"; import type { CameraFollowPlayerEvent } from "./CameraFollowPlayerEvent"; import { isColorEvent } from "./ColorEvent"; +import { isMovePlayerToEventConfig } from "./MovePlayerToEvent"; +import { isMovePlayerToEventAnswer } from "./MovePlayerToEventAnswer"; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -173,6 +175,10 @@ export const iframeQueryMapTypeGuards = { query: tg.isUndefined, answer: isPlayerPosition, }, + movePlayerTo: { + query: isMovePlayerToEventConfig, + answer: isMovePlayerToEventAnswer, + }, }; type GuardedType = T extends (x: unknown) => x is infer T ? T : never; diff --git a/front/src/Api/Events/MovePlayerToEvent.ts b/front/src/Api/Events/MovePlayerToEvent.ts new file mode 100644 index 00000000..462e2f43 --- /dev/null +++ b/front/src/Api/Events/MovePlayerToEvent.ts @@ -0,0 +1,11 @@ +import * as tg from "generic-type-guard"; + +export const isMovePlayerToEventConfig = new tg.IsInterface() + .withProperties({ + x: tg.isNumber, + y: tg.isNumber, + speed: tg.isOptional(tg.isNumber), + }) + .get(); + +export type MovePlayerToEvent = tg.GuardedType; diff --git a/front/src/Api/Events/MovePlayerToEventAnswer.ts b/front/src/Api/Events/MovePlayerToEventAnswer.ts new file mode 100644 index 00000000..67d2f9ae --- /dev/null +++ b/front/src/Api/Events/MovePlayerToEventAnswer.ts @@ -0,0 +1,11 @@ +import * as tg from "generic-type-guard"; + +export const isMovePlayerToEventAnswer = new tg.IsInterface() + .withProperties({ + x: tg.isNumber, + y: tg.isNumber, + cancelled: tg.isBoolean, + }) + .get(); + +export type MovePlayerToEventAnswer = tg.GuardedType; diff --git a/front/src/Api/iframe/player.ts b/front/src/Api/iframe/player.ts index 0c71ae33..cf10dfa5 100644 --- a/front/src/Api/iframe/player.ts +++ b/front/src/Api/iframe/player.ts @@ -84,6 +84,13 @@ export class WorkadventurePlayerCommands extends IframeApiContribution { + return await queryWorkadventure({ + type: "movePlayerTo", + data: { x, y, speed }, + }); + } + get userRoomToken(): string | undefined { if (userRoomToken === undefined) { throw new Error( diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 26e4004a..05c0483f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -569,7 +569,11 @@ export class GameScene extends DirtyScene { waScaleManager ); - this.pathfindingManager = new PathfindingManager(this, this.gameMap.getCollisionsGrid()); + this.pathfindingManager = new PathfindingManager( + this, + this.gameMap.getCollisionsGrid(), + this.gameMap.getTileDimensions() + ); biggestAvailableAreaStore.recompute(); this.cameraManager.startFollowPlayer(this.CurrentPlayer); @@ -1456,6 +1460,17 @@ ${escapedMessage} y: this.CurrentPlayer.y, }; }); + + iframeListener.registerAnswerer("movePlayerTo", async (message) => { + const index = this.getGameMap().getTileIndexAt(message.x, message.y); + const startTile = this.getGameMap().getTileIndexAt(this.CurrentPlayer.x, this.CurrentPlayer.y); + const path = await this.getPathfindingManager().findPath(startTile, index, true, true); + path.shift(); + if (path.length === 0) { + throw new Error("no path available"); + } + return this.CurrentPlayer.setPathToFollow(path, message.speed); + }); } private setPropertyLayer( diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 451a057b..c5134f1c 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -12,6 +12,8 @@ export const requestEmoteEventName = "requestEmote"; export class Player extends Character { private pathToFollow?: { x: number; y: number }[]; + private followingPathPromiseResolve?: (result: { x: number; y: number; cancelled: boolean }) => void; + private pathWalkingSpeed?: number; constructor( Scene: GameScene, @@ -43,7 +45,7 @@ export class Player extends Character { } if (this.pathToFollow && activeUserInputEvents.anyExcept(UserInputEvent.SpeedUp)) { - this.pathToFollow = undefined; + this.finishFollowingPath(true); } let x = 0; @@ -68,9 +70,22 @@ export class Player extends Character { this.scene.connection?.emitFollowConfirmation(); } - public setPathToFollow(path: { x: number; y: number }[]): void { + public async setPathToFollow( + path: { x: number; y: number }[], + speed?: number + ): Promise<{ x: number; y: number; cancelled: boolean }> { + const isPreviousPathInProgress = this.pathToFollow !== undefined && this.pathToFollow.length > 0; // take collider offset into consideraton this.pathToFollow = this.adjustPathToFollowToColliderBounds(path); + this.pathWalkingSpeed = speed; + return new Promise((resolve) => { + this.followingPathPromiseResolve?.call(this, { x: this.x, y: this.y, cancelled: isPreviousPathInProgress }); + this.followingPathPromiseResolve = resolve; + }); + } + + private deduceSpeed(speedUp: boolean, followMode: boolean): number { + return this.pathWalkingSpeed ? this.pathWalkingSpeed : speedUp && !followMode ? 25 : 9; } private adjustPathToFollowToColliderBounds(path: { x: number; y: number }[]): { x: number; y: number }[] { @@ -95,8 +110,8 @@ export class Player extends Character { // Compute movement deltas const followMode = get(followStateStore) !== "off"; - const speedup = activeEvents.get(UserInputEvent.SpeedUp) && !followMode ? 25 : 9; - const moveAmount = speedup * 20; + const speed = this.deduceSpeed(activeEvents.get(UserInputEvent.SpeedUp), followMode); + const moveAmount = speed * 20; x = x * moveAmount; y = y * moveAmount; @@ -148,8 +163,8 @@ export class Player extends Character { } private computeFollowPathMovement(): number[] { - if (this.pathToFollow?.length === 0) { - this.pathToFollow = undefined; + if (this.pathToFollow !== undefined && this.pathToFollow.length === 0) { + this.finishFollowingPath(); } if (!this.pathToFollow) { return [0, 0]; @@ -166,6 +181,12 @@ export class Player extends Character { return this.getMovementDirection(xDistance, yDistance, distance); } + private finishFollowingPath(cancelled: boolean = false): void { + this.pathToFollow = undefined; + this.pathWalkingSpeed = undefined; + this.followingPathPromiseResolve?.call(this, { x: this.x, y: this.y, cancelled }); + } + private getMovementDirection(xDistance: number, yDistance: number, distance: number): [number, number] { return [xDistance / Math.sqrt(distance), yDistance / Math.sqrt(distance)]; } diff --git a/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts b/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts index 2915901d..19bb02bb 100644 --- a/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts +++ b/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts @@ -31,18 +31,11 @@ export class GameSceneUserInputHandler implements UserInputHandlerInterface { .getTileIndexAt(this.gameScene.CurrentPlayer.x, this.gameScene.CurrentPlayer.y); this.gameScene .getPathfindingManager() - .findPath(startTile, index, true) + .findPath(startTile, index, true, true) .then((path) => { - const tileDimensions = this.gameScene.getGameMap().getTileDimensions(); - const pixelPath = path.map((step) => { - return { - x: step.x * tileDimensions.width + tileDimensions.width * 0.5, - y: step.y * tileDimensions.height + tileDimensions.height * 0.5, - }; - }); // Remove first step as it is for the tile we are currently standing on - pixelPath.shift(); - this.gameScene.CurrentPlayer.setPathToFollow(pixelPath); + path.shift(); + this.gameScene.CurrentPlayer.setPathToFollow(path).catch((reason) => {}); }) .catch((reason) => { console.warn(reason); diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index ffaf7d1f..ffa67c3a 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -54,7 +54,7 @@ export class UserInputManager { private scene: Phaser.Scene; private isInputDisabled: boolean; - private joystick!: MobileJoystick; + private joystick?: MobileJoystick; private joystickEvents = new ActiveEventList(); private joystickForceThreshold = 60; private joystickForceAccuX = 0; @@ -81,9 +81,9 @@ export class UserInputManager { initVirtualJoystick() { this.joystick = new MobileJoystick(this.scene); this.joystick.on("update", () => { - this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0; - this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0; - const cursorKeys = this.joystick.createCursorKeys(); + this.joystickForceAccuX = this.joystick?.forceX ? this.joystickForceAccuX : 0; + this.joystickForceAccuY = this.joystick?.forceY ? this.joystickForceAccuY : 0; + const cursorKeys = this.joystick?.createCursorKeys(); for (const name in cursorKeys) { const key = cursorKeys[name as Direction]; switch (name) { @@ -192,7 +192,7 @@ export class UserInputManager { return eventsMap; } this.joystickEvents.forEach((value, key) => { - if (value) { + if (value && this.joystick) { switch (key) { case UserInputEvent.MoveUp: case UserInputEvent.MoveDown: @@ -253,7 +253,7 @@ export class UserInputManager { this.scene.input.on( Phaser.Input.Events.POINTER_UP, (pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => { - this.joystick.hide(); + this.joystick?.hide(); this.userInputHandler.handlePointerUpEvent(pointer, gameObjects); } ); @@ -267,9 +267,9 @@ export class UserInputManager { this.userInputHandler.handlePointerDownEvent(pointer, gameObjects); // Let's only display the joystick if there is one finger on the screen if ((pointer.event as TouchEvent).touches.length === 1) { - this.joystick.showAt(pointer.x, pointer.y); + this.joystick?.showAt(pointer.x, pointer.y); } else { - this.joystick.hide(); + this.joystick?.hide(); } } ); diff --git a/front/src/Utils/PathfindingManager.ts b/front/src/Utils/PathfindingManager.ts index 2d0cae27..71205070 100644 --- a/front/src/Utils/PathfindingManager.ts +++ b/front/src/Utils/PathfindingManager.ts @@ -6,20 +6,23 @@ export class PathfindingManager { private easyStar; private grid: number[][]; + private tileDimensions: { width: number; height: number }; - constructor(scene: Phaser.Scene, collisionsGrid: number[][]) { + constructor(scene: Phaser.Scene, collisionsGrid: number[][], tileDimensions: { width: number; height: number }) { this.scene = scene; this.easyStar = new EasyStar.js(); this.easyStar.enableDiagonals(); this.grid = collisionsGrid; + this.tileDimensions = tileDimensions; this.setEasyStarGrid(collisionsGrid); } public async findPath( start: { x: number; y: number }, end: { x: number; y: number }, + measuredInPixels: boolean = true, tryFindingNearestAvailable: boolean = false ): Promise<{ x: number; y: number }[]> { let endPoints: { x: number; y: number }[] = [end]; @@ -48,12 +51,21 @@ export class PathfindingManager { // rejected Promise will return undefined for path path = await this.getPath(start, endPoint).catch(); if (path && path.length > 0) { - return path; + return measuredInPixels ? this.mapTileUnitsToPixels(path) : path; } } return []; } + private mapTileUnitsToPixels(path: { x: number; y: number }[]): { x: number; y: number }[] { + return path.map((step) => { + return { + x: step.x * this.tileDimensions.width + this.tileDimensions.width * 0.5, + y: step.y * this.tileDimensions.height + this.tileDimensions.height * 0.5, + }; + }); + } + private getNeighbouringTiles(tile: { x: number; y: number }): { x: number; y: number }[] { const xOffsets = [-1, 0, 1, 1, 1, 0, -1, -1]; const yOffsets = [-1, -1, -1, 0, 1, 1, 1, 0]; diff --git a/maps/tests/MovePlayer/move_player_api_test.json b/maps/tests/MovePlayer/move_player_api_test.json new file mode 100644 index 00000000..d9dc63c6 --- /dev/null +++ b/maps/tests/MovePlayer/move_player_api_test.json @@ -0,0 +1,207 @@ +{ "compressionlevel":-1, + "height":30, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":30, + "id":1, + "name":"floor", + "opacity":1, + "properties":[ + { + "name":"openWebsite", + "type":"string", + "value":"script.php" + }, + { + "name":"openWebsiteAllowApi", + "type":"bool", + "value":true + }], + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 29, 28, 29, 28, 29, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 0, 0, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 0, 0, 0, 0, 0, 0, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 28, 29, 28, 29, 28, 29, 28, 29, 28, 29, 0, 0, 28, 29, 28, 29, 28, 29, 28, 29, 28, 29, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 29, 28, 29, 28, 29, 28, 29, 28, 29, 28, 29, 28, 29, 28, 29, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 6, 7, 6, 7, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 6, 7, 6, 7, 0, 17, 18, 17, 18, 17, 18, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 17, 18, 17, 18, 0, 28, 29, 28, 29, 28, 29, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 29, 28, 29, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":30, + "id":6, + "name":"furnitures", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":30, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":159.371714951095, + "id":4, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"MS Shell Dlg 2", + "pixelsize":21, + "text":"Set position of where you want the player character to move to. On movement's end or interruption, player's current position will be logged into the console", + "wrap":true + }, + "type":"", + "visible":true, + "width":315.438, + "x":64.7309301350722, + "y":95.2559217512811 + }, + { + "height":30.8202477876106, + "id":8, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"MS Shell Dlg 2", + "pixelsize":21, + "text":"x: 16 y: 16", + "wrap":true + }, + "type":"", + "visible":true, + "width":111.991330228225, + "x":39.0206367023754, + "y":2.47529762459247 + }, + { + "height":30.8202, + "id":9, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"MS Shell Dlg 2", + "pixelsize":21, + "text":"x: 464 y: 432", + "wrap":true + }, + "type":"", + "visible":true, + "width":149.997520726595, + "x":483.920662086633, + "y":417.193532976246 + }, + { + "height":113.333333333333, + "id":10, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"MS Shell Dlg 2", + "pixelsize":21, + "text":"Top: 256\/512 Center: 496\/655 Width: 480 Height: 286", + "wrap":true + }, + "type":"", + "visible":true, + "width":172, + "x":256, + "y":512 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":7, + "nextobjectid":11, + "orientation":"orthogonal", + "properties":[ + { + "name":"openWebsite", + "type":"string", + "value":"script.php" + }, + { + "name":"openWebsiteAllowApi", + "type":"bool", + "value":true + }], + "renderorder":"right-down", + "tiledversion":"1.7.2", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"..\/tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tiles":[ + { + "id":16, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":17, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":27, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":28, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":"1.6", + "width":30 +} \ No newline at end of file diff --git a/maps/tests/MovePlayer/script.php b/maps/tests/MovePlayer/script.php new file mode 100644 index 00000000..1188277b --- /dev/null +++ b/maps/tests/MovePlayer/script.php @@ -0,0 +1,48 @@ + + + + + + + +X:
+Y:
+Speed:
+ + + + + + diff --git a/maps/tests/index.html b/maps/tests/index.html index 4a80634c..4af51be5 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -227,6 +227,14 @@ Test camera API + + + Success Failure Pending + + + Test Player Movement API + + Success Failure Pending