Adding the ability to inject websites directly inside maps
This PR adds the ability to inject a website INSIDE a map (as an iframe inside a Phaser HTML object) The iFrame will be rendered transparently, unless you set a background-color on the body, which opens a number of cool possibilities. Needs to be done: allowing the iframe API in those iframes.
This commit is contained in:
parent
a09f27b448
commit
ce3c53ae3f
24
docs/maps/website-in-map.md
Normal file
24
docs/maps/website-in-map.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{.section-title.accent.text-primary}
|
||||||
|
# Putting a website inside a map
|
||||||
|
|
||||||
|
You can inject a website directly into your map, at a given position.
|
||||||
|
|
||||||
|
To do this in Tiled:
|
||||||
|
|
||||||
|
- Select an object layer
|
||||||
|
- Create a rectangular object, at the position where you want your website to appear
|
||||||
|
- Add a `url` property to your object pointing to the URL you want to open
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<figure class="figure">
|
||||||
|
<img src="https://workadventu.re/img/docs/website_url_property.png" class="figure-img img-fluid rounded" alt="" style="width: 70%" />
|
||||||
|
<figcaption class="figure-caption">A "website" object</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
The `url` can be absolute, or relative to your map.
|
||||||
|
|
||||||
|
{.alert.alert-info}
|
||||||
|
Internally, WorkAdventure will create an "iFrame" to load the website.
|
||||||
|
Some websites forbid being opened by iframes using the [`X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options)
|
||||||
|
HTTP header.
|
@ -84,6 +84,7 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor
|
|||||||
import { SharedVariablesManager } from "./SharedVariablesManager";
|
import { SharedVariablesManager } from "./SharedVariablesManager";
|
||||||
import { playersStore } from "../../Stores/PlayersStore";
|
import { playersStore } from "../../Stores/PlayersStore";
|
||||||
import { chatVisibilityStore } from "../../Stores/ChatStore";
|
import { chatVisibilityStore } from "../../Stores/ChatStore";
|
||||||
|
import { PropertyUtils } from "../Map/PropertyUtils";
|
||||||
import Tileset = Phaser.Tilemaps.Tileset;
|
import Tileset = Phaser.Tilemaps.Tileset;
|
||||||
import { userIsAdminStore } from "../../Stores/GameStore";
|
import { userIsAdminStore } from "../../Stores/GameStore";
|
||||||
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||||
@ -198,6 +199,7 @@ export class GameScene extends DirtyScene {
|
|||||||
private preloading: boolean = true;
|
private preloading: boolean = true;
|
||||||
private startPositionCalculator!: StartPositionCalculator;
|
private startPositionCalculator!: StartPositionCalculator;
|
||||||
private sharedVariablesManager!: SharedVariablesManager;
|
private sharedVariablesManager!: SharedVariablesManager;
|
||||||
|
private objectsByType = new Map<string, ITiledMapObject[]>();
|
||||||
|
|
||||||
constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) {
|
constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) {
|
||||||
super({
|
super({
|
||||||
@ -337,27 +339,27 @@ export class GameScene extends DirtyScene {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Scan the object layers for objects to load and load them.
|
// Scan the object layers for objects to load and load them.
|
||||||
const objects = new Map<string, ITiledMapObject[]>();
|
this.objectsByType = new Map<string, ITiledMapObject[]>();
|
||||||
|
|
||||||
for (const layer of this.mapFile.layers) {
|
for (const layer of this.mapFile.layers) {
|
||||||
if (layer.type === "objectgroup") {
|
if (layer.type === "objectgroup") {
|
||||||
for (const object of layer.objects) {
|
for (const object of layer.objects) {
|
||||||
let objectsOfType: ITiledMapObject[] | undefined;
|
let objectsOfType: ITiledMapObject[] | undefined;
|
||||||
if (!objects.has(object.type)) {
|
if (!this.objectsByType.has(object.type)) {
|
||||||
objectsOfType = new Array<ITiledMapObject>();
|
objectsOfType = new Array<ITiledMapObject>();
|
||||||
} else {
|
} else {
|
||||||
objectsOfType = objects.get(object.type);
|
objectsOfType = this.objectsByType.get(object.type);
|
||||||
if (objectsOfType === undefined) {
|
if (objectsOfType === undefined) {
|
||||||
throw new Error("Unexpected object type not found");
|
throw new Error("Unexpected object type not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objectsOfType.push(object);
|
objectsOfType.push(object);
|
||||||
objects.set(object.type, objectsOfType);
|
this.objectsByType.set(object.type, objectsOfType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [itemType, objectsOfType] of objects) {
|
for (const [itemType, objectsOfType] of this.objectsByType) {
|
||||||
// FIXME: we would ideally need for the loader to WAIT for the import to be performed, which means writing our own loader plugin.
|
// FIXME: we would ideally need for the loader to WAIT for the import to be performed, which means writing our own loader plugin.
|
||||||
|
|
||||||
let itemFactory: ItemFactoryInterface;
|
let itemFactory: ItemFactoryInterface;
|
||||||
@ -477,6 +479,25 @@ export class GameScene extends DirtyScene {
|
|||||||
if (object.text) {
|
if (object.text) {
|
||||||
TextUtils.createTextFromITiledMapObject(this, object);
|
TextUtils.createTextFromITiledMapObject(this, object);
|
||||||
}
|
}
|
||||||
|
if (object.type === "website") {
|
||||||
|
// Let's load iframes in the map
|
||||||
|
const url = PropertyUtils.mustFindStringProperty(
|
||||||
|
"url",
|
||||||
|
object.properties,
|
||||||
|
'in the "' + object.name + '" object of type "website"'
|
||||||
|
);
|
||||||
|
const absoluteUrl = new URL(url, this.MapUrlFile).toString();
|
||||||
|
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.src = absoluteUrl;
|
||||||
|
iframe.style.width = object.width + "px";
|
||||||
|
iframe.style.height = object.height + "px";
|
||||||
|
iframe.style.margin = "0";
|
||||||
|
iframe.style.padding = "0";
|
||||||
|
iframe.style.border = "none";
|
||||||
|
|
||||||
|
this.add.dom(object.x, object.y).setElement(iframe).setOrigin(0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
front/src/Phaser/Map/PropertyUtils.ts
Normal file
36
front/src/Phaser/Map/PropertyUtils.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import type { ITiledMapProperty } from "./ITiledMap";
|
||||||
|
|
||||||
|
export class PropertyUtils {
|
||||||
|
public static findProperty(
|
||||||
|
name: string,
|
||||||
|
properties: ITiledMapProperty[] | undefined
|
||||||
|
): string | boolean | number | undefined {
|
||||||
|
return properties?.find((property) => property.name === name)?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static mustFindProperty(
|
||||||
|
name: string,
|
||||||
|
properties: ITiledMapProperty[] | undefined,
|
||||||
|
context?: string
|
||||||
|
): string | boolean | number {
|
||||||
|
const property = PropertyUtils.findProperty(name, properties);
|
||||||
|
if (property === undefined) {
|
||||||
|
throw new Error('Could not find property "' + name + '"' + (context ? " (" + context + ")" : ""));
|
||||||
|
}
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static mustFindStringProperty(
|
||||||
|
name: string,
|
||||||
|
properties: ITiledMapProperty[] | undefined,
|
||||||
|
context?: string
|
||||||
|
): string {
|
||||||
|
const property = PropertyUtils.mustFindProperty(name, properties, context);
|
||||||
|
if (typeof property !== "string") {
|
||||||
|
throw new Error(
|
||||||
|
'Expected property "' + name + '" to be a string. ' + (context ? " (" + context + ")" : "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
}
|
@ -385,6 +385,10 @@ body {
|
|||||||
|
|
||||||
#game {
|
#game {
|
||||||
position: relative; /* Position relative is needed for the game-overlay. */
|
position: relative; /* Position relative is needed for the game-overlay. */
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.audioplayer:first-child {
|
.audioplayer:first-child {
|
||||||
|
@ -210,6 +210,14 @@
|
|||||||
<a href="#" class="testLink" data-testmap="TriggerMessageApi/triggerMessage.json" target="_blank">Testing trigger message API</a>
|
<a href="#" class="testLink" data-testmap="TriggerMessageApi/triggerMessage.json" target="_blank">Testing trigger message API</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-website-objects"> Success <input type="radio" name="test-website-objects"> Failure <input type="radio" name="test-website-objects" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="website_in_map.json" target="_blank">Testing websites inside a map</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
10
maps/tests/integrated_website_1.html
Normal file
10
maps/tests/integrated_website_1.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello world</h1>
|
||||||
|
<p>This is a webpage integrated in your map</p>
|
||||||
|
|
||||||
|
<p>Here is a form, right in your map! <input type="text" value="because I can!"> <button>Submit!</button></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
99
maps/tests/website_in_map.json
Normal file
99
maps/tests/website_in_map.json
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
{ "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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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,
|
||||||
|
"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":83.6666666666666,
|
||||||
|
"id":1,
|
||||||
|
"name":"",
|
||||||
|
"rotation":0,
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"fontfamily":"Sans Serif",
|
||||||
|
"pixelsize":13,
|
||||||
|
"text":"Test:\nWalk around the map\nResult:\nYou should see a transparent website at the center of the map, that is \"fixed\" on the map floor.",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":315.4375,
|
||||||
|
"x":68.4021076998051,
|
||||||
|
"y":8.73391812865529
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":419.805068226121,
|
||||||
|
"id":2,
|
||||||
|
"name":"demo website",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"url",
|
||||||
|
"type":"string",
|
||||||
|
"value":"integrated_website_1.html"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"website",
|
||||||
|
"visible":true,
|
||||||
|
"width":637.504873294347,
|
||||||
|
"x":189.005847953216,
|
||||||
|
"y":156.569200779727
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":6,
|
||||||
|
"nextobjectid":3,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"2021.03.23",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tileset1.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.5,
|
||||||
|
"width":30
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user