Skip to content

Commit

Permalink
Merge branch 'rituals'
Browse files Browse the repository at this point in the history
  • Loading branch information
TrueWill committed May 7, 2019
2 parents 209f79e + 803b63e commit 5322a6c
Show file tree
Hide file tree
Showing 27 changed files with 1,487 additions and 38 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ Currently deployment is manual, so the site may not reflect the latest changes.

## To Do

* Long discipline names print over dots
* Improve styling/responsiveness
* Print options (A4 size, etc.)
* Improve test coverage
* Rituals
* Techniques (clear if change Generation)
* Elder Powers (clear if change Generation)
* Eerie Presence Flaw can be purchased multiple times
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
"classnames": "^2.2.6",
"jspdf": "^1.5.3",
"lodash.chunk": "^4.2.0",
"lodash.startswith": "^4.2.1",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-app-polyfill": "^0.2.2",
"react-dom": "^16.8.6",
"react-redux": "^7.0.2",
"react-scripts": "2.1.8",
"react-select": "^2.4.2",
"react-select": "^2.4.3",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0",
Expand Down
2 changes: 1 addition & 1 deletion reference-files/state_1.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"payload": "[{\"type\":\"SET_RANK\",\"payload\":{\"category\":\"attributes\",\"trait\":\"physical\",\"dotsFromRank\":7}},{\"type\":\"SET_RANK\",\"payload\":{\"category\":\"attributes\",\"trait\":\"social\",\"dotsFromRank\":5}},{\"type\":\"SET_RANK\",\"payload\":{\"category\":\"attributes\",\"trait\":\"mental\",\"dotsFromRank\":3}},{\"type\":\"SET_FOCUS\",\"payload\":{\"attribute\":\"physical\",\"focus\":\"Strength\"}},{\"type\":\"SET_FOCUS\",\"payload\":{\"attribute\":\"social\",\"focus\":\"Manipulation\"}},{\"type\":\"SET_FOCUS\",\"payload\":{\"attribute\":\"mental\",\"focus\":\"Intelligence\"}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"performance\",\"startingDots\":4}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"performance2\",\"startingDots\":3}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"performance3\",\"startingDots\":3}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"science\",\"startingDots\":2}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"science2\",\"startingDots\":2}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"science3\",\"startingDots\":2}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"subterfuge\",\"startingDots\":1}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"academics\",\"startingDots\":1}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"animalKen\",\"startingDots\":1}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"skills\",\"trait\":\"crafts\",\"startingDots\":1}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"skills\",\"trait\":\"crafts2\"}},{\"type\":\"ADD_MERIT\",\"payload\":{\"name\":\"Skill Aptitude\",\"points\":2}},{\"type\":\"ADD_MERIT\",\"payload\":{\"name\":\"Skill Aptitude\",\"points\":2}},{\"type\":\"ADD_MERIT\",\"payload\":{\"name\":\"Emissary to the Camarilla\",\"points\":1}},{\"type\":\"ADD_FLAW\",\"payload\":{\"name\":\"Cannot Cross Running Water\",\"points\":2}},{\"type\":\"ADD_FLAW\",\"payload\":{\"name\":\"Beacon of the Unholy\",\"points\":3}},{\"type\":\"ADD_FLAW\",\"payload\":{\"name\":\"Sectarian\",\"points\":2}},{\"type\":\"REMOVE_MERIT\",\"payload\":{\"name\":\"Skill Aptitude\"}},{\"type\":\"REMOVE_MERIT\",\"payload\":{\"name\":\"Skill Aptitude\"}},{\"type\":\"ADD_MERIT\",\"payload\":{\"name\":\"Expanded Consciousness\",\"points\":1}},{\"type\":\"UPDATE_MORALITY\",\"payload\":{\"path\":\"Path of Power & the Inner Voice\",\"meritPoints\":3}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"disciplines.inClan\",\"trait\":\"Auspex\",\"startingDots\":2}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"disciplines.inClan\",\"trait\":\"Dominate\",\"startingDots\":1}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"disciplines.inClan\",\"trait\":\"Obfuscate\",\"startingDots\":1}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"disciplines.outOfClan\",\"trait\":\"Animalism\"}},{\"type\":\"TOGGLE_PENCIL_ERASER_MODE\"},{\"type\":\"UNPURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"UNPURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"UNPURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"UNPURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"UNPURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"UNPURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"TOGGLE_PENCIL_ERASER_MODE\"},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\",\"startingDots\":3}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"influence_underworld\",\"startingDots\":2}},{\"type\":\"SET_STARTING_DOTS\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"influence_elite\",\"startingDots\":1}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"generation\"}},{\"type\":\"PURCHASE_DOT\",\"payload\":{\"category\":\"backgrounds\",\"trait\":\"alternateIdentity\"}}]",
"preloadedState": "{\"mode\":{\"isEraser\":false},\"setting\":{\"name\":\"Camarilla\"},\"character\":{\"basicInfo\":{\"archetype\":\"Type-A Personality\",\"clan\":{\"name\":\"Malkavian\",\"bloodline\":\"Knights of the Moon\",\"meritPoints\":2}},\"attributes\":{\"physical\":{},\"social\":{},\"mental\":{}},\"skills\":{\"availableStartingDots\":[{\"dots\":4,\"count\":1},{\"dots\":3,\"count\":2},{\"dots\":2,\"count\":3},{\"dots\":1,\"count\":4}]},\"backgrounds\":{\"availableStartingDots\":[{\"dots\":3,\"count\":1},{\"dots\":2,\"count\":1},{\"dots\":1,\"count\":0}],\"generation\":{\"startingDots\":1}},\"disciplines\":{\"inClan\":{\"availableStartingDots\":[{\"dots\":2,\"count\":1},{\"dots\":1,\"count\":2}]},\"outOfClan\":{\"availableStartingDots\":[]}},\"merits\":[],\"flaws\":[],\"morality\":{\"path\":\"Humanity\",\"startingDots\":5}}}"
"preloadedState": "{\"mode\":{\"isEraser\":false},\"setting\":{\"name\":\"Camarilla\"},\"character\":{\"basicInfo\":{\"archetype\":\"Type-A Personality\",\"clan\":{\"name\":\"Malkavian\",\"bloodline\":\"Knights of the Moon\",\"meritPoints\":2}},\"attributes\":{\"physical\":{},\"social\":{},\"mental\":{}},\"skills\":{\"availableStartingDots\":[{\"dots\":4,\"count\":1},{\"dots\":3,\"count\":2},{\"dots\":2,\"count\":3},{\"dots\":1,\"count\":4}]},\"backgrounds\":{\"availableStartingDots\":[{\"dots\":3,\"count\":1},{\"dots\":2,\"count\":1},{\"dots\":1,\"count\":0}],\"generation\":{\"startingDots\":1}},\"disciplines\":{\"inClan\":{\"availableStartingDots\":[{\"dots\":2,\"count\":1},{\"dots\":1,\"count\":2}]},\"outOfClan\":{\"availableStartingDots\":[]},\"rituals\":{\"necromantic\":[],\"thaumaturgic\":[]}},\"merits\":[],\"flaws\":[],\"morality\":{\"path\":\"Humanity\",\"startingDots\":5}}}"
}
8 changes: 8 additions & 0 deletions src/actions/characterCreationActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,11 @@ export const updateMoralityIfPointsAvailable = path => (dispatch, getState) => {
dispatch(updateMorality(path, newMeritPoints));
}
};

export const updateRituals = (ritualType, rituals) => ({
type: types.UPDATE_RITUALS,
payload: {
ritualType,
rituals
}
});
2 changes: 2 additions & 0 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import PencilEraserContainer from '../containers/PencilEraserContainer';
import MoralityContainer from '../containers/MoralityContainer';
import ValidationContainer from '../containers/ValidationContainer';
import Section from '../components/Section';
import RitualsContainer from '../containers/RitualsContainer';
import styles from './App.module.css';

export default function App() {
Expand Down Expand Up @@ -61,6 +62,7 @@ export default function App() {
<div className="container-fluid">
<DisciplinesContainer affinity="inClan" />
<DisciplinesContainer affinity="outOfClan" />
<RitualsContainer />
</div>
</div>
</div>
Expand Down
40 changes: 40 additions & 0 deletions src/components/Rituals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import RitualsForType from './RitualsForType';
import Section from './Section';

export default function Rituals({ rituals, updateRituals }) {
if (rituals.length === 0) {
return null;
}

const ritualsForTypes = rituals.map(r => (
<RitualsForType
key={r.ritualType}
ritualType={r.ritualType}
displayName={r.displayName}
permutations={r.permutations}
selected={r.selected}
updateRituals={updateRituals}
/>
));

return <Section header="Rituals">{ritualsForTypes}</Section>;
}

Rituals.propTypes = {
rituals: PropTypes.arrayOf(
PropTypes.shape({
ritualType: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
permutations: PropTypes.arrayOf(
PropTypes.shape({
description: PropTypes.string.isRequired,
value: PropTypes.arrayOf(PropTypes.number).isRequired
})
).isRequired,
selected: PropTypes.arrayOf(PropTypes.number).isRequired
})
).isRequired,
updateRituals: PropTypes.func.isRequired
};
56 changes: 56 additions & 0 deletions src/components/RitualsForType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function RitualsForType({
ritualType,
displayName,
permutations,
selected,
updateRituals
}) {
const handleChange = e => {
const rituals = JSON.parse(e.target.value);

updateRituals(ritualType, rituals);
};

const options = permutations.map(p => {
const valueAsString = JSON.stringify(p.value);

return (
<option value={valueAsString} key={valueAsString}>
{p.description}
</option>
);
});

return (
<div className="row">
<div className="col-sm-12">
<div className="row">
<div className="col-sm-12">{displayName}</div>
</div>
<div className="row">
<div className="col-sm-12">
<select value={JSON.stringify(selected)} onChange={handleChange}>
{options}
</select>
</div>
</div>
</div>
</div>
);
}

RitualsForType.propTypes = {
ritualType: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
permutations: PropTypes.arrayOf(
PropTypes.shape({
description: PropTypes.string.isRequired,
value: PropTypes.arrayOf(PropTypes.number).isRequired
})
).isRequired,
selected: PropTypes.arrayOf(PropTypes.number).isRequired,
updateRituals: PropTypes.func.isRequired
};
1 change: 1 addition & 0 deletions src/constants/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export const UNPURCHASE_MORALITY_DOT = 'UNPURCHASE_MORALITY_DOT';
export const TOGGLE_PENCIL_ERASER_MODE = 'TOGGLE_PENCIL_ERASER_MODE';
export const UPDATE_MORALITY = 'UPDATE_MORALITY';
export const UPDATE_SETTING = 'UPDATE_SETTING';
export const UPDATE_RITUALS = 'UPDATE_RITUALS';
2 changes: 1 addition & 1 deletion src/constants/application.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// General application constants.
export const version = '0.9.4';
export const version = '0.10.0';
export const docUrl = 'https://github.com/TrueWill/embracer';
4 changes: 4 additions & 0 deletions src/constants/characterOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ const standardDotCost = {
per: 'newLevel'
}
},
rituals: {
xp: 2,
per: 'level'
},
morality: {
xp: 10,
per: 'each'
Expand Down
17 changes: 17 additions & 0 deletions src/containers/RitualsContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { connect } from 'react-redux';
import { updateRituals } from '../actions/characterCreationActions';
import getRituals from '../selectors/getRituals';
import Rituals from '../components/Rituals';

const mapStateToProps = state => ({
rituals: getRituals(state)
});

const mapDispatchToProps = {
updateRituals
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(Rituals);
41 changes: 38 additions & 3 deletions src/reducers/disciplinesReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
addPurchasedDot,
removePurchasedDot
} from '../utils/categoryPurchaser';
import { getRitualInfoForDiscipline } from '../utils/ritualUtils';

const isDisciplines = category => category.lastIndexOf('disciplines.', 0) === 0;

Expand All @@ -17,8 +18,27 @@ const getMaxDots = affinity =>
? outOfClanDisciplineLevelLimit
: standardTraitMaxDots;

const clearRitualTypeIfMagic = (trait, state) => {
const ritualInfo = getRitualInfoForDiscipline(trait);

if (
ritualInfo.hasRituals &&
state.rituals[ritualInfo.ritualType].length > 0
) {
return {
...state,
rituals: {
...state.rituals,
[ritualInfo.ritualType]: []
}
};
}

return state;
};

export default (state = initialState.character.disciplines, action) => {
let category, trait, startingDots, affinity, maxDots;
let category, trait, startingDots, affinity, maxDots, newState;

switch (action.type) {
case types.SET_STARTING_DOTS:
Expand All @@ -31,7 +51,7 @@ export default (state = initialState.character.disciplines, action) => {
affinity = getAffinity(category);
maxDots = getMaxDots(affinity);

return {
newState = {
...state,
[affinity]: setDotsFromStartingDots(
state[affinity],
Expand All @@ -40,6 +60,9 @@ export default (state = initialState.character.disciplines, action) => {
maxDots
)
};

// Currently clearing ritual type even if increasing starting dots.
return clearRitualTypeIfMagic(trait, newState);
case types.PURCHASE_DOT:
({ category, trait } = action.payload);

Expand All @@ -63,10 +86,22 @@ export default (state = initialState.character.disciplines, action) => {

affinity = getAffinity(category);

return {
newState = {
...state,
[affinity]: removePurchasedDot(state[affinity], trait)
};

return clearRitualTypeIfMagic(trait, newState);
case types.UPDATE_RITUALS:
const { ritualType, rituals } = action.payload;

return {
...state,
rituals: {
...state.rituals,
[ritualType]: rituals
}
};
case types.UPDATE_CLAN:
// reset
return initialState.character.disciplines;
Expand Down
Loading

0 comments on commit 5322a6c

Please sign in to comment.