Add Skillchecks for Magical Talents (#25)

* initial implementation of spells

* added return list of spells. Moved them into seperate json

* added _some_ chants, restructuring data from spells

* added more chants, added command for getting info

* added related messages, added skillcheck for spells (cast)

* better error handling, added chant command

* (fix) Chants were referring to spells

* cleanup testing variables and code

* updated README

* fixed storage location issues and minor bugs

* removed db storage location

* rc1

* (fix) chants_title

* (fix) Uncaught ReferenceError: Discord is not defined

* (fix) more reference errors

Co-authored-by: Marcus Netz <marcus.netz@godyo.com>
This commit is contained in:
2021-04-28 19:33:30 +02:00
committed by GitHub
parent b49873e1d4
commit 23418bbc60
22 changed files with 656 additions and 193 deletions

17
.gitignore vendored
View File

@ -1,10 +1,15 @@
node_modules
/node_modules/
.env
.eslintrc.js
.eslintrc.json
.vscode
.github
data/dsabot.db
data/dsabot.db~
data
.jshint
.prettierrc
/.vscode/
/.github/
.DS_Store
Thumbs.db
/data/dsabot.db
!/data/*.js
!/data/*.json
/private

3
.jshintrc Normal file
View File

@ -0,0 +1,3 @@
{
"esversion": 9
}

19
.prettierrc Normal file
View File

@ -0,0 +1,19 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxBracketSameLine": true,
"jsxSingleQuote": true,
"printWidth": 100,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleQuote": true,
"tabWidth": 4,
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false
}

View File

@ -1,4 +1,5 @@
[![CodeQL](https://github.com/TobenderZephyr/dsabot/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/TobenderZephyr/dsabot/actions/workflows/codeql-analysis.yml)
# DSA Discord Bot
This Project is a fork of LucaSchwan/dsa-bot.
@ -10,37 +11,44 @@ It can roll dice and even compare results with your characters attributes.
# Commands
### !help
Send the user a simple command list with things to do.
## Dice Rolling
### !roll *x* W/D *y*
### !roll _x_ W/D _y_
This command can be used to roll dice. To use it one has to put in the number of dice to use as the *x* and the number of sides as the *y* in between those Numbers stands W/D which simply means dice(D) or the german word "Würfel" (W). Theoretically it doesn't matter what Letter is in between but for roleplay gamers this should feel natural.
This command can be used to roll dice. To use it one has to put in the number of dice to use as the _x_ and the number of sides as the _y_ in between those Numbers stands W/D which simply means dice(D) or the german word "Würfel" (W). Theoretically it doesn't matter what Letter is in between but for roleplay gamers this should feel natural.
ie `!roll 3w20`
### !heads
Throw a coin. Heads or Tails?
## Tracking System
As this Bot should help with tracking your character, simply attach your .tdc file in a private message.
### !ep / !ap [Value]
Probes on your attributes.
Rolls 1 die and compares it with the given value. Rolls another, if returned number is 1 or 20.
ie `!ep 11`
### !ep / !ap [Attribute or Abbreviation]
Does the same as above. You don't need to remember your stats (if you sent a `tdc`-File, that is.)
ie. `!ep Mut` or `!ep KK`
### !skill [skillname]
Returns the current level of the desired skill.
### !attack [weaponname] ([-Disadvantages/+Advantages])
Rolls an attack by using the weapon based skillset. Calculates your combat technique value and takes into account for `MU` or `FF`.
`caution`: It does not yet make use of any benefits or disadvantages your character possesses.
@ -49,19 +57,24 @@ ie. `!attack Waqqif`
For a list of weapons to chose from, use `!weapons`
### !talent [skillname] ([-Disadvantages/+Advantages])
Rolls 3 dice and compares the results of each with your current level of character attributes including your bonus on that particular skill.
`caution`: It does not yet make use of any benefits or disadvantages your character has (at the moment).
ie. `!talent klettern -2`
```
@TobenderZephyr, Du würfelst auf das Talent Klettern.
Deine Werte für MU, GE, KK sind 12, 13, 12. (Bonus: 6)
Das waren deine 🎲: 1, 16, 2. Damit hast du 3/3 Proben bestanden. Dein Bonus: 3/6.
```
### !talents
Sends the User a list of talents to use on the `!talent` command.
### !tp [attribute1] [attribute2] [attribute3] ([bonus] [-Disadvantage/+Advantages])
This command also rolls 3 dice and compares their values with entered arguments.
This one is better suited for people, who did not provide a `.tdc`-File or if one of the other numerous Advantages and/or Disadvantages need to be taken into account.
@ -74,9 +87,35 @@ Roll with a set Bonus of 4:
Roll with a Benefit given by the GM:
`!tp 11 10 11 0 +2`
### !spells [spell]
This command returns a list of spells known to you. This differs from `!talents` because there would be many more additions to certain
spells through rule books.
If you define a spell by not leaving arguments empty, it will display your current level and checks on your stats.
### !chants [chant]
See `!spells`. This uses karma point talents.
### !cast [spell] ([-Disadvantages/+Advantages])
Rolls 3 dice and compares the results of each with your current level of character attributes including your bonus on that particular spell.
`caution`: It does not yet make use of any benefits or disadvantages your character has (at the moment).
ie. `!cast fulminictus +1`
### !chant [chant] ([-Disadvantages/+Advantages])
See `!cast`. This uses karma point talents.
ie. `!chant Lautlos +1`
# How to use
To connect the script to a Bot, you need to create a Bot using the Discord Developer Portal. To do this you can follow a guide like [this](https://discordpy.readthedocs.io/en/latest/discord.html).
## Docker
Just pull the docker image and you are good to go.
`docker pull tobenderzephyr/dsabot:latest`
@ -85,12 +124,14 @@ The container needs at least the following Environment variables:
`BOT_TOKEN`: The token which you retrieve for your bot Discord Developer Portal
`CMDPREFIX`: Instead of using `!` as your prefix, you may want to use your own.
You may run the container with `docker run -rm -v ./data:/usr/src/app/data -e BOT_TOKEN=[token] -e CMDPREFIX=! tobenderzephyr/dsabot:latest`
You may run the container with `docker run -rm -v ./data:/usr/src/app/data/ -e BOT_TOKEN=[token] -e CMDPREFIX=! tobenderzephyr/dsabot:latest`
The database is stored under `./data`, so you are good to just mount this as a volume as seen in the example above.
### docker-compose
You may aswell use a simple `docker-compose.yml` and use docker-compose for easy running the app:
```
version: "3"
services:
@ -102,7 +143,7 @@ services:
BOT_TOKEN: <YOUR TOKEN>
CMDPREFIX: !
volumes:
- ./data:/usr/src/app/data
- ./data/:/usr/src/app/data/
```
## manual setup
@ -114,12 +155,10 @@ Then you need to run the following code inside of a terminal while beeing in the
`npm install`
Rename `.sample.env` file to `.env`
Enter the token you received on the Discord Developer Portal into the variable BOT_TOKEN inside `.env`.
Enter the token you received on the Discord Developer Portal into the variable BOT_TOKEN inside `.env`.
When you joined your Bot to your desired channel all you need to enter inside the bots directory:
`npm start`
Then your Bot should run and be working in your channel.

90
commands/Cast.js Normal file
View File

@ -0,0 +1,90 @@
const globals = require('../globals');
const Discord = require('discord.js');
const db = globals.db;
const { roll } = require('@dsabot/Roll');
const { findMessage } = require('@dsabot/findMessage');
const { getSpell } = require('@dsabot/getSpell');
const { CalculateQuality } = require('@dsabot/CalculateQuality');
const { CompareResults } = require('@dsabot/CompareResults');
const { CreateResultTable, f } = require('@dsabot/CreateResultTable');
module.exports = {
name: 'cast',
description:
' Du machst eine Fertigkeitsprobe auf Magietalente.\n' +
' Es werden drei Würfel auf deine Eigenschaftswerte geworfen. Deine Boni werden in' +
' die Berechnung einbezogen.',
aliases: ['zaubern'],
usage: '<Zaubern> [<-Erschwernis> / <+Erleichterung>]',
needs_args: false,
async exec(message, args) {
db.find({ user: message.author.tag }, (err, docs) => {
if (docs.length === 0) {
return message.reply(findMessage('NOENTRY'));
}
if (!docs[0].character.hasOwnProperty('spells')) return message.reply(findMessage('NO_SPELLS'));
if (!isNaN(args[0])) {
return message.reply(findMessage('WRONG_ARGUMENTS'));
}
const Spell = getSpell({ Character: docs[0].character, spell_name: args[0] });
if (!Spell) {
return message.reply(findMessage('SPELL_UNKNOWN'));
}
if (!Spell.Level || !Spell.Attributes) {
return;
}
const Attributes = Spell.Attributes;
const DiceThrow = roll(3, 20, message.author.tag).dice;
const Bonus = parseInt(args[1]) || 0;
let { Passed, CriticalHit, Fumbles, PointsUsed, PointsRemaining } = CompareResults(
DiceThrow,
Attributes.map(attr => attr.Level),
Bonus,
Spell.Level
);
const Reply = new Discord.MessageEmbed();
Reply.addFields({
name: `Du würfelst auf den Zauber **${Spell.Name}** ( Stufe ${Spell.Level} ${
Bonus ? `${f(Bonus)} ` : ''
})`,
value: CreateResultTable({
Attributes: Attributes,
Throws: DiceThrow,
PointsUsed: PointsUsed,
Bonus: Bonus,
}),
inline: false,
});
if (Fumbles >= 2) {
Reply.setColor('#900c3f');
Reply.addFields({
name: findMessage('TITLE_CRIT_FAILURE'),
value: findMessage('MSG_CRIT_FAILURE'),
inline: false,
});
} else if (CriticalHit >= 2) {
Reply.setColor('#1E8449');
Reply.addFields({
name: findMessage('TITLE_CRIT_SUCCESS'),
value: findMessage('MSG_CRIT_SUCCESS'),
inline: false,
});
} else if (Passed < 3) {
Reply.addFields({
name: findMessage('TITLE_FAILURE'),
value: `${Passed === 0 ? 'Keine Probe' : `nur ${Passed}/3 Proben`} erfolgreich. 😪`,
inline: false,
});
} else {
Reply.addFields({
name: findMessage('TITLE_SUCCESS'),
value: `Dein verbleibender Bonus: ${PointsRemaining}/${Spell.Level} (QS${CalculateQuality(
PointsRemaining
)})`,
inline: false,
});
}
message.reply(Reply);
});
},
};

89
commands/Chant.js Normal file
View File

@ -0,0 +1,89 @@
const globals = require('../globals');
const Discord = require('discord.js');
const db = globals.db;
const { roll } = require('@dsabot/Roll');
const { findMessage } = require('@dsabot/findMessage');
const { getChant } = require('@dsabot/getChant');
const { CalculateQuality } = require('@dsabot/CalculateQuality');
const { CompareResults } = require('@dsabot/CompareResults');
const { CreateResultTable, f } = require('@dsabot/CreateResultTable');
module.exports = {
name: 'chant',
description:
' Du machst eine Fertigkeitsprobe auf Magietalente.\n' +
' Es werden drei Würfel auf deine Eigenschaftswerte geworfen. Deine Boni werden in' +
' die Berechnung einbezogen.',
aliases: [''],
usage: '<Liturgie/Zeremonie> [<-Erschwernis> / <+Erleichterung>]',
needs_args: false,
async exec(message, args) {
db.find({ user: message.author.tag }, (err, docs) => {
if (docs.length === 0) {
return message.reply(findMessage('NOENTRY'));
}
if (!docs[0].character.hasOwnProperty('chants')) return message.reply(findMessage('NO_CHANTS'));
if (!isNaN(args[0])) {
return message.reply(findMessage('WRONG_ARGUMENTS'));
}
const Chant = getChant({ Character: docs[0].character, chant_name: args[0] });
if (!Chant) {
return message.reply(findMessage('CHANT_UNKNOWN'));
}
if (!Chant.Level || !Chant.Attributes) {
return;
}
const Attributes = Chant.Attributes;
const DiceThrow = roll(3, 20, message.author.tag).dice;
const Bonus = parseInt(args[1]) || 0;
let { Passed, CriticalHit, Fumbles, PointsUsed, PointsRemaining } = CompareResults(
DiceThrow,
Attributes.map(attr => attr.Level),
Bonus,
Chant.Level
);
const Reply = new Discord.MessageEmbed();
Reply.addFields({
name: `Du würfelst auf die Liturgie **${Chant.Name}** ( Stufe ${Chant.Level} ${
Bonus ? `${f(Bonus)} ` : ''
})`,
value: CreateResultTable({
Attributes: Attributes,
Throws: DiceThrow,
PointsUsed: PointsUsed,
Bonus: Bonus,
}),
inline: false,
});
if (Fumbles >= 2) {
Reply.setColor('#900c3f');
Reply.addFields({
name: findMessage('TITLE_CRIT_FAILURE'),
value: findMessage('MSG_CRIT_FAILURE'),
inline: false,
});
} else if (CriticalHit >= 2) {
Reply.setColor('#1E8449');
Reply.addFields({
name: findMessage('TITLE_CRIT_SUCCESS'),
value: findMessage('MSG_CRIT_SUCCESS'),
inline: false,
});
} else if (Passed < 3) {
Reply.addFields({
name: findMessage('TITLE_FAILURE'),
value: `${Passed === 0 ? 'Keine Probe' : `nur ${Passed}/3 Proben`} erfolgreich. 😪`,
inline: false,
});
} else {
Reply.addFields({
name: findMessage('TITLE_SUCCESS'),
value: `Dein verbleibender Bonus: ${PointsRemaining}/${Chant.Level} (QS${CalculateQuality(
PointsRemaining
)})`,
inline: false,
});
}
message.reply(Reply);
});
},
};

61
commands/Chants.js Normal file
View File

@ -0,0 +1,61 @@
//const globals = require('../globals');
const globals = require('../globals');
const Discord = require('discord.js');
const db = globals.db;
const { findMessage } = require('@dsabot/findMessage');
const { getChant } = require('@dsabot/getChant');
module.exports = {
name: 'chants',
description: 'Zeigt dir deinen Fertigkeitswert im jeweiligen Magietalent (Götterwirken).',
aliases: ['segen', 'liturgie', 'liturgien', 'zeremonien'],
usage: '[<Liturgie / Zeremonie>]',
needs_args: false,
async exec(message, args) {
db.find({ user: message.author.tag }, (err, docs) => {
if (docs.length === 0) {
return message.reply(findMessage('NOENTRY'));
}
Character = docs[0].character;
if (!Character.hasOwnProperty('chants')) return message.reply(findMessage('NO_CHANTS'));
if (args.length === 0) {
const Embed = new Discord.MessageEmbed()
.setColor('#0099ff')
.setTitle(findMessage('CHANTS_TITLE'))
.setDescription(findMessage('CHANTS_DESCRIPTION'))
.addField(ReplyChantList(createChantList(Character)), '\u200B', true);
return message.reply(Embed);
}
const Chant = getChant({
Character: Character,
chant_name: args[0],
});
if (!Chant) {
return message.reply(findMessage('SPELL_UNKNOWN'));
}
return message.reply(ReplyChant(Chant));
});
},
};
const createChantList = (Character = {}) => {
if (!Character || !Character.hasOwnProperty('chants')) return;
let ChantList = [];
// todo: send 'chant' to getChant() so we can filter out blessings.
Character.chants.forEach(chant => ChantList.push(getChant({ Character: Character, chant_name: chant.id })));
return ChantList.filter(value => value !== undefined);
};
const ReplyChantList = (ChantList = []) => {
if (!ChantList) return;
return `${ChantList.map(chant => `${chant.Name} ${chant.Level ? `(${chant.Level})` : ''}`).join('\n')}`;
};
const ReplyChant = (Chant = {}) => {
if (!Chant) return;
return `Deine Werte für ${Chant.Name} ${Chant.Level ? '(' + Chant.Level + ')' : ''} sind:
${Chant.Attributes.map(attribute => `${attribute.Name}: ${attribute.Level}`).join(' ')}
`;
};

View File

@ -1,31 +1,24 @@
const globals = require('../globals');
const db = globals.db;
const { findMessage }= require('@dsabot/findMessage');
const { findMessage } = require('@dsabot/findMessage');
const { getSkill } = require('@dsabot/getSkill');
module.exports = {
name: 'skill',
description: 'Zeigt dir deinen Fertigkeitswert im jeweiligen Talent.',
aliases: [],
usage: '<Fertigkeit>',
needs_args: true,
name: 'skill',
description: 'Zeigt dir deinen Fertigkeitswert im jeweiligen Talent.',
aliases: [],
usage: '<Fertigkeit>',
needs_args: true,
async exec(message, args) {
try {
db.find({
user: message.author.tag,
}, function(err, docs) {
if (docs.length === 0) {
return message.reply(findMessage('NOENTRY'));
}
else {
const Skill = getSkill({Character: docs[0].character, args: args});
if(!Skill) { return message.reply(findMessage('TALENT_UNKNOWN'));}
return message.reply(`Du hast folgenden Wert in **${Skill.Name}**: ${Skill.Level}`)
}
});
}
catch (e) {
throw e;
}
},
};
async exec(message, args) {
db.find({ user: message.author.tag }, (err, docs) => {
if (docs.length === 0) {
return message.reply(findMessage('NOENTRY'));
}
const Skill = getSkill({ Character: docs[0].character, args: args });
if (!Skill) {
return message.reply(findMessage('TALENT_UNKNOWN'));
}
return message.reply(`Du hast folgenden Wert in **${Skill.Name}**: ${Skill.Level}`);
});
},
};

57
commands/Spells.js Normal file
View File

@ -0,0 +1,57 @@
//const globals = require('../globals');
const globals = require('../globals');
const db = globals.db;
const Discord = require('discord.js');
const { findMessage } = require('@dsabot/findMessage');
const { getSpell } = require('@dsabot/getSpell');
module.exports = {
name: 'spells',
description: 'Zeigt dir deinen Fertigkeitswert im jeweiligen Magietalent.',
aliases: ['spell', 'zauber'],
usage: '<Zauber>',
needs_args: false,
async exec(message, args) {
db.find({ user: message.author.tag }, (err, docs) => {
if (docs.length === 0) {
return message.reply(findMessage('NOENTRY'));
}
Character = docs[0].character;
if (!Character.hasOwnProperty('spells')) return message.reply(findMessage('NO_SPELLS'));
if (args.length === 0) {
const Embed = new Discord.MessageEmbed()
.setColor('#0099ff')
.setTitle(findMessage('SPELLS_TITLE'))
.setDescription(findMessage('SPELLS_DESCRIPTION'))
.addField(ReplySpellList(createSpellList(Character)), '\u200B', true);
return message.reply(Embed);
}
const Spell = getSpell({
Character: Character,
spell_name: args[0],
});
if (!Spell) return message.reply(findMessage('SPELL_UNKNOWN'));
return message.reply(ReplySpell(Spell));
});
},
};
const ReplySpellList = (SpellList = []) => {
if (!SpellList) return findMessage('NO_SPELLS');
return `${SpellList.map(s => `${s.Name} (${s.Level})`).join('\n')}`;
};
const ReplySpell = (Spell = {}) => {
if (!Spell) return;
return `Deine Werte für ${Spell.Name} (${Spell.Level}) sind:
${Spell.Attributes.map(attribute => `${attribute.Name}: ${attribute.Level}`).join(' ')}
`;
};
const createSpellList = (Character = {}) => {
if (!Character || !Character.hasOwnProperty('spells')) return;
let SpellList = [];
Character.spells.forEach(spell => SpellList.push(getSpell({ Character: Character, spell_name: spell.id })));
return SpellList.filter(value => value !== undefined); //?+
};

View File

@ -1,101 +1,85 @@
const globals = require('../globals');
const Discord = require('discord.js');
const db = globals.db;
const { roll } = require('@dsabot/Roll');
const { findMessage } = require('@dsabot/findMessage');
const { roll } = require('@dsabot/Roll');
const { findMessage } = require('@dsabot/findMessage');
const { getSkill } = require('@dsabot/getSkill');
const { CalculateQuality } = require('@dsabot/CalculateQuality');
const { CompareResults } = require('@dsabot/CompareResults');
const { CreateResultTable } = require('@dsabot/CreateResultTable');
module.exports = {
name: 'talent',
description: ' Du machst eine Fertigkeitsprobe.\n' +
' Es werden drei Würfel auf deine Eigenschaftswerte geworfen. Hast du Boni auf dein Talent und/oder' +
' ist der Wurf erleichtert oder erschwert, wird dies in die Berechnung einbezogen.',
aliases: ['t'],
usage: '<Talent> [<-Erschwernis> / <+Erleichterung>]',
needs_args: true,
async exec(message, args) {
try {
db.find({
user: message.author.tag,
}, function (err, docs) {
if (docs.length === 0) {
return message.reply(findMessage('NOENTRY'));
}
if (!isNaN(args[0])) {
return message.reply(findMessage('WRONG_ARGUMENTS'));
} else {
const Skill = getSkill({Character: docs[0].character, args: args});
if(!Skill) { return message.reply(findMessage('TALENT_UNKNOWN'));}
const Attributes = Skill.Attributes;
const DiceThrow = roll(3, 20, message.author.tag).dice;
const Bonus = parseInt(args[1]) || 0;
let { Passed,
CriticalHit,
Fumbles,
PointsUsed,
PointsRemaining } = CompareResults(
DiceThrow,
Attributes.map(attr => attr.Level),
Bonus,
Skill.Level);
const Reply = new Discord.MessageEmbed();
Reply.addFields({
name: `Du würfelst auf das Talent **${Skill.Name}** (Stufe ${Skill.Level} + ${Bonus})`,
value: CreateTable({Attributes: Attributes, Throws: DiceThrow, PointsUsed: PointsUsed}),
inline: false
});
if (Fumbles >= 2) {
Reply.setColor('#900c3f');
Reply.addFields({
name: findMessage('TITLE_CRIT_FAILURE'),
value: findMessage('MSG_CRIT_FAILURE'),
inline: false
});
} else if (CriticalHit >= 2) {
Reply.setColor('#1E8449');
Reply.addFields({
name: findMessage('TITLE_CRIT_SUCCESS'),
value: findMessage('MSG_CRIT_SUCCESS'),
inline: false
});
} else if (Passed < 3) {
Reply.addFields({
name: findMessage('TITLE_FAILURE'),
value: `${(Passed === 0) ? 'Keine Probe' : `nur ${Passed}/3 Proben`} erfolgreich. 😪`,
inline: false
});
} else {
Reply.addFields({
name: findMessage('TITLE_SUCCESS'),
value: `Dein verbleibender Bonus: ${PointsRemaining}/${Skill.Level} (QS${CalculateQuality(PointsRemaining)})`,
inline: false
});
}
message.reply(Reply);
}
});
} catch (e) {
throw e;
}
},
name: 'talent',
description:
' Du machst eine Fertigkeitsprobe.\n' +
' Es werden drei Würfel auf deine Eigenschaftswerte geworfen. Hast du Boni auf dein Talent und/oder' +
' ist der Wurf erleichtert oder erschwert, wird dies in die Berechnung einbezogen.',
aliases: ['t'],
usage: '<Talent> [<-Erschwernis> / <+Erleichterung>]',
needs_args: true,
async exec(message, args) {
db.find({ user: message.author.tag }, (err, docs) => {
if (docs.length === 0) {
return message.reply(findMessage('NOENTRY'));
}
if (!isNaN(args[0])) {
return message.reply(findMessage('WRONG_ARGUMENTS'));
}
const Skill = getSkill({ Character: docs[0].character, args: args });
if (!Skill) {
return message.reply(findMessage('TALENT_UNKNOWN'));
}
const Attributes = Skill.Attributes;
const DiceThrow = roll(3, 20, message.author.tag).dice;
const Bonus = parseInt(args[1]) || 0;
let { Passed, CriticalHit, Fumbles, PointsUsed, PointsRemaining } = CompareResults(
DiceThrow,
Attributes.map(attr => attr.Level),
Bonus,
Skill.Level
);
const Reply = new Discord.MessageEmbed();
Reply.addFields({
name: `Du würfelst auf das Talent **${Skill.Name}** (Stufe ${Skill.Level} + ${Bonus})`,
value: CreateResultTable({
Attributes: Attributes,
Throws: DiceThrow,
PointsUsed: PointsUsed,
Bonus: Bonus,
}),
inline: false,
});
if (Fumbles >= 2) {
Reply.setColor('#900c3f');
Reply.addFields({
name: findMessage('TITLE_CRIT_FAILURE'),
value: findMessage('MSG_CRIT_FAILURE'),
inline: false,
});
} else if (CriticalHit >= 2) {
Reply.setColor('#1E8449');
Reply.addFields({
name: findMessage('TITLE_CRIT_SUCCESS'),
value: findMessage('MSG_CRIT_SUCCESS'),
inline: false,
});
} else if (Passed < 3) {
Reply.addFields({
name: findMessage('TITLE_FAILURE'),
value: `${Passed === 0 ? 'Keine Probe' : `nur ${Passed}/3 Proben`} erfolgreich. 😪`,
inline: false,
});
} else {
Reply.addFields({
name: findMessage('TITLE_SUCCESS'),
value: `Dein verbleibender Bonus: ${PointsRemaining}/${Skill.Level} (QS${CalculateQuality(
PointsRemaining
)})`,
inline: false,
});
}
message.reply(Reply);
});
},
};
function Pad(Number = 0) {
return Number.toString().padStart(1, ' ');
}
const CreateTable = ({Attributes: Attributes, Throws: Throws, PointsUsed: PointsUsed}) => {
return `
\`\`\`
${' '.padEnd(15)} ${Attributes.map(attr => `${attr.Name}`.padStart(5)).join('\t|\t')}\t|
${'Dein Wert'.padEnd(15)} ${Attributes.map(attr => `${attr.Level}`.padStart(5)).join('\t|\t')}\t|
${'Dein Wurf'.padEnd(15)} ${Throws.map(Throw => `${Throw}`.padStart(5)).join('\t|\t')}\t|
${'Abzüge'.padEnd(15)} ${PointsUsed.map(Points => `${Points}`.replace(0,'--').padStart(5)).join('\t|\t')}\t|
${'Gesamt'.padEnd(15)} ${PointsUsed.reduce((acc,cur) => acc+cur).toString().padStart(5)}
\`\`\`
`;
};

View File

@ -1 +0,0 @@

View File

@ -1,35 +1,51 @@
const CompareResults = (Throws = [], AttributeLevels = [8, 8, 8], Bonus = 0, PointsRemaining = 0) => {
/**
* Compares each item inside an array Throws
* with corresponding AttributeLevels (With added bonus)
*
* @param {Array} Throws=[]
* @param {Array} AttributeLevels=[8,8,8]
* @param {BigInt} Bonus=0
* @param {BigInt} PointsRemaining=0
*/
const CompareResults = (
Throws = [],
AttributeLevels = [8, 8, 8],
Bonus = 0,
PointsRemaining = 0
) => {
let Passed = 0;
let Fumbles = 0;
let CriticalHit = 0;
let AllPointsUsed = [];
let Passed = 0;
let Fumbles = 0;
let CriticalHit = 0;
let AllPointsUsed = [];
for (let i = 0; i < Throws.length; i++) {
let PointsUsed = 0;
if (Math.floor(AttributeLevels[i] + Bonus) >= Throws[i]) {
Passed++;
} else if (Math.floor(AttributeLevels[i] + PointsRemaining + Bonus) >= Throws[i]) {
Passed++;
PointsUsed = (Throws[i] - Bonus - AttributeLevels[i]);
PointsRemaining -= PointsUsed;
}
else {
// We need to use all our points, so that next die/dice
// would not return a 'Passed'.
PointsUsed = PointsRemaining;
PointsRemaining -= PointsUsed;
}
if (Throws[i] == 1) { CriticalHit++; }
if (Throws[i] == 20) { Fumbles++; }
AllPointsUsed.push(PointsUsed);
}
return {
Passed: Passed,
CriticalHit: CriticalHit,
Fumbles: Fumbles,
PointsUsed: AllPointsUsed,
PointsRemaining: PointsRemaining
};
for (let i = 0; i < Throws.length; i++) {
let PointsUsed = 0;
if (Math.floor(AttributeLevels[i] + Bonus) >= Throws[i]) {
Passed++;
} else if (Math.floor(AttributeLevels[i] + PointsRemaining + Bonus) >= Throws[i]) {
Passed++;
PointsUsed = Throws[i] - Bonus - AttributeLevels[i];
PointsRemaining -= PointsUsed;
} else {
// We need to use all our points, so that next die/dice
// would not return a 'Passed'.
PointsUsed = PointsRemaining;
PointsRemaining -= PointsUsed;
}
if (Throws[i] == 1) {
CriticalHit++;
}
if (Throws[i] == 20) {
Fumbles++;
}
AllPointsUsed.push(PointsUsed);
}
return {
Passed: Passed,
CriticalHit: CriticalHit,
Fumbles: Fumbles,
PointsUsed: AllPointsUsed,
PointsRemaining: PointsRemaining,
};
};
module.exports = { CompareResults };

View File

@ -0,0 +1,28 @@
const CreateResultTable = ({
Attributes: Attributes,
Throws: Throws,
PointsUsed: PointsUsed,
Bonus: Bonus = 0,
}) => {
return `
\`\`\`
${''.padEnd(15)} ${Attributes.map(attr => `${attr.Name}`.padStart(6)).join('\t|\t')}\t|
${'Dein Wert'.padEnd(15)} ${Attributes.map(attr =>
`${attr.Level}${Bonus ? `(${f(Bonus)})` : ``}`.padStart(6)
).join('\t|\t')}\t|
${'Dein Wurf'.padEnd(15)} ${Throws.map(Throw => `${Throw}`.padStart(6)).join('\t|\t')}\t|
${'Abzüge'.padEnd(15)} ${PointsUsed.map(Points => `${Points}`.replace(0, '--').padStart(6)).join(
'\t|\t'
)}\t|
${'Gesamt'.padEnd(15)} ${PointsUsed.reduce((acc, cur) => acc + cur)
.toString()
.padStart(6)}
\`\`\`
`;
};
const f = n => {
return (n > 0 ? '+' : '') + n;
};
module.exports = { CreateResultTable, f };

28
functions/getChant.js Normal file
View File

@ -0,0 +1,28 @@
const { getAttributeLevels } = require('@dsabot/getAttributeLevels');
const Chants = require('@Lib/Chants.json');
const getChant = ({ Character: Character = [], chant_name: chant_name = '' } = {}) => {
//if (!Character.hasOwnProperty('chants')) return;
let chant_entry =
Chants.find(chant => chant.id.toLowerCase() === chant_name.toLowerCase()) ||
Chants.find(chant => chant.name.toLowerCase() === chant_name.toLowerCase());
if (!chant_entry) {
console.log(`getChant() Did not find entry for ${chant_name}`);
return;
}
let Level = 0; // This is the minimum attributes value.
let Chant = Character.chants.find(chant => chant.id === chant_entry.id) || {};
if (Chant && Chant.hasOwnProperty('level')) {
Level = Chant.level || 0;
}
let Attributes = getAttributeLevels(chant_entry.attributes, Character);
return {
Name: chant_entry.name,
Level: Level,
Attributes: Attributes,
};
};
module.exports = { getChant };

View File

@ -1,24 +1,27 @@
const globals = require('../globals');
const { getAttributeLevels } = require("@dsabot/getAttributeLevels");
const { getAttributeLevels } = require('@dsabot/getAttributeLevels');
const getSkill = ({ Character: Character = [], args: args = [] } = {}) => {
let skill_entry = globals.Talente.find(skill => skill.id.toLowerCase() === args[0].toLowerCase()) ||
globals.Talente.find(skill => skill.name.toLowerCase() === args[0].toLowerCase());
let skill_entry =
globals.Talente.find(skill => skill.id.toLowerCase() === args[0].toLowerCase()) ||
globals.Talente.find(skill => skill.name.toLowerCase() === args[0].toLowerCase());
if (!skill_entry) { return; }
if (!skill_entry) {
return;
}
let Level = 0; // This is the minimum attributes value.
let cSkill = Character.skills.find(skill => skill.id === skill_entry.id) || {};
if (cSkill) {
Level = cSkill.level || 0;
}
let Name = globals.Talente.find(skill => skill.id === skill_entry.id).name;
let Attributes = getAttributeLevels(skill_entry.values, Character);
let Level = 0; // This is the minimum attributes value.
let cSkill = Character.skills.find(skill => skill.id === skill_entry.id) || {};
if (cSkill) {
Level = cSkill.level || 0;
}
let Name = globals.Talente.find(skill => skill.id === skill_entry.id).name;
let Attributes = getAttributeLevels(skill_entry.values, Character);
return {
Name: Name,
Level: Level,
Attributes: Attributes
};
return {
Name: Name,
Level: Level,
Attributes: Attributes,
};
};
module.exports = { getSkill };

30
functions/getSpell.js Normal file
View File

@ -0,0 +1,30 @@
const { getAttributeLevels } = require('@dsabot/getAttributeLevels');
const Spells = require('@Lib/Spells.json');
const getSpell = ({ Character: Character = [], spell_name: spell_name = '' } = {}) => {
const spell_entry =
Spells.find(spell => spell.id.toLowerCase() === spell_name.toLowerCase()) ||
Spells.find(spell => spell.name.toLowerCase() === spell_name.toLowerCase());
if (!spell_entry) {
console.log(`getSpell() did not find entry for ${spell_name}`);
return;
}
let Level = 0; // This is the minimum attributes value.
if (!Character.hasOwnProperty('spells')) return;
let Spell = Character.spells.find(spell => spell.id === spell_entry.id) || {}; //?+
if (Spell && Spell.hasOwnProperty('level')) {
Level = Spell.level || 0;
}
let ModifiedBy = spell_entry.modified_by;
let Attributes = getAttributeLevels(spell_entry.attributes, Character);
return {
Name: spell_entry.name,
Level: Level,
Attributes: Attributes,
ModifiedBy: ModifiedBy,
};
};
module.exports = { getSpell };

View File

@ -139,7 +139,15 @@ const Replies = [
{ id: 'PARRY_SUCCESS', string: 'Parade erfolgreich.'},
{ id: 'PARRY_CRIT_SUCCESS', string: 'Kritischer Erfolg! Du darfst einen Passierschlag ausführen!'},
{ id: 'ROLL', string: 'Du würfelst:'},
{ id: 'HEADS_OR_TAILS', string: 'Die Münze landet auf '}
{ id: 'HEADS_OR_TAILS', string: 'Die Münze landet auf ' },
{ id: 'SPELL_UNKNOWN', string: 'Diesen Zauber kenne ich nicht.' },
{ id: 'NO_SPELLS', string: 'Du kennst keine Zaubersprüche.' },
{ id: 'SPELLS_TITLE', string: 'Zaubersprüche'},
{ id: 'SPELLS_DESCRIPTION', string: 'Folgende Zaubersprüche beherrschst du:'},
{ id: 'NO_CHANTS', string: 'Du kennst keine Liturgien.' },
{ id: 'CHANTS_TITLE', string: 'Liturgien'},
{ id: 'CHANTS_DESCRIPTION', string: 'Folgende Liturgien beherrschst du:' },
{ id: 'CHANT_UNKNOWN', string: 'Diese Liturgie kenne ich nicht.'}
];
const Declination = ['dem', 'der', 'dem', '']; // Maskulinum, Feminimum, Neutrum, None
const Articles = ['Der','Die','Das',''];
@ -231,5 +239,6 @@ const Advantages = [
const Disadvantages = [
{}
];
module.exports = { Werte, Talente, Coin, TalentKategorien, DiceRegex, Discord, MessageEmbed, db, Replies, MeleeWeapons, Weapons, RangedWeapons, CombatTechniques, Articles, Declination };

View File

@ -1,8 +1,10 @@
module.exports = {
testEnvironment: 'node',
moduleNameMapper: {
"@dsabot/(.*)": "<rootDir>/functions/$1",
"@Commands/(.*)": "<rootDir>/commands/$1",
"@Root/(.*)": "<rootDir>/$1",
}
};
testEnvironment: 'node',
moduleNameMapper: {
'@dsabot/(.*)': '<rootDir>/functions/$1',
'@Commands/(.*)': '<rootDir>/commands/$1',
'@Root/(.*)': '<rootDir>/$1',
'@data/(.*)': '<rootDir>/data/$i',
'@lib/(.*)': '<rootDir>/lib/$i',
},
};

View File

@ -3,11 +3,14 @@
"baseUrl": ".",
"paths": {
"@dsabot/*": ["./functions/*"],
"@globals": ["./globals"]
"@globals": ["./globals"],
"@data/*": ["./data/*"],
"@Commands/*": ["./commands/*"],
"@Lib/*": ["./lib/*"]
},
},
"exclude": ["node_modules"],
"typeAcquisition": {
"exclude": [ "dotenv" ]
"exclude": [ "dotenv", "source-map" ]
},
}

1
lib/Chants.json Normal file

File diff suppressed because one or more lines are too long

1
lib/Spells.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "dsabot",
"version": "1.1.0",
"version": "1.5.0",
"description": "",
"main": "index.js",
"scripts": {
@ -9,7 +9,10 @@
"test": "jest"
},
"_moduleAliases": {
"@dsabot": "functions"
"@dsabot": "functions",
"@data": "data",
"@Commands": "commands",
"@Lib": "lib"
},
"author": "",
"license": "ISC",