Merge branch 'develop' of github.com:thecodingmachine/workadventure into main
This commit is contained in:
commit
2701d656da
@ -15,6 +15,7 @@
|
|||||||
- Use `WA.room.getCurrentUser(): Promise<User>` to get the ID, name and tags of the current player
|
- Use `WA.room.getCurrentUser(): Promise<User>` to get the ID, name and tags of the current player
|
||||||
- Use `WA.room.getCurrentRoom(): Promise<Room>` to get the ID, JSON map file, url of the map of the current room and the layer where the current player started
|
- Use `WA.room.getCurrentRoom(): Promise<Room>` to get the ID, JSON map file, url of the map of the current room and the layer where the current player started
|
||||||
- Use `WA.ui.registerMenuCommand(): void` to add a custom menu
|
- Use `WA.ui.registerMenuCommand(): void` to add a custom menu
|
||||||
|
- Use `WA.room.setTiles(): void` to change an array of tiles
|
||||||
|
|
||||||
## Version 1.4.3 - 1.4.4 - 1.4.5
|
## Version 1.4.3 - 1.4.4 - 1.4.5
|
||||||
|
|
||||||
|
@ -112,3 +112,35 @@ WA.room.getCurrentUser().then((user) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Changing tiles
|
||||||
|
```
|
||||||
|
WA.room.setTiles(tiles: TileDescriptor[]): void
|
||||||
|
```
|
||||||
|
Replace the tile at the `x` and `y` coordinates in the layer named `layer` by the tile with the id `tile`.
|
||||||
|
|
||||||
|
If `tile` is a string, it's not the id of the tile but the value of the property `name`.
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<img src="https://workadventu.re/img/docs/nameIndexProperty.png" class="figure-img img-fluid rounded" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
`TileDescriptor` has the following attributes :
|
||||||
|
* **x (number) :** The coordinate x of the tile that you want to replace.
|
||||||
|
* **y (number) :** The coordinate y of the tile that you want to replace.
|
||||||
|
* **tile (number | string) :** The id of the tile that will be placed in the map.
|
||||||
|
* **layer (string) :** The name of the layer where the tile will be placed.
|
||||||
|
|
||||||
|
**Important !** : If you use `tile` as a number, be sure to add the `firstgid` of the tileset of the tile that you want to the id of the tile in Tiled Editor.
|
||||||
|
|
||||||
|
|
||||||
|
Example :
|
||||||
|
```javascript
|
||||||
|
WA.room.setTiles([
|
||||||
|
{x: 6, y: 4, tile: 'blue', layer: 'setTiles'},
|
||||||
|
{x: 7, y: 4, tile: 109, layer: 'setTiles'},
|
||||||
|
{x: 8, y: 4, tile: 109, layer: 'setTiles'},
|
||||||
|
{x: 9, y: 4, tile: 'blue', layer: 'setTiles'}
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
@ -18,7 +18,7 @@ import type { PlaySoundEvent } from "./PlaySoundEvent";
|
|||||||
import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent";
|
import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent";
|
||||||
import type { MenuItemRegisterEvent } from './ui/MenuItemRegisterEvent';
|
import type { MenuItemRegisterEvent } from './ui/MenuItemRegisterEvent';
|
||||||
import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent";
|
import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent";
|
||||||
|
import type { SetTilesEvent } from "./SetTilesEvent";
|
||||||
|
|
||||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||||
data: T
|
data: T
|
||||||
@ -26,7 +26,6 @@ export interface TypedMessageEvent<T> extends MessageEvent {
|
|||||||
|
|
||||||
export type IframeEventMap = {
|
export type IframeEventMap = {
|
||||||
//getState: GameStateEvent,
|
//getState: GameStateEvent,
|
||||||
// updateTile: UpdateTileEvent
|
|
||||||
loadPage: LoadPageEvent
|
loadPage: LoadPageEvent
|
||||||
chat: ChatEvent,
|
chat: ChatEvent,
|
||||||
openPopup: OpenPopupEvent
|
openPopup: OpenPopupEvent
|
||||||
@ -46,7 +45,8 @@ export type IframeEventMap = {
|
|||||||
getDataLayer: undefined
|
getDataLayer: undefined
|
||||||
loadSound: LoadSoundEvent
|
loadSound: LoadSoundEvent
|
||||||
playSound: PlaySoundEvent
|
playSound: PlaySoundEvent
|
||||||
stopSound: null,
|
stopSound: null
|
||||||
|
setTiles: SetTilesEvent
|
||||||
getState: undefined,
|
getState: undefined,
|
||||||
registerMenuCommand: MenuItemRegisterEvent
|
registerMenuCommand: MenuItemRegisterEvent
|
||||||
}
|
}
|
||||||
|
15
front/src/Api/Events/SetTilesEvent.ts
Normal file
15
front/src/Api/Events/SetTilesEvent.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
|
export const isSetTilesEvent =
|
||||||
|
tg.isArray(
|
||||||
|
new tg.IsInterface().withProperties({
|
||||||
|
x: tg.isNumber,
|
||||||
|
y: tg.isNumber,
|
||||||
|
tile: tg.isUnion(tg.isNumber, tg.isString),
|
||||||
|
layer: tg.isString
|
||||||
|
}).get()
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* A message sent from the iFrame to the game to set one or many tiles.
|
||||||
|
*/
|
||||||
|
export type SetTilesEvent = tg.GuardedType<typeof isSetTilesEvent>;
|
@ -18,7 +18,6 @@ import {
|
|||||||
TypedMessageEvent
|
TypedMessageEvent
|
||||||
} from "./Events/IframeEvent";
|
} from "./Events/IframeEvent";
|
||||||
import type {UserInputChatEvent} from "./Events/UserInputChatEvent";
|
import type {UserInputChatEvent} from "./Events/UserInputChatEvent";
|
||||||
//import { isLoadPageEvent } from './Events/LoadPageEvent';
|
|
||||||
import {isPlaySoundEvent, PlaySoundEvent} from "./Events/PlaySoundEvent";
|
import {isPlaySoundEvent, PlaySoundEvent} from "./Events/PlaySoundEvent";
|
||||||
import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent";
|
import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent";
|
||||||
import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent";
|
import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent";
|
||||||
@ -30,6 +29,7 @@ import type {GameStateEvent} from "./Events/GameStateEvent";
|
|||||||
import type {HasPlayerMovedEvent} from "./Events/HasPlayerMovedEvent";
|
import type {HasPlayerMovedEvent} from "./Events/HasPlayerMovedEvent";
|
||||||
import {isLoadPageEvent} from "./Events/LoadPageEvent";
|
import {isLoadPageEvent} from "./Events/LoadPageEvent";
|
||||||
import {handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent} from "./Events/ui/MenuItemRegisterEvent";
|
import {handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent} from "./Events/ui/MenuItemRegisterEvent";
|
||||||
|
import {SetTilesEvent, isSetTilesEvent} from "./Events/SetTilesEvent";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens to messages from iframes and turn those messages into easy to use observables.
|
* Listens to messages from iframes and turn those messages into easy to use observables.
|
||||||
@ -106,6 +106,9 @@ class IframeListener {
|
|||||||
private readonly _loadSoundStream: Subject<LoadSoundEvent> = new Subject();
|
private readonly _loadSoundStream: Subject<LoadSoundEvent> = new Subject();
|
||||||
public readonly loadSoundStream = this._loadSoundStream.asObservable();
|
public readonly loadSoundStream = this._loadSoundStream.asObservable();
|
||||||
|
|
||||||
|
private readonly _setTilesStream: Subject<SetTilesEvent> = new Subject();
|
||||||
|
public readonly setTilesStream = this._setTilesStream.asObservable();
|
||||||
|
|
||||||
private readonly iframes = new Set<HTMLIFrameElement>();
|
private readonly iframes = new Set<HTMLIFrameElement>();
|
||||||
private readonly iframeCloseCallbacks = new Map<HTMLIFrameElement, (() => void)[]>();
|
private readonly iframeCloseCallbacks = new Map<HTMLIFrameElement, (() => void)[]>();
|
||||||
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
||||||
@ -202,6 +205,8 @@ class IframeListener {
|
|||||||
this._unregisterMenuCommandStream.next(data);
|
this._unregisterMenuCommandStream.next(data);
|
||||||
})
|
})
|
||||||
handleMenuItemRegistrationEvent(payload.data)
|
handleMenuItemRegistrationEvent(payload.data)
|
||||||
|
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {
|
||||||
|
this._setTilesStream.next(payload.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
|
@ -31,6 +31,14 @@ interface User {
|
|||||||
tags: string[];
|
tags: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TileDescriptor {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
tile: number | string
|
||||||
|
layer: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function getGameState(): Promise<GameStateEvent> {
|
function getGameState(): Promise<GameStateEvent> {
|
||||||
if (immutableDataPromise === undefined) {
|
if (immutableDataPromise === undefined) {
|
||||||
immutableDataPromise = new Promise<GameStateEvent>((resolver, thrower) => {
|
immutableDataPromise = new Promise<GameStateEvent>((resolver, thrower) => {
|
||||||
@ -129,6 +137,13 @@ export class WorkadventureRoomCommands extends IframeApiContribution<Workadventu
|
|||||||
return { id: gameState.uuid, nickName: gameState.nickname, tags: gameState.tags };
|
return { id: gameState.uuid, nickName: gameState.nickname, tags: gameState.tags };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
setTiles(tiles: TileDescriptor[]) {
|
||||||
|
sendToWorkadventure({
|
||||||
|
type: 'setTiles',
|
||||||
|
data: tiles
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new WorkadventureRoomCommands();
|
export default new WorkadventureRoomCommands();
|
||||||
|
@ -13,8 +13,9 @@ export class GameMap {
|
|||||||
private key: number | undefined;
|
private key: number | undefined;
|
||||||
private lastProperties = new Map<string, string | boolean | number>();
|
private lastProperties = new Map<string, string | boolean | number>();
|
||||||
private callbacks = new Map<string, Array<PropertyChangeCallback>>();
|
private callbacks = new Map<string, Array<PropertyChangeCallback>>();
|
||||||
|
private tileNameMap = new Map<string, number>();
|
||||||
|
|
||||||
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapLayerProperty> } = {}
|
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapLayerProperty> } = {};
|
||||||
public readonly flatLayers: ITiledMapLayer[];
|
public readonly flatLayers: ITiledMapLayer[];
|
||||||
public readonly phaserLayers: TilemapLayer[] = [];
|
public readonly phaserLayers: TilemapLayer[] = [];
|
||||||
|
|
||||||
@ -36,6 +37,9 @@ export class GameMap {
|
|||||||
if (tile.properties) {
|
if (tile.properties) {
|
||||||
this.tileSetPropertyMap[tileset.firstgid + tile.id] = tile.properties
|
this.tileSetPropertyMap[tileset.firstgid + tile.id] = tile.properties
|
||||||
tile.properties.forEach(prop => {
|
tile.properties.forEach(prop => {
|
||||||
|
if (prop.name == 'name' && typeof prop.value == "string") {
|
||||||
|
this.tileNameMap.set(prop.value, tileset.firstgid + tile.id);
|
||||||
|
}
|
||||||
if (prop.name == "exitUrl" && typeof prop.value == "string") {
|
if (prop.name == "exitUrl" && typeof prop.value == "string") {
|
||||||
this.exitUrls.push(prop.value);
|
this.exitUrls.push(prop.value);
|
||||||
}
|
}
|
||||||
@ -130,6 +134,10 @@ export class GameMap {
|
|||||||
return this.map;
|
return this.map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getTileProperty(index: number): Array<ITiledMapLayerProperty> {
|
||||||
|
return this.tileSetPropertyMap[index];
|
||||||
|
}
|
||||||
|
|
||||||
private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) {
|
private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) {
|
||||||
const callbacksArray = this.callbacks.get(propName);
|
const callbacksArray = this.callbacks.get(propName);
|
||||||
if (callbacksArray !== undefined) {
|
if (callbacksArray !== undefined) {
|
||||||
@ -165,4 +173,50 @@ export class GameMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private putTileInFlatLayer(index: number, x: number, y: number, layer: string): void {
|
||||||
|
const fLayer = this.findLayer(layer);
|
||||||
|
if ( fLayer == undefined ) {
|
||||||
|
console.error("The layer that you want to change doesn't exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fLayer.type !== 'tilelayer') {
|
||||||
|
console.error("The layer that you want to change is not a tilelayer. Tile can only be put in tilelayer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof fLayer.data === "string") {
|
||||||
|
console.error("Data of the layer that you want to change is only readable.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fLayer.data[x+y*fLayer.height] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public putTile(tile: string | number, x: number, y: number, layer: string): void {
|
||||||
|
const phaserLayer = this.findPhaserLayer(layer);
|
||||||
|
if ( phaserLayer ) {
|
||||||
|
const tileIndex = this.getIndexForTileType(tile);
|
||||||
|
if ( tileIndex !== undefined ) {
|
||||||
|
this.putTileInFlatLayer(tileIndex, x, y, layer);
|
||||||
|
const phaserTile = phaserLayer.putTileAt(tileIndex, x, y);
|
||||||
|
for (const property of this.getTileProperty(tileIndex)) {
|
||||||
|
if ( property.name === "collides" && property.value === "true") {
|
||||||
|
phaserTile.setCollision(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error("The tile that you want to place doesn't exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error("The layer that you want to change is not a tilelayer. Tile can only be put in tilelayer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getIndexForTileType(tile: string | number): number | undefined {
|
||||||
|
if (typeof tile == "number") {
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
return this.tileNameMap.get(tile);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -996,7 +996,7 @@ export class GameScene extends DirtyScene {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.iframeSubscriptionList.push(
|
this.iframeSubscriptionList.push(
|
||||||
iframeListener.enablePlayerControlStream.subscribe(() => {
|
iframeListener.enablePlayerControlStream.subscribe(()=>{
|
||||||
this.userInputManager.restoreControls();
|
this.userInputManager.restoreControls();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -1085,6 +1085,13 @@ export class GameScene extends DirtyScene {
|
|||||||
}, this.userInputManager);
|
}, this.userInputManager);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
)
|
||||||
|
this.iframeSubscriptionList.push(iframeListener.setTilesStream.subscribe((eventTiles) => {
|
||||||
|
for (const eventTile of eventTiles) {
|
||||||
|
this.gameMap.putTile(eventTile.tile, eventTile.x, eventTile.y, eventTile.layer);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setPropertyLayer(
|
private setPropertyLayer(
|
||||||
|
@ -16,6 +16,7 @@ import player from "./Api/iframe/player";
|
|||||||
import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor";
|
import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor";
|
||||||
import type { Popup } from "./Api/iframe/Ui/Popup";
|
import type { Popup } from "./Api/iframe/Ui/Popup";
|
||||||
import type { Sound } from "./Api/iframe/Sound/Sound";
|
import type { Sound } from "./Api/iframe/Sound/Sound";
|
||||||
|
import {sendToWorkadventure} from "./Api/iframe/IframeApiContribution";
|
||||||
|
|
||||||
const wa = {
|
const wa = {
|
||||||
ui,
|
ui,
|
||||||
@ -36,6 +37,7 @@ const wa = {
|
|||||||
console.warn('Method WA.sendChatMessage is deprecated. Please use WA.chat.sendChatMessage instead');
|
console.warn('Method WA.sendChatMessage is deprecated. Please use WA.chat.sendChatMessage instead');
|
||||||
chat.sendChatMessage(message, author);
|
chat.sendChatMessage(message, author);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use WA.chat.disablePlayerControls instead
|
* @deprecated Use WA.chat.disablePlayerControls instead
|
||||||
*/
|
*/
|
||||||
|
230
maps/tests/Metadata/map.json
Normal file
230
maps/tests/Metadata/map.json
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":10,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"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, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10,
|
||||||
|
"id":1,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 33, 34, 34, 34, 34, 34, 34, 35, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 49, 50, 50, 50, 50, 50, 50, 51, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46],
|
||||||
|
"height":10,
|
||||||
|
"id":2,
|
||||||
|
"name":"bottom",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 52, 52, 0, 0, 0, 0, 0, 0, 0, 52, 52, 52, 0, 0, 0, 0, 0, 0, 0, 52, 52, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10,
|
||||||
|
"id":4,
|
||||||
|
"name":"metadata",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":5,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19],
|
||||||
|
"height":10,
|
||||||
|
"id":3,
|
||||||
|
"name":"wall",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":6,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"script",
|
||||||
|
"type":"string",
|
||||||
|
"value":"script.js"
|
||||||
|
}],
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.4.3",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":8,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tileset_dungeon.png",
|
||||||
|
"imageheight":256,
|
||||||
|
"imagewidth":256,
|
||||||
|
"margin":0,
|
||||||
|
"name":"TDungeon",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":64,
|
||||||
|
"tileheight":32,
|
||||||
|
"tiles":[
|
||||||
|
{
|
||||||
|
"id":0,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":2,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":3,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":4,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":8,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":9,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":10,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":11,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":12,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":16,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":17,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":18,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":19,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":20,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.4,
|
||||||
|
"width":10
|
||||||
|
}
|
9
maps/tests/Metadata/script.js
Normal file
9
maps/tests/Metadata/script.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/*WA.getMapUrl().then((map) => {console.log('mapUrl : ', map)});
|
||||||
|
WA.getUuid().then((uuid) => {console.log('Uuid : ',uuid)});
|
||||||
|
WA.getRoomId().then((roomId) => console.log('roomID : ',roomId));*/
|
||||||
|
|
||||||
|
//WA.onPlayerMove(console.log);
|
||||||
|
WA.setProperty('metadata', 'openWebsite', 'https://fr.wikipedia.org/');
|
||||||
|
WA.getDataLayer().then((data) => {console.log('data 1 : ', data)});
|
31
maps/tests/Metadata/setTiles.html
Normal file
31
maps/tests/Metadata/setTiles.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
var script = document.createElement('script');
|
||||||
|
// Don't do this at home kids! The "document.referrer" part is actually inserting a XSS security.
|
||||||
|
// We are OK in this precise case because the HTML page is hosted on the "maps" domain that contains only static files.
|
||||||
|
script.setAttribute('src', document.referrer + 'iframe_api.js');
|
||||||
|
document.head.appendChild(script);
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
WA.room.setTiles([
|
||||||
|
{x: 0, y: 0, tile: 92, layer: 'setTiles'},
|
||||||
|
{x: 0, y: 2, tile: 'Red', layer: 'setTiles'},
|
||||||
|
{x: 0, y: 3, tile: 99, layer: 'setTiles'},
|
||||||
|
{x: 0, y: 5, tile: 117, layer: 'setTiles'},
|
||||||
|
{x: 0, y: 6, tile: 117, layer: 'setTiles'},
|
||||||
|
{x: 0, y: 9, tile: 74, layer: 'setTiles'}
|
||||||
|
]);
|
||||||
|
WA.room.setTiles([
|
||||||
|
{x: 6, y: 4, tile: 'blue', layer: 'setTiles'},
|
||||||
|
{x: 7, y: 4, tile: 109, layer: 'setTiles'},
|
||||||
|
{x: 8, y: 4, tile: 109, layer: 'setTiles'},
|
||||||
|
{x: 9, y: 4, tile: 'blue', layer: 'setTiles'}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
348
maps/tests/Metadata/setTiles.json
Normal file
348
maps/tests/Metadata/setTiles.json
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"editorsettings":
|
||||||
|
{
|
||||||
|
"export":
|
||||||
|
{
|
||||||
|
"target":"."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"height":10,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10,
|
||||||
|
"id":1,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 49, 50, 50, 50, 50, 50, 50, 50, 50, 51],
|
||||||
|
"height":10,
|
||||||
|
"id":2,
|
||||||
|
"name":"bottom",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":10,
|
||||||
|
"id":4,
|
||||||
|
"name":"metadata",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"openWebsite",
|
||||||
|
"type":"string",
|
||||||
|
"value":"setTiles.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"openWebsiteAllowApi",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 65, 65, 65, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":10,
|
||||||
|
"id":8,
|
||||||
|
"name":"setTiles",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":5,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":191.866671635267,
|
||||||
|
"id":1,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"fontfamily":"Sans Serif",
|
||||||
|
"pixelsize":9,
|
||||||
|
"text":"Test : \nWalk on the grass\n\nResult : \nThe Yellow Tile opens a Jitsi with Trigger.\n\nThe Red Tile opens cowebsite Wikipedia. The highest Red Tile is found by 'name' property index, the lowest by 'number' index.\n\nThe White Tiles are silent tiles. You cannot open a bubble in it. (Even if the other player didn't activate the script.)\n\nThe Pale Tile (Lowest) is an exitUrl tile to customMenu.json.\n\nThe Blue Tile are 'collides' tile. The two tiles in the center are 'number' index. The others are 'name' property index.\n",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":274.674838251912,
|
||||||
|
"x":32.5473600365393,
|
||||||
|
"y":128.305680721763
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":9,
|
||||||
|
"nextobjectid":2,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.4.3",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":8,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tileset_dungeon.png",
|
||||||
|
"imageheight":256,
|
||||||
|
"imagewidth":256,
|
||||||
|
"margin":0,
|
||||||
|
"name":"TDungeon",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":64,
|
||||||
|
"tileheight":32,
|
||||||
|
"tiles":[
|
||||||
|
{
|
||||||
|
"id":0,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":2,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":3,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":4,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":8,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":9,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":10,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":11,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":12,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":16,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":17,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":18,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":19,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":20,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"tilewidth":32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns":8,
|
||||||
|
"firstgid":65,
|
||||||
|
"image":"floortileset.png",
|
||||||
|
"imageheight":288,
|
||||||
|
"imagewidth":256,
|
||||||
|
"margin":0,
|
||||||
|
"name":"Floor",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":72,
|
||||||
|
"tileheight":32,
|
||||||
|
"tiles":[
|
||||||
|
{
|
||||||
|
"id":9,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"exitUrl",
|
||||||
|
"type":"string",
|
||||||
|
"value":"customMenu.json"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":27,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"jitsiRoom",
|
||||||
|
"type":"string",
|
||||||
|
"value":"TEST"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"jitsiTrigger",
|
||||||
|
"type":"string",
|
||||||
|
"value":"onaction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"jitsiUrl",
|
||||||
|
"type":"string",
|
||||||
|
"value":"meet.jit.si"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":34,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"name",
|
||||||
|
"type":"string",
|
||||||
|
"value":"Red"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"openWebsite",
|
||||||
|
"type":"string",
|
||||||
|
"value":"https:\/\/fr.wikipedia.org\/wiki\/Wikip%C3%A9dia:Accueil_principal"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":40,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"name",
|
||||||
|
"type":"string",
|
||||||
|
"value":""
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":44,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"name",
|
||||||
|
"type":"string",
|
||||||
|
"value":"blue"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":52,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"silent",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.4,
|
||||||
|
"width":10
|
||||||
|
}
|
@ -178,6 +178,14 @@
|
|||||||
<a href="#" class="testLink" data-testmap="Metadata/cowebsiteAllowApi.json" target="_blank">Test cowebsite opened by script is allowed to use IFrame API</a>
|
<a href="#" class="testLink" data-testmap="Metadata/cowebsiteAllowApi.json" target="_blank">Test cowebsite opened by script is allowed to use IFrame API</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-set-tiles"> Success <input type="radio" name="test-set-tiles"> Failure <input type="radio" name="test-set-tiles" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="Metadata/setTiles.json" target="_blank">Test set tiles</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
Loading…
Reference in New Issue
Block a user