sparkletown/sparkle

View on GitHub
src/components/templates/AnimateMap/game/map/MapContainer.ts

Summary

Maintainability
D
1 day
Test Coverage
import { Engine } from "@ash.ts/ash";
import { Application, Container } from "pixi.js";
import { Viewport } from "pixi-viewport";

import { setAnimateMapPointer } from "store/actions/AnimateMap";
import { ReplicatedUser } from "store/reducers/AnimateMap";

import { Point } from "types/utility";

import playerModel from "../../bridges/DataProvider/Structures/PlayerModel";
import EventProvider, {
  EventType,
} from "../../bridges/EventProvider/EventProvider";
import { TimeoutCommand } from "../commands/TimeoutCommand";
import { MAP_JSON, sounds } from "../constants/AssetConstants";
import { stubArtcarsData } from "../constants/StubData";
import { GameInstance } from "../GameInstance";
import KeyPoll from "../utils/KeyPollSingleton";
import { PlaygroundMap } from "../utils/PlaygroundMap";

import EntityFactory from "./entities/EntityFactory";
import { AnimationNode } from "./nodes/AnimationNode";
import { AnimationSystem } from "./systems/AnimationSysem";
import { AvatarTuningSystem } from "./systems/AvatarTuningSystem";
import { BubbleSystem } from "./systems/BubbleSystem";
import { ClickableSpriteSystem } from "./systems/ClickableSpriteSystem";
import { DeadSystem } from "./systems/DeadSystem";
import { DebugSystem } from "./systems/DebugSystem";
import { FirebarrelSystem } from "./systems/FirebarrelSystem";
import { FixScaleByViewportZoomSystem } from "./systems/FixScaleByViewportZoomSystem";
import { HoverableSpriteSystem } from "./systems/HoverableSpriteSystem";
import { LineOfSightSystem } from "./systems/LineOfSightSystem";
import { MotionArtcarSystem } from "./systems/MotionArtcarSystem";
import { MotionBotSystem } from "./systems/MotionBotSystem";
import { MotionClickSystem } from "./systems/MotionClickSystem";
import { MotionCollisionSystem } from "./systems/MotionCollisionSystem";
import { MotionControlSwitchSystem } from "./systems/MotionControlSwitchSystem";
import { MotionJoystickSystem } from "./systems/MotionJoystickSystem";
import { MotionKeyboardSystem } from "./systems/MotionKeyboardSystem";
import { MotionTeleportSystem } from "./systems/MotionTeleportSystem";
import { MovementSystem } from "./systems/MovementSystem";
import { SoundEmitterSystem } from "./systems/SoundEmitterSystem";
import { SpriteSystem } from "./systems/SpriteSystem";
import { SystemPriorities } from "./systems/SystemPriorities";
import { TooltipSystem } from "./systems/TooltipSystem";
import { VenueSystem } from "./systems/VenueSystem";
import { ViewportBackgroundSystem } from "./systems/ViewportBackgroundSystem";
import { ViewportSystem } from "./systems/ViewportSystem";

export class MapContainer extends Container {
  private _app: Application;

  private _viewport?: Viewport;

  private _engine?: Engine;
  public entityFactory?: EntityFactory;
  public _entityContainer?: Container;
  private _tooltipContainer?: Container;
  private _bubbleContainer?: Container;

  private _joystickContainer?: Container;

  private _debugContainer?: Container;

  private _player: ReplicatedUser | undefined;

  constructor(app: Application) {
    super();

    this._app = app;

    if (
      playerModel.x !== 0 &&
      playerModel.y !== 0 &&
      playerModel.data.id !== ""
    ) {
      this._player = playerModel;
    } else {
      const clbck = (player: ReplicatedUser) => {
        this._player = player;
        EventProvider.off(EventType.PLAYER_MODEL_READY, clbck);
      };
      EventProvider.on(EventType.PLAYER_MODEL_READY, clbck);
    }
  }

  public async start(): Promise<void> {
    await this.initEntities().then(() => {
      if (this._player) {
        this.entityFactory?.createPlayer(this._player);
      }
    });
  }

  public async init(): Promise<void> {
    this.initViewport();
    this.initViewportLayers();
    this.initSystems();
    this.initMap(MAP_JSON);

    this._viewport?.on("clicked", (e: { world: Point }) =>
      GameInstance.instance.getStore().dispatch(
        setAnimateMapPointer({
          x: e.world.x,
          y: e.world.y,
        })
      )
    );
  }

  private initViewport() {
    this._viewport = new Viewport({ noTicker: true, divWheel: this._app.view });
    this.addChild(this._viewport);
  }

  private initViewportLayers() {
    this._debugContainer = new Container(); //TODO: maybe relocate all instantiate to constructor?
    this._viewport?.addChild(this._debugContainer);

    this._entityContainer = new Container();
    this._viewport?.addChild(this._entityContainer);
  }

  private initSystems() {
    this._tooltipContainer = new Container();
    this.addChild(this._tooltipContainer);
    this._bubbleContainer = new Container();
    this.addChild(this._bubbleContainer);
    this._joystickContainer = new Container();
    this.addChild(this._joystickContainer);

    // const keyPoll = keyPoll;
    this._engine = new Engine();
    this.entityFactory = new EntityFactory(this._engine);

    this._engine.addSystem(new VenueSystem(), SystemPriorities.update);
    this._engine.addSystem(
      new DeadSystem(this._engine),
      SystemPriorities.update
    );
    this._engine.addSystem(new VenueSystem(), SystemPriorities.update);
    this._engine.addSystem(
      new MotionControlSwitchSystem(),
      SystemPriorities.update
    );
    this._engine.addSystem(
      new MotionKeyboardSystem(KeyPoll, this.entityFactory),
      SystemPriorities.update
    );
    this._engine.addSystem(
      new MotionClickSystem(this.entityFactory),
      SystemPriorities.update
    );
    this._engine.addSystem(
      new MotionTeleportSystem(this.entityFactory),
      SystemPriorities.update
    );
    this._engine.addSystem(
      new MotionJoystickSystem(this._joystickContainer, this.entityFactory),
      SystemPriorities.update
    );
    this._engine.addSystem(
      new FixScaleByViewportZoomSystem(),
      SystemPriorities.update
    );
    this._engine.addSystem(
      new MotionBotSystem(this.entityFactory),
      SystemPriorities.update
    );

    this._engine.addSystem(
      new MotionArtcarSystem(this.entityFactory),
      SystemPriorities.update
    );
    this._engine.addSystem(new SoundEmitterSystem(), SystemPriorities.update);
    this._engine.addSystem(
      new AvatarTuningSystem(this.entityFactory),
      SystemPriorities.update
    );

    if (this._debugContainer && this._viewport) {
      this._engine.addSystem(
        new DebugSystem(
          this._debugContainer,
          this.entityFactory,
          this._viewport
        ),
        SystemPriorities.move
      );
    }

    this._engine.addSystem(new MovementSystem(), SystemPriorities.move);
    this._engine.addSystem(
      new LineOfSightSystem(this.entityFactory),
      SystemPriorities.move
    );
    this._engine.addSystem(
      new MotionCollisionSystem(this.entityFactory),
      SystemPriorities.resolveCollisions
    );

    this._engine.addSystem(
      new AnimationSystem(AnimationNode),
      SystemPriorities.animate
    );

    this._engine.addSystem(
      new SpriteSystem(this._entityContainer),
      SystemPriorities.render
    );
    this._engine.addSystem(
      new HoverableSpriteSystem(this._entityContainer as Container),
      SystemPriorities.render
    );
    this._engine.addSystem(
      new ClickableSpriteSystem(this._entityContainer as Container),
      SystemPriorities.render
    );
    this._engine.addSystem(
      new TooltipSystem(this._tooltipContainer),
      SystemPriorities.render
    );
    this._engine.addSystem(
      new BubbleSystem(this._bubbleContainer, this.entityFactory),
      SystemPriorities.render
    );

    this._engine.addSystem(
      new ViewportSystem(
        this._app,
        this._viewport as Viewport,
        this.entityFactory
      ),
      SystemPriorities.render
    );
    this._engine.addSystem(
      new ViewportBackgroundSystem(this._viewport as Viewport, this._app),
      SystemPriorities.render
    );
    this._engine.addSystem(
      new FirebarrelSystem(this.entityFactory),
      SystemPriorities.render
    );
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  private initMap(config: any) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const objectsLayer = config.layers.find((o: any) => o.name === "objects");

    if (objectsLayer) {
      const objectsData = objectsLayer.objects;

      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      objectsData.forEach((objectData: any) => {
        const width = objectData.width ?? 0;
        const height = objectData.height ?? 0;
        const x = objectData.x + width / 2 ?? 0;
        const y = objectData.y + height / 2 ?? 0;

        const props = objectData.properties;

        if (props) {
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          const typeProp = props.find((o: any) => o.name === "type");

          if (typeProp) {
            const type = typeProp.value;

            switch (type) {
              case "sound": {
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                const soundProp = props.find((o: any) => o.name === "sound");

                if (soundProp) {
                  const sound = soundProp.value;

                  if (sounds.hasOwnProperty(sound)) {
                    this.entityFactory?.createSoundEmitter(
                      x,
                      y,
                      Math.min(width, height),
                      // eslint-disable-next-line @typescript-eslint/no-explicit-any
                      (sounds as any)[sound]
                    );
                  }
                }
              }
            }
          }
        }
      });
    }
  }

  private initEntities(): Promise<void> {
    if (!this.entityFactory) {
      return Promise.reject();
    }

    return (
      new TimeoutCommand(1000)
        .execute()
        .then(() => {
          if (this.entityFactory) {
            GameInstance.instance.dataProvider.firebarrelsData.forEach(
              (firebarrel) => {
                this.entityFactory?.createFireBarrel(firebarrel);
              }
            );
          }
        })
        .then(() => {
          if (this.entityFactory) {
            const map: PlaygroundMap = GameInstance.instance.getConfig()
              .playgroundMap;
            const bots = GameInstance.instance.getState().users;
            const itrb: IterableIterator<ReplicatedUser> = bots.values();
            const self: MapContainer = this;
            const loop = async () => {
              for (
                let bot: ReplicatedUser = itrb.next().value;
                bot;
                bot = itrb.next().value
              ) {
                await new Promise((resolve) => {
                  const point: Point = map.getRandomPointOnThePlayground();
                  bot.x = point.x;
                  bot.y = point.y;
                  self.entityFactory?.createBot(bot);
                  setTimeout(() => {
                    resolve(true);
                  }, 30);
                });
              }
            };
            loop();
          }
          return Promise.resolve();
        })
        .then(() => {
          if (this.entityFactory) {
            const venue = GameInstance.instance.dataProvider.venuesData;
            venue.forEach((venue) => {
              this.entityFactory?.createVenue(venue);
            });
          }
          return Promise.resolve();
        })
        .then(() => {
          return new TimeoutCommand(1000).execute();
        })
        // .then(() => {
        //   if (this.entityFactory) {
        //     const config = GameInstance.instance.getConfig();
        //     if (playerModel.x < 0 || playerModel.x > config.worldWidth) {
        //       playerModel.x = config.worldWidth / 2;
        //     }
        //     if (playerModel.y < 0 || playerModel.y > config.worldHeight) {
        //       playerModel.y = config.worldHeight / 2;
        //     }
        //     this.entityFactory.createPlayer(playerModel);
        //   }
        // })
        .then(() => {
          return new TimeoutCommand(1000).execute();
        })
        .then(() => {
          if (this.entityFactory) {
            const self: MapContainer = this;
            const artcars = stubArtcarsData();
            const loop = async () => {
              for (let i = 0; i < artcars.length; i++) {
                await new Promise((resolve) => {
                  self.entityFactory?.createArtcar(artcars[i]);
                  setTimeout(() => {
                    resolve(true);
                  }, 30);
                });
              }
            };
            loop();
          }
        })
    );
  }

  public resize(width: number, height: number) {
    if (this._viewport) {
      this._viewport.resize(width, height);
    }
    if (this._joystickContainer) {
      this._joystickContainer.position.set(86, height - 86);
    }
  }

  public async release(): Promise<void> {
    this.releaseSystems();
    this.releaseLayers();
  }

  private releaseLayers() {}

  private releaseSystems() {
    this._engine?.removeAllEntities();
    this._engine?.removeAllSystems();
  }

  public update(dt: number) {
    this._engine?.update(dt);
  }
}