diff --git a/index.html b/index.html index a13526d..859bfcb 100644 --- a/index.html +++ b/index.html @@ -88,6 +88,6 @@
- + diff --git a/src/components/play/Board.js b/src/components/play/Board.js new file mode 100644 index 0000000..320aed0 --- /dev/null +++ b/src/components/play/Board.js @@ -0,0 +1,302 @@ +import React from 'react'; +import {observable} from "mobx"; +import {observer, inject} from 'mobx-react'; +import API from '../SpreadsheetData'; + +// array of array of accesible spaces per swift +const adjacency_6 = { + 1: [2, 3], + 2: [1, 3, 4, 5], + 3: [1, 2, 5, 6], + 4: [2, 5, 7, 8], + 5: [2, 3, 4, 6, 7, 8, 9], + 6: [3, 5, 8, 9], + 7: [4, 5, 8, 10], + 8: [4, 5, 6, 7, 9, 10, 11], + 9: [5, 6, 8, 11], + 10: [7, 8, 11, 12], + 11: [8, 9, 10, 12], + 12: [10, 11] +} + +@observer +export default class Board extends React.Component { + @observable spaces = []; + @observable phase = "none"; + @observable source = -1; + + constructor(props) { + super(props); + this.selectCard = this.selectCard.bind(this); + + this.spaces = Array(12).fill({ + 'creatures': [this.empty_card("creatures")], + 'battlegear': [this.empty_card("battlegear")], + 'mirage': [this.empty_card("mirage")] + }); + + // todo remove / currently simulating + this.submitChange({event: "phase", action: "movement"}); + this.loadcards(); + } + + // This is a wraper function for preforming changes to game state + // 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 could use "make change" + submitChange = (change) => { + this.props.submitChange(change); + this.makeChange(change); + } + + // TODO if the board state is changed externally + // {'event': 'action'} + makeChange = (change) => { + // seriously calling without a change? + if (!change) return; + console.log(change.event, change.action); + let action = change.action; + + // change the game's phase + if (change.event == "phase") { + // action: "string" + this.phase = action; + console.log(this.phase); + } + + // add a card to a space + else if (change.event == "add") { + // action: {space, type, card} + this.spaces[action.space][action.type] = action.card; + } + + // remove a specific card at a space + else if (change.event == "remove") { + // action: {space, type} + this.space[action.space][action.type] = this.empty_card(action.type); + } + + // this is a short hand for moving a creature and its battlegear + else if (change.event == "movement") { + // action: {src, dest} + let src = action.src; + let dest = action.dest; + this.spaces[dest].creatures = (this.spaces[src].creatures); + this.spaces[dest].battlegear = (this.spaces[src].battlegear); + this.spaces[src].creatures = [this.empty_card("creatures")]; + this.spaces[src].battlegear = [this.empty_card("battlegear")]; + } + } + + empty_card(type) { + let card = { + 'data': null, + 'selected': false, + 'selectable': true, + 'controlled': false + } + if (type == "creatures") { + card.moveable = true; + } + return card; + } + + // TODO + loadcards() { + this.spaces[5].creatures[0].data = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Maxxor", 'i')}}); + this.spaces[5].battlegear[0].data = API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Maxxor's Torch", 'i')}}); + this.spaces[2].creatures[0].data = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Staluk", 'i')}}); + this.spaces[2].battlegear[0].data = API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Vlaric Shard", 'i')}}); + for (let i = 0; i < 6; i++) { + this.spaces[i].creatures[0].controlled = true; + this.spaces[i].battlegear[0].controlled = true; + } + this.spaces[8].creatures[0].data = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Chaor", 'i')}}); + this.spaces[8].battlegear[0].data = API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Whepcrack", 'i')}}); + } + + resetCardSelection() { + this.spaces.forEach((space) => { + space.battlegear.forEach((card) => { + card.selectable = true; + card.selected = false; + }); + space.creatures.forEach((card) => { + card.selectable = true; + card.selected = false; + }); + }); + this.source = -1; + } + + // TODO + canAttack(id) { + return false; + } + + moveSelection(source, destination, movement) { + if (source == destination) return false; + + let src_card = this.spaces[source].creatures[0]; + if (!src_card.moveable) return false; // If already moved, no valid movement + + let des_card = this.spaces[destination].creatures[0]; + + let valid = adjacency_6[source]; + // for each level of swift, increase moveable range + // TODO account for range + for (let i = 0; i < movement; i++) { + if (valid.includes(destination)) { + // Check if space occupied + if (des_card.data != null) { + // Can't move into occupied space of own tribe + if (des_card.controlled) return false; + // Check if can start combat + else return this.canAttack(); + } + return true; + } + valid.forEach((i) => { + valid = [].concat(valid, adjacency_6[i]); + }); + } + + return false; // if all else its probably not moveable + } + + selectCard(e) { + let id = e.target.id.substr(1); + let type = (() => { + switch (e.target.id.charAt(0)) { + case 'b': return 'battlegear'; + case 'c': return 'creatures'; + default: return ""; + } + })(); + + // Reset selection + if (this.spaces[id][type][0].selectable == false) { + return this.resetCardSelection(); + } + + if (this.phase == "movement" && type == "creatures") { + // TODO combat + // TODO if self selection, check activated ability + if (this.source == id) { + return this.resetCardSelection(); + } + // moving into a space + if (this.source > 0) { + this.submitChange({event: "movement", action: {src: this.source, dest: id}}); + this.spaces[id].moveable = false; + return this.resetCardSelection(); + } + + // don't select a blank space + if (this.spaces[id].creatures[0].data == null) return; + + // select a card + this.source = id; + + let mv = ((data) => { + let swift = (new RegExp(/swift ([0-9]+)/gi).exec(data.gsx$ability)); + if (swift) return parseInt(swift[1]) + 1; + return 1; + })(this.spaces[id].creatures[0].data); + + // set selectable options + this.spaces.forEach((space, i) => { + space.battlegear.forEach((card) => { + card.selectable = false; + }); + space.creatures.forEach((card) => { + card.selectable = this.moveSelection(id, i, mv); + }); + }); + + // creature is now selected + this.spaces[id].creatures[0].selected = true; + this.spaces[id].creatures[0].selectable = false; + } + } + + render() { + return( +
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ ); + } +} + + +@inject((stores, props, context) => props) @observer +class Space extends React.Component { + + constructor(props) { + super(props); + } + + render() { + if (this.props.cards[0].data) { + return ( + + ); + } + else { + return ( + + ); + } + } +} diff --git a/src/components/play/index.js b/src/components/play/index.js index 353b9bf..fa2451f 100644 --- a/src/components/play/index.js +++ b/src/components/play/index.js @@ -3,6 +3,7 @@ import {observable} from "mobx"; import {observer, inject} from 'mobx-react'; import API from '../SpreadsheetData'; import {Loading} from '../Snippets'; +import Board from './Board.js' import '../../scss/play.scss'; @inject((stores, props, context) => props) @observer @@ -17,6 +18,10 @@ export default class Play extends React.Component { } + componentDidUpdate() { + this.makeChange(); + } + render() { if (this.loaded == false) { API.LoadDB([{'cards': 'attacks'}, {'cards': 'battlegear'}, {'cards': 'creatures'}, {'cards': 'locations'}, {'cards': 'mugic'}]) @@ -24,192 +29,13 @@ export default class Play extends React.Component { return (); } - return (); - } -} - -@inject((stores, props, context) => props) @observer -class Board extends React.Component { - @observable spaces = []; - @observable phase = "reveal_location"; - @observable source = -1; - - constructor(props) { - super(props); - this.selectCard = this.selectCard.bind(this); - - this.spaces = Array(12).fill({ - 'creatures': [this.empty_card()], - 'battlegear': [this.empty_card()], - 'mirage': [this.empty_card()] - }); - - this.loadcards(); - // TODO - this.phase = "move_creature"; - } - - empty_card() { - return { - 'card': null, - 'selected': false, - 'selectable': true - } - } - - // TODO - loadcards() { - this.spaces[5].creatures[0].card = API.cards.creatures.findOne({'gsx$name': {'$regex': new RegExp("Maxxor", 'i')}}); - this.spaces[5].battlegear[0].card = API.cards.battlegear.findOne({'gsx$name': {'$regex': new RegExp("Maxxor's Torch", 'i')}}); - } - - // TODO - checkrange() { - return 1; - } - - // TODO - selectCard(e) { - let id = e.target.id.substr(1); - let type = (() => { - switch (e.target.id.charAt(0)) { - case 'b': return 'battlegear'; - case 'c': return 'creatures'; - default: return ""; - } - })(); - - const resetSelection = () => { - this.spaces.forEach((space, i) => { - space.battlegear.forEach((card) => { - card.selectable = true; - card.selected = false; - }); - space.creatures.forEach((card) => { - card.selectable = true; - card.selected = false; - }); - }); - this.source = -1; - } - - // Reset selection - if (this.spaces[id][type][0].selectable == false) { - return resetSelection(); - } - - console.log(this.spaces[id][type][0]); - - if (this.phase == "move_creature" && type == "creatures") { - // TODO combat - if (this.source >= 0 && this.source != id) { - this.spaces[id].creatures = (this.spaces[this.source].creatures); - this.spaces[id].battlegear = (this.spaces[this.source].battlegear); - this.spaces[this.source].creatures = [this.empty_card()]; - this.spaces[this.source].battlegear = [this.empty_card()]; - return resetSelection(); - } - - if (this.spaces[id].creatures[0].card == null) { - return; - } - - // else select a card - this.source = id; - - this.spaces.forEach((space, i) => { - space.battlegear.forEach((card) => { - card.selectable = false; - }); - if (i !== id) { - // TODO check swift / check ocuppied - space.creatures.forEach((card) => { - card.selectable = true; - }); - } - }); - - this.spaces[id].creatures[0].selected = true; - this.spaces[id].creatures[0].selectable = false; - } - } - - render() { - return( + return (
-
-
- - -
-
-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
+ {if (n) this.makeChange = n.makeChange}} + />
); } } - - -@inject((stores, props, context) => props) @observer -class Space extends React.Component { - - constructor(props) { - super(props); - } - - render() { - if (this.props.cards[0].card) { - return ( - - ); - } - else { - return ( - - ); - } - } -} diff --git a/src/scss/play.scss b/src/scss/play.scss index 1a1fc93..19cb334 100644 --- a/src/scss/play.scss +++ b/src/scss/play.scss @@ -14,10 +14,6 @@ height: #{$card_height}; border: 1px solid #36a8ff; - img:hover, img.selected { - - } - img.selectable:hover, img.selected { // border: 1px solid #f41111; box-shadow: 5px 5px 5px red, -5px 5px 5px red, 5px -5px 5px red, -5px -5px 5px red; @@ -30,6 +26,9 @@ width: #{$card_height}; height: #{$card_width}; transform: rotate(90deg); + &.opposing { + transform: rotate(270deg); + } } :first-child { @@ -52,9 +51,15 @@ left: calc(#{$margin_offset+px} + 5px); :first-child { margin: -87px 25px; + &.opposing { + margin: -87px 45px; + } } :nth-child(2) { margin: -25px 45px; + &.opposing { + margin: -25px 25px; + } } } } @@ -64,9 +69,15 @@ left: calc(#{$margin_offset+px} + #{$card_width} + 25px + 10px); :first-child { margin: -77px 25px; + &.opposing { + margin: -77px 45px; + } } :nth-child(2) { margin: -97px 45px; + &.opposing { + margin: -97px 25px; + } } } :first-child { @@ -82,9 +93,15 @@ left: calc(#{$margin_offset+px} + (#{$card_width} + 25px + 10px)*2); :first-child { margin: -67px 25px; + &.opposing { + margin: -67px 45px; + } } :nth-child(2) { margin: -87px 45px; + &.opposing { + margin: -87px 25px; + } } } :first-child { @@ -103,9 +120,15 @@ left: calc(#{$margin_offset+px} + (#{$card_width} + 25px + 10px)*3); :first-child { margin: -67px 45px; + &.controlled { + margin: -67px 25px; + } } :nth-child(2) { margin: -87px 25px; + &.controlled { + margin: -87px 45px; + } } } :first-child {