Gave effects their own collection, they no longer live in arrays attached to skills/attributes

Also improved the display of features and generally iterated on their manipulation.

Characters now fetch the relevant effects directly when making a calculation, simplifying almost everything.

Effects now store a reference to their source if they have one.

Effect names are now optional, they can be fetched from the source's name if the source exists.
This commit is contained in:
Thaum
2015-01-23 11:04:07 +00:00
parent 84512beb72
commit 6a2e7f0832
32 changed files with 340 additions and 642 deletions

View File

@@ -9,6 +9,14 @@ this.GlobalUI = (function() {
toast.text = text;
return toast.show();
};
GlobalUI.setDialog = function(opts){
this.dialog = $("[global-dialog]")[0];
Session.set("global.ui.dialogHeader", opts.heading);
Session.set("global.ui.dialogData", opts.data);
Session.set("global.ui.dialogTemplate", opts.template);
Session.set("global.ui.dialogFullOnMobile", opts.fullOnMobile != null);
};
GlobalUI.showDialog = function(opts) {
this.dialog = $("[global-dialog]")[0];

View File

@@ -90,6 +90,25 @@ paper-button {
transform: translate(-50%, -50%);
}
.scroll-y {
overflow-y: auto;
}
.fab-buffer {
height: 88px;
}
*[hidden] {
visibility: hidden;
}
@media (max-width: 640px) {
html /deep/ paper-action-dialog[global-dialog] {
top: 0 !important;
left: 0 !important;
width: 100%;
height: 100%;
margin: 0;
border-radius: 0;
}
}

View File

@@ -1,99 +1,5 @@
<template name="stats">
<core-animated-pages selected={{selectedSection}} transitions="hero-transition cross-fade" fit style="overflow-y: scroll">
<section id="stats">
{{> abilityCards}}
</section>
<section id="detailContainer">
<div cross-fade id="darkOverlay"></div>
{{> attributeDialog character=this}}
{{> skillDialog character=this}}
</section>
</core-animated-pages>
</template>
<template name="spellSlots">
{{attributeValue "level1SpellSlots"}}
{{attributeValue "level2SpellSlots"}}
{{attributeValue "level3SpellSlots"}}
{{attributeValue "level4SpellSlots"}}
{{attributeValue "level5SpellSlots"}}
{{attributeValue "level6SpellSlots"}}
{{attributeValue "level7SpellSlots"}}
{{attributeValue "level8SpellSlots"}}
{{attributeValue "level9SpellSlots"}}
</template>
<template name="statCard">
<paper-shadow {{isHero id}} class="card {{type}}" hero-id={{id}}>
<div id="{{id}}Heading" class="card-top headline {{class}}">
{{> UI.contentBlock}}
</div>
<div class="subhead">
{{title}}
</div>
</paper-shadow>
</template>
<template name="attributeDialog">
<!--needs character, attributeName, attributeTitle-->
{{#if attributeName}}
<paper-shadow id="attributeDialog"
class="detailCard"
hero-id={{session "selectedAttribute"}}
hero
heading={{session "selectedAttributeTitle"}}
z="2">
{{character.attributeValue attributeName}}<br>
{{#each effects.add}}
{{> attributeEffect}}
{{/each}}
{{#each effects.mul}}
{{> attributeEffect}}
{{/each}}
{{#each effects.min}}
{{> attributeEffect}}
{{/each}}
{{#each effects.max}}
{{> attributeEffect}}
{{/each}}
{{signedString attribute.adjustment}}
</paper-shadow>
{{/if}}
</template>
<template name="attributeEffect">
{{#if editing}}
<div class="editEffect">
<paper-dropdown-menu label="Operation">
<paper-dropdown layered class="dropdown">
<core-menu id="operationSelector" selected={{operationNumber}} class="menu">
<paper-item>add</paper-item>
<paper-item>multiply</paper-item>
<paper-item>min</paper-item>
<paper-item>max</paper-item>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<paper-input id="effectValueInput" label="Value" value={{value}} floatinglabel></paper-input>
<paper-input id="effectNameInput" label="Name" value={{name}} floatinglabel></paper-input>
<paper-icon-button id="doneButton" icon="done" title="save" role="button" aria-label="save"></paper-icon-button>
<paper-icon-button id="cancelButton" icon="clear" title="cancel" role="button" aria-label="cancel"></paper-icon-button>
<paper-icon-button id="deleteButton" icon="delete" title="delete" role="button" aria-label="delete"></paper-icon-button>
</div>
{{else}}
<div class="effect">
<div class="effectValue">{{operation}} {{signedEffectValue}}</div><div class="effectName"> {{name}}</div>
{{#if editable}}
<paper-icon-button id="editButton" icon="create" title="edit" role="button" aria-label="edit"></paper-icon-button>
{{/if}}
</div>
{{/if}}
</template>
<template name="skillDialog">
<!--needs character and skill string-->
<paper-dialog layered="false" id="skillDialog" backdrop transition="core-transition-center">
</paper-dialog>
<div class="scroll-y" fit>
{{> abilityCards}}
</div>
</template>

View File

@@ -1,151 +1,7 @@
selectAttribute = function(name, title){
Session.set("selectedAttribute", name);
Session.set("selectedAttributeTitle", title);
Session.set("editingEffect", null);
};
selectSkill = function(name, title){
Session.set("selectedSkill", name);
Session.set("selectedSkillTitle", title);
Session.set("editingEffect", null);
};
Template.stats.created = function(){
this.selectedSection = new ReactiveVar(0);
}
Template.stats.events({
"tap .attribute": function(event){
var instance = Template.instance();
var selected = $(event.currentTarget).attr("hero-id");
var current = Session.get("selectedAttribute");
var f = function(){
selectAttribute(selected, "Title");
instance.selectedSection.set(1);
}
//we already have the dialog open
if(instance.selectedSection.get() === 1 && current != selected){
instance.selectedSection.set(0);
_.delay(f, 200);
} else {
f();
}
},
"tap #darkOverlay": function(event){
Template.instance().selectedSection.set(0);
// let the user click through before it is completely gone
$("#darkOverlay").css("pointer-events", "none");
// make clickable again later
_.delay(function(){
$("#darkOverlay").css("pointer-events", "auto");
}, 500);
}
});
Template.stats.helpers({
selectedSection: function(){
return Template.instance().selectedSection.get();
},
isHero: function(string){
if(string === Session.get("selectedAttribute")||
string === Session.get("selectedSkill")){
return "hero";
}
}
});
Template.statCard.helpers({
isHero: function(string){
if(string === Session.get("selectedAttribute")||
string === Session.get("selectedSkill")){
return "hero";
}
}
});
Template.attributeDialog.helpers({
attributeTitle: function(){
return Session.get("selectedAttributeTitle");
},
attributeName: function(){
return Session.get("selectedAttribute");
},
attribute: function(){
return this.character.getField(Session.get("selectedAttribute"));
},
effects: function(){
var attribute = this.character.getField(Session.get("selectedAttribute"));
return _.groupBy(attribute.effects, "operation");
},
effectValue: function(){
return evaluateEffect(Template.parentData(1).character._id, this);
}
});
Template.attributeEffect.helpers({
editing: function(){
return Session.get("editingEffect") === this._id;
},
editable: function(){
return this.type === "editable";
},
operation: function(){
switch(this.operation){
case "add":
return;
case "mul":
return Spacebars.SafeString("&times;");
case "min":
return "min";
case "max":
return "max";
default:
return this.operation;
}
},
operationNumber: function(){
switch(this.operation){
case "add":
return 0;
case "mul":
return 1;
case "min":
return 2;
case "max":
return 3;
default:
return -1;
}
},
signedEffectValue: function(){
var value = evaluateEffect(Template.parentData(1).character._id, this);
return signedString(value);
}
});
Template.attributeEffect.events({
"tap #editButton": function(event){
Session.set("editingEffect", this._id);
},
"tap #doneButton": function(event){
var newEffect = {
};
//TODO setup the changed effect
var attribute = Session.get("selectedAttribute");
var charId = Template.parentData(2)._id;
Meteor.call("updateEffect", charId, attribute, this._id, newEffect)
Session.set("editingEffect", null);
},
"tap #cancelButton": function(event){
Session.set("editingEffect", null);
},
"tap #deleteButton": function(event){
var attribute = Session.get("selectedAttribute");
var pullObject = {};
pullObject[attribute + ".effects"] = {_id: this._id};
Characters.update(Template.parentData(2)._id, {$pull: pullObject});
Session.set("editingEffect", null);
}
});

View File

@@ -6,6 +6,15 @@
<template name="featureDialog">
{{#with feature}}
<paper-menu-button>
<paper-icon-button role="button" tabindex="0" icon="delete" aria-label="Delete Feature" noink></paper-icon-button>
<paper-dropdown class="dropdown">
<paper-button id="deleteFeature">Delete Feature</paper-button>
<paper-button>Cancel</paper-button>
</paper-dropdown>
</paper-menu-button>
<div class="featureDialogWidth"></div>
<paper-input id="featureNameInput" label="Name" floatinglabel value={{name}}></paper-input>
<paper-input-decorator label="Description" floatinglabel layout vertical>

View File

@@ -4,20 +4,22 @@ Template.featureDialog.rendered = function(){
var feature = Features.findOne(Template.currentData().featureId, {fields: {name: 1}});
if(feature && feature.name) Session.set("global.ui.dialogHeader", feature.name);
})
//after the dialog is built, open it
_.defer(function(){GlobalUI.dialog.open()});
}
Template.featureDialog.events({
"tap #deleteFeature": function(){
Features.remove(this._id);
GlobalUI.closeDialog()
},
"tap #addEffectButton": function(){
var numUpdated = Features.update(this._id, {
$push: {
"effects": {
name: "fe",
operation: "add",
type: "feature"
}
}
Effects.insert({
charId: Template.currentData().charId,
sourceId: this._id,
operation: "add",
type: "feature"
});
console.log("pushed add button ", numUpdated, " updated");
},
"change #featureNameInput": function(event){
var name = Template.instance().find("#featureNameInput").value;
@@ -32,5 +34,9 @@ Template.featureDialog.events({
Template.featureDialog.helpers({
feature: function(){
return Features.findOne(this.featureId);
},
effects: function(){
var cursor = Effects.find({charId: Template.currentData().charId, type: "feature", sourceId: this._id})
return cursor;
}
});

View File

@@ -1,43 +1,45 @@
<template name="featureEffect">
<paper-dropdown-menu id="statGroupDropDown" label="Stat Group">
<paper-dropdown class="dropdown">
<core-menu id="statGroupMenu" class="menu" selected={{selectedStatGroup}}>
{{#each statGroups}}
<paper-item>{{this}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
{{#if stats}}
<paper-dropdown-menu id="statDropDown" label="Stat">
<div class="featureEffect">
<paper-dropdown-menu id="statGroupDropDown" label="Stat Group">
<paper-dropdown class="dropdown">
<core-menu id="statMenu" class="menu" selected={{selectedStat}}>
{{#each stats}}
<paper-item>{{name}}</paper-item>
<core-menu id="statGroupMenu" class="menu" selected={{selectedStatGroup}}>
{{#each statGroups}}
<paper-item>{{this}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
{{/if}}
{{#if operations}}
<paper-dropdown-menu id="operationDropDown" label="Operation">
<paper-dropdown class="dropdown">
<core-menu id="operationMenu" class="menu" selected={{selectedOperation}}>
{{#each operations}}
<paper-item>{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
{{/if}}
{{> Template.dynamic template=effectValueTemplate data=valueTemplateData}}
{{#if needsCommit}}
<paper-icon-button id="commitChanges" role="button" tabindex="0" icon="check" aria-label="Commit Changes"></paper-icon-button>
<paper-icon-button id="clearChanges" role="button" tabindex="0" icon="clear" aria-label="Clear Changes"></paper-icon-button>
{{else}}
<paper-icon-button id="deleteEffect" role="button" tabindex="0" icon="delete" aria-label="Delete"></paper-icon-button>
{{/if}}
<br>
{{#if stats}}
<paper-dropdown-menu id="statDropDown" label="Stat">
<paper-dropdown class="dropdown">
<core-menu id="statMenu" class="menu" selected={{selectedStat}}>
{{#each stats}}
<paper-item>{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
{{/if}}
{{#if operations}}
<paper-dropdown-menu id="operationDropDown" label="Operation">
<paper-dropdown class="dropdown">
<core-menu id="operationMenu" class="menu" selected={{selectedOperation}}>
{{#each operations}}
<paper-item>{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
{{/if}}
{{> Template.dynamic template=effectValueTemplate data=valueTemplateData}}
{{#if needsCommit}}
<paper-icon-button id="commitChanges" role="button" tabindex="0" icon="check" aria-label="Commit Changes"></paper-icon-button>
<paper-icon-button id="clearChanges" role="button" tabindex="0" icon="clear" aria-label="Clear Changes"></paper-icon-button>
{{else}}
<paper-icon-button id="deleteEffect" role="button" tabindex="0" icon="delete" aria-label="Delete"></paper-icon-button>
{{/if}}
<br>
</div>
</template>
<template name="regularEffectValue">

View File

@@ -233,19 +233,19 @@ Template.featureEffect.helpers({
Template.featureEffect.events({
"tap #commitChanges": function(event){
var newEffect = this;
var changedFields = {};
var inst = Template.instance();
newEffect.operation = inst.selectedOperation.get();
newEffect.stat = inst.selectedStat.get();
changedFields.operation = inst.selectedOperation.get();
changedFields.stat = inst.selectedStat.get();
var val = inst.value.get();
if(_.isNumber(val)){
newEffect.value = val;
newEffect.calculation = null;
changedFields.value = val;
changedFields.calculation = null;
} else if(_.isString(val)) {
newEffect.calculation = val;
newEffect.value = null;
changedFields.calculation = val;
changedFields.value = null;
}
Meteor.call("updateFeatureEffect", Template.parentData()._id, newEffect);
Effects.update(this._id, {$set: changedFields});
},
"tap #clearChanges": function(event){
//essentially re-render
@@ -262,10 +262,12 @@ Template.featureEffect.events({
inst.value.set(value);
},
"tap #deleteEffect": function(event){
Features.update(Template.parentData()._id, { $pull: { "effects": {_id: this._id} } });
Effects.remove(this._id);
},
"core-select #statGroupMenu": function(event){
var groupIndex = Template.instance().find("#statGroupMenu").selected;
var groupMenu = Template.instance().find("#statGroupMenu")
if(!groupMenu) return;
var groupIndex = groupMenu.selected;
var groupName = statGroupNames[groupIndex]
var oldName = Template.instance().selectedStatGroup.get();
if(oldName != groupName){
@@ -277,8 +279,11 @@ Template.featureEffect.events({
}
},
"core-select #statMenu": function(event){
var statIndex = Template.instance().find("#statMenu").selected;
var groupIndex = Template.instance().find("#statGroupMenu").selected;
var statMenu = Template.instance().find("#statMenu");
var groupMenu = Template.instance().find("#statGroupMenu");
if(!statMenu || !groupMenu) return;
var statIndex = statMenu.selected;
var groupIndex = groupMenu.selected;
var groupName = statGroupNames[groupIndex]
var group = statGroups[groupName];
var statObj = group[statIndex];
@@ -289,7 +294,9 @@ Template.featureEffect.events({
"core-select #operationMenu": function(event){
var groupName = Template.instance().selectedStatGroup.get();
var opGroup = (groupName === "Saving Throws" || groupName === "Skills")? skillOperations : attributeOperations;
var opIndex = Template.instance().find("#operationMenu").selected;
var opMenu = Template.instance().find("#operationMenu")
if(!opMenu) return;
var opIndex = opMenu.selected;
var op = opGroup[opIndex];
if(!op) return;
var opName = op.operation;
@@ -297,7 +304,9 @@ Template.featureEffect.events({
},
"core-select #multiplierMenu": function(event){
var inst = Template.instance();
var selected = Template.instance().find("#multiplierMenu").selected;
var mulMenu = Template.instance().find("#multiplierMenu");
if(!mulMenu) return;
var selected = mulMenu.selected;
if(selected === 0){
inst.value.set(0.5);
inst.selectedOperation.set("mul");
@@ -311,7 +320,9 @@ Template.featureEffect.events({
},
"core-select #proficiencyMenu": function(event){
var inst = Template.instance();
var selected = inst.find("#proficiencyMenu").selected;
var profMenu = inst.find("#proficiencyMenu");
if(!profMenu) return;
var selected = profMenu.selected;
var value;
if(selected === 0){
inst.value.set(1);
@@ -323,7 +334,9 @@ Template.featureEffect.events({
},
"change #effectValueInput": function(event){
var inst = Template.instance();
var value = inst.find("#effectValueInput").value;
var input = inst.find("#effectValueInput");
if(!input) return;
var value = input.value;
inst.value.set(value);
}
});

View File

@@ -1,5 +1,38 @@
paper-shadow.featureCard {
width: 300px;
padding: 16px;
margin: 8px;
margin: 4px;
background: white;
}
flex-grow: 1;
border-radius: 2px;
}
.features {
display: flex !important;
justify-content: center;
align-items: stretch;
flex-wrap: wrap;
padding: 4px;
}
.featureCardTop {
margin-bottom: 8px;
}
.featureCardBottom {
white-space: pre-line;
}
#addFeature {
position: absolute;
bottom: 24px;
right: 24px;
}
@media (max-width: 640px) {
#addFeature {
bottom: 16px;
right: 24px;
}
}

View File

@@ -1,22 +1,25 @@
<template name="features">
<div class="resources"><!--resources-->
<div fit>
<div class="scroll-y" fit>
<div class="resources"><!--resources-->
</div>
<div class="actions">
</div>
<div class="features">
{{#each features}}
<paper-shadow class="featureCard">
<div class="featureCardTop">
<h2>{{name}}</h2>
</div>
<div class="featureCardBottom"><p>{{description}}</p></div>
<paper-ripple fit></paper-ripple>
</paper-shadow>
{{/each}}
</div>
<div class="fab-buffer"></div>
</div>
<paper-fab id="addFeature" icon="add" title="Add" role="button" tabindex="0" aria-label="Add"></paper-fab>
</div>
<div class="actions">
</div>
<div class="features">
{{#each features}}
<paper-shadow class="featureCard">
<div class="featureCardTop">
<h1>{{name}}</h1>
</div>
<div class="featureCardBottom">
<p>{{description}}</p>
</div>
<paper-ripple fit></paper-ripple>
</paper-shadow>
{{/each}}
</div>
<paper-fab id="addFeature" icon="add" title="Add" role="button" tabindex="0" aria-label="Add"></paper-fab>
</template>

View File

@@ -11,16 +11,16 @@ Template.features.events({
GlobalUI.showDialog({
heading: "New Feature",
template: "featureDialog",
data: {featureId: featureId},
data: {featureId: featureId, charId: this._id},
fullOnMobile: true
})
},
"tap .featureCard": function(event){
var featureId = this._id;
GlobalUI.showDialog({
GlobalUI.setDialog({
heading: this.name,
template: "featureDialog",
data: {featureId: featureId},
data: {featureId: featureId, charId: Template.parentData()._id},
fullOnMobile: true
})
}

View File

@@ -1,5 +1,5 @@
body /deep/ .featureDialogWidth {
width: 600px;
width: 560px;
}
body /deep/ #statGroupDropDown {
@@ -20,4 +20,13 @@ body /deep/ #damageMultiplierDropDown {
body /deep/ #proficiencyDropDown {
width: 120px;
}
html /deep/ paper-input {
margin-bottom: 1px;
}
html /deep/ .featureEffect {
display: flex;
align-items: flex-end;
}

View File

@@ -6,10 +6,13 @@
<link rel="import" href="/components/core-item/core-item.html">
<link rel="import" href="/components/core-menu/core-menu.html">
<link rel="import" href="/components/core-scaffold/core-scaffold.html">
<link rel="import" href="/components/core-transition/core-transition.html">
<!--paper components-->
<link rel="import" href="/components/paper-button/paper-button.html">
<link rel="import" href="/components/paper-dialog/paper-action-dialog.html">
<link rel="import" href="/components/paper-dialog/paper-dialog.html">
<link rel="import" href="/components/paper-dialog/paper-dialog-transition.html">
<link rel="import" href="/components/paper-dropdown/paper-dropdown.html">
<link rel="import" href="/components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="/components/paper-fab/paper-fab.html">
@@ -18,6 +21,7 @@
<link rel="import" href="/components/paper-input/paper-input.html">
<link rel="import" href="/components/paper-input/paper-input-decorator.html">
<link rel="import" href="/components/paper-item/paper-item.html">
<link rel="import" href="/components/paper-menu-button/paper-menu-button.html">
<link rel="import" href="/components/paper-shadow/paper-shadow.html">
<link rel="import" href="/components/paper-spinner/paper-spinner.html">
<link rel="import" href="/components/paper-tabs/paper-tabs.html">

View File

@@ -14,8 +14,8 @@
</core-drawer-panel>
<paper-action-dialog global-dialog backdrop
transition="paper-dialog-transition-bottom"
class={{#if globalDialogFullOnMobile}}full-on-mobile{{/if}}
transition="core-transition-center"
class="scrolling {{#if globalDialogFullOnMobile}}full-on-mobile{{/if}}"
autoclosedisabled
heading={{globalDialogHeader}}
layered>