// IMPORT FUNCTIONS FOR REACT AND REDUX
const {
createStore,
bindActionCreators
} = Redux;
const {
Provider,
connect
} = ReactRedux;
const render = ReactDOM.render
// DESIGNATE DEFAULT STATE FOR REDUX
const DataList={
'Name':null,
'Class':null,
'Race':null,
'Background':null,
'Modifiers':[0,0,0,0,0,0],
'Abilities':[0,0,0,0,0,0],
'Saving Throws':[0,0,0,0,0,0],
'Skills':[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
'Speed':0,
'Hit Points':0,
'Proficiency':2
};
// FUNCTIONS
/*
CALLS A PROVIDED FUNCTION ON EACH PROPERTY OF THE PROVIDED OBJECT; RETURNS AN ARRAY
ARGUMENTS: {{}, function(key, value, index)}
*/
function mapObject(obj, callback) {
return Object.keys(obj).map((k, i)=>{
return callback(k, obj[k], i);
});
}
/*
#####################################################
# #
# NAVBAR NAVBAR NAVBAR NAVBAR NAVBAR NAVBAR NAVBAR #
# #
#####################################################
*/
// GLOBALS
const LOGO = {'Character Builder':'#'};
const SITELINKS = {};
const KEYTRACK = [1];
const TABINDEX = [];
const TWEETPRE = 'https://twitter.com/intent/tweet?text=';
// REACT
/* LINKS FOR NAVBAR LOCATIONS
PROPS: {places: {}}
*/
const NavPlaces = (props) =>{
KEYTRACK[0]++;
return mapObject(props.places, function (key, value, iter) {
return (<a
href={value}
key={key+KEYTRACK[0].toString()+iter.toString()}>{key}
</a>);
});
}
NavPlaces.propTypes={
places: PropTypes.objectOf(PropTypes.string).isRequired
}
/* LEFT NAVBAR
PROPS: {sWidth: #, places: {}}
*/
const LeftNav = (props) =>{
return(<div id={'dispLeft'} className={'navLeft'}>
<NavPlaces places={LOGO} />
{props.sWidth>610 ? <NavPlaces places={props.places} />:null}
</div>);
}
LeftNav.propTypes={
sWidth: PropTypes.number.isRequired,
places: PropTypes.objectOf(PropTypes.string).isRequired
}
/* SOCIAL NETWORK LINKS
PROPS: {link: string, name: string}
*/
const SocialLink = (props)=>{
return <a href={props.link} target='_blank'><i className={'fa fa-'+props.name}></i></a>
}
SocialLink.propTypes={
link: PropTypes.string,
name: PropTypes.name
}
/* RIGHT NAVBAR
PROPS: {menuHandler: function}
*/
const RightNav = (props)=>{
return(<div id={'dispRight'} className={'navRight'}>
<SocialLink
link={props.tweetLink}
name={'twitter'}/>
{props.sWidth<611 ?
(<a href='#' id={'responseMenu'} class={'navDrop'} onClick={props.menuHandler}>
<i class='fa fa-bars'></i>
</a>):
null}
</div>);
}
RightNav.propTypes={
menuHandler: PropTypes.func.isRequired
}
/* NAVBAR CONTAINER FOR LEFT AND RIGHT NAVBARS
PROPS: {logo: {}, places: {}, sWidth: #, menuHandler:function}
*/
const NavParent = (props)=>{
return(<nav className={'navbarContainer'}>
<LeftNav logo={props.logo} places={props.places} sWidth={props.sWidth}/>
<RightNav sWidth={props.sWidth} menuHandler={props.menuHandler} tweetLink={props.tweetLink} />
</nav>
);
}
NavParent.propTypes={
logo: PropTypes.objectOf(PropTypes.string).isRequired,
places: PropTypes.objectOf(PropTypes.string).isRequired,
sWidth: PropTypes.number.isRequired,
menuHandler: PropTypes.func.isRequired
}
/* NAVBAR DROPDOWN COMPONENT
PROPS: {places:{}, menuHandler:function}
*/
const ResponsiveNav = (props)=>{
return(<div className={'dropMenu'} onClick={props.menuHandler}>
<NavPlaces places={props.places}/>
</div>
);
}
ResponsiveNav.propTypes={
places: PropTypes.objectOf(PropTypes.string).isRequired,
menuHandler: PropTypes.func.isRequired
}
/* NAVBAR PARENT COMPONENT
CURRENTLY NO PROPS
*/
class Navbar extends React.Component {
constructor(props){
super(props);
this.state={
width: window.innerWidth,
respondMenu: false
};
this.updateWidth=this.updateWidth.bind(this);
this.responsiveClickHandler=this.responsiveClickHandler.bind(this);
}
componentDidMount(){
window.addEventListener('resize', this.updateWidth);
}
updateWidth(){
this.setState({width: window.innerWidth});
}
responsiveClickHandler(){
this.setState(p=>({respondMenu:!p.respondMenu}));
}
render(){
return(<div>
<NavParent
logo={LOGO}
places={SITELINKS}
tweetLink={this.props.tweetLink}
sWidth={this.state.width}
menuHandler={this.responsiveClickHandler} />
{this.state.respondMenu?
<ResponsiveNav
places={SITELINKS}
menuHandler={this.responsiveClickHandler}/>:
null}
</div>);
}
componentWillUnmount(){
window.removeEventListener('resize', this.updateDimensions);
}
}
/*
#####################################################
# #
# MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN #
# #
#####################################################
*/
// GLOBALS
const SUBJECTLIB = {
'Class':['Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter', 'Monk', 'Paladin', 'Ranger', 'Rogue', 'Sorcerer', 'Warlock', 'Wizard'],
'Race':['Hill Dwarf', 'Mountain Dwarf', 'High Elf', 'Wood Elf', 'Dark Elf', 'Lightfoot Halfling', 'Stout Halfling', 'Human', 'Dragonborn', 'Forest Gnome', 'Rock Gnome', 'Half-Elf', 'Half-Orc', 'Tiefling'],
'Background':['Acolyte', 'Charlatan', 'Criminal', 'Entertainer', 'Folk Hero', 'Guild Artisan', 'Hermit', 'Noble', 'Outlander', 'Sage', 'Sailor', 'Soldier', 'Urchin'],
'Skills':['Acrobatics', 'Animal Handling', 'Arcana', 'Athletics', 'Deception','History', 'Insight', 'Intimidation', 'Investigation', 'Medicine', 'Nature', 'Perception', 'Performance', 'Persuasion', 'Religion', 'Sleight of Hand', 'Stealth', 'Survival'],
'Abilities':['Strength','Dexterity','Constitution','Intelligence','Wisdom','Charisma']
};
const ATTRIBLIB = {
// Race : {Abilities : [STRENGTH, DEXTERITY, CONSTITUTION, INTELLIGENCE, WISDOM, CHARISMA, OPTIONAL, HIT-POINT BOOST], Walk : WALK SPEED, Size : SIZE}
'Hill Dwarf': {'Abilities':[0,0,2,0,1,0],'Optional':0, 'Hit':1, 'Walk':25, 'Size':'Medium'},
'Mountain Dwarf': {'Abilities':[2,0,2,0,0,0],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Medium'},
'High Elf': {'Abilities':[0,2,0,1,0,0],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'},
'Wood Elf': {'Abilities':[0,2,0,0,1,0],'Optional':0, 'Hit':0, 'Walk':35, 'Size':'Medium'},
'Dark Elf': {'Abilities':[0,2,0,0,0,1],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'},
'Lightfoot Halfling': {'Abilities':[0,2,0,0,0,1],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Small'},
'Stout Halfling': {'Abilities':[0,2,1,0,0,0],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Small'},
'Human': {'Abilities':[1,1,1,1,1,1],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'},
'Dragonborn': {'Abilities':[2,0,0,0,0,1],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'},
'Forest Gnome': {'Abilities':[0,1,0,2,0,0],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Small'},
'Rock Gnome': {'Abilities':[0,0,1,2,0,0],'Optional':0, 'Hit':0, 'Walk':25, 'Size':'Small'},
'Half-Elf': {'Abilities':[0,0,0,0,0,2],'Optional':2, 'Hit':0, 'Walk':30, 'Size':'Medium'},
'Half-Orc': {'Abilities':[2,0,1,0,0,0],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'},
'Tiefling': {'Abilities':[0,0,0,1,0,2],'Optional':0, 'Hit':0, 'Walk':30, 'Size':'Medium'}
};
const MATCHSKILL = {
'Acrobatics':'Dexterity', 'Animal Handling':'Wisdom', 'Arcana':'Intelligence', 'Athletics':'Strength', 'Deception':'Charisma','History':'Intelligence', 'Insight':'Wisdom', 'Intimidation':'Charisma', 'Investigation':'Intelligence', 'Medicine':'Wisdom', 'Nature':'Intelligence', 'Perception':'Wisdom', 'Performance':'Charisma', 'Persuasion':'Charisma', 'Religion':'Intelligence', 'Sleight of Hand':'Dexterity', 'Stealth':'Dexterity', 'Survival':'Wisdom'
}
const SKILLLIB = {
'Barbarian':[0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,1],
'Bard':[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
'Cleric':[0,0,0,0,0,1,1,0,0,1,0,0,0,1,1,0,0,0],
'Druid':[0,1,1,0,0,0,1,0,0,1,1,1,0,0,1,0,0,1],
'Fighter':[1,1,0,1,0,1,1,1,0,0,0,1,0,0,0,0,0,1],
'Monk':[1,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,1,0],
'Paladin':[0,0,0,1,0,0,1,1,0,1,0,0,0,1,1,0,0,0],
'Ranger':[0,1,0,1,0,0,1,0,1,0,1,1,0,0,0,0,1,1],
'Rogue':[1,0,0,1,1,0,1,1,1,0,0,1,1,1,0,1,1,0],
'Sorcerer':[0,0,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,0],
'Warlock':[0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0],
'Wizard':[0,0,1,0,0,1,1,0,1,1,0,0,0,0,1,0,0,0],
'Acolyte':[0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0],
'Charlatan':[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0],
'Criminal':[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0],
'Entertainer':[1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0],
'Folk Hero':[0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
'Guild Artisan':[0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0],
'Hermit':[0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0],
'Noble':[0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0],
'Outlander':[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
'Sage':[0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0],
'Sailor':[0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0],
'Soldier':[0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0],
'Urchin':[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0]
}
const SAVINGLIB = {
'Barbarian':[1,0,1,0,0,0], 'Bard':[0,1,0,0,0,1], 'Cleric':[0,0,0,0,1,1], 'Druid':[0,0,0,1,1,0], 'Fighter':[1,0,1,0,0,0], 'Monk':[1,1,0,0,0,0], 'Paladin':[0,0,0,0,1,1], 'Ranger':[1,1,0,0,0,0], 'Rogue':[0,1,0,1,0,0], 'Sorcerer':[0,0,1,0,0,1], 'Warlock':[0,0,0,0,1,1], 'Wizard':[0,0,0,1,1,0]
}
const HITDIE = {
'Barbarian':12, 'Bard':8, 'Cleric':8, 'Druid':8, 'Fighter':10, 'Monk':8, 'Paladin':10, 'Ranger':10, 'Rogue':8, 'Sorcerer':6, 'Warlock':8, 'Wizard':8
}
const USESKILL = {
'Barbarian':2, 'Bard':3, 'Cleric':2, 'Druid':2, 'Fighter':2, 'Monk':2, 'Paladin':2, 'Ranger':3, 'Rogue':4, 'Sorcerer':2, 'Warlock':2, 'Wizard':2
}
// MAIN UI FUNCTIONS
/*
GENERATES A RANDOM NUMBER BETWEEN 1 AND DESIRED VALUE
ARGUMENTS: {Number (n)}
*/
function rollADie(n){
return Math.floor(Math.random()*n)+1;
}
/*
GENERATES FOUR RANDOM NUMBERS (1-6) AND SUMS THE THREE HIGHEST
ARGUMENTS: NONE
*/
function rollAbility(){
var results = [];
for (var i=0; i<4; i++){
results.push(rollADie(6));
}
results.sort((a,b)=>a-b);
results.shift()
return results.reduce((c,d)=>c+d);
}
/* RETURNS SIX VALUES USING THE rollAbility FUNCTION*/
const rollCharacter =()=>{
var s = [];
for(var i=0; i<6; i++){
s.push(rollAbility());
}
return s;
}
/* REMOVES NAME PROPERTY FROM UISection1 charValues
ARGS: {m {}}
*/
const selectsOnly=(m)=>{
if (m.hasOwnProperty('Name')){ delete m.Name }
return m;
}
/* OPTIONS FOR SELECT MENUS IN REACT COMPONENTS
ARGS: {NAME/VALUE (n), EVENT LISTENER (v)}
*/
const makeOption=(n,v)=>{
KEYTRACK[0]++;
return(<option className='dOption' value={n} key={n+KEYTRACK[0].toString()}>{n}</option>)
}
/* MAKE SELECT MENUS FOR REACT COMPONENT
ARGS: {LABEL (b), LIST (l), EVENT LISTENER (v), ITERABLE (i), SELECTED (s), WARN CONDITIONAL (w)}
*/
const makeSelect=(b,l,s,i,v,w)=>{
//KEYTRACK[0]++;
var opts= l.map((x)=> makeOption(x,v));
return(<div className='inputContain'>
<select
className='dSelect character'
name={b}
onChange={v}
value={s?s:b}
key={'dontChange'+i.toString()}>
<option
className='dOption'
value={b}
disabled>{b}</option>
{opts}
</select>
<div>{w[[b]]?<div className='inputWarn'>Please make a selection.</div>:null}</div>
</div>);
}
/* REACT MENU COMPONENT
PROPS: {menus [], handler: function, warn:{}}
*/
class MakeMenus extends React.Component{
render(){
return(<div>
{mapObject(this.props.menus, (x,y,i)=>makeSelect(x,SUBJECTLIB[x],y,i,this.props.handler, this.props.warn))}
</div>);
}
}
MakeMenus.propTypes={
menus: PropTypes.object.isRequired,
handler: PropTypes.func.isRequired,
warn: PropTypes.objectOf(PropTypes.bool).isRequired
}
/* UI SECTION 1
PROPS: {handleComplete: function, changeNCRB: function (Redux), changeSpeed: function (Redux)}
*/
class UISection1 extends React.Component{
constructor(props){
super(props);
this.state={
charValues:{'Name':null,'Class':null,'Race':null,'Background':null},
charWarnings:{'Name':false,'Class':false,'Race':false,'Background':false},
completed: false
}
this.handleCharAcc=this.handleCharAcc.bind(this);
this.handleNameEntry=this.handleNameEntry.bind(this);
this.handleOppClick=this.handleOppClick.bind(this);
}
handleNameEntry(e){
this.setState({charValues: Object.assign({...this.state.charValues},{'Name':e.target.value})});
}
handleOppClick(e){
if(e.target.name){
this.setState({charValues: Object.assign({...this.state.charValues},{[e.target.name]:e.target.value})});
}
e.preventDefault();
}
handleCharAcc(e){
const states=Object.assign({}, this.state.charValues);
if(Object.values(states).reduce((x,y)=>Boolean(x)&&Boolean(y))){
this.props.changeNCRB(states);
this.props.changeSpeed(ATTRIBLIB[states.Race].Walk);
e.target.disabled=true;
this.props.handleComplete();
} else {
for(var i in states){states[i]=!Boolean(states[i])}
this.setState({charWarnings:states});
}
}
render(){
//this.props.testLog();
return(<section className='mainSection'>
<fieldset><legend>Character</legend>
<h4>What is your name, hero?</h4>
<div className='inputContain'>
<input className='textBox character' type='text' name='Name' onChange={this.handleNameEntry}></input>
<div>{this.state.charWarnings.Name?<div className='inputWarn'>Please enter a name.</div>:null}</div>
</div>
<MakeMenus menus={selectsOnly({...this.state.charValues})} handler={this.handleOppClick} warn={this.state.charWarnings}/>
<div><button onClick={this.handleCharAcc}>Accept</button></div>
</fieldset>
</section>);
}
}
UISection1.propTypes={
changeNCRB: PropTypes.func,
changeSpeed: PropTypes.func,
handleComplete: PropTypes.func
}
/* UI Section 1a
PROPS: {data (Redux)}
*/
class UISection1a extends React.Component{
constructor(props){
super(props);
}
render(){
return(<section className='mainSection'>
<fieldset><legend>Character</legend>
<h4 className='nameTitle'>{this.props.data.Name}</h4>
<div className='nameTitle'>{this.props.data.Race}</div>
<div className='nameTitle'>{this.props.data.Class}</div>
<div className='nameTitle'>{this.props.data.Background}</div>
</fieldset>
</section>)
}
}
// RETURNS INDEX; Properties: {c: LIST-NAME (''), a: VALUE ('')}
const getDescriptIndex=(c,a)=>{
return SUBJECTLIB[c].findIndex((x)=>x===a);
}
// CUTS END OF INPUT NAME OFF TO MATCH TO OBJECT KEY: {n: NAME OF INPUT}
const nameToKey=(n)=>{
return n.split('_')[0];
}
// MATCHES VALUES OF TWO ARRAYS; RETURNS false IF THERE IS A MISMATCH: {a,b: ARRAYS}
function matchArray(a,b){
var c = [...b];
for(var g=0, h=a.length; g<h; g++){
for(var i=c.length-1; i>=0; i--){
if(c[i]===a[g]){
c.splice(i,1);
break;
}
}
}
if (c.length){ return false; } else return true;
}
/*
function checkAllValues(o){
for (var n in o){
if(!o[n]){
return false;
}
}
return true;
}*/
const increaseAbility=(r, a, i)=>{
return a+ATTRIBLIB[r]['Abilities'][i];
}
const modAbility=(a)=>{
return Math.floor((a-10)/2);
}
const savAbility=(c,m,p,i)=>{
if (SAVINGLIB[c][i]){return p+m;}else return m;
}
const renderStats=(k,v,r,c,p)=>{
const it=getDescriptIndex('Abilities',k);
const ab=increaseAbility(r,v,it);
const mo=modAbility(ab);
const sa=savAbility(c,mo,p,it);
return {abilities:ab,mods:mo,saves:sa};
}
// PROPS: {statChoices: [stats], ability:''}
const AbilityChoice = (props)=>{
return props.rollValues.map((x,y)=>{
return(<input
className='numGrid'
key={props.ability+y.toString()}
type={"radio"}
name={props.ability+'_Ability'}
value={x}
onClick={props.handleChoice}>
</input>);
});
}
// PROPS: {ability: '', dataR: ''(user chosen r)}
class AbilityLabel extends React.Component{
constructor(props){
super(props);
}
render(){
const bonus = ATTRIBLIB[this.props.dataR].Abilities[getDescriptIndex('Abilities',this.props.ability)];
return(<div htmlFor={this.props.ability+'_Ability'}>
{this.props.ability}
{bonus?<span>{'(+'+bonus.toString()+')'}</span>:null}
</div>)
}
}
// PROPS: {ability: '', dataR:'', rollValues:[], handleChoice: func, iter:#}
class AbilityInput extends React.Component{
constructor(props){
super(props);
}
render(){
return(<div className='abilityInputs' key={'abilityInput'+this.props.iter.toString()}>
<AbilityLabel ability={this.props.ability} dataR={this.props.dataR}/>
<AbilityChoice rollValues={this.props.rollValues} ability={this.props.ability} handleChoice={this.props.handleChoice}/>
</div>);
}
}
class DistributeTable extends React.Component{
constructor(props){
super(props);
}
render(){
var inputs=SUBJECTLIB['Abilities'].map((x,y)=><AbilityInput
dataR={this.props.data.Race}
rollValues={this.props.rollValues}
handleChoice={this.props.handleChoice}
ability={x}
iter={y} />);
return(<div name="abilityForm">
{inputs}
<button onClick={this.props.handleAccept}>Accept</button>
</div>
);
}
}
class UISection2 extends React.Component{
constructor(props){
super(props);
this.state={
rollValues:[],
optValues:{},
abilities:{},
saves:{},
mods:{},
warning:false
}
this.handleRoll=this.handleRoll.bind(this);
this.handleChoice=this.handleChoice.bind(this);
this.handleAccept=this.handleAccept.bind(this);
}
handleRoll(){
this.setState({rollValues:rollCharacter()});
}
handleChoice(e){
const ky=nameToKey(e.target.name);
const st=renderStats(ky,parseInt(e.target.value),this.props.data.Race,this.props.data.Class,this.props.data.Proficiency);
this.setState({optValues:Object.assign({},{...this.state.optValues},{[ky]: parseInt(e.target.value)}),
abilities:Object.assign({},{...this.state.abilities},{[ky]: st.abilities}),
saves:Object.assign({},{...this.state.saves},{[ky]: st.saves}),
mods:Object.assign({},{...this.state.mods},{[ky]: st.mods})});
}
handleAccept(e){
var f = Object.values({...this.state.optValues});
if(matchArray(f, this.state.rollValues)){
var p = 0;
for(var n in this.state.abilities){
p=getDescriptIndex('Abilities',n);
this.props.changeAbil(this.state.abilities[n],p);
this.props.changeMod(this.state.mods[n],p);
this.props.changeSaves(this.state.saves[n],p);
}
this.props.changeHit(HITDIE[this.props.data.Class]+this.state.mods.Constitution+ATTRIBLIB[this.props.data.Race].Hit);
this.props.handleComplete();
} else {
this.setState({warning:true});
}
e.preventDefault();
e.stopPropagation();
}
render(){
var values = this.state.rollValues.map((x,y)=>{
return (<div className='numHead' key={'rollValues'+y.toString()}> {x} </div>)});
return(<section className='mainSection'>
<fieldset><legend>Abilities</legend>
<button onClick={this.handleRoll}>Roll</button>
{this.state.rollValues.length>0?
<div className='abilityInputs'>
{[(<div className='strHead'>Stats</div>),...values]}
</div>:null}
{this.state.rollValues.length>0 ?
<DistributeTable
data={this.props.data}
rollValues={this.state.rollValues}
handleAccept={this.handleAccept}
handleChoice={this.handleChoice}/>:null}
<div>{this.state.warning?<div className='inputWarn'>Please select one different value for each ability.</div>:null}</div>
</fieldset>
</section>);
}
}
const getWalkSpeed=()=>{
return ATTRIBLIB[USERDATA['Race']]['Walk'];
}
const getHitPoints=()=>{
return HITDIE[USERDATA['Class']]+USERDATA['Modifiers']['Constitution'];
}
const AbilityResult=(props)=>{
return props.data.Abilities.map((x,y)=>{
return(<div className='abilityResult' key={'AbilityDisplay'+y.toString()}>
<div className='strGrid'>{SUBJECTLIB.Abilities[y]}</div>
<div className='numGrid'>{x}</div>
<div className='numGrid'>{modAbility(x)}</div>
</div>);
});
}
class UISection3 extends React.Component{
constructor(props){
super(props);
this.state={
}
}
render(){
return(<section className='mainSection'>
<fieldset>
<legend>Abilities</legend>
<AbilityResult data={this.props.data}/>
</fieldset>
</section>);
}
}
const SavingResult=(props)=>{
return SUBJECTLIB.Abilities.map((x,y)=>{
return(<div className='savingResult' key={'savingThrows'+y.toString()}>
<div className='strGrid'>{x}</div><div className='numGrid'>{props.data['Saving Throws'][y]}</div>
</div>);
});
}
const SavingThrows=(props)=>{
return(<fieldset><legend>Saves</legend>
<SavingResult data={props.data} />
</fieldset>);
}
const PerceptionSpeedHit=(props)=>{
return(<fieldset><legend>Characteristics</legend>
<div className='savingResult'><div className='strGrid'>Hit Points</div><div className='numGrid'>{props.data['Hit Points']}</div></div>
<div className='savingResult'><div className='strGrid'>Speed</div><div className='numGrid'>{props.data['Speed']}</div></div>
</fieldset>);
}
// ARGUMENTS: {User Class (c), User Background (b), Skill Index (n)}
const checkAble=(c,b,n)=>{
if(SKILLLIB[c][n]&&!SKILLLIB[b][n]){
return false;
} else return true;
}
// ARGUMENTS: {User Proficiencey (p), User Background (b), Current Index (n)}
const applyBackToSkill=(p,b,n)=>{
if(SKILLLIB[b][n]){
return p;
} else return 0;
}
// ARGUMENTS: {Skill name (s), Mod list (m)}
const applyModsToSkill=(s,m)=>{
return m[getDescriptIndex('Abilities',MATCHSKILL[s])];
}
const respondCheckUncheck=(p,i)=>{
if(p.hasOwnProperty(i)){return p[i];} else return 0;
}
const MakeSkillSelect=(props)=>{
return SUBJECTLIB.Skills.map((x,y)=>{
var w=0;
if(props.complete){
w=props.data.Skills[y];
} else w= props.skills[y]+respondCheckUncheck(props.prof,x);
var u=checkAble(props.data.Class, props.data.Background, y);
return(<div className='skillSelect'>
{!props.complete?<input
type='checkBox'
name={x}
disabled={u}
className='numGrid'
onClick={props.checker}>
</input>:<div></div>}
<div className='strGrid'>{x}</div>
<div className='numGrid'>{w}</div>
</div>)
})
}
const Skills=(props)=>{
return(<fieldset><legend>Skills</legend>
{!props.complete?<div>{'Selections Remaining: '+props.useSkill.toString()}</div>:null}
<MakeSkillSelect data={props.data} checker={props.checker} skills={props.skills} prof={props.prof} complete={props.complete} />
{!props.complete?<button onClick={props.accept}>Accept</button>:null}
</fieldset>);
}
class UISection4 extends React.Component{
constructor(props){
super(props);
this.state={
useSkill: 0,
skillChoice:{},
Skills:[],
skillComplete:false
}
this.handleCheck=this.handleCheck.bind(this);
this.handleAccept=this.handleAccept.bind(this);
}
componentDidMount(){
const skillset = SUBJECTLIB.Skills.map((x,y)=>{
return applyModsToSkill(x,this.props.data.Modifiers)+applyBackToSkill(this.props.data.Proficiency,this.props.data.Background,y)
})
this.setState({useSkill:USESKILL[this.props.data.Class], Skills:skillset});
}
handleCheck(e){
const n=e.target.name;
if(e.target.checked){
this.setState(prevState=>(
{useSkill:prevState.useSkill-=1,
skillChoice:Object.assign({...prevState.skillChoice},{[n]:this.props.data.Proficiency})}));
}
else {
this.setState(prevState=>({useSkill:prevState.useSkill+=1,
skillChoice:Object.assign({...prevState.skillChoice},{[n]:0})}));
}
}
handleAccept(e){
if(this.state.useSkill!==0){
// Deliver Warning
} else{
for(var i=0; i<this.state.Skills.length; i++){
this.props.changeSkill(this.state.Skills[i]+(this.state.skillChoice.hasOwnProperty(SUBJECTLIB.Skills[i])?this.state.skillChoice[SUBJECTLIB.Skills[i]]:0),i);
}
e.target.disabled=true;
this.setState({skillComplete:true});
this.props.handleComplete();
}
}
render(){
return(<div>
<PerceptionSpeedHit data={this.props.data} />
<SavingThrows data={this.props.data} />
<Skills
data={this.props.data}
checker={this.handleCheck}
skills={this.state.Skills}
prof={this.state.skillChoice}
useSkill={this.state.useSkill}
accept={this.handleAccept}
complete={this.state.skillComplete}/>
</div>);
}
}
const abilToString=(s)=>{
return s.join(`%2D`);
}
class MainUI extends React.Component{
constructor(props){
super(props);
this.state={
uiLevel:1,
tweetLink: TWEETPRE+'Check+out+this+character+builder!+https%3A%2F%2Fcodepen.io%2Fvtox%2Ffull%2FYjBzpz%2F'
}
this.completeUI=this.completeUI.bind(this);
this.createNewTweet=this.createNewTweet.bind(this);
}
createNewTweet(){
var t = TWEETPRE+'I+just+created+a+new+character%3A%0D'+
this.props.data.Name.replace(/\s/gi,'+')+'%0D'+
this.props.data.Race+'+'+this.props.data.Class+'+'+this.props.data.Background+'%0D'+
'stats%3A+'+abilToString(this.props.data.Abilities)+'%0D'+
'Try+it+at+https%3A%2F%2Fcodepen.io%2Fvtox%2Ffull%2FYjBzpz%2F';
this.setState({tweetLink:t})
}
completeUI(){
this.setState({uiLevel: this.state.uiLevel+1});
console.log(this.state.uiLevel);
if(this.state.uiLevel>=3){
this.createNewTweet();
}
}
render(){
//this.props.testLog();
return(<div><Navbar tweetLink={this.state.tweetLink}/>
<div className={'mainContainer'}>
{this.state.uiLevel===1?<UISection1
testLog={this.props.testLog}
data={this.props.data}
changeNCRB={this.props.changeNCRB}
addName={this.props.addName}
changeClass={this.props.changeClass}
changeRace={this.props.changeRace}
changeBack={this.props.changeBack}
changeSpeed={this.props.changeSpeed}
handleComplete={this.completeUI}/>:
<UISection1a data={this.props.data}/>}
{this.state.uiLevel===2?
<UISection2
data={this.props.data}
handleComplete={this.completeUI}
changeAbil={this.props.changeAbil}
changeMod={this.props.changeMod}
changeSaves={this.props.changeSaves}
changeHit={this.props.changeHit}
handleComplete={this.completeUI}/>:null}
{this.state.uiLevel>=3?<UISection3 data={this.props.data} />:null}
{this.state.uiLevel>=3?
<UISection4
data={this.props.data}
changeSkill={this.props.changeSkill}
handleComplete={this.completeUI} />:null}
</div></div>);
}
}
const CHANGE_CLASS='CHANGE_CLASS';
const CHANGE_SPEED='CHANGE_SPEED';
const CHANGE_BACK='CHANGE_BACK';
const CHANGE_RACE='CHANGE_RACE';
const ADD_NAME='ADD_NAME';
const CHANGE_HIT='CHANGE_HIT';
const CHANGE_ABIL='CHANGE_ABIL';
const CHANGE_MOD='CHANGE_MOD';
const CHANGE_SKILL='CHANGE_SKILL';
const CHANGE_SAVES='CHANGE_SAVES';
const CHANGE_NCRB='CHANGE_NCRB';
const TEST_LOG='TEST_LOG';
// REDUX ACTIONS
const actions={
changeClass:(c)=>{
return {
type:CHANGE_CLASS,
payload:{'Class':c}
}
},
addName:(n)=>{
return{
type:ADD_NAME,
payload:{'Name':n}
}
},
changeRace:(r)=>{
return{
type:CHANGE_RACE,
payload:{'Race':r}
}
},
changeBack:(b)=>{
return{
type:CHANGE_BACK,
payload:{'Background':b}
}
},
changeSpeed:(s)=>{
return{
type:CHANGE_SPEED,
payload:{'Speed':s}
}
},
changeHit:(h)=>{
return{
type:CHANGE_HIT,
payload:{'Hit Points':h}
}
},
changeNCRB:(ncrb)=>{
return{
type:CHANGE_NCRB,
payload:{...ncrb}
}
},
changeAbil:(a,i)=>{
return{
type:CHANGE_ABIL,
payload:{keyName:'Abilities',value:a,iter:i}
}
},
changeMod:(m,i)=>{
return{
type:CHANGE_MOD,
payload:{keyName:'Modifiers',value:m,iter:i}
}
},
changeSkill:(s,i)=>{
return{
type:CHANGE_SKILL,
payload:{keyName:'Skills',
value:s,
iter:i}
}
},
changeSaves:(s,i)=>{
return{
type:CHANGE_SAVES,
payload:{keyName:'Saving Throws',
value:s,
iter:i}
}
},
testLog:()=>{
console.log('ACTION CREATED');
return{
type:TEST_LOG,
payload:{log: 'PAYLOAD'}
}
}
}
function reducer(state=[], action){
const {type, payload}=action;
switch(type){
case TEST_LOG:
console.log('REDUCER CAUGHT');
return state;
case CHANGE_MOD:
case CHANGE_SKILL:
case CHANGE_SAVES:
case CHANGE_ABIL:
const {keyName,value,iter}=payload;
return {...state,[keyName]: state[keyName].slice().map((x,y)=>{
if(y!==iter){ return x; } else return value;
})};
case ADD_NAME:
case CHANGE_CLASS:
case CHANGE_RACE:
case CHANGE_BACK:
case CHANGE_SPEED:
case CHANGE_HIT:
case CHANGE_NCRB:
return Object.assign({},{...state},payload);
default:
return state;
}
}
const RRContainer = connect(
function mapStateToProps(state){ return { data: state }; },
function mapDispatchToProps(dispatch){
return bindActionCreators(actions,dispatch);
}
)(MainUI);
const store = createStore(reducer, DataList);
// render(<Navbar />, document.getElementById('rootContainer'));
render(<Provider store={store}><RRContainer /></Provider>, document.getElementById('rootUI'));
View Compiled