class Game{ //Absolute Main class for everything that is happening in pong //Contains everything that appears in the gameplay //Update method applies frame updates to every entity and/or process //Settings settings: Settings.Game //Entities players: Player[] balls: Ball[] //Items boosts: Boost[] wormholes: Wormhole[] newBalls: NewBall[] //Gameplay finished: boolean paused: boolean constructor(settings: Settings.Game){ this.settings = settings } init(lobby?: Lobby): void{ this.players = this.createPlayers(['0', '1']) this.balls = this.createBalls('0') this.boosts = [] this.wormholes = [] this.newBalls = [] this.play() } createPlayer(side: Direction, id: string, otherCount: number): Player{ return new Player(side, id, this.settings.player, otherCount) } createPlayers(ids: string[]): Player[]{ switch (ids.length){ case 2: return [ this.createPlayer(Direction.Left, ids[0], 2), this.createPlayer(Direction.Right, ids[1], 2) ] case 3: return [ this.createPlayer(Direction.Left, ids[0], 3), this.createPlayer(Direction.Right, ids[1], 3), this.createPlayer(Direction.Top, ids[2], 3) ] case 4: return [ this.createPlayer(Direction.Left, ids[0], 4), this.createPlayer(Direction.Right, ids[1], 4), this.createPlayer(Direction.Top, ids[2], 4), this.createPlayer(Direction.Bottom, ids[3], 4) ] } } createBall(id: string): Ball{ return new Ball(this.settings.ball, id) } createBalls(...ids: string[]): Ball[]{ let balls = [] for (let id of ids) balls.push(this.createBall(id)) return balls } finish(): void{ this.pause() this.finished = true } restart(): void{ this.finished = false this.init() } play(): void{ if (this.finished) this.restart() this.paused = false $('#toggle-play').addClass('paused') } pause(): void{ if (this.finished) return this.paused = true $('#toggle-play').removeClass('paused') } togglePlay(fromClient?: boolean, fromInitiator?: boolean): void{ $('#toggle-play').blur() if (this.paused){ this.play() } else { this.pause() } } update(online?: boolean): void{ if (this.paused) return for (let p of this.players) p.update(online) for (let b of this.balls) b.update(this.balls, this.players, this.boosts, this.wormholes, this.newBalls) for (let b of this.boosts) b.update() for (let b of this.wormholes) b.update() for (let n of this.newBalls) n.update() this.tryCreateItem(Boost, this.settings.item.boost, this.boosts) this.tryCreateItem(Wormhole, this.settings.item.wormhole, this.wormholes) this.tryCreateItem(NewBall, this.settings.item.newBall, this.newBalls) for (let p of this.players) if (p.hasLost) this.finish() } tryCreateItem(Type: typeof Item, settings: Settings.Item, items: Item[]){ if (ranBool(p.int(p.frameRate()) * settings.spawnTime)){ items.push(Item.fromNew(this.players, settings, Type)) } } display(): void{ this.deadZone() for (let p of this.players) p.show() for (let b of this.balls) b.show() for (let b of this.boosts) b.show() for (let w of this.wormholes) w.show() for (let n of this.newBalls) n.show() } deadZone(): void{ p.noFill() p.stroke(255, 0, 0) p.strokeWeight(3) for (let pl of this.players){ p.beginShape() if (pl.isLeft || pl.isRight){ for (let y = pl.boxTopSide; y < pl.boxBottomSide; y++){ let x = p.map(p.noise(p.random(p.frameCount)), 0, 1, -pl.width / 2, pl.width / 2) + pl.centerX p.vertex(x, y) } } if (pl.isTop || pl.isBottom){ for (let x = pl.boxLeftSide; x < pl.boxRightSide; x++){ let y = p.map(p.noise(p.random(p.frameCount)), 0, 1, -pl.height / 2, pl.height / 2) + pl.centerY p.vertex(x, y) } } p.endShape() } } } class OnlineGame extends Game{ //Main class for a game that is played via WebRTC Connection id: string lobby: Lobby socket: any p2p: any input: Serialized.Player.Input initiator: boolean packetManager: { count: number lastTime: number deltaTime: number time: number } constructor(socket: any, p2p: any, settings: Settings.Game){ super(settings) this.socket = socket this.p2p = p2p this.packetManager = { count: 0, lastTime: Date.now(), deltaTime: 0, time: 0 } } set data(data: Serialized.Game){ let pm = this.packetManager pm.count++ pm.deltaTime = Date.now() - pm.lastTime pm.lastTime = Date.now() pm.time += pm.deltaTime if (pm.time > 5000){ console.log(pm.count + ' Data packs received from leading Peer, last one: ', data) pm.count = 0 pm.time = 0 } //Function to apply new values to objects which can disappear or appear function applyValues(Type: typeof GameObject | typeof Player | typeof Ball | typeof Item, objects1: Serialized.GameObject[], objects2: GameObject[], settings: any): void{ for (let o1 of objects1){ let found = false for (let o2 of objects2){ if (o1.id === o2.id){ o2.applyValues(o1) found = true } } if (!found){ let newObject switch (Type){ case Boost: case Wormhole: newObject = Item.fromSerialized(o1, settings, Type) break case Ball: newObject = Ball.fromSerialized(o1, settings) break case NewBall: newObject = Item.fromSerialized(o1, settings, Type) break } objects2.push(newObject) } } for (let o2 of objects2){ let found = false for (let o1 of objects1){ if (o1.id === o2.id) found = true } if (!found){ let index = objects2.indexOf(o2) objects2.splice(index, 1) } } } applyValues(Player, data.players, this.players, this.settings.player) applyValues(Ball, data.balls, this.balls, this.settings.ball) applyValues(Boost, data.boosts, this.boosts, this.settings.item.boost) applyValues(Wormhole, data.wormholes, this.wormholes, this.settings.item.wormhole) applyValues(NewBall, data.newBalls, this.newBalls, this.settings.item.newBall) } init(lobby: Lobby): void{ this.input = {up: false, down: false, left: false, right: false} this.lobby = lobby this.id = this.lobby.id //The initiator runs the actual game //and sends the game to other peers this.initiator = (this.socket.id === this.lobby.leader.id) let ids = [] for (let p of this.lobby.clients) ids.push(p.id) this.players = this.createPlayers(ids) this.balls = this.createBalls(this.lobby.leader.id) this.boosts = [] this.wormholes = [] this.newBalls = [] } createPlayer(side: Direction, id: string, otherCount: number): Player{ return new Player(side, id, this.settings.player, otherCount, this.lobby) } setPlayerInput(player: any): void{ for (let p of this.players){ if (p.id === player.id){ p.input = player.input } } } sendInput(): void{ let player = { id: this.getThisPlayer().id, input: this.input, lobby: this.getThisPlayer().lobby } if (this.initiator){ this.setPlayerInput(player) } else { this.p2p.emit('player-input', player) } } getThisPlayer(): Player{ for (let p of this.players){ if (p.id === this.socket.id) return p } } togglePlay(fromClient: boolean, fromInitiator: boolean): void{ if (this.initiator){ super.togglePlay() this.emitAction('togglePlay') } else { if (fromInitiator){ super.togglePlay() } else { if (fromClient){ this.emitAction('togglePlay') } } } } update(): void{ if (this.initiator){ super.update(true) this.p2p.emit('game-data', this.serialized()) } } emitAction(action: string){ this.p2p.emit('game-action', { action: action, lobby: this.lobby.serialized(), fromInitiator: this.initiator }) } finish(): void{ super.finish() if (this.initiator) this.emitAction('finish') } restart(){ this.finished = false this.init(this.lobby) } serialized(): Serialized.Game{ let players: Serialized.Player[] = [], balls: Serialized.Ball[] = [], boosts: Serialized.Boost[] = [], wormholes: Serialized.Wormhole[] = [], newBalls: Serialized.NewBall[] = [] for (let b of this.balls) balls.push(b.serialized()) for (let p of this.players) players.push(p.serialized()) for (let b of this.boosts) boosts.push(b.serialized()) for (let w of this.wormholes) wormholes.push(w.serialized()) for (let n of this.newBalls) newBalls.push(n.serialized()) return { players: players, balls: balls, boosts: boosts, wormholes: wormholes, newBalls: newBalls, id: this.id } } }