C6ESHRT7FZVXLL3273NDDKISU5LTU24RSPASCLN5VJHZE5VSIMPAC import React from 'react';import { IS_DEBUG } from '../environment';export type DebugFlagsType = {[key: string]: boolean;};const LOCAL_STORAGE_KEY = "debug flags";export const ReadDebugFlagsFromLocalStorage = <T extends DebugFlagsType>(defaultFlags: T): T => {if (IS_DEBUG) {const prevStoredFlags = JSON.parse((window.localStorage.getItem(LOCAL_STORAGE_KEY) || "{}")) as DebugFlagsType;// delete flags that don't existfor (const flagName of Object.keys(prevStoredFlags)) {if (!defaultFlags.hasOwnProperty(flagName)) {delete prevStoredFlags[flagName];}}return {...defaultFlags,...prevStoredFlags,};} else {return defaultFlags;}}const SaveDebugFlagsToLocalStorage = (flags: DebugFlagsType) => {window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(flags));};type DebugFlagButtonsProps = {flags: DebugFlagsType;};export class DebugFlagButtons extends React.Component<DebugFlagButtonsProps, {}> {render() {const flagNames = Object.keys(this.props.flags);return (<div>{flagNames.map(flagName => {const flag = this.props.flags[flagName];return (<divkey={flagName}><inputtype="checkbox"checked={ flag }onChange={ () => {// NOTE: This is TERRIBLE React code. DO NOT LEARN FROM// THIS. DO NOT IMITATE THIS. IN FACT, RUN FAR AWAY FROM// THIS!!!// The reason this works at all is because GameReactWrapper// does a forceUpdate() on a setInterval to keep everything// in sync. DebugFlagButtons will be captured in the// setInterval and forced to update.// The point being that the value gets synced back into the// game with no one being the wiser. MAGIC!this.props.flags[flagName] = !this.props.flags[flagName];SaveDebugFlagsToLocalStorage(this.props.flags);}}/> { flagName }</div>);})}</div>)}}
import React from 'react';import { Container, Graphics } from 'pixi.js';import { Entity } from '../entity';import { Debug } from '../debug';import { IGameState } from 'Library';type HierarchyProps = {root: Entity | Container;setSelected: (obj: Entity | Container) => void;setMoused: (obj: Entity | Container | null) => void;gameState: IGameState;selectedEntity: Entity | Container | null;};export class Hierarchy extends React.Component<HierarchyProps, {hover: boolean;collapsed: boolean;}> {constructor(props: HierarchyProps) {super(props);this.state = {hover: false,collapsed: true,};setInterval(() => {this.updateDebugGraphics();}, 200);}oldTint: { [key: number]: number } = {};hoverGraphics: Graphics[] = [];hoverTarget: Entity | Container | null = null;updateDebugGraphics = () => {// clear debug graphicsfor (const graphic of this.hoverGraphics) {graphic.parent.removeChild(graphic);graphic.destroy();}this.hoverGraphics = [];if (this.hoverTarget !== null) {this.hoverGraphics = [...Debug.DrawBounds(this.props.root, 0xff0000, true, "stage")];if (this.props.root instanceof Entity) {const point = Debug.DrawPoint(this.props.root.position, 0xff0000, true);this.hoverGraphics = [...this.hoverGraphics,point,];}}if (this.props.selectedEntity === this.props.root) {this.hoverGraphics = [...this.hoverGraphics, ...Debug.DrawBounds(this.props.selectedEntity, 0xff0000, true, "stage")];if (this.props.root instanceof Entity) {const point = Debug.DrawPoint(this.props.selectedEntity.position, 0xff0000, true);this.hoverGraphics = [...this.hoverGraphics,point,];}}};mouseOver = () => {this.setState({ hover: true })if (this.props.root instanceof Entity) {this.oldTint[this.props.root.id] = this.props.root.sprite.tint;this.props.root.sprite.tint = 0x000099;}this.hoverTarget = this.props.root;this.props.setMoused(this.props.root);};mouseOut = () => {this.setState({ hover: false })if (this.props.root instanceof Entity) {this.props.root.sprite.tint = this.oldTint[this.props.root.id];}this.hoverTarget = null;this.props.setMoused(null);};click = () => {this.props.setSelected(this.props.root);console.log(this.props.root);};renderLeaf(root: any) {let active = true;if (root instanceof Entity) {active = root.activeModes.includes(this.props.gameState.mode);}return (<div style={{color: active ? "white" : "gray",}}>{this.props.selectedEntity === this.props.root ? <strong>{root.name}</strong> : root.name} (depth: { root.zIndex})</div>)}render() {const root = this.props.root;let allChildren = (root instanceof Entity ? root.children() : []);let children = allChildren;let canCollapse = children.length > 20;let didCollapse = false;if (canCollapse) {if (this.state.collapsed) {children = children.slice(0, 20);didCollapse = true;}}if (children)return (<divstyle={{paddingLeft: "10px",fontFamily: 'Arial',fontSize: '14px',backgroundColor: this.state.hover ? "darkgray" : "black"}}><divonMouseEnter={this.mouseOver}onMouseLeave={this.mouseOut}onClick={this.click}>{this.renderLeaf(root)}</div>{canCollapse? <div onClick={() => this.setState({ collapsed: !this.state.collapsed })} style={{ padding: "8px 0" }}>{didCollapse? <span>[see {allChildren.length - 20} more]</span>: <span>[collapse]</span>}</div>: null}{children.map(child => {return <Hierarchy selectedEntity={this.props.selectedEntity} setMoused={this.props.setMoused} setSelected={this.props.setSelected} root={child} gameState={this.props.gameState} />})}</div>)};}
import React from 'react';import ReactDOM from 'react-dom';import { BaseGame } from '../base_game';import { Hierarchy } from './hierarchy';import { DebugFlagButtons, DebugFlagsType } from './debug_flag_buttons';import { IS_DEBUG } from '../environment';import { Entity } from '../entity';import { Container } from 'pixi.js';import { TextEntity } from '../text_entity';import { Debug } from '../debug';type ReactWrapperProps = {game: BaseGame<{}>;debugFlags: DebugFlagsType;};type ReactWrapperState = {selected: Entity | Container | null;moused: Entity | Container | null;};export class GameReactWrapper extends React.Component<ReactWrapperProps, ReactWrapperState> {mounted = false;constructor(props: ReactWrapperProps) {super(props);this.state = {selected: this.props.game.stage,moused: null,};setInterval(() => this.monitorHierarchyUpdates(), 500);}componentDidMount() {this.mounted = true;}componentWillUnmount() {console.error("This should never happen!!!! very bad?!?");}monitorHierarchyUpdates = () => {if (this.mounted) {this.forceUpdate();}};setSelected = (obj: Entity | Container) => {this.setState({selected: obj,});};setMoused = (obj: Entity | Container | null) => {this.setState({moused: obj,});};renderSelected = () => {const target = this.state.moused || this.state.selected;if (target === null) { return null; }if (target instanceof Container) {return (<div style={{ fontWeight: 600, fontFamily: 'arial', paddingTop: '8px', paddingBottom: '8px', fontSize: '18px' }}>Stage</div>);}let active = true;if (target instanceof Entity) {active = target.activeModes.includes(this.props.game.state.mode);}return (<div><div style={{ fontWeight: 600, fontFamily: 'arial', paddingTop: '8px', paddingBottom: '8px', fontSize: '18px' }}>{target.name}</div><div>x: {target.x}, y: {target.y}</div><div>{active ? "Active" : "Inactive"}</div><div>xAbs: {target.positionAbsolute().x}, yAbs: {target.positionAbsolute().y}</div><div>width: {target.width}, height: {target.height}</div><div>visible: {target.visible ? "true" : "false"}</div><div>scaleX: {target.scale.x.toFixed(2)} scaleY: {target.scale.y.toFixed(2)}</div>{target instanceof TextEntity? <div>text: {target.html}</div>: <div></div>}</div>);};renderHierarchy() {return (<div><HierarchyselectedEntity={this.state.selected}setMoused={this.setMoused}setSelected={this.setSelected}root={this.props.game.stage}gameState={this.props.game.state}/><HierarchyselectedEntity={this.state.selected}setMoused={this.setMoused}setSelected={this.setSelected}root={this.props.game.fixedCameraStage}gameState={this.props.game.state}/></div>)}render() {return (<div style={{display: "flex",flexDirection: "row",borderLeft: IS_DEBUG ? "1px solid lightgray" : 0,marginLeft: '16px',paddingLeft: '8px',}}><div style={{overflow: "auto",height: "90vh",fontFamily: 'arial',fontSize: '14px',}}>{this.props.game && this.props.game.stage && IS_DEBUG &&<div style={{ paddingLeft: '8px', }}><div style={{ fontFamily: "arial", marginBottom: '8px', fontSize: '14px', width: '300px', padding: '8px' }}>Note: This debugging panel is only shown in development, or production with ?debug=true.</div><div style={{ fontWeight: 600, fontFamily: 'arial', paddingBottom: '8px', fontSize: '18px' }}>Debug Options</div><DebugFlagButtons flags={this.props.debugFlags} /><div>Draw Count: {Debug.GetDrawCount()}</div>{this.renderSelected()}<div style={{ fontWeight: 600, fontFamily: 'arial', paddingTop: '8px', paddingBottom: '8px', fontSize: '18px' }}>Debug Hierarchy</div>{this.renderHierarchy()}</div>}</div></div>);}}export const CreateGame = (game: BaseGame<any, any, any>, debugFlags: DebugFlagsType) => {ReactDOM.render(<React.StrictMode><GameReactWrappergame={game}debugFlags={debugFlags}/></React.StrictMode>,document.getElementById('root'));ReactDOM.render(<React.StrictMode><Log /></React.StrictMode>,document.getElementById('log'));}export const originalConsoleLog = console.log;const allLogs: any[][] = [];// console.log = (...data: any[]) => {// allLogs.push(data);// };export const Log: React.FC<{}> = (props: {}) => {const [logs, setLogs] = React.useState<any[][]>([]);React.useEffect(() => {const interval = setInterval(() => {setLogs(allLogs.slice());}, 400);return () => clearInterval(interval);}, []);return (<div>{logs.map(logItems => {return (<div>{logItems.map(log => <span>{JSON.stringify(log)}</span>)}</div>);})}</div>)}