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
7 changed files with 207 additions and 5 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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 {
|
||||
position: relative; /* Position relative is needed for the game-overlay. */
|
||||
|
||||
iframe {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
.audioplayer:first-child {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue