mirror of
https://github.com/chaoticbackup/chaoticbackup.github.io.git
synced 2026-08-21 08:04:18 -05:00
[add] client connections, lobby
This commit is contained in:
@@ -4,7 +4,7 @@ import {observable} from "mobx";
|
||||
import {observer, inject} from 'mobx-react';
|
||||
import API from '../SpreadsheetData';
|
||||
import Movement from './rules/movement';
|
||||
import {Phase} from './rules/turn';
|
||||
import Phase from './rules/phase';
|
||||
import '../../scss/play/battleboard.scss';
|
||||
|
||||
@observer
|
||||
@@ -16,31 +16,57 @@ export default class Board extends Base {
|
||||
super(props);
|
||||
this.selectCard = this.selectCard.bind(this);
|
||||
|
||||
// Movement Rule
|
||||
this.movement = new Movement();
|
||||
|
||||
// Create board
|
||||
this.spaces = Array(12).fill({
|
||||
'creatures': [this.empty_card("creatures")],
|
||||
'battlegear': [this.empty_card("battlegear")],
|
||||
'mirage': [this.empty_card("mirage")]
|
||||
});
|
||||
|
||||
this.loadcards();
|
||||
}
|
||||
|
||||
// TODO
|
||||
loadcards() {
|
||||
this.spaces[4].creatures[0].data = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Maxxor", 'i')}});
|
||||
this.spaces[4].battlegear[0].data = API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Maxxor's Torch", 'i')}});
|
||||
this.spaces[1].creatures[0].data = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Staluk", 'i')}});
|
||||
this.spaces[1].battlegear[0].data = API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Vlaric Shard", 'i')}});
|
||||
this.spaces[0].creatures[0].data = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Vidav", 'i')}});
|
||||
this.spaces[2].creatures[0].data = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Dractyl", 'i')}});
|
||||
for (let i = 0; i < 6; i++) {
|
||||
this.spaces[i].creatures[0].controlled = true;
|
||||
this.spaces[i].battlegear[0].controlled = true;
|
||||
}
|
||||
this.spaces[7].creatures[0].data = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Chaor", 'i')}});
|
||||
this.spaces[7].battlegear[0].data = API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Whepcrack", 'i')}});
|
||||
|
||||
setTimeout(() => this.loadgame(), 500); // TODO
|
||||
}
|
||||
|
||||
// todo remove / currently simulating
|
||||
loadgame() {
|
||||
if (this.props.player == 0 || this.props.player == 1) {
|
||||
this.submitChange({event: "load", action: {space: 0, type: "creatures",
|
||||
data: API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Vidav", 'i')}})
|
||||
}});
|
||||
this.submitChange({event: "load", action: {space: 1, type: "creatures",
|
||||
data: API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Staluk", 'i')}})
|
||||
}});
|
||||
this.submitChange({event: "load", action: {space: 1, type: "battlegear",
|
||||
data: API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Vlaric Shard", 'i')}})
|
||||
}});
|
||||
this.submitChange({event: "load", action: {space: 2, type: "creatures",
|
||||
data: API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Dractyl", 'i')}})
|
||||
}});
|
||||
this.submitChange({event: "load", action: {space: 4, type: "creatures",
|
||||
data: API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Maxxor", 'i')}})
|
||||
}});
|
||||
this.submitChange({event: "load", action: {space: 4, type: "battlegear",
|
||||
data: API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Maxxor's Torch", 'i')}})
|
||||
}});
|
||||
|
||||
this.makeChange({event:"active", action: true});
|
||||
this.submitChange({event: "phase", action: "movement"}, "turn");
|
||||
}
|
||||
else {
|
||||
this.submitChange({event: "load", action: {space: 4, type: "creatures",
|
||||
data: API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Chaor", 'i')}})
|
||||
}});
|
||||
this.submitChange({event: "load", action: {space: 4, type: "battlegear",
|
||||
data: API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Whepcrack", 'i')}})
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
empty_card(type) {
|
||||
@@ -58,9 +84,8 @@ export default class Board extends Base {
|
||||
|
||||
// {'event': 'action'}
|
||||
makeChange = (change) => {
|
||||
// seriously calling without a change?
|
||||
if (!change) return;
|
||||
console.log("board ->", change); // TODO remove
|
||||
console.log(change);
|
||||
|
||||
let action = change.action;
|
||||
switch (change.event) {
|
||||
@@ -90,6 +115,15 @@ export default class Board extends Base {
|
||||
break;
|
||||
}
|
||||
|
||||
// loads the card data into a blank space
|
||||
// used when initial card loading,
|
||||
// or when bringing back cards from discard
|
||||
case "load": {
|
||||
// action: {space, type, data}
|
||||
this.spaces[action.space][action.type][0].data = action.data;
|
||||
break;
|
||||
}
|
||||
|
||||
// add a card to a space
|
||||
case "add": {
|
||||
// action: {space, type, card}
|
||||
@@ -146,17 +180,13 @@ export default class Board extends Base {
|
||||
this.movement.source = (-1);
|
||||
}
|
||||
|
||||
// TODO
|
||||
canAttack(id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
selectCard(e) {
|
||||
let id = parseInt(e.target.id.substr(1));
|
||||
let type = (() => {
|
||||
switch (e.target.id.charAt(0)) {
|
||||
case 'b': return 'battlegear';
|
||||
case 'c': return 'creatures';
|
||||
case 'm': return 'mirage';
|
||||
default: return "";
|
||||
}
|
||||
})();
|
||||
@@ -167,54 +197,21 @@ export default class Board extends Base {
|
||||
}
|
||||
|
||||
if (Phase.phase == "movement" && type == "creatures") {
|
||||
// TODO combat
|
||||
// TODO if self selection, check activated ability
|
||||
if (this.movement.source == id) {
|
||||
return this.resetCardSelection();
|
||||
}
|
||||
// moving into a space
|
||||
if (this.movement.source > 0) {
|
||||
// TODO combat
|
||||
this.submitChange({event: "movement", action: {src: this.movement.source, dest: id}});
|
||||
// Only one move per turn
|
||||
this.spaces[id].creatures[0].moveable = false;
|
||||
return this.resetCardSelection();
|
||||
this.resetCardSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
// don't select a blank space
|
||||
if (this.spaces[id].creatures[0].data == null) return;
|
||||
|
||||
// can't select opposing creatures
|
||||
if (this.spaces[id].creatures[0].controlled == false) return;
|
||||
|
||||
// select a card
|
||||
let src_card = this.spaces[id].creatures[0];
|
||||
this.movement.source = id;
|
||||
|
||||
let {valid, attackable} = this.movement.moves(this.spaces, id);
|
||||
|
||||
// set selectable options
|
||||
this.spaces.forEach((space, i) => {
|
||||
space.battlegear.forEach((card) => {
|
||||
card.selectable = false;
|
||||
});
|
||||
let des_card = space.creatures[0];
|
||||
des_card.selectable = ((src_id, dest_id) => {
|
||||
// If already moved, no valid movement
|
||||
if (!src_card.moveable) return false;
|
||||
// No self space movement
|
||||
else if (src_id == dest_id) return false;
|
||||
// If its a valid spot
|
||||
else if (valid.includes(dest_id)) return true;
|
||||
// TODO different effect for attackable?
|
||||
else if (attackable.includes(dest_id) && this.canAttack()) return true;
|
||||
// if all else its probably not moveable
|
||||
else return false;
|
||||
})(id, i);
|
||||
});
|
||||
|
||||
// creature is now selected
|
||||
this.spaces[id].creatures[0].selected = true;
|
||||
this.spaces[id].creatures[0].selectable = false;
|
||||
this.spaces = this.movement.moves(this.spaces, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class Gui extends Base {
|
||||
}
|
||||
|
||||
endTurn() {
|
||||
this.props.submitChange({event: "active", action: true}, "board");
|
||||
this.submitChange({event: "active", action: true}, "board");
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -25,7 +25,7 @@ export default class Gui extends Base {
|
||||
<div className="location"><img src="https://drive.google.com/uc?id=0B6oyUfwoM3u1SHJ5ejJPTk85THM" /></div>
|
||||
</div>
|
||||
<div className="hand">
|
||||
<span className="card_name">Hand</span>
|
||||
<span className="card_name">Player {this.props.player} Hand</span>
|
||||
<div className="attacks">
|
||||
<div></div>
|
||||
<div></div>
|
||||
|
||||
41
src/components/play/Lobby.js
Normal file
41
src/components/play/Lobby.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import Base from './_base';
|
||||
import {observable} from "mobx";
|
||||
import {observer, inject} from 'mobx-react';
|
||||
|
||||
|
||||
@observer
|
||||
export default class Board extends Base {
|
||||
@observable o_id = "";
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
single() {
|
||||
this.props.submitChange({event: "single", action: "load"}, "game");
|
||||
}
|
||||
|
||||
connect(e) {
|
||||
if (this.o_id == this.props.uid) {
|
||||
return; // TODO
|
||||
}
|
||||
this.props.submitChange({event: "connect", action: this.o_id}, "game");
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
<div className="lobby">
|
||||
<span>You're id is {this.props.uid}</span>
|
||||
<br /><br />
|
||||
<form>
|
||||
<input type="text" name="opponent's id" value={this.o_id} onChange={(e) => this.o_id = e.target.value} />
|
||||
<input type="button" value="Connect" onClick={this.connect.bind(this)} />
|
||||
</form>
|
||||
<br /><br />
|
||||
<button onClick={this.single.bind(this)}>Single Player</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
39
src/components/play/Turn.js
Normal file
39
src/components/play/Turn.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import Base from './_base';
|
||||
import {observable} from "mobx";
|
||||
import {observer, inject} from 'mobx-react';
|
||||
import Phase from './rules/phase';
|
||||
|
||||
@observer
|
||||
export default class Turn extends Base {
|
||||
activeplayer = 0;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
// {'event': 'action'}
|
||||
makeChange = (change) => {
|
||||
if (!change) return;
|
||||
console.log('turn ->', change);
|
||||
// TODO
|
||||
let action = change.action;
|
||||
switch (change.event) {
|
||||
|
||||
// change the game's phase
|
||||
case "phase": {
|
||||
// action: "string"
|
||||
Phase.phase = action;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// class doesn't have gui
|
||||
return(<div />);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,9 +10,9 @@ export default class Base extends React.Component {
|
||||
// It preforms the change locally and propegates it higher
|
||||
// So that a network listener can reflect the change on the other client
|
||||
// Local changes only can simply use "makeChange"
|
||||
submitChange = (change) => {
|
||||
this.props.submitChange(change);
|
||||
this.makeChange(change);
|
||||
submitChange = (change, destination) => {
|
||||
this.props.submitChange(change, destination);
|
||||
if (!destination) this.makeChange(change); // self change
|
||||
}
|
||||
|
||||
makeChange = (change) => {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
import {observable} from "mobx";
|
||||
import {observable, reaction} from "mobx";
|
||||
import {observer, inject} from 'mobx-react';
|
||||
import Peer from 'peerjs';
|
||||
import API from '../SpreadsheetData';
|
||||
import {Loading} from '../Snippets';
|
||||
import Board from './Board';
|
||||
import Gui from './Gui';
|
||||
import {Turn} from './rules/turn';
|
||||
import Lobby from './Lobby';
|
||||
import Turn from './Turn';
|
||||
|
||||
import '../../scss/play/play.scss';
|
||||
import '../../scss/play/gui.scss';
|
||||
@@ -13,31 +15,62 @@ import '../../scss/play/gui.scss';
|
||||
@inject((stores, props, context) => props) @observer
|
||||
export default class Play extends React.Component {
|
||||
@observable loaded = false;
|
||||
@observable id = "";
|
||||
@observable player = -1;
|
||||
@observable format = 6;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const turn = new Turn(this.receiveTurnChange.bind(this));
|
||||
this.makeTurnChange = turn.makeChange;
|
||||
this.setupNetworking();
|
||||
|
||||
// TODO, temperary
|
||||
// const reaction1 = reaction(
|
||||
// () => this.player,
|
||||
// (player, reaction) => {
|
||||
// console.log(player);
|
||||
// this.loadgame();
|
||||
// reaction.dispose();
|
||||
// },
|
||||
// {delay: 500} // wait for component to load
|
||||
// );
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// todo remove / currently simulating
|
||||
this.makeBoardChange({event:"active", action: true});
|
||||
this.makeTurnChange({event: "phase", action: "movement"});
|
||||
setupNetworking() {
|
||||
let peer = new Peer();
|
||||
|
||||
peer.on('open', (id) => {
|
||||
this.id = id;
|
||||
});
|
||||
|
||||
peer.on('connection', (conn) => {
|
||||
if (this.conn) return;
|
||||
this.conn = conn;
|
||||
this.conn.on("open", () => {
|
||||
this.player = 2; // TODO properly assign player
|
||||
this.conn.on('data', this.receiveNetworkChange.bind(this));
|
||||
});
|
||||
});
|
||||
|
||||
this.peer = peer;
|
||||
}
|
||||
|
||||
sendNetworkChange(change, destination) {
|
||||
// TODO implement network change
|
||||
console.log("network ->", destination, change);
|
||||
// If in single player mode, ignore network sends
|
||||
if (this.player < 1) return;
|
||||
// console.log("->", destination, change);
|
||||
this.conn.send({change: change, destination: destination});
|
||||
}
|
||||
|
||||
// wrapper function
|
||||
receiveNetworkChange(change, destination) {
|
||||
this.recieveChange(change, destination);
|
||||
receiveNetworkChange(packet) {
|
||||
console.log("<-", packet.destination, packet.change);
|
||||
this.recieveChange(packet.change, packet.destination);
|
||||
}
|
||||
|
||||
// Parent function to handle any intercomponent or remote commands
|
||||
// send the command to that destination
|
||||
// When receiving a change that does not include a destination,
|
||||
// it is assumed that the component has handled its internal state
|
||||
recieveChange(change, destination) {
|
||||
switch(destination) {
|
||||
case "board":
|
||||
@@ -49,6 +82,12 @@ export default class Play extends React.Component {
|
||||
case "turn":
|
||||
this.makeTurnChange(change);
|
||||
break;
|
||||
case "lobby":
|
||||
this.makeLobbyChange(change);
|
||||
break;
|
||||
case "game":
|
||||
this.makeChange(change);
|
||||
break;
|
||||
default:
|
||||
throw "invalid destination";
|
||||
}
|
||||
@@ -59,15 +98,51 @@ export default class Play extends React.Component {
|
||||
this.sendNetworkChange(change, destination || "gui");
|
||||
}
|
||||
|
||||
// When handling movement, flip the card's owner before sending network
|
||||
// Flip the board's numbers
|
||||
receiveBoardChange(change, destination) {
|
||||
if (destination) this.recieveChange(change, destination);
|
||||
this.sendNetworkChange(change, destination || "board");
|
||||
let ch = JSON.parse(JSON.stringify(change)); // make "copy"
|
||||
// When handling movement, flip the board's numbers before sending network
|
||||
if (typeof(ch.action) === 'object') {
|
||||
if (ch.action.hasOwnProperty('space')) {
|
||||
ch.action.space = (this.format * 2 - ch.action.space) - 1;
|
||||
}
|
||||
else if (ch.action.hasOwnProperty('src')) {
|
||||
ch.action.src = (this.format * 2 - ch.action.src) - 1;
|
||||
ch.action.dest = (this.format * 2 - ch.action.dest) - 1;
|
||||
}
|
||||
}
|
||||
this.sendNetworkChange(ch, destination || "board");
|
||||
}
|
||||
|
||||
receiveTurnChange(change, destination) {
|
||||
if (destination) this.recieveChange(change, destination);
|
||||
this.sendNetworkChange(change, destination || "turn");
|
||||
}
|
||||
|
||||
recieveLobbyChange(change, destination) {
|
||||
this.recieveChange(change, destination || "lobby");
|
||||
}
|
||||
|
||||
// for switching between lobby and games
|
||||
makeChange(change) {
|
||||
if (!change) return;
|
||||
let action = change.action;
|
||||
switch (change.event) {
|
||||
case "single": {
|
||||
this.player = 0;
|
||||
break;
|
||||
}
|
||||
case "connect": {
|
||||
this.conn = this.peer.connect(change.action);
|
||||
this.conn.on("open", () => {
|
||||
this.player = 1;
|
||||
this.conn.on("data", this.receiveNetworkChange.bind(this));
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -77,15 +152,32 @@ export default class Play extends React.Component {
|
||||
return (<Loading />);
|
||||
}
|
||||
|
||||
if (this.player < 0)
|
||||
return (
|
||||
<div className="play">
|
||||
<Lobby
|
||||
submitChange={this.recieveLobbyChange.bind(this)}
|
||||
ref={n => {if (n) this.makeLobbyChange = n.makeChange}}
|
||||
uid={this.id}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
else
|
||||
return (
|
||||
<div className="play">
|
||||
<Turn
|
||||
submitChange={this.receiveTurnChange.bind(this)}
|
||||
ref={n => {if (n) this.makeTurnChange = n.makeChange}}
|
||||
/>
|
||||
<Gui
|
||||
submitChange={this.recieveGuiChange.bind(this)}
|
||||
ref={n => {if (n) this.makeGuiChange = n.makeChange}}
|
||||
player={this.player}
|
||||
/>
|
||||
<Board
|
||||
submitChange={this.receiveBoardChange.bind(this)}
|
||||
ref={n => {if (n) this.makeBoardChange = n.makeChange}}
|
||||
player={this.player}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,22 @@ export default class Movement extends Rule {
|
||||
super();
|
||||
}
|
||||
|
||||
// TODO
|
||||
canAttack(id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
moves(spaces, id) {
|
||||
// don't select a blank space
|
||||
if (spaces[id].creatures[0].data == null) return spaces;
|
||||
|
||||
// can't select opposing creatures
|
||||
if (spaces[id].creatures[0].controlled == false) return spaces;
|
||||
|
||||
// select a card
|
||||
let src_card = spaces[id].creatures[0];
|
||||
this.source = id;
|
||||
|
||||
// Calculate range of spaces based on swift
|
||||
let swift = ((data) => {
|
||||
let s = (new RegExp(/swift ([0-9]+)/gi).exec(data.gsx$ability));
|
||||
@@ -78,6 +93,30 @@ export default class Movement extends Rule {
|
||||
// remove invalid spaces
|
||||
valid = valid.filter(x => !invalid.includes(x));
|
||||
|
||||
return {valid: valid, attackable: attackable};
|
||||
// set selectable options
|
||||
spaces.forEach((space, i) => {
|
||||
space.battlegear.forEach((card) => {
|
||||
card.selectable = false;
|
||||
});
|
||||
let des_card = space.creatures[0];
|
||||
des_card.selectable = ((src_id, dest_id) => {
|
||||
// If already moved, no valid movement
|
||||
if (!src_card.moveable) return false;
|
||||
// No self space movement
|
||||
else if (src_id == dest_id) return false;
|
||||
// If its a valid spot
|
||||
else if (valid.includes(dest_id)) return true;
|
||||
// TODO different effect for attackable?
|
||||
else if (attackable.includes(dest_id) && this.canAttack()) return true;
|
||||
// if all else its probably not moveable
|
||||
else return false;
|
||||
})(id, i);
|
||||
});
|
||||
|
||||
// creature is now selected
|
||||
spaces[id].creatures[0].selected = true;
|
||||
spaces[id].creatures[0].selectable = false;
|
||||
|
||||
return spaces;
|
||||
}
|
||||
}
|
||||
|
||||
18
src/components/play/rules/phase.js
Normal file
18
src/components/play/rules/phase.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import Rule from './_rule';
|
||||
|
||||
class Phase_Singleton extends Rule {
|
||||
phase = "none";
|
||||
|
||||
// Singleton
|
||||
static getInstance() {
|
||||
if (!this.instance) { this.instance = new Phase_Singleton(); }
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.phase = "game start";
|
||||
}
|
||||
|
||||
}
|
||||
export default Phase_Singleton.getInstance();
|
||||
@@ -1,48 +0,0 @@
|
||||
import Rule from './_rule';
|
||||
|
||||
export class Turn extends Rule {
|
||||
activeplayer = 0;
|
||||
|
||||
constructor(receiveTurnChange, player1, player2) {
|
||||
super();
|
||||
this.submitChange = receiveTurnChange;
|
||||
}
|
||||
|
||||
// {'event': 'action'}
|
||||
makeChange = (change) => {
|
||||
if (!change) return;
|
||||
console.log('turn ->', change);
|
||||
// TODO
|
||||
let action = change.action;
|
||||
switch (change.event) {
|
||||
|
||||
// change the game's phase
|
||||
case "phase": {
|
||||
// action: "string"
|
||||
Phase.phase = action;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Phase_Singleton extends Rule {
|
||||
phase = "none";
|
||||
|
||||
// Singleton
|
||||
static getInstance() {
|
||||
if (!this.instance) { this.instance = new Phase_Singleton(); }
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.phase = "game start";
|
||||
}
|
||||
|
||||
}
|
||||
const Phase = Phase_Singleton.getInstance();
|
||||
export {Phase};
|
||||
1
src/scss/play/lobby.scss
Normal file
1
src/scss/play/lobby.scss
Normal file
@@ -0,0 +1 @@
|
||||
@import 'shared';
|
||||
Reference in New Issue
Block a user