mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-24 22:53:00 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22a480871b | ||
|
|
daa3b096b3 | ||
|
|
bfcf6ca7f2 | ||
|
|
15ffb138eb | ||
|
|
7321cc81ec | ||
|
|
ba6ba0e51f |
@@ -1,5 +1,5 @@
|
||||
|
||||
@import (less) 'naturalcrit/phbStyle/phb.style.less';
|
||||
@import (less) './client/homebrew/phbStyle/phb.style.less';
|
||||
.pane{
|
||||
position : relative;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 327 B |
|
Before Width: | Height: | Size: 530 B After Width: | Height: | Size: 530 B |
@@ -1,7 +1,7 @@
|
||||
|
||||
@import (less) 'shared/naturalcrit/styles/reset.less';
|
||||
@import (less) 'shared/naturalcrit/phbStyle/phb.fonts.css';
|
||||
@import (less) 'shared/naturalcrit/phbStyle/phb.assets.less';
|
||||
@import (less) './client/homebrew/phbStyle/phb.fonts.css';
|
||||
@import (less) './client/homebrew/phbStyle/phb.assets.less';
|
||||
//Colors
|
||||
@background : #EEE5CE;
|
||||
@noteGreen : #e0e5c1;
|
||||
|
Before Width: | Height: | Size: 864 B After Width: | Height: | Size: 864 B |
@@ -22,13 +22,13 @@ var Main = React.createClass({
|
||||
beta : false
|
||||
},
|
||||
{
|
||||
id : 'spellsort',
|
||||
path : '/spellsort',
|
||||
name : 'Spellsort',
|
||||
id : 'homebrew2',
|
||||
path : '/homebrew',
|
||||
name : 'The Homebrewery',
|
||||
icon : <HomebrewIcon />,
|
||||
desc : 'Sort and search through spells',
|
||||
desc : 'Make authentic-looking 5e homebrews using Markdown',
|
||||
|
||||
show : true,
|
||||
show : false,
|
||||
beta : true
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var Sorter = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
spells : []
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
renderSpell : function(spell){
|
||||
return <div className='spell' key={spell.id}>
|
||||
{spell.name}
|
||||
</div>
|
||||
},
|
||||
|
||||
renderSpells : function(){
|
||||
return _.map(this.props.spells, (spell)=>{
|
||||
return this.renderSpell(spell)
|
||||
});
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='sorter'>
|
||||
{this.renderSpells()}
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Sorter;
|
||||
@@ -1,97 +0,0 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var Markdown = require('marked');
|
||||
|
||||
var SpellRenderer = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
spells : []
|
||||
};
|
||||
},
|
||||
|
||||
//TODO: Add in ritual tag
|
||||
getSubtitle : function(spell){
|
||||
if(spell.level == 0) return <p><em>{spell.school} cantrip</em></p>;
|
||||
if(spell.level == 1) return <p><em>{spell.level}st-level {spell.school}</em></p>;
|
||||
if(spell.level == 2) return <p><em>{spell.level}nd-level {spell.school}</em></p>;
|
||||
if(spell.level == 3) return <p><em>{spell.level}rd-level {spell.school}</em></p>;
|
||||
return <p><em>{spell.level}th-level {spell.school}</em></p>;
|
||||
},
|
||||
|
||||
getComponents : function(spell){
|
||||
var result = [];
|
||||
if(spell.components.v) result.push('V');
|
||||
if(spell.components.s) result.push('S');
|
||||
if(spell.components.m) result.push('M ' + spell.components.m);
|
||||
return result.join(', ');
|
||||
},
|
||||
|
||||
getHigherLevels : function(spell){
|
||||
if(!spell.scales) return null;
|
||||
return <p>
|
||||
<strong><em>At Higher Levels. </em></strong>
|
||||
<span dangerouslySetInnerHTML={{__html: Markdown(spell.scales)}} />
|
||||
</p>;
|
||||
},
|
||||
|
||||
getClasses : function(spell){
|
||||
if(!spell.classes || !spell.classes.length) return null;
|
||||
|
||||
var classes = _.map(spell.classes, (cls)=>{
|
||||
return _.capitalize(cls);
|
||||
}).join(', ');
|
||||
|
||||
return <li>
|
||||
<strong>Classes:</strong> {classes}
|
||||
</li>
|
||||
},
|
||||
|
||||
|
||||
renderSpell : function(spell){
|
||||
console.log('rendering', spell);
|
||||
return <div className='spell' key={spell.id}>
|
||||
|
||||
<h4>{spell.name}</h4>
|
||||
{this.getSubtitle(spell)}
|
||||
<hr />
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Casting Time:</strong> {spell.casting_time}
|
||||
</li>
|
||||
<li>
|
||||
<strong>Range:</strong> {spell.range}
|
||||
</li>
|
||||
<li>
|
||||
<strong>Components:</strong> {this.getComponents(spell)}
|
||||
</li>
|
||||
<li>
|
||||
<strong>Duration:</strong> {spell.duration}
|
||||
</li>
|
||||
{this.getClasses(spell)}
|
||||
</ul>
|
||||
|
||||
<span dangerouslySetInnerHTML={{__html: Markdown(spell.description)}} />
|
||||
{this.getHigherLevels(spell)}
|
||||
</div>
|
||||
},
|
||||
|
||||
renderSpells : function(){
|
||||
return _.map(this.props.spells, (spell)=>{
|
||||
return this.renderSpell(spell);
|
||||
})
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='spellRenderer'>
|
||||
|
||||
<div className='phb'>
|
||||
{this.renderSpells()}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SpellRenderer;
|
||||
@@ -1,40 +0,0 @@
|
||||
@import (less) 'naturalcrit/phbStyle/phb.style.less';
|
||||
.pane{
|
||||
position : relative;
|
||||
}
|
||||
.spellRenderer{
|
||||
overflow-y : scroll;
|
||||
|
||||
&>.phb{
|
||||
margin-right : auto;
|
||||
margin-bottom : 30px;
|
||||
margin-left : auto;
|
||||
box-shadow : 1px 4px 14px #000;
|
||||
|
||||
height : auto;
|
||||
width : 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
|
||||
column-count : initial;
|
||||
column-fill : initial;
|
||||
column-gap : initial;
|
||||
column-width : initial;
|
||||
-webkit-column-count : initial;
|
||||
-moz-column-count : initial;
|
||||
-webkit-column-width : initial;
|
||||
-moz-column-width : initial;
|
||||
-webkit-column-gap : initial;
|
||||
-moz-column-gap : initial;
|
||||
|
||||
.spell{
|
||||
display: inline;
|
||||
width : 8cm;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var Nav = require('naturalcrit/nav/nav.jsx');
|
||||
var Navbar = require('./navbar/navbar.jsx');
|
||||
|
||||
var SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
||||
var SpellRenderer = require('./spellRenderer/spellRenderer.jsx');
|
||||
var Sorter = require('./sorter/sorter.jsx');
|
||||
|
||||
var SpellSort = React.createClass({
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
spells : []
|
||||
};
|
||||
},
|
||||
|
||||
handleSplitMove : function(){
|
||||
|
||||
},
|
||||
|
||||
render : function(){
|
||||
console.log(this.props.spells);
|
||||
|
||||
return <div className='spellsort page'>
|
||||
<Navbar>
|
||||
<Nav.section>
|
||||
yo
|
||||
</Nav.section>
|
||||
</Navbar>
|
||||
<div className='content'>
|
||||
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
|
||||
|
||||
<Sorter spells={this.props.spells} />
|
||||
<SpellRenderer spells={this.props.spells} />
|
||||
|
||||
</SplitPane>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SpellSort;
|
||||
@@ -1,4 +0,0 @@
|
||||
@import 'naturalcrit/styles/core.less';
|
||||
.spellsort{
|
||||
|
||||
}
|
||||
@@ -8,8 +8,8 @@ var Navbar = React.createClass({
|
||||
return <Nav.base>
|
||||
<Nav.section>
|
||||
<Nav.logo />
|
||||
<Nav.item href='/spellsort' className='spellsortLogo'>
|
||||
<div>Spellsort</div>
|
||||
<Nav.item href='/tpk' className='tpkLogo'>
|
||||
<div>Total Player Knolling</div>
|
||||
</Nav.item>
|
||||
<Nav.item>v0.0.0</Nav.item>
|
||||
</Nav.section>
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
.spellsort nav{
|
||||
.spellsortLogo{
|
||||
.tpk nav{
|
||||
.tpkLogo{
|
||||
.animate(color);
|
||||
font-family : CodeBold;
|
||||
font-size : 12px;
|
||||
@@ -13,4 +13,4 @@
|
||||
color : @teal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
client/tpk/sheetEditor/sheetEditor.jsx
Normal file
49
client/tpk/sheetEditor/sheetEditor.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
|
||||
|
||||
var SheetEditor = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
value : "",
|
||||
onChange : function(){}
|
||||
};
|
||||
},
|
||||
cursorPosition : {
|
||||
line : 0,
|
||||
ch : 0
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
var paneHeight = this.refs.main.parentNode.clientHeight;
|
||||
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
|
||||
},
|
||||
|
||||
handleTextChange : function(text){
|
||||
this.props.onChange(text);
|
||||
},
|
||||
handleCursorActivty : function(curpos){
|
||||
this.cursorPosition = curpos;
|
||||
},
|
||||
|
||||
//Called when there are changes to the editor's dimensions
|
||||
update : function(){
|
||||
this.refs.codeEditor.updateSize();
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='sheetEditor' ref='main'>
|
||||
<CodeEditor
|
||||
ref='codeEditor'
|
||||
wrap={true}
|
||||
language='jsx'
|
||||
value={this.props.value}
|
||||
onChange={this.handleTextChange}
|
||||
onCursorActivity={this.handleCursorActivty} />
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SheetEditor;
|
||||
54
client/tpk/sheetRenderer/parts/box/box.jsx
Normal file
54
client/tpk/sheetRenderer/parts/box/box.jsx
Normal file
@@ -0,0 +1,54 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var utils = require('../utils');
|
||||
|
||||
var Box = React.createClass({
|
||||
mixins : [utils],
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
//name : 'box',
|
||||
defaultData : {},
|
||||
|
||||
id : '',
|
||||
title : '',
|
||||
label : '',
|
||||
shadow : false,
|
||||
border : false
|
||||
};
|
||||
},
|
||||
|
||||
handleChange : function(newData){
|
||||
this.updateData(newData);
|
||||
},
|
||||
|
||||
renderChildren : function(){
|
||||
return React.Children.map(this.props.children, (child)=>{
|
||||
if(!React.isValidElement(child)) return null;
|
||||
return React.cloneElement(child, {
|
||||
onChange : this.handleChange,
|
||||
data : this.data()
|
||||
})
|
||||
})
|
||||
},
|
||||
renderTitle : function(){
|
||||
if(this.props.title) return <h5 className='title'>{this.props.title}</h5>
|
||||
},
|
||||
renderLabel : function(){
|
||||
if(this.props.label) return <h5 className='label'>{this.props.label}</h5>
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className={cx('box', this.props.className, {
|
||||
shadow : this.props.shadow,
|
||||
border : this.props.border
|
||||
})}>
|
||||
{this.renderTitle()}
|
||||
{this.renderChildren()}
|
||||
{this.renderLabel()}
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Box;
|
||||
30
client/tpk/sheetRenderer/parts/box/box.less
Normal file
30
client/tpk/sheetRenderer/parts/box/box.less
Normal file
@@ -0,0 +1,30 @@
|
||||
.box{
|
||||
position : relative;
|
||||
padding : 10px;
|
||||
margin: 10px;
|
||||
|
||||
&.border{
|
||||
border: 1px solid black;
|
||||
}
|
||||
&.shadow{
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
|
||||
h5{
|
||||
text-transform: uppercase;
|
||||
font-size : 0.6em;
|
||||
text-align: center;
|
||||
width : 100%;
|
||||
font-weight: 800;
|
||||
|
||||
&.title{
|
||||
margin-top: -5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
&.label{
|
||||
margin-bottom: -5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
client/tpk/sheetRenderer/parts/index.js
Normal file
13
client/tpk/sheetRenderer/parts/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
TextInput : require('./textInput/textInput.jsx'),
|
||||
PlayerInfo : require('./playerInfo/playerInfo.jsx'),
|
||||
|
||||
SkillList : require('./skillList/skillList.jsx'),
|
||||
Skill : require('./skill/skill.jsx'),
|
||||
|
||||
//ShadowBox : require('./shadowBox/shadowBox.jsx'),
|
||||
//BorderBox : require('./borderBox/borderBox.jsx'),
|
||||
|
||||
|
||||
Box : require('./box/box.jsx')
|
||||
}
|
||||
25
client/tpk/sheetRenderer/parts/playerInfo/playerInfo.jsx
Normal file
25
client/tpk/sheetRenderer/parts/playerInfo/playerInfo.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var TextInput = require('../textInput/textInput.jsx');
|
||||
var Box = require('../box/box.jsx');
|
||||
|
||||
var PlayerInfo = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
title: "player info",
|
||||
border : true
|
||||
};
|
||||
},
|
||||
render : function(){
|
||||
return <Box className='playerInfo' {...this.props} >
|
||||
<TextInput label="Name" placeholder="name" />
|
||||
<TextInput label="Class" />
|
||||
<TextInput label="Race" />
|
||||
{this.props.children}
|
||||
</Box>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = PlayerInfo;
|
||||
@@ -0,0 +1,3 @@
|
||||
.playerInfo{
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
62
client/tpk/sheetRenderer/parts/skill/skill.jsx
Normal file
62
client/tpk/sheetRenderer/parts/skill/skill.jsx
Normal file
@@ -0,0 +1,62 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var utils = require('../utils');
|
||||
|
||||
var Skill = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
name : 'skill',
|
||||
defaultData : {
|
||||
prof : false,
|
||||
expert : false,
|
||||
val : ''
|
||||
},
|
||||
|
||||
id : '',
|
||||
label : '',
|
||||
sublabel : '',
|
||||
showExpert : false
|
||||
};
|
||||
},
|
||||
|
||||
id : utils.id,
|
||||
data : utils.data,
|
||||
updateData : utils.updateData,
|
||||
|
||||
handleToggleProf : function(){
|
||||
this.updateData({
|
||||
prof : !this.data().prof
|
||||
})
|
||||
},
|
||||
handleToggleExpert : function(){
|
||||
this.updateData({
|
||||
expert : !this.data().expert
|
||||
})
|
||||
},
|
||||
handleValChange : function(e){
|
||||
console.log('yo');
|
||||
this.updateData({
|
||||
val : e.target.value
|
||||
})
|
||||
},
|
||||
renderExpert : function(){
|
||||
if(this.props.showExpert){
|
||||
return <input type="radio" className='expertToggle' onChange={this.handleToggleExpert} checked={this.data().expert} />
|
||||
}
|
||||
},
|
||||
render : function(){
|
||||
return <div className='skill'>
|
||||
{this.renderExpert()}
|
||||
<input type="radio" className='skillToggle' onChange={this.handleToggleProf} checked={this.data().prof} />
|
||||
<input type='text' onChange={this.handleValChange} value={this.data().val} />
|
||||
<label>
|
||||
{this.props.label}
|
||||
<small>{this.props.sublabel}</small>
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Skill;
|
||||
35
client/tpk/sheetRenderer/parts/skill/skill.less
Normal file
35
client/tpk/sheetRenderer/parts/skill/skill.less
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
.skill{
|
||||
position : relative;
|
||||
padding-left : 15px;
|
||||
input[type="radio"]{
|
||||
margin : 0px;
|
||||
}
|
||||
.expertToggle{
|
||||
position : absolute;
|
||||
top : 1px;
|
||||
left : 0px;
|
||||
}
|
||||
input[type="text"]{
|
||||
width : 25px;
|
||||
margin-left : 10px;
|
||||
background-color : transparent;
|
||||
text-align : center;
|
||||
border : none;
|
||||
border-bottom : 1px solid black;
|
||||
outline : none;
|
||||
&:focus{
|
||||
background-color : #ddd;
|
||||
}
|
||||
}
|
||||
label{
|
||||
margin-left : 10px;
|
||||
font-size : 0.8em;
|
||||
small{
|
||||
margin-left : 5px;
|
||||
font-size : 0.8em;
|
||||
color : #999;
|
||||
text-transform : uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
client/tpk/sheetRenderer/parts/skillList/skillList.jsx
Normal file
61
client/tpk/sheetRenderer/parts/skillList/skillList.jsx
Normal file
@@ -0,0 +1,61 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var Skill = require('../skill/skill.jsx');
|
||||
var Box = require('../box/box.jsx');
|
||||
|
||||
|
||||
var skill_list = [
|
||||
{name : 'Acrobatics', stat : 'Dex'},
|
||||
{name : 'Animal Handling', stat : 'Wis'},
|
||||
{name : 'Arcana', stat : 'Int'},
|
||||
{name : 'Athletics', stat : 'Str'},
|
||||
{name : 'Deception', stat : 'Cha'},
|
||||
{name : 'History', stat : 'Int'},
|
||||
{name : 'Insight', stat : 'Wis'},
|
||||
{name : 'Intimidation', stat : 'Cha'},
|
||||
{name : 'Investigation', stat : 'Int'},
|
||||
{name : 'Medicine', stat : 'Wis'},
|
||||
{name : 'Nature', stat : 'Int'},
|
||||
{name : 'Perception', stat : 'Wis'},
|
||||
{name : 'Performance', stat : 'Cha'},
|
||||
{name : 'Persuasion', stat : 'Cha'},
|
||||
{name : 'Religion', stat : 'Int'},
|
||||
{name : 'Sleight of Hand', stat : 'Dex'},
|
||||
{name : 'Stealth', stat : 'Dex'},
|
||||
{name : 'Survival', stat : 'Wis'}
|
||||
]
|
||||
|
||||
|
||||
var SkillList = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
name : 'skills',
|
||||
|
||||
//title : 'Skills',
|
||||
shadow : true,
|
||||
border : false,
|
||||
showExpert : false
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
renderSkills : function(){
|
||||
return _.map(skill_list, (skill)=>{
|
||||
return <Skill
|
||||
label={skill.name}
|
||||
sublabel={'(' + skill.stat + ')'}
|
||||
showExpert={this.props.showExpert} />
|
||||
})
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <Box className='skillList' {...this.props}>
|
||||
{this.renderSkills()}
|
||||
{this.props.children}
|
||||
</Box>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SkillList;
|
||||
3
client/tpk/sheetRenderer/parts/skillList/skillList.less
Normal file
3
client/tpk/sheetRenderer/parts/skillList/skillList.less
Normal file
@@ -0,0 +1,3 @@
|
||||
.COM{
|
||||
|
||||
}
|
||||
44
client/tpk/sheetRenderer/parts/textInput/textInput.jsx
Normal file
44
client/tpk/sheetRenderer/parts/textInput/textInput.jsx
Normal file
@@ -0,0 +1,44 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var utils = require('../utils');
|
||||
|
||||
var TextInput = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
name : 'text',
|
||||
defaultData : '',
|
||||
|
||||
id : '',
|
||||
label : '',
|
||||
};
|
||||
},
|
||||
|
||||
id : utils.id,
|
||||
data : utils.data,
|
||||
updateData : utils.updateData,
|
||||
|
||||
handleChange : function(e){
|
||||
this.updateData(e.target.value);
|
||||
},
|
||||
|
||||
renderLabel : function(){
|
||||
if(this.props.label) return <label htmlFor={this.id()}>{this.props.label}</label>
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='textInput'>
|
||||
{this.renderLabel()}
|
||||
<input
|
||||
id={this.id()}
|
||||
type='text'
|
||||
onChange={this.handleChange}
|
||||
value={this.data()}
|
||||
placeholder={this.props.placeholder}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = TextInput;
|
||||
6
client/tpk/sheetRenderer/parts/textInput/textInput.less
Normal file
6
client/tpk/sheetRenderer/parts/textInput/textInput.less
Normal file
@@ -0,0 +1,6 @@
|
||||
.textInput{
|
||||
label{
|
||||
display: inline-block;
|
||||
width : 50px;
|
||||
}
|
||||
}
|
||||
33
client/tpk/sheetRenderer/parts/utils.js
Normal file
33
client/tpk/sheetRenderer/parts/utils.js
Normal file
@@ -0,0 +1,33 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
module.exports = {
|
||||
id : function(){
|
||||
if(this.props.id) return this.props.id;
|
||||
if(this.props.label) return _.snakeCase(this.props.label);
|
||||
if(this.props.title) return _.snakeCase(this.props.title);
|
||||
return this.props.name;
|
||||
},
|
||||
data : function(){
|
||||
if(!this.id()) return this.props.data || this.props.defaultData;
|
||||
if(this.props.data && this.props.data[this.id()]) return this.props.data[this.id()];
|
||||
return this.props.defaultData;
|
||||
},
|
||||
updateData : function(val){
|
||||
if(typeof this.props.onChange !== 'function') throw "No onChange handler set";
|
||||
|
||||
var newVal = val;
|
||||
|
||||
//Clone the data if it's an object to avoid mutation bugs
|
||||
if(_.isObject(val)) newVal = _.extend({}, this.data(), val);
|
||||
|
||||
if(this.id()){
|
||||
this.props.onChange({
|
||||
[this.id()] : newVal
|
||||
});
|
||||
}else{
|
||||
//If the box has no id, don't add it to the chain
|
||||
this.props.onChange(newVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
79
client/tpk/sheetRenderer/sheetRenderer.jsx
Normal file
79
client/tpk/sheetRenderer/sheetRenderer.jsx
Normal file
@@ -0,0 +1,79 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var jsx2json = require('naturalcrit/jsx-parser');
|
||||
|
||||
var Parts = require('./parts');
|
||||
|
||||
|
||||
var SheetRenderer = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
code : '',
|
||||
characterData : {},
|
||||
onChange : function(){},
|
||||
};
|
||||
},
|
||||
|
||||
renderElement : function(node, key){
|
||||
if(!node.tag) return null;
|
||||
|
||||
if(!Parts[node.tag]) throw 'Could Not Find Element: ' + node.tag
|
||||
|
||||
return React.createElement(
|
||||
Parts[node.tag],
|
||||
{key : key, ...node.props},
|
||||
...this.renderChildren(node.children))
|
||||
},
|
||||
renderChildren : function(nodes){
|
||||
return _.map(nodes, (node, index)=>{
|
||||
if(_.isString(node)) return node;
|
||||
return this.renderElement(node, index);
|
||||
})
|
||||
},
|
||||
renderSheet : function(){
|
||||
try{
|
||||
var nodes = jsx2json(this.props.code);
|
||||
nodes = _.map(nodes, (node)=>{
|
||||
node.props.data = this.props.characterData;
|
||||
node.props.onChange = (newData)=>{
|
||||
this.props.onChange(_.extend(this.props.characterData, newData));
|
||||
}
|
||||
return node
|
||||
})
|
||||
return this.renderChildren(nodes);
|
||||
}catch(e){
|
||||
return <div>Error bruh {e.toString()}</div>
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
render : function(){
|
||||
return <div className='sheetRenderer'>
|
||||
|
||||
<h2>Character Sheet</h2>
|
||||
|
||||
<div className='sheetContainer' ref='sheetContainer'>
|
||||
{this.renderSheet()}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = SheetRenderer;
|
||||
|
||||
|
||||
/*
|
||||
|
||||
<Temp text="cool">yo test <a href="google.com">link</a> </Temp>
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
11
client/tpk/sheetRenderer/sheetRenderer.less
Normal file
11
client/tpk/sheetRenderer/sheetRenderer.less
Normal file
@@ -0,0 +1,11 @@
|
||||
.sheetRenderer{
|
||||
|
||||
padding-right : 10px;
|
||||
|
||||
|
||||
.sheetContainer{
|
||||
background-color: white;
|
||||
padding : 20px;
|
||||
}
|
||||
|
||||
}
|
||||
78
client/tpk/tpk.jsx
Normal file
78
client/tpk/tpk.jsx
Normal file
@@ -0,0 +1,78 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var Nav = require('naturalcrit/nav/nav.jsx');
|
||||
var Navbar = require('./navbar/navbar.jsx');
|
||||
|
||||
var SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
||||
var SheetEditor = require('./sheetEditor/sheetEditor.jsx');
|
||||
var SheetRenderer = require('./sheetRenderer/sheetRenderer.jsx');
|
||||
|
||||
|
||||
const SPLATSHEET_TEMPLATE = 'splatsheet_template';
|
||||
const SPLATSHEET_DATA = 'splatsheet_data';
|
||||
|
||||
|
||||
|
||||
var TPK = React.createClass({
|
||||
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
sheetCode: "<Box>\n\t<TextInput label='test' />\n</Box>",
|
||||
|
||||
sheetData : {}
|
||||
};
|
||||
},
|
||||
|
||||
//remove later
|
||||
componentDidMount: function() {
|
||||
this.setState({
|
||||
sheetCode : localStorage.getItem(SPLATSHEET_TEMPLATE) || this.state.sheetCode,
|
||||
sheetData : JSON.parse(localStorage.getItem(SPLATSHEET_DATA)) || this.state.sheetData
|
||||
})
|
||||
},
|
||||
|
||||
handleSplitMove : function(){
|
||||
this.refs.editor.update();
|
||||
},
|
||||
|
||||
handleCodeChange : function(code){
|
||||
this.setState({
|
||||
sheetCode : code
|
||||
});
|
||||
|
||||
localStorage.setItem(SPLATSHEET_TEMPLATE, code);
|
||||
},
|
||||
|
||||
handleDataChange : function(data){
|
||||
this.setState({
|
||||
sheetData : JSON.parse(JSON.stringify(data)),
|
||||
});
|
||||
localStorage.setItem(SPLATSHEET_DATA, JSON.stringify(data));
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='tpk page'>
|
||||
<Navbar>
|
||||
<Nav.section>
|
||||
<Nav.item>
|
||||
yo dawg
|
||||
</Nav.item>
|
||||
</Nav.section>
|
||||
</Navbar>
|
||||
<div className='content'>
|
||||
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
|
||||
<SheetEditor value={this.state.sheetCode} onChange={this.handleCodeChange} ref='editor' />
|
||||
<SheetRenderer
|
||||
code={this.state.sheetCode}
|
||||
characterData={this.state.sheetData}
|
||||
onChange={this.handleDataChange} />
|
||||
</SplitPane>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = TPK;
|
||||
19
client/tpk/tpk.less
Normal file
19
client/tpk/tpk.less
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
@import 'naturalcrit/styles/core.less';
|
||||
.tpk{
|
||||
height : 100%;
|
||||
background-color : @steel;
|
||||
.page{
|
||||
display : flex;
|
||||
height : 100%;
|
||||
flex-direction : column;
|
||||
.content{
|
||||
position : relative;
|
||||
//height : calc(~"100% - 29px"); //Navbar height
|
||||
|
||||
height : 100%;
|
||||
|
||||
flex : auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ var gulp = vitreumTasks(gulp, {
|
||||
entryPoints: [
|
||||
'./client/main',
|
||||
'./client/homebrew',
|
||||
'./client/spellsort',
|
||||
'./client/tpk',
|
||||
'./client/admin'
|
||||
],
|
||||
|
||||
@@ -30,6 +30,7 @@ var gulp = vitreumTasks(gulp, {
|
||||
"codemirror",
|
||||
"codemirror/mode/gfm/gfm.js",
|
||||
'codemirror/mode/javascript/javascript.js',
|
||||
'codemirror/mode/jsx/jsx.js',
|
||||
|
||||
"moment",
|
||||
"superagent",
|
||||
@@ -44,7 +45,7 @@ var gulp = vitreumTasks(gulp, {
|
||||
var rename = require('gulp-rename');
|
||||
var less = require('gulp-less');
|
||||
gulp.task('phb', function(){
|
||||
gulp.src('./shared/naturalcrit/phbStyle/phb.style.less')
|
||||
gulp.src('./client/homebrew/phbStyle/phb.style.less')
|
||||
.pipe(less())
|
||||
.pipe(rename('phb.standalone.css'))
|
||||
.pipe(gulp.dest('./'));
|
||||
|
||||
@@ -45,9 +45,8 @@ app.get('/admin', function(req, res){
|
||||
app = require('./server/homebrew.api.js')(app);
|
||||
app = require('./server/homebrew.server.js')(app);
|
||||
|
||||
|
||||
//Populate Spellsort routes
|
||||
app = require('./server/spellsort.server.js')(app);
|
||||
//Populate TPK routes
|
||||
app = require('./server/tpk.server.js')(app);
|
||||
|
||||
|
||||
app.get('*', function (req, res) {
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
module.exports = {
|
||||
"Abi-Dalzim's Horrid Wilting": {
|
||||
"athigherlevel": "",
|
||||
"description": "(a bit of sponge)\r\nYou draw the moisture from every creature in a 30-foot cube centered on a point you choose within range. Each creature in that area must make a Constitution saving throw. Constructs and undead aren't affected, and plants and water elementals make this saving throw with disadvantage. A creature takes 10d8 necrotic damage on a failed save, or half as much damage on a successful one.",
|
||||
"source": "Elemental Evil",
|
||||
"level": "8",
|
||||
"range": "150 feet",
|
||||
"casting_time": "1 Action",
|
||||
"id": "406",
|
||||
"slug": "abi-dalzims-horrid-wilting",
|
||||
"scag": "0",
|
||||
"page": "15",
|
||||
"components": "V, S, M",
|
||||
"ritual": "No",
|
||||
"name": "Abi-Dalzim's Horrid Wilting",
|
||||
"school": "Necromancy",
|
||||
"ee": "1",
|
||||
"classes": [
|
||||
"Sorcerer",
|
||||
"Wizard"
|
||||
],
|
||||
"duration": "Instantaneous",
|
||||
"concentration": "No"
|
||||
},
|
||||
"Absorb Elements ": {
|
||||
"athigherlevel": " When you cast this spell using a spell slot of 2nd level or higher, the extra damage increases by 1d6 for each slot level above 1st.",
|
||||
"description": "1 Reaction, which you take when you take acid, cold, fire, lightning, or thunder damage\n\nThe spell captures some of the incoming energy, lessening its effect on you and storing it for your next melee attack. You have resistance to the triggering damage type until the start of your next turn. Also, the first time you hit with a melee attack on your next turn, the target takes an extra 1d6 damage of the triggering type, and the spell ends.\r\n",
|
||||
"source": "Elemental Evil",
|
||||
"level": "1",
|
||||
"range": "Self",
|
||||
"casting_time": "Special",
|
||||
"id": "377",
|
||||
"slug": "absorb-elements",
|
||||
"scag": "0",
|
||||
"page": "15",
|
||||
"components": "S",
|
||||
"ritual": "No",
|
||||
"name": "Absorb Elements ",
|
||||
"school": "Abjuration",
|
||||
"ee": "1",
|
||||
"classes": [
|
||||
"Druid",
|
||||
"Ranger",
|
||||
"Wizard"
|
||||
],
|
||||
"duration": "1 round",
|
||||
"concentration": "No"
|
||||
},
|
||||
"Acid Splash": {
|
||||
"athigherlevel": "This spell's damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).",
|
||||
"description": "You hurl a bubble of acid. \nChoose one creature within range, or choose two creatures within range that are within 5 feet of each other. A target must succeed on a Dexterity saving throw or take 1d6 acid damage. \n\n",
|
||||
"source": "Player's Handbook",
|
||||
"level": "0",
|
||||
"range": "60 feet",
|
||||
"casting_time": "1 Action",
|
||||
"id": "1",
|
||||
"slug": "acid-splash",
|
||||
"scag": "0",
|
||||
"page": "211",
|
||||
"components": "V, S",
|
||||
"ritual": "No",
|
||||
"name": "Acid Splash",
|
||||
"school": "Conjuration",
|
||||
"ee": "0",
|
||||
"classes": [
|
||||
"Sorcerer",
|
||||
"Wizard"
|
||||
],
|
||||
"duration": "Instantaneous",
|
||||
"concentration": "No"
|
||||
},
|
||||
"Aganazzar's Scorcher": {
|
||||
"athigherlevel": " When you cast this spell using a spell slot of 3rd level or higher, the damage increases by 1d8 for each slot level above 2nd.",
|
||||
"description": "A line of roaring flame 30 feet long and 5 feet wide emanates from you in a direction you choose. Each creature in the line must make a Dexterity saving throw. A creature takes 3d8 fire damage on a failed save, or half as much damage on a successful one.\r\n",
|
||||
"source": "Elemental Evil",
|
||||
"level": "2",
|
||||
"range": "30 feet",
|
||||
"casting_time": "1 Action",
|
||||
"id": "399",
|
||||
"slug": "aganazzars-scorcher",
|
||||
"scag": "0",
|
||||
"page": "15",
|
||||
"components": "V, S, M",
|
||||
"ritual": "No",
|
||||
"name": "Aganazzar's Scorcher",
|
||||
"school": "Evocation",
|
||||
"ee": "1",
|
||||
"classes": [
|
||||
"Sorcerer",
|
||||
"Wizard"
|
||||
],
|
||||
"duration": "Instantaneous",
|
||||
"concentration": "No"
|
||||
},
|
||||
"Aid": {
|
||||
"athigherlevel": "When you cast this spell using a spell slot of 3rd level or higher, a target's hit points increase by an additional 5 for each slot level above 2nd.",
|
||||
"description": "Your spell bolsters your allies with toughness and resolve. \nChoose up to three creatures within range. Each target's hit point maximum and current hit points increase by 5 for the duration.",
|
||||
"source": "Player's Handbook",
|
||||
"level": "2",
|
||||
"range": "30 feet",
|
||||
"casting_time": "1 Action",
|
||||
"id": "2",
|
||||
"slug": "aid",
|
||||
"scag": "0",
|
||||
"page": "211",
|
||||
"components": "V, S, M (a tiny strip of white cloth)",
|
||||
"ritual": "No",
|
||||
"name": "Aid",
|
||||
"school": "Abjuration",
|
||||
"ee": "0",
|
||||
"classes": [
|
||||
"Cleric",
|
||||
"Paladin"
|
||||
],
|
||||
"duration": "8 hours",
|
||||
"concentration": "No"
|
||||
},
|
||||
"Alarm (Ritual)": {
|
||||
"athigherlevel": "",
|
||||
"description": "You set an alarm against unwanted intrusion. \r\nChoose a door, a window, or an area within range that is no larger than a 20-foot cube. Until the spell ends, an alarm alerts you whenever a tiny or larger creature touches or enters the warded area. When you cast the spell, you can designate creatures that won't set off the alarm. You also choose whether the alarm is mental or audible. \n\nA mental alarm alerts you with a ping in your mind if you are within 1 mile of the warded area. This ping awakens you if you are sleeping. \r\nAn audible alarm produces the sound of a hand bell for 10 seconds within 60 feet.",
|
||||
"source": "Player's Handbook",
|
||||
"level": "1",
|
||||
"range": "30 feet",
|
||||
"casting_time": "1 Minute",
|
||||
"id": "3",
|
||||
"slug": "alarm-ritual",
|
||||
"scag": "0",
|
||||
"page": "211",
|
||||
"components": "V, S, M (a tiny bell and a piece of fine silver wire)",
|
||||
"ritual": "Yes",
|
||||
"name": "Alarm (Ritual)",
|
||||
"school": "Abjuration",
|
||||
"ee": "0",
|
||||
"classes": [
|
||||
"Ranger",
|
||||
"Wizard"
|
||||
],
|
||||
"duration": "8 hours",
|
||||
"concentration": "No"
|
||||
},
|
||||
"Alter Self": {
|
||||
"athigherlevel": "",
|
||||
"description": "You assume a different form.\r\nWhen you cast the spell, choose one of the following options, the effects of which last for the duration of the spell. While the spell lasts, you can end one option as an action to gain the benefits of a different one.\r\n\r\nAquatic Adaptation. You adapt your body to an aquatic environment, sprouting gills, and growing webbing between your fingers. You can breathe underwater and gain a swimming speed equal to your walking speed.\r\n\r\nChange Appearance. You transform your appearnce. You decide what you look like, including your height, weight, facial features, sound of your voice, hair length, coloration, and distinguishing characteristics, if any. \r\nYou can make yourself appear as a member of another race, though none of your statistics change. You also don't appear as a creature of a different size than you, and your basic shape stays the same; if you're bipedal, you can't use this spell to become quadrupedal, for instance. At any time for the duration of the spell, you can use your action to change your appearance in this way again.\r\n\n Natural Weapons. You grow claws, fangs, spines, horns, or a different natural weapon of your choice. Your unarmed strikes deal 1d6 bludgeoning, piercing, or slashing damage, as appropriate to the natural weapon you chose, and you are proficient with your unarmed strikes. Finally, the natural weapon is magic and you have a +1 bonus to the attack and damage rolls you make using it.",
|
||||
"source": "Player's Handbook",
|
||||
"level": "2",
|
||||
"range": "Self",
|
||||
"casting_time": "1 Action",
|
||||
"id": "4",
|
||||
"slug": "alter-self",
|
||||
"scag": "0",
|
||||
"page": "211",
|
||||
"components": "V, S",
|
||||
"ritual": "No",
|
||||
"name": "Alter Self",
|
||||
"school": "Transmutation",
|
||||
"ee": "0",
|
||||
"classes": [
|
||||
"Sorcerer",
|
||||
"Wizard"
|
||||
],
|
||||
"duration": "Concentration, up to 1 hour",
|
||||
"concentration": "Yes"
|
||||
},
|
||||
}
|
||||
@@ -7,6 +7,15 @@ var HomebrewModel = require('./homebrew.model.js').model;
|
||||
|
||||
module.exports = function(app){
|
||||
|
||||
/*
|
||||
app.get('/homebrew/new', function(req, res){
|
||||
var newHomebrew = new HomebrewModel();
|
||||
newHomebrew.save(function(err, obj){
|
||||
return res.redirect('/homebrew/edit/' + obj.editId);
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
|
||||
//Edit Page
|
||||
app.get('/homebrew/edit/:id', function(req, res){
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
var spells = require('./5espells.js');
|
||||
|
||||
String.prototype.replaceAll = function(s,r){return this.split(s).join(r)}
|
||||
|
||||
|
||||
var parsedSpells = _.map(spells, (spell)=>{
|
||||
|
||||
var comp = {}
|
||||
|
||||
var name = spell.name.replace(' (Ritual)', '');
|
||||
|
||||
|
||||
return {
|
||||
id : _.snakeCase(name),
|
||||
name : name,
|
||||
description : spell.description
|
||||
.replaceAll('\r\n', '\n')
|
||||
.replaceAll(' ', ''),
|
||||
|
||||
scales : spell.athigherlevel,
|
||||
|
||||
components : {},
|
||||
classes : _.map(spell.classes || [], (cls)=>{return cls.toLowerCase();}),
|
||||
|
||||
|
||||
level : Number(spell.level),
|
||||
|
||||
ritual : spell.ritual == "Yes",
|
||||
concentration : spell.concentration == "Yes",
|
||||
|
||||
range : spell.range,
|
||||
duration : spell.duration,
|
||||
|
||||
|
||||
school : spell.school.toLowerCase(),
|
||||
|
||||
source : spell.source,
|
||||
page : spell.page,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
module.exports = parsedSpells;
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
var spells = [
|
||||
{
|
||||
name : "Acid Splash",
|
||||
casting_time : "1 action",
|
||||
components : {v : true, s : true},
|
||||
description : `You hurl a bubble of acid. Choose one creature within range, or choose two creatures within range that are within 5 feet of each other.
|
||||
A target must succeed on a Dexterity saving throw or take 1d6 acid damage.`,
|
||||
scales : 'This spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).',
|
||||
duration : "Instantaneous",
|
||||
concentration : false,
|
||||
level : 0,
|
||||
ritual : false,
|
||||
range : "60 feet",
|
||||
school : "Conjuration",
|
||||
classes : ["sorcerer", "wizard"],
|
||||
source : "Player's Handbook",
|
||||
page : "pg.211"
|
||||
},
|
||||
{
|
||||
name : "Aid",
|
||||
casting_time : "1 action",
|
||||
components : {v : true, s : true, m : "(a tiny strip of white cloth)"},
|
||||
description : `Your spell bolsters your allies with toughness and resolve. Choose up to three creatures within range. Each target’s hit point maximum and current hit points increase by 5 for the duration. At Higher Levels. When you cast this spell using a spell slot of 3rd level or higher, a target’s hit points increase by an additional 5 for each slot level above 2nd.`,
|
||||
duration : "8 hours",
|
||||
concentration : false,
|
||||
level : 2,
|
||||
ritual : false,
|
||||
range : "30 feet",
|
||||
school : "Abjuration",
|
||||
classes : [],
|
||||
source : "Player's Handbook",
|
||||
page : "pg.211"
|
||||
},
|
||||
{
|
||||
name : "Antimagic Field",
|
||||
casting_time : "1 action",
|
||||
components : {v : true, s : true, m : "(a pinch of powdered iron or iron filings)"},
|
||||
description : `A 10-foot-radius invisible sphere of antimagic surrounds you. This area is divorced from the magical energy that suffuses the multiverse. Within the sphere, spells can’t be cast, summoned creatures disappear, and even magic items become mundane. Until the spell ends, the sphere moves with you, centered on you. Spells and other magical effects, except those created by an artifact or a deity, are suppressed in the sphere and can’t protrude into it. A slot expended to cast a suppressed spell is consumed. While an effect is suppressed, it doesn’t function, but the time it spends suppressed counts against its duration. Targeted Effects. Spells and other magical effects, such as magic missile and charm person, that target a creature or an object in the sphere have no effect on that target. Areas of Magic. The area of another spell or magical effect, such as fireball, can’t extend into the sphere. If the sphere overlaps an area of magic, the part of the area that is covered by the sphere is suppressed. For example, the flames created by a wall of fire are suppressed within the sphere, creating a gap in the wall if the overlap is large enough. Spells. Any active spell or other magical effect on a creature or an object in the sphere is suppressed while the creature or object is in it. Magic Items. The properties and powers of magic items are suppressed in the sphere. For example, a +1 longsword in the sphere functions as a nonmagical longsword. A magic weapon’s properties and powers are suppressed if it is used against a target in the sphere or wielded by an attacker in the sphere. If a magic weapon or a piece of magic ammunition fully leaves the sphere (for example, if you fire a magic arrow or throw a magic spear at a target outside the sphere), the magic of the item ceases to be suppressed as soon as it exits. Magical Travel. Teleportation and planar travel fail to work in the sphere, whether the sphere is the destination or the departure point for such magical travel. A portal to another location, world, or plane of existence, as well as an opening to an extradimensional space such as that created by the rope trick spell, temporarily closes while in the sphere. Creatures and Objects. A creature or object summoned or created by magic temporarily winks out of existence in the sphere. Such a creature instantly reappears once the space the creature occupied is no longer within the sphere. Dispel Magic. Spells and magical effects such as dispel magic have no effect on the sphere. Likewise, the spheres created by different antimagic field spells don’t nullify each other.`,
|
||||
duration : "Concentration, up to 1 hour",
|
||||
concentration : true,
|
||||
level : 8,
|
||||
ritual : false,
|
||||
range : "Self (10-foot-radius sphere)",
|
||||
school : "Abjuration",
|
||||
classes : [],
|
||||
source : "Player's Handbook",
|
||||
page : "pg.213"
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
module.exports = _.map(spells, (spell)=>{
|
||||
spell.id = _.snakeCase(spell.name);
|
||||
return spell;
|
||||
});
|
||||
@@ -1,21 +1,16 @@
|
||||
var _ = require('lodash');
|
||||
var vitreumRender = require('vitreum/render');
|
||||
|
||||
|
||||
|
||||
console.log(JSON.stringify(require('./parsespell.js'), null, ' '));
|
||||
|
||||
|
||||
module.exports = function(app){
|
||||
app.get('/spellsort*', (req, res)=>{
|
||||
|
||||
//Edit Page
|
||||
app.get('/tpk*', function(req, res){
|
||||
vitreumRender({
|
||||
page: './build/spellsort/bundle.dot',
|
||||
page: './build/tpk/bundle.dot',
|
||||
globals:{},
|
||||
prerenderWith : './client/spellsort/spellsort.jsx',
|
||||
prerenderWith : './client/tpk/tpk.jsx',
|
||||
initialProps: {
|
||||
url: req.originalUrl,
|
||||
//spells : require('./spellsort.spells.js')
|
||||
spells : require('./parsespell.js')
|
||||
},
|
||||
clearRequireCache : !process.env.PRODUCTION,
|
||||
}, function (err, page) {
|
||||
@@ -10,6 +10,7 @@ if(typeof navigator !== 'undefined'){
|
||||
//Language Modes
|
||||
require('codemirror/mode/gfm/gfm.js'); //Github flavoured markdown
|
||||
require('codemirror/mode/javascript/javascript.js');
|
||||
require('codemirror/mode/jsx/jsx.js');
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +39,7 @@ var CodeEditor = React.createClass({
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps){
|
||||
if(this.codeMirror && nextProps.value !== undefined && this.codeMirror.getValue() != nextProps.value) {
|
||||
if(this.codeMirror && nextProps.value && this.codeMirror.getValue() != nextProps.value) {
|
||||
this.codeMirror.setValue(nextProps.value);
|
||||
}
|
||||
},
|
||||
|
||||
228
shared/naturalcrit/jsx-parser.js
Normal file
228
shared/naturalcrit/jsx-parser.js
Normal file
@@ -0,0 +1,228 @@
|
||||
var WHITESPACE = /(\s|\t|\n|\r)/g;
|
||||
var NUMBERS = /[0-9]/;
|
||||
var LETTERS = /[a-zA-Z_]/;
|
||||
|
||||
var tokenizer = function(input){
|
||||
var tokens = [];
|
||||
var current = 0;
|
||||
var inTag = false;
|
||||
|
||||
while(current < input.length){
|
||||
var char = input[current];
|
||||
|
||||
var getToken = function(regex){
|
||||
var value = '';
|
||||
while(regex.test(char) && current < input.length){
|
||||
value += char;
|
||||
char = input[++current];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
if(inTag){
|
||||
if(char == '>'){
|
||||
inTag = false;
|
||||
tokens.push({
|
||||
type : 'closeTag'
|
||||
})
|
||||
}
|
||||
else if(char == '/' && input[current+1] == '>'){
|
||||
inTag = false;
|
||||
tokens.push({
|
||||
type : 'endTag'
|
||||
})
|
||||
current++;
|
||||
}
|
||||
else if(char == '='){
|
||||
tokens.push({
|
||||
type : 'equals'
|
||||
});
|
||||
}
|
||||
else if(WHITESPACE.test(char)){
|
||||
|
||||
}
|
||||
else if(NUMBERS.test(char)){
|
||||
tokens.push({
|
||||
type : 'number',
|
||||
value : getToken(NUMBERS)*1
|
||||
});
|
||||
current--;
|
||||
}
|
||||
else if(LETTERS.test(char)){
|
||||
var word = getToken(LETTERS);
|
||||
if(word == 'true' || word == 'false'){
|
||||
tokens.push({
|
||||
type : 'boolean',
|
||||
value : word == 'true'
|
||||
});
|
||||
}else{
|
||||
tokens.push({
|
||||
type : 'word',
|
||||
value : word
|
||||
});
|
||||
}
|
||||
current--;
|
||||
}
|
||||
else if(char == "'"){
|
||||
char = input[++current]
|
||||
tokens.push({
|
||||
type : 'text',
|
||||
value : getToken(/[^\']/)
|
||||
});
|
||||
}
|
||||
else if(char == '"'){
|
||||
char = input[++current]
|
||||
tokens.push({
|
||||
type : 'text',
|
||||
value : getToken(/[^\"]/)
|
||||
});
|
||||
}
|
||||
}
|
||||
//Not tokenizing a tag definition
|
||||
else{
|
||||
//End tag
|
||||
if(char == '<' && input[current+1] == '/'){
|
||||
char = input[++current]
|
||||
char = input[++current]
|
||||
tokens.push({
|
||||
type : 'endTag',
|
||||
value : getToken(LETTERS)
|
||||
})
|
||||
}
|
||||
else if(char == '<'){
|
||||
inTag = true;
|
||||
char = input[++current];
|
||||
tokens.push({
|
||||
type : 'openTag',
|
||||
value : getToken(LETTERS)
|
||||
})
|
||||
current--;
|
||||
}
|
||||
else{
|
||||
//Handle slush text
|
||||
var value = '';
|
||||
while(char != '<' && current < input.length){
|
||||
value += char;
|
||||
char = input[++current];
|
||||
}
|
||||
value = value.trim()
|
||||
if(value){
|
||||
tokens.push({
|
||||
type : 'text',
|
||||
value : value
|
||||
});
|
||||
}
|
||||
current--;
|
||||
}
|
||||
}
|
||||
current++;
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
var parser = function(tokens){
|
||||
var nodes = [];
|
||||
var current = 0;
|
||||
var token = tokens[current];
|
||||
|
||||
var parseProps = function(){
|
||||
var props = {};
|
||||
var key = null;
|
||||
var last = null;
|
||||
|
||||
while(current < tokens.length && token.type != 'endTag' && token.type != 'closeTag'){
|
||||
if(last && token.type == 'word'){
|
||||
props[last] = true;
|
||||
last = token.value;
|
||||
}else if(!key && token.type == 'word'){
|
||||
last = token.value;
|
||||
}else if(last && token.type == 'equals'){
|
||||
key = last;
|
||||
last = null;
|
||||
}else if(key && (token.type == 'number' || token.type == 'text' || token.type == 'boolean')){
|
||||
props[key] = token.value;
|
||||
key = null;
|
||||
last = null;
|
||||
}else{
|
||||
throw "Invalid property value: " + key + '=' + token.value;
|
||||
}
|
||||
token = tokens[++current];
|
||||
}
|
||||
if(last) props[last] = true;
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
var genNode = function(tagType){
|
||||
token = tokens[++current];
|
||||
var node = {
|
||||
tag : tagType,
|
||||
props : parseProps(),
|
||||
children : getChildren(tagType)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
var getChildren = function(tagType){
|
||||
var children = [];
|
||||
while(current < tokens.length){
|
||||
if(token.type == 'endTag'){
|
||||
if(token.value && token.value != tagType){
|
||||
throw "Invalid closing tag: " + token.value + ". Expected closing tag of type: " + tagType
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(token.type == 'openTag'){
|
||||
children.push(genNode(token.value));
|
||||
}else if(token.type == 'text'){
|
||||
children.push(token.value);
|
||||
}
|
||||
token = tokens[++current];
|
||||
}
|
||||
return children;
|
||||
}
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
module.exports = function(input){
|
||||
return parser(tokenizer(input));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
SOME TEST, remove later
|
||||
|
||||
|
||||
|
||||
|
||||
var test1 = `
|
||||
<div test="hey there champ" more_cool=false size=0>
|
||||
<span>
|
||||
Hey there!
|
||||
<a>so fucking cool </a>
|
||||
</span>
|
||||
let's go party
|
||||
<a href='neato' />
|
||||
</div>
|
||||
`
|
||||
|
||||
var test2 = "<div cool=0 same>Hey there!</div>"
|
||||
|
||||
|
||||
var tokens = tokenizer(test1);
|
||||
|
||||
console.log(test1, '\n---\n', tokens, '---\n', JSON.stringify(parser(tokens), null, ' '));
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
1
todo.md
1
todo.md
@@ -68,6 +68,5 @@ X Print Dialog now auto-opens on print page
|
||||
- should support `has:somatic`, `class:wizard`, `school:divination`, `level:6`
|
||||
- Add a dropdown box with clickable elements to inject search terms
|
||||
- Clean up the 5th edition spells json
|
||||
- Split up the spells into separate josn based on the source
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user