Files
DiceCloud/rpg-docs/client/views/character/spells/spells.js

388 lines
11 KiB
JavaScript

var spellLevels = [
{name: "Cantrips", level: 0},
{name: "Level 1", level: 1},
{name: "Level 2", level: 2},
{name: "Level 3", level: 3},
{name: "Level 4", level: 4},
{name: "Level 5", level: 5},
{name: "Level 6", level: 6},
{name: "Level 7", level: 7},
{name: "Level 8", level: 8},
{name: "Level 9", level: 9},
];
var materialNeedsGp = function(string) {
if (!string) return false;
gpRegExp = /\b[0-9]+ ?(cp|sp|gp)\b/i;
return gpRegExp.test(string);
}
const showUnprepared = (listId) => {
return Session.get(`showUnprepared.${listId}`);
}
Template.spells.helpers({
spellLists: function(){
return SpellLists.find({charId: this._id}, {sort: {color: 1, name: 1}});
},
spellCount: function(list, charId){
if (!list) return;
if (showUnprepared(list._id)){
return Spells.find(
{charId: charId, "parent.id": list._id, level: this.level},
{fields: {_id: 1, level: 1}}
).count() > 0;
} else {
return Spells.find(
{
charId: charId,
"parent.id": list._id,
level: this.level,
prepared: {$in: ["prepared", "always"]},
},
{fields: {_id: 1, level: 1}}
).count() > 0;
}
},
spells: function(listId, charId){
return Spells.find(
{charId: charId, "parent.id": listId, level: this.level},
{sort: {color: 1, name: 1}}
);
},
levels: function(){
return spellLevels;
},
showUnprepared: function(listId){
return showUnprepared(listId);
},
numPrepared: function(){
return Spells.find({
charId: Template.parentData()._id,
"parent.id": this._id,
prepared: "prepared",
}).count();
},
order: function(){
return _.indexOf(_.keys(colorOptions), this.color);
},
spellComponents: function(){
var components = "";
if (this.components.verbal){
components += "V";
}
if (this.components.somatic){
components += components ? ", S" : "S";
}
if (this.components.material){
components += components ? ", M" : "M";
if (materialNeedsGp(this.components.material)) {components += "gp";}
}
if (this.components.concentration){
components += components ? ", C" : "C";
}
return components;
},
isPrepared: function(){
return this.prepared === "prepared" || this.prepared === "always";
},
showSpell: function(listId){
if (showUnprepared(listId)) {
return true;
} else {
return this.prepared === "prepared" || this.prepared === "always";
}
},
cantUnprepare: function(){
return this.prepared === "always";
},
cantCast: function(level, char){
for (var i = level; i <= 9; i++){
if (Characters.calculate.attributeValue(
char._id, "level" + i + "SpellSlots"
) > 0){
return false;
}
}
return true;
},
showSlots: function(char){
return this.level && Characters.calculate.attributeBase(
char._id, "level" + this.level + "SpellSlots"
);
},
hasSlots: function(){
for (var i = 1; i <= 9; i += 1){
if (Characters.calculate.attributeBase(
this._id, "level" + i + "SpellSlots"
)){
return true;
}
}
return false;
},
slotBubbles: function(char){
var baseSlots = Characters.calculate.attributeBase(
char._id, "level" + this.level + "SpellSlots"
);
var currentSlots = Characters.calculate.attributeValue(
char._id, "level" + this.level + "SpellSlots"
);
var slotsUsed = baseSlots - currentSlots;
var bubbles = [];
var i;
for (i = 0; i < currentSlots; i++){
bubbles.push({
icon: "radio-button-checked",
disabled: i !== currentSlots - 1 || !canEditCharacter(char._id), //last full slot not disabled
attribute: "level" + this.level + "SpellSlots",
charId: char._id,
});
}
for (i = 0; i < slotsUsed; i++){
bubbles.push({
icon: "radio-button-unchecked",
disabled: i !== 0 || !canEditCharacter(char._id), //first empty slot not disabled
attribute: "level" + this.level + "SpellSlots",
charId: char._id,
});
}
return bubbles;
},
slotStatName: function() {
return "level" + this.level + "SpellSlots";
},
});
Template.spells.events({
"click .slotBubble": function(event){
var modifier;
if (!event.currentTarget.disabled){
var char = Characters.findOne(this.charId);
if (event.currentTarget.icon === "radio-button-unchecked"){
if (
Characters.calculate.attributeValue(char._id, this.attribute) <
Characters.calculate.attributeBase(char._id, this.attribute)
){
modifier = {$inc: {}};
modifier.$inc[this.attribute + ".adjustment"] = 1;
Characters.update(this.charId, modifier, {validate: false});
}
} else {
if (Characters.calculate.attributeValue(char._id, this.attribute) > 0){
modifier = {$inc: {}};
modifier.$inc[this.attribute + ".adjustment"] = -1;
Characters.update(this.charId, modifier, {validate: false});
}
}
}
event.stopPropagation();
},
"click .spellSlot": function(event, instance) {
var name = "Level " + this.level + " Spell Slots";
var stat = "level" + this.level + "SpellSlots";
var charId = instance.data._id;
pushDialogStack({
template: "attributeDialog",
data: {name: name, statName: stat, charId: charId},
element: event.currentTarget,
});
},
"click .spellList .top": function(event){
pushDialogStack({
template: "spellListDialog",
data: {spellListId: this._id, charId: this.charId},
element: event.currentTarget.parentElement,
});
},
"click .spell": function(event){
pushDialogStack({
template: "spellDialog",
data: {spellId: this._id, charId: this.charId},
element: event.currentTarget,
});
},
"click .addSpellList": function(event, instance){
var charId = this.charId;
var id = SpellLists.insert({
name: "New SpellList",
charId: this._id,
saveDC: "8 + intelligenceMod + proficiencyBonus",
attackBonus: "intelligenceMod + proficiencyBonus",
});
pushDialogStack({
template: "spellListDialog",
data: {spellListId: id, charId: charId, startEditing: true},
element: event.currentTarget,
returnElement: () => instance.find(`.spellList[data-id='${id}']`),
});
},
"click .addSpell": function(event, instance){
var charId = this._id;
var list = SpellLists.findOne({charId});
var listId = list && list._id
if (!listId){
listId = SpellLists.insert({
name: "New SpellList",
charId: charId,
saveDC: "8 + intelligenceMod + proficiencyBonus",
attackBonus: "intelligenceMod + proficiencyBonus",
});
}
var id = Spells.insert({
name: "New Spell",
charId: this._id,
parent: {
id: listId,
collection: "SpellLists",
},
prepared: "prepared",
});
pushDialogStack({
template: "spellDialog",
data: {spellId: id, charId: charId, startEditing: true},
element: event.currentTarget,
returnElement: () => instance.find(`.spell[data-id='${id}']`),
});
},
"click .librarySpell": function(event, instance){
var charId = this._id;
var spellId = Random.id();
var list = SpellLists.findOne({charId});
var listId = list && list._id
pushDialogStack({
template: "spellLibraryDialog",
element: event.currentTarget,
callback: (resultArray) => {
if (!resultArray) return;
if (!listId){
listId = SpellLists.insert({
name: "New SpellList",
charId: charId,
saveDC: "8 + intelligenceMod + proficiencyBonus",
attackBonus: "intelligenceMod + proficiencyBonus",
});
}
//loop through all returned spells
_.each(resultArray, (rawSpell, index) =>{
// Make the library spell into a regular spell
let spell = _.omit(rawSpell, "_id", "library", "attacks", "effects");
// Use the ID generated earlier for the first spell so we
// can animate to it
if (index == 0) {
spell._id = spellId;
}
spell.charId = charId;
spell.parent = {
id: listId,
collection: "SpellLists",
};
spell.prepared = "prepared";
Spells.insert(spell);
// Copy over attacks and effects
_.each(rawSpell.attacks, (attack) => {
if (!("attackBonus" in attack)) {attack.attackBonus = "attackBonus"} //if no attack bonus provided, use spell list's
attack.charId = charId;
attack.parent = {id: spellId, collection: "Spells"};
Attacks.insert(attack);
});
_.each(rawSpell.effects, (effect) => {
effect.charId = charId;
effect.parent = {id: spellId, collection: "Spells"};
Effects.insert(effect);
});
_.each(rawSpell.buffs, (buff) => {
buff.charId = charId;
buff.parent = {id: spellId, collection: "Spells"};
buffId = Buffs.insert(buff);
_.each(buff.attacks, (attack) => {
if (!(attackBonus in attack)) {attack.attackBonus = "attackBonus"} //if no attack bonus provided, use spell list's
attack.charId = charId;
attack.parent = {id: buffId, collection: "Buffs"};
Attacks.insert(attack);
});
_.each(buff.effects, (effect) => {
effect.charId = charId;
effect.parent = {id: buffId, collection: "Buffs"};
Effects.insert(effect);
});
_.each(buff.proficiencies, (prof) => {
prof.charId = charId;
prof.parent = {id: buffId, collection: "Buffs"};
Proficiencies.insert(prof);
});
});
});
},
returnElement: () => $(`[data-id='${spellId}']`).get(0),
})
},
"click .preparedCheckbox": function(event){
event.stopPropagation();
},
"change .preparedCheckbox": function(event){
var value = event.currentTarget.checked;
if (this.prepared === "unprepared" && value)
Spells.update(this._id, {$set: {prepared: "prepared"}});
else if (this.prepared === "prepared" && !value)
Spells.update(this._id, {$set: {prepared: "unprepared"}});
},
"click .prepSpells": function(event){
Session.set(`showUnprepared.${this._id}`, true);
event.stopPropagation();
},
"click .finishPrep": function(event){
Session.set(`showUnprepared.${this._id}`, false);
event.stopPropagation();
},
});
Template.layout.events({
"dragstart .spellItem": function(event, instance){
event.originalEvent.dataTransfer.setData("dicecloud-id/spells", this._id);
Session.set("spellLists.dragSpellId", this._id);
},
"dragend .spellItem": function(event, instance){
Session.set("spellLists.dragSpellId", null);
},
"dragover .spellList, dragenter .spellList": function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/spells")){
event.preventDefault();
}
},
"drop .spellList": function(event, instance){
var spellId = event.originalEvent.dataTransfer.getData("dicecloud-id/spells");
if (event.ctrlKey){
//copy spell to new list
Meteor.call("copySpellToList", spellId, this._id);
} else {
//move spell to new list
Meteor.call("moveSpellToList", spellId, this._id);
}
Session.set("spellLists.dragSpellId", null);
},
"dragover .characterRepresentative, dragenter .characterRepresentative": function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/spells")){
event.preventDefault();
}
},
"drop .characterRepresentative": function(event, instance) {
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/spells")){ //to prevent conflicts with item drag/drop
var spellId = event.originalEvent.dataTransfer.getData("dicecloud-id/spells");
if (event.ctrlKey){
//copy spell to character
Meteor.call("copySpellToCharacter", spellId, this._id);
} else {
//move spell to character
Meteor.call("moveSpellToCharacter", spellId, this._id);
}
Session.set("spellLists.dragSpellId", null);
}
},
});