Skip to content

Commit

Permalink
Refactor logics of the location
Browse files Browse the repository at this point in the history
  • Loading branch information
kjirou committed Jan 7, 2017
1 parent c255fdb commit 05f4224
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 93 deletions.
14 changes: 10 additions & 4 deletions src/actions/index.js
Expand Up @@ -2,7 +2,9 @@
const { ACTION_TYPES, BOARD_TYPES, PARAMETERS, STYLES } = require('../immutable/constants');
const { JOB_IDS } = require('../immutable/jobs');
const { computeTick, findOneSquareFromBoardsByPlacement } = require('../state-models/complex-apis');
const locationMethods = require('../state-models/location');
const { areSamePlacements, isPlacedOnBoard } = require('../state-models/placement');
const placementMethods = require('../state-models/placement');
const { findSquareByCoordinate, parseMapText } = require('../state-models/square-matrix');
const unitMethods = require('../state-models/unit');
const { createNewUnitCollectionState, findUnitsByPlacement } = require('../state-models/unit-collection');
Expand Down Expand Up @@ -193,22 +195,26 @@ const initializeApp = () => {
const allies = createNewUnitCollectionState().concat([
Object.assign(unitMethods.createNewAllyState(), {
jobId: JOB_IDS.FIGHTER,
placement: { boardType: BOARD_TYPES.SORTIE_BOARD, coordinate: [0, 0] },
placement: placementMethods.createNewPlacementState(BOARD_TYPES.SORTIE_BOARD, [0, 0]),
}),
Object.assign(unitMethods.createNewAllyState(), {
jobId: JOB_IDS.HEALER,
placement: { boardType: BOARD_TYPES.SORTIE_BOARD, coordinate: [0, 1] },
placement: placementMethods.createNewPlacementState(BOARD_TYPES.SORTIE_BOARD, [0, 1]),
}),
Object.assign(unitMethods.createNewAllyState(), {
jobId: JOB_IDS.MAGE,
placement: { boardType: BOARD_TYPES.SORTIE_BOARD, coordinate: [1, 3] },
placement: placementMethods.createNewPlacementState(BOARD_TYPES.SORTIE_BOARD, [1, 3]),
}),
]);

const enemies = createNewUnitCollectionState().concat([
Object.assign(unitMethods.createNewEnemyState(), {
jobId: JOB_IDS.FIGHTER,
destinations: [[0 * 48, 5 * 48], [7 * 48, 5 * 48], [7 * 48, 1 * 48]],
destinations: [
locationMethods.createNewLocationState(0 * 48, 5 * 48),
locationMethods.createNewLocationState(7 * 48, 5 * 48),
locationMethods.createNewLocationState(7 * 48, 1 * 48),
],
}),
]);

Expand Down
4 changes: 2 additions & 2 deletions src/components/SquareMatrix.js
Expand Up @@ -46,8 +46,8 @@ const SquareMatrix = ({ squareMatrix, cursorCoordinate, units, unitsOnSquares, h
return React.createElement(Unit, {
key: 'square-matrix-unit-' + unit.uid,
iconId: getIconId(unit),
top: unit.location[0],
left: unit.location[1],
top: unit.location.y,
left: unit.location.x,
classNames: [
'square-matrix__unit',
isAlly(unit) ? 'unit--ally' : 'unit--enemy',
Expand Down
28 changes: 16 additions & 12 deletions src/state-models/complex-apis.js
Expand Up @@ -4,7 +4,7 @@ const boxCollide = require('box-collide');

const config = require('../config');
const { ACT_AIM_RANGE_TYPES, BOARD_TYPES, STYLES } = require('../immutable/constants');
const { expandReachToRelativeCoordinates, matrixAdd } = require('../lib/core');
const { expandReachToRelativeCoordinates } = require('../lib/core');
const locationMethods = require('./location');
const squareMatrixMethods = require('./square-matrix');
const unitMethods = require('./unit');
Expand All @@ -15,7 +15,8 @@ const unitMethods = require('./unit');
* @return {State~Location} A location of square
*/
const coordinateToSquareLocation = (coordinate) => {
return [coordinate[0] * STYLES.SQUARE_HEIGHT, coordinate[1] * STYLES.SQUARE_WIDTH];
return locationMethods.createNewLocationState(
coordinate[0] * STYLES.SQUARE_HEIGHT, coordinate[1] * STYLES.SQUARE_WIDTH);
};

/**
Expand All @@ -24,8 +25,8 @@ const coordinateToSquareLocation = (coordinate) => {
*/
const squareLocationToRect = (squareLocation) => {
return {
x: squareLocation[1],
y: squareLocation[0],
x: squareLocation.x,
y: squareLocation.y,
width: STYLES.SQUARE_WIDTH,
height: STYLES.SQUARE_HEIGHT,
};
Expand Down Expand Up @@ -59,8 +60,10 @@ const getUnitPositionAsLocation = (unit) => {
*/
const createReachableRects = (centerSquareLocation, reach) => {
return expandReachToRelativeCoordinates(0, reach)
.map(relativeCoordinate => matrixAdd([centerSquareLocation], [coordinateToSquareLocation(relativeCoordinate)]))
.map(([location]) => squareLocationToRect(location));
.map(relativeCoordinate =>
locationMethods.addLocations(centerSquareLocation, coordinateToSquareLocation(relativeCoordinate))
)
.map(location => squareLocationToRect(location));
};

/**
Expand Down Expand Up @@ -135,16 +138,17 @@ const canActorAimActAtTargetedUnit = (actor, act, target) => {

/**
* @param {State~Unit} actor
* @param {Act} act
* @param {Function} act
* @param {State~Unit[]} units
* @return {State~Unit[]}
* @return {?State~Unit}
*/
const choiceAimedUnit = (actor, act, units) => {
const aimableUnits = units
.filter(unit => willActorAimActAtUnit(actor, act, unit))
.filter(unit => canActorAimActAtTargetedUnit(actor, act, unit));

const actorLocation = getUnitPositionAsLocation(actor);

aimableUnits.sort((a, b) => {
const aLocation = getUnitPositionAsLocation(a);
const bLocation = getUnitPositionAsLocation(b);
Expand All @@ -159,11 +163,11 @@ const choiceAimedUnit = (actor, act, units) => {
}

// 2nd; Sort by clock-wise
const angleA = angles.fromSlope([-actorLocation[0], actorLocation[1]], [-aLocation[0], aLocation[1]]);
const angleB = angles.fromSlope([-actorLocation[0], actorLocation[1]], [-bLocation[0], bLocation[1]]);
if (angleA < angleB) {
const angleA = locationMethods.measureAngleWithTopAsZero(actorLocation, aLocation);
const angleB = locationMethods.measureAngleWithTopAsZero(actorLocation, bLocation);
if (angleA === null || angleA < angleB) {
return -1;
} else if (angleA > angleB) {
} else if (angleB === null || angleA > angleB) {
return 1;
}

Expand Down
99 changes: 71 additions & 28 deletions src/state-models/location.js
@@ -1,14 +1,40 @@
// TODO: `Location` -> `Point` & `[y,x]` -> {x,y}

/**
* @typedef {number[]} State~Location
* @description [y, x] (= [top, left]) position on the battle-board
* @typedef {Object} State~Location
* @description A position on the battle-board
* @property {number} y - A distance from top to bottom in other word. In other words in CSS term is "top".
* @property {number} x - A distance from left to right. In other words in CSS terms is "left".
*/


/** @module */
const angles = require('angles');


const createNewLocationState = (y, x) => {
return [y, x];
return { y, x };
};

/**
* @param {...State~Location} locations
* @return {boolean}
*/
const areSameLocations = (...locations) => {
const [first, ...rest] = locations;
return rest.every(v => first.y === v.y && first.x === v.x);
};

/**
* @param {...State~Location} locations
* @return {State~Location}
*/
const addLocations = (...locations) => {
const [first, ...rest] = locations;
let { y, x } = first;
rest.forEach(v => {
y += v.y;
x += v.x;
});
return createNewLocationState(y, x);
};

/**
Expand All @@ -17,44 +43,61 @@ const createNewLocationState = (y, x) => {
* @return {number}
*/
const measureDistance = (a, b) => {
return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
return Math.sqrt(Math.pow(a.y - b.y, 2) + Math.pow(a.x - b.x, 2));
};

/**
* ベクトルの和を行うが、方向は上下左右に限定する。
* 大量に呼び出されるので処理の速さを優先する。
* @param {State~Location} initialLocation
* @param {State~Location} terminalLocation
* @param {number} vector
* @return {State~Location} [movedY, movedX]
* Measure the angle of the line with the top as 0
* @param {State~Location} from
* @param {State~Location} to
* @return {?number} ex) from(0, 0) / to(-1, 0) -> 0
* from(0, 0) / to(-1, 1) -> 45
* from(0, 0) / to(0, 1) -> 90
* from(0, 0) / to(1, 0) -> 180
* from(0, 0) / to(0, -1) -> 270
* from(0, 0) / to(0, 0) -> null
*/
const performPseudoVectorAddition = (initialLocation, terminalLocation, vector) => {
const [initialY, initialX] = initialLocation;
const [terminalTop, terminalLeft] = terminalLocation;
const measureAngleWithTopAsZero = (from, to) => {
if (areSameLocations(from, to)) return null;
return angles.normalize(angles.fromSlope([from.x, from.y], [to.x, to.y]) - 270);
};

if (initialY !== terminalTop && initialX !== terminalLeft) {
/**
* ベクトルの和を行うが、方向は上下左右に限定する。
* 大量に呼び出されるので処理の速さに配慮する。
* @param {State~Location} initial
* @param {State~Location} terminal
* @param {number} scalar
* @todo Memoize?
* @return {State~Location} { y: movedY, x: movedX }
*/
const performPseudoVectorAddition = (initial, terminal, scalar) => {
if (initial.y !== terminal.y && initial.x !== terminal.x) {
throw new Error('It is possible to move only up / down / left / right.');
}

let movedY = initialY;
let movedX = initialX;

if (initialY < terminalTop) {
movedY = Math.min(terminalTop, initialY + vector);
} else if (initialY > terminalTop) {
movedY = Math.max(terminalTop, initialY - vector);
} else if (initialX < terminalLeft) {
movedX = Math.min(terminalLeft, initialX + vector);
} else if (initialX > terminalLeft) {
movedX = Math.max(terminalLeft, initialX - vector);
let movedY = initial.y;
let movedX = initial.x;

if (initial.y < terminal.y) {
movedY = Math.min(terminal.y, initial.y + scalar);
} else if (initial.y > terminal.y) {
movedY = Math.max(terminal.y, initial.y - scalar);
} else if (initial.x < terminal.x) {
movedX = Math.min(terminal.x, initial.x + scalar);
} else if (initial.x > terminal.x) {
movedX = Math.max(terminal.x, initial.x - scalar);
}

return [movedY, movedX];
return createNewLocationState(movedY, movedX);
};


module.exports = {
addLocations,
areSameLocations,
createNewLocationState,
measureAngleWithTopAsZero,
measureDistance,
performPseudoVectorAddition,
};
7 changes: 4 additions & 3 deletions src/state-models/placement.js
Expand Up @@ -9,12 +9,13 @@
const uuidV4 = require('uuid/v4');

const { BOARD_TYPES } = require('../immutable/constants');
const { createNewCoordinateState } = require('./coordinate');


const createNewPlacementState = () => {
const createNewPlacementState = (boardType = null, coordinate = null) => {
return {
boardType: null,
coordinate: null,
boardType,
coordinate: coordinate === null ? coordinate : createNewCoordinateState(...coordinate),
};
};

Expand Down
6 changes: 3 additions & 3 deletions src/state-models/unit.js
Expand Up @@ -33,7 +33,7 @@ const { FACTION_TYPES, FRIENDSHIP_TYPES, PARAMETERS } = require('../immutable/co
const { ACT_IDS, acts } = require('../immutable/acts');
const { JOB_IDS, jobs } = require('../immutable/jobs');
const { createNewPlacementState } = require('./placement');
const { performPseudoVectorAddition } = require('./location');
const { areSameLocations, performPseudoVectorAddition } = require('./location');


const createNewUnitState = () => {
Expand Down Expand Up @@ -146,8 +146,8 @@ const calculateMovementResults = (unit) => {
newLocation = currentDestination;
}

const newDestinationIndex = unit.destinationIndex +
(newLocation[0] === currentDestination[0] && newLocation[1] === currentDestination[1] ? 1 : 0);
const newDestinationIndex =
unit.destinationIndex + (areSameLocations(newLocation, currentDestination) ? 1 : 0);

return {
location: newLocation,
Expand Down
47 changes: 28 additions & 19 deletions test/state-models/complex-apis.js
Expand Up @@ -37,9 +37,9 @@ describe('state-models/complex-apis', () => {
});
};

const _createLocatedUnit = (top, left) => {
const _createLocatedUnit = (y, x) => {
return Object.assign(unitMethods.createNewUnitState(), {
location: locationMethods.createNewLocationState(top, left),
location: locationMethods.createNewLocationState(y, x),
});
};

Expand All @@ -61,12 +61,12 @@ describe('state-models/complex-apis', () => {
it('can execute correctly', () => {
assert.deepStrictEqual(
coordinateToSquareLocation(coordinateMethods.createNewCoordinateState(0, 0)),
[0, 0]
locationMethods.createNewLocationState(0, 0)
);

assert.deepStrictEqual(
coordinateToSquareLocation(coordinateMethods.createNewCoordinateState(1, 2)),
[48, 96]
locationMethods.createNewLocationState(48, 96)
);
});
});
Expand Down Expand Up @@ -97,21 +97,30 @@ describe('state-models/complex-apis', () => {

describe('createReachableRects', () => {
it('can execute correctly', () => {
assert.deepStrictEqual(createReachableRects([0, 0], 0), [
{ x: 0, y: 0, width: 48, height: 48 },
]);

assert.deepStrictEqual(createReachableRects([0, 0], 1), [
{ x: 0, y: 0, width: 48, height: 48 },
{ x: 0, y: -48, width: 48, height: 48 },
{ x: 48, y: 0, width: 48, height: 48 },
{ x: 0, y: 48, width: 48, height: 48 },
{ x: -48, y: 0, width: 48, height: 48 },
]);

assert.deepStrictEqual(createReachableRects([100, 150], 0), [
{ x: 150, y: 100, width: 48, height: 48 },
]);
assert.deepStrictEqual(
createReachableRects(locationMethods.createNewLocationState(0, 0), 0),
[
{ x: 0, y: 0, width: 48, height: 48 },
]
);

assert.deepStrictEqual(
createReachableRects(locationMethods.createNewLocationState(0, 0), 1),
[
{ x: 0, y: 0, width: 48, height: 48 },
{ x: 0, y: -48, width: 48, height: 48 },
{ x: 48, y: 0, width: 48, height: 48 },
{ x: 0, y: 48, width: 48, height: 48 },
{ x: -48, y: 0, width: 48, height: 48 },
]
);

assert.deepStrictEqual(
createReachableRects(locationMethods.createNewLocationState(100, 150), 0),
[
{ x: 150, y: 100, width: 48, height: 48 },
]
);
});
});

Expand Down

0 comments on commit 05f4224

Please sign in to comment.