diff --git a/config/achievements.hjson b/config/achievements.hjson index 8f1babfe..ce841e93 100644 --- a/config/achievements.hjson +++ b/config/achievements.hjson @@ -198,5 +198,75 @@ } } } + + user_door_runs: { + type: userStat + statName: door_run_total_count + retroactive: true + match: { + 1: { + title: "Nostalgia Toe Dip", + globalText: "{userName} ran a door!" + text: "You ran a door!" + points: 5 + }, + 10: { + title: "This is Kinda Fun" + globalText: "{userName} ran {achievedValue} doors!" + text: "You've run {achievedValue} doors!" + points: 10 + } + 50: { + title: "Gamer" + globalText: "{userName} ran {achievedValue} doors!" + text: "You've run {achievedValue} doors!" + points: 15 + } + 100: { + title: "Textmode is All You Need" + globalText: "{userName} must really like textmode and has run {achievedValue} doors!" + text: "You've run {achievedValue} doors! You must really like textmode!" + points: 25 + } + 200: { + title: "Dropfile Enthusiast" + globalText: "{userName} the dropfile enthusiast ran {achievedValue} doors!" + text: "You're a dropfile enthusiast! You've run {achievedValue} doors!" + points: 50 + } + } + } + + user_door_total_minutes: { + type: userStat + statName: door_run_total_minutes + retroactive: true + match: { + 1: { + title: "Nevermind!" + globalText: "{userName} ran a door for {achievedValue!durationSeconds}. Guess it's not their thing!" + text: "You ran a door for only {achievedValue!durationSeconds}. Not your thing?" + points: 5 + } + 10: { + title: "It's OK I Guess" + globalText: "{userName} ran a door for {achievedValue!durationSeconds}!" + text: "You ran a door for {achievedValue!durationSeconds}!" + points: 10 + } + 30: { + title: "Good Game" + globalText: "{userName} ran a door for {achievedValue!durationSeconds}!" + text: "You ran a door for {achievedValue!durationSeconds}!" + points: 20 + } + 60: { + title: "Textmode Dragon Slayer" + globalText: "{userName} has spent {achievedValue!durationSeconds} in a door!" + text: "You've spent {achievedValue!durationSeconds} in a door!" + points: 25 + } + } + } } } \ No newline at end of file diff --git a/core/abracadabra.js b/core/abracadabra.js index e448315b..42731ac0 100644 --- a/core/abracadabra.js +++ b/core/abracadabra.js @@ -8,12 +8,15 @@ const theme = require('./theme.js'); const ansi = require('./ansi_term.js'); const Events = require('./events.js'); const { Errors } = require('./enig_error.js'); +const StatLog = require('./stat_log.js'); +const UserProps = require('./user_property.js'); // deps const async = require('async'); const assert = require('assert'); const _ = require('lodash'); const paths = require('path'); +const moment = require('moment'); const activeDoorNodeInstances = {}; @@ -149,6 +152,7 @@ exports.getModule = class AbracadabraModule extends MenuModule { } runDoor() { + StatLog.incrementUserStat(this.client.user, UserProps.DoorRunTotalCount, 1); Events.emit(Events.getSystemEvents().UserRunDoor, { user : this.client.user } ); this.client.term.write(ansi.resetScreen()); @@ -164,7 +168,15 @@ exports.getModule = class AbracadabraModule extends MenuModule { node : this.client.node, }; + const startTime = moment(); + this.doorInstance.run(exeInfo, () => { + const endTime = moment(); + const runTimeMinutes = Math.floor(moment.duration(endTime.diff(startTime)).asMinutes()); + if(runTimeMinutes > 0) { + StatLog.incrementUserStat(this.client.user, UserProps.DoorRunTotalMinutes, runTimeMinutes); + } + // // Try to clean up various settings such as scroll regions that may // have been set within the door diff --git a/core/predefined_mci.js b/core/predefined_mci.js index 01b1e285..39e339cc 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -155,6 +155,12 @@ const PREDEFINED_MCI_GENERATORS = { AC : function achievementCount(client) { return userStatAsString(client, UserProps.AchievementTotalCount, 0); }, AP : function achievementPoints(client) { return userStatAsString(client, UserProps.AchievementTotalPoints, 0); }, + DR : function doorRuns(client) { return userStatAsString(client, UserProps.DoorRunTotalCount, 0); }, + DM : function doorFriendlyRunTime(client) { + const minutes = client.user.properties[UserProps.DoorRunTotalMinutes] || 0; + return moment.duration(minutes, 'minutes').humanize(); + }, + // // Date/Time // diff --git a/core/string_format.js b/core/string_format.js index a756db72..4a5b110c 100644 --- a/core/string_format.js +++ b/core/string_format.js @@ -14,6 +14,7 @@ const { // deps const _ = require('lodash'); +const moment = require('moment'); /* String formatting HEAVILY inspired by David Chambers string-format library @@ -281,6 +282,10 @@ const transformers = { countWithAbbr : (n) => formatCount(n, true, 0), countWithoutAbbr : (n) => formatCount(n, false, 0), countAbbr : (n) => formatCountAbbr(n), + + durationHours : (h) => moment.duration(h, 'hours').humanize(), + durationMinutes : (m) => moment.duration(m, 'minutes').humanize(), + durationSeconds : (s) => moment.duration(s, 'seconds').humanize(), }; function transformValue(transformerName, value) { diff --git a/core/user_property.js b/core/user_property.js index dafd7170..a1489e82 100644 --- a/core/user_property.js +++ b/core/user_property.js @@ -50,6 +50,9 @@ module.exports = { MessageAreaTag : 'message_area_tag', MessagePostCount : 'post_count', + DoorRunTotalCount : 'door_run_total_count', + DoorRunTotalMinutes : 'door_run_total_minutes', + AchievementTotalCount : 'achievement_total_count', AchievementTotalPoints : 'achievement_total_points', }; diff --git a/docs/art/mci.md b/docs/art/mci.md index 5ec86805..3e5e18c5 100644 --- a/docs/art/mci.md +++ b/docs/art/mci.md @@ -60,6 +60,8 @@ for a full listing. Many codes attempt to pay homage to Oblivion/2, iNiQUiTY, et | `SW` | Current user's term width | | `AC` | Current user's total achievements | | `AP` | Current user's total achievement points | +| `DR` | Current user's number of door runs | +| `DM` | Current user's total amount of time spent in doors | | `DT` | Current date (using theme date format) | | `CT` | Current time (using theme time format) | | `OS` | System OS (Linux, Windows, etc.) | @@ -155,6 +157,21 @@ Standard style types available for `textStyle` and `focusTextStyle`: Various strings can be formatted using a syntax that allows width & precision specifiers, text styling, etc. Depending on the context, various elements can be referenced by `{name}`. Additional text styles can be supplied as well. The syntax is largely modeled after Python's [string format mini language](https://docs.python.org/3/library/string.html#format-specification-mini-language). ### Additional Text Styles +Some of the text styles mentioned above are also available in the mini format language: + +| Style | Description | +|-------|-------------| +| `normal` | Leaves text as-is. This is the default. | +| `toUpperCase` or `styleUpper` | ENIGMA BULLETIN BOARD SOFTWARE | +| `toLowerCase` or `styleLower` | enigma bulletin board software | +| `styleTitle` | Enigma Bulletin Board Software | +| `styleFirstLower` | eNIGMA bULLETIN bOARD sOFTWARE | +| `styleSmallVowels` | eNiGMa BuLLeTiN BoaRD SoFTWaRe | +| `styleBigVowels` | EniGMa bUllEtIn bOArd sOftwArE | +| `styleSmallI` | ENiGMA BULLETiN BOARD SOFTWARE | +| `styleMixed` | EnIGma BUlLEtIn BoaRd SOfTWarE (randomly assigned) | +| `styleL33t` | 3n1gm4 bull371n b04rd 50f7w4r3 | + Additional text styles are available for numbers: | Style | Description | @@ -165,6 +182,9 @@ Additional text styles are available for numbers: | `countWithAbbr` | Count with abbreviation such as `100 K`, `4.3 B`, etc. | | `countWithoutAbbr` | Just the count | | `countAbbr` | Just the abbreviation such as `M` for millions. | +| `durationHours` | Converts the provided *hours* value to something friendly such as `4 hours`, or `4 days`. | +| `durationMinutes` | Converts the provided *minutes* to something friendly such as `10 minutes` or `2 hours` | +| `durationSeconds` | Converts the provided *seconds* to something friendly such as `23 seconds` or `2 minutes` | #### Examples