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:
David Négrier 2021-07-23 20:22:45 +02:00
parent a09f27b448
commit ce3c53ae3f
7 changed files with 207 additions and 5 deletions

View file

@ -84,6 +84,7 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor
import { SharedVariablesManager } from "./SharedVariablesManager";
import { playersStore } from "../../Stores/PlayersStore";
import { chatVisibilityStore } from "../../Stores/ChatStore";
import { PropertyUtils } from "../Map/PropertyUtils";
import Tileset = Phaser.Tilemaps.Tileset;
import { userIsAdminStore } from "../../Stores/GameStore";
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
@ -198,6 +199,7 @@ export class GameScene extends DirtyScene {
private preloading: boolean = true;
private startPositionCalculator!: StartPositionCalculator;
private sharedVariablesManager!: SharedVariablesManager;
private objectsByType = new Map<string, ITiledMapObject[]>();
constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) {
super({
@ -337,27 +339,27 @@ export class GameScene extends DirtyScene {
});
// 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) {
if (layer.type === "objectgroup") {
for (const object of layer.objects) {
let objectsOfType: ITiledMapObject[] | undefined;
if (!objects.has(object.type)) {
if (!this.objectsByType.has(object.type)) {
objectsOfType = new Array<ITiledMapObject>();
} else {
objectsOfType = objects.get(object.type);
objectsOfType = this.objectsByType.get(object.type);
if (objectsOfType === undefined) {
throw new Error("Unexpected object type not found");
}
}
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.
let itemFactory: ItemFactoryInterface;
@ -477,6 +479,25 @@ export class GameScene extends DirtyScene {
if (object.text) {
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);
}
}
}
}

View 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;
}
}

View file

@ -385,6 +385,10 @@ body {
#game {
position: relative; /* Position relative is needed for the game-overlay. */
iframe {
pointer-events: all;
}
}
.audioplayer:first-child {