Achievements

This commit is contained in:
Acamaeda 2020-10-14 21:43:16 -04:00
parent d9d2f777a7
commit 923f096aa0
12 changed files with 173 additions and 14 deletions

View file

@ -39,4 +39,5 @@ All display text can be basic HTML instead (But you can't use most Vue features
- [Custom Tab Layouts](custom-tab-layouts.md): An optional way to give your tabs a different layout. - [Custom Tab Layouts](custom-tab-layouts.md): An optional way to give your tabs a different layout.
You can even create entirely new components to use. You can even create entirely new components to use.
- [Subtabs and Microtabs](subtabs-and-microtabs.md): Create subtabs for your tabs, as well as "microtab" components that you can put inside the tabs. - [Subtabs and Microtabs](subtabs-and-microtabs.md): Create subtabs for your tabs, as well as "microtab" components that you can put inside the tabs.
- [Achievements](milestones.md): How to create achievements for a layer (or for the whole game).
- [Updating TMT](updating-tmt.md): Using Github Desktop to update your mod's version of TMT. - [Updating TMT](updating-tmt.md): Using Github Desktop to update your mod's version of TMT.

55
docs/achievements.md Normal file
View file

@ -0,0 +1,55 @@
#Achievements
Achievements are awarded to the player when they meet a certain goal, and give some benefit.
Currently, they are pretty basic, but additional features will be added later to help.
You can make global achievements by putting them in a side layer (make its row "side" instead of a number)
Useful functions for dealing with achievements and implementing their effects:
- hasAchievement(layer, id): determine if the player has the Achievement
- achievementEffect(layer, id): Returns the current effects of the achievement, if any
Achievements should be formatted like this:
```js
achievements: {
rows: # of rows
cols: # of columns
11: {
name: "Blah",
more features
}
etc
}
```
Each achievement should have an id where the first digit is the row and the second digit is the column.
Individual achievement can have these features:
- name: **optional**, displayed at the top of the achievement. The only visible text.
It can also be a function that returns updating text. Can use basic HTML.
- done(): A function returning a boolean to determine if the achievement should be awarded.
- goalTooltip: Appears when the achievement is hovered over and locked. This is to display the goal (or a hint).
It can also be a function that returns updating text. Can use basic HTML.
- doneTooltip: Appears when the achievement is hovered over and completed. This can display what the player achieved (the goal),
and the rewards, if any. It can also be a function that returns updating text. Can use basic HTML.
- effect(): **optional**, A function that calculates and returns the current values of any bonuses from the achievement.
Can return a value or an object containing multiple values.
- unlocked(): **optional**, A function returning a bool to determine if the achievement is visible or not. Default is unlocked.
- onComplete() - **optional**, this function will be called when the achievement is completed.
- style: **Optional**, Applies CSS to this achievement, in the form of an object where the keys are CSS attributes,
and the values are the values for those attributes (both as strings)
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar
- id: **Assigned automagically**. It's the "key" which the achievement was stored under, for convenient access.
The achievement in the example's id is 11.

View file

@ -36,14 +36,14 @@ These are the existing components, but you can create more in v.js:
- prestige-button: The argument is a string that the prestige button should say before the amount of - prestige-button: The argument is a string that the prestige button should say before the amount of
currency you will gain. It can also be a function that returns updating text. currency you will gain. It can also be a function that returns updating text.
- upgrades, milestones, challs: Display the upgrades, milestones, and challenges for a layer, as appropriate. - upgrades, milestones, challs, achievements: Display the upgrades, milestones, and challenges for a layer, as appropriate.
- buyables, clickables: Display all of the buyables/clickables for this layer, as appropriate. The argument optional, - buyables, clickables: Display all of the buyables/clickables for this layer, as appropriate. The argument optional,
and is the size of the boxes in pixels. and is the size of the boxes in pixels.
- microtabs: Display a set of subtabs for an area. The argument is the name of the set of microtabs in the "microtabs" feature. - microtabs: Display a set of subtabs for an area. The argument is the name of the set of microtabs in the "microtabs" feature.
- upgrade, milestone, chall, buyable: An individual upgrade, challenge, etc. The argument is the id. - upgrade, milestone, chall, buyable, clickable, achievement: An individual upgrade, challenge, etc. The argument is the id.
This can be used if you want to have upgrades split up across multiple subtabs, for example. This can be used if you want to have upgrades split up across multiple subtabs, for example.
- bar: Display a bar. The argument is the id of the bar to display. - bar: Display a bar. The argument is the id of the bar to display.

View file

@ -90,6 +90,10 @@ Key:
- bars: Display some information as a progress bar, gague, or similar. They are highly customizable, and can be vertical as well. - bars: Display some information as a progress bar, gague, or similar. They are highly customizable, and can be vertical as well.
[Explanations are in a separate file.](bars.md) [Explanations are in a separate file.](bars.md)
- achievements: Kind of like milestones, but with a different display style and some other differences. Extra features are on the way at a later date!
[Explanations are in a separate file.](achievements.md)
# Prestige formula features # Prestige formula features
- baseResource: The name of the resource that determines how much of the main currency you gain on reset. - baseResource: The name of the resource that determines how much of the main currency you gain on reset.

View file

@ -1,6 +1,6 @@
#Milestones #Milestones
Milestones should be formatted like this: Milestones are awarded to the player when they meet a certain goal, and give some benefit. Milestones should be formatted like this:
```js ```js
milestones: { milestones: {
@ -21,7 +21,7 @@ Milestone features:
- effectDesc: A string describing the reward for having the milestone. *You will have to implement the reward elsewhere.* - effectDesc: A string describing the reward for having the milestone. *You will have to implement the reward elsewhere.*
It can also be a function that returns updating text. Can use basic HTML. It can also be a function that returns updating text. Can use basic HTML.
- done(): A function returning a boolean to determine if the milestone has been fulfilled. - done(): A function returning a boolean to determine if the milestone should be awarded.
- toggles: *optional*, Creates toggle buttons that appear on the milestone when it is unlocked. - toggles: *optional*, Creates toggle buttons that appear on the milestone when it is unlocked.
The toggles can toggle a given boolean value in a layer. The toggles can toggle a given boolean value in a layer.

View file

@ -1,7 +1,5 @@
# Upgrades # Upgrades
Upgrades are stored in the following format:
Useful functions for dealing with Upgrades and implementing their effects: Useful functions for dealing with Upgrades and implementing their effects:
- hasUpgrade(layer, id): determine if the player has the upgrade - hasUpgrade(layer, id): determine if the player has the upgrade
@ -9,6 +7,7 @@ Useful functions for dealing with Upgrades and implementing their effects:
Hint: Basic point gain is calculated in game.js's "getPointGain". Hint: Basic point gain is calculated in game.js's "getPointGain".
Upgrades are stored in the following format:
```js ```js
upgrades: { upgrades: {

View file

@ -17,9 +17,9 @@ let modInfo = {
// Set your version in num and name, but leave the tmt values so people know what version it is // Set your version in num and name, but leave the tmt values so people know what version it is
let VERSION = { let VERSION = {
num: "2.0", num: "2.0",
name: "Finally making some progress!", name: "Pinnacle of Achievement Mountain",
tmtNum: "2.0", tmtNum: "2.0",
tmtName: "Finally making some progress!" tmtName: "Pinnacle of Achievement Mountain"
} }
// Determines if it should show points/sec // Determines if it should show points/sec
@ -182,6 +182,7 @@ function doReset(layer, force=false) {
addPoints(layer, gain) addPoints(layer, gain)
updateMilestones(layer) updateMilestones(layer)
updateAchievements(layer)
if (!player[layer].unlocked) { if (!player[layer].unlocked) {
player[layer].unlocked = true; player[layer].unlocked = true;
@ -312,6 +313,7 @@ function gameLoop(diff) {
for (layer in layers){ for (layer in layers){
if (layers[layer].milestones) updateMilestones(layer); if (layers[layer].milestones) updateMilestones(layer);
if (layers[layer].achievements) updateAchievements(layer)
} }
if (player.hasNaN&&!NaNalert) { if (player.hasNaN&&!NaNalert) {

View file

@ -52,6 +52,16 @@ function updateLayers(){
} }
} }
} }
if (layers[layer].achievements){
for (thing in layers[layer].achievements){
if (!isNaN(thing)){
layers[layer].achievements[thing].id = thing
layers[layer].achievements[thing].layer = layer
if (layers[layer].achievements[thing].unlocked === undefined)
layers[layer].achievements[thing].unlocked = true
}
}
}
if (layers[layer].challenges){ if (layers[layer].challenges){
for (thing in layers[layer].challenges){ for (thing in layers[layer].challenges){
if (!isNaN(thing)){ if (!isNaN(thing)){

View file

@ -576,8 +576,8 @@ addLayer("a", {
}}, }},
color: "yellow", color: "yellow",
requires: new Decimal (1), requires: new Decimal (1),
resource: "idk", resource: "achievement power",
baseResource: "candies", baseResource: "achievements",
baseAmount() {return player.points}, baseAmount() {return player.points},
type: "normal", // A "Custom" type which is effectively static type: "normal", // A "Custom" type which is effectively static
exponent: 0.5, exponent: 0.5,
@ -589,9 +589,35 @@ addLayer("a", {
}, },
row: "side", row: "side",
layerShown() {return true}, layerShown() {return true},
tooltipUnlocked() { // Optional, tooltip displays when the layer is locked tooltip() { // Optional, tooltip displays when the layer is locked
return ("YEETS") return ("Achievements")
}, },
achievements: {
rows: 2,
cols: 3,
11: {
name: "Get me!",
done() {return true}, // This one is a freebie
goalTooltip: "How did this happen?", // Shows when achievement is not completed
doneTooltip: "You did it!", // Showed when the achievement is completed
},
12: {
name: "Impossible!",
done() {return false},
goalTooltip: "Mwahahaha!", // Shows when achievement is not completed
doneTooltip: "HOW????", // Showed when the achievement is completed
},
13: {
name: "EIEIO",
done() {return player.f.points.gte(1)},
goalTooltip: "Get a farm point.", // Shows when achievement is not completed
doneTooltip: "Get a farm point.\n\nReward: The dinosaur is now your friend.", // Showed when the achievement is completed
onComplete() {console.log("Bork bork bork!")}
},
},
tabFormat: [
"main-display", "blank", "blank", "achievements",
]
}, },
) )

View file

@ -93,6 +93,7 @@ function getStartPlayer() {
playerdata[layer].spentOnBuyables = new Decimal(0) playerdata[layer].spentOnBuyables = new Decimal(0)
playerdata[layer].upgrades = [] playerdata[layer].upgrades = []
playerdata[layer].milestones = [] playerdata[layer].milestones = []
playerdata[layer].achievements = []
playerdata[layer].challenges = getStartChallenges(layer) playerdata[layer].challenges = getStartChallenges(layer)
if (layers[layer].tabFormat && !Array.isArray(layers[layer].tabFormat)) { if (layers[layer].tabFormat && !Array.isArray(layers[layer].tabFormat)) {
playerdata.subtabs[layer] = {} playerdata.subtabs[layer] = {}
@ -368,6 +369,10 @@ function hasMilestone(layer, id){
return (player[layer].milestones.includes(toNumber(id)) || player[layer].milestones.includes(id.toString())) return (player[layer].milestones.includes(toNumber(id)) || player[layer].milestones.includes(id.toString()))
} }
function hasAchievement(layer, id){
return (player[layer].achievements.includes(toNumber(id)) || player[layer].achievements.includes(id.toString()))
}
function hasChallenge(layer, id){ function hasChallenge(layer, id){
return (player[layer].challenges[id]) return (player[layer].challenges[id])
} }
@ -404,6 +409,9 @@ function buyableEffect(layer, id){
return (tmp[layer].buyables[id].effect) return (tmp[layer].buyables[id].effect)
} }
function achievementEffect(layer, id){
return (tmp[layer].achievements[id].effect)
}
function canAffordPurchase(layer, thing, cost) { function canAffordPurchase(layer, thing, cost) {
if (thing.currencyInternalName){ if (thing.currencyInternalName){
@ -540,6 +548,15 @@ function updateMilestones(layer){
} }
} }
function updateAchievements(layer){
for (id in layers[layer].achievements){
if (!isNaN(id) && !(player[layer].achievements.includes(id)) && layers[layer].achievements[id].done()) {
player[layer].achievements.push(id)
if (layers[layer].achievements[id].onComplete) layers[layer].achievements[id].onComplete()
}
}
}
function addTime(diff, layer) { function addTime(diff, layer) {
let data = player let data = player
let time = data.timePlayed let time = data.timePlayed

30
js/v.js
View file

@ -291,6 +291,36 @@ function loadVue() {
}) })
Vue.component('achievements', {
props: ['layer'],
template: `
<div v-if="layers[layer].achievements" class="upgTable">
<div v-for="row in layers[layer].achievements.rows" class="upgRow">
<div v-for="col in layers[layer].achievements.cols"><div v-if="layers[layer].achievements[row*10+col]!== undefined && tmp[layer].achievements[row*10+col].unlocked" class="upgAlign">
<achievement :layer = "layer" :data = "row*10+col" v-bind:style="tmp[layer].componentStyles.achievement"></achievement>
</div></div>
</div>
<br>
</div>
`
})
// data = id
Vue.component('achievement', {
props: ['layer', 'data'],
template: `
<div v-if="layers[layer].achievements && layers[layer].achievements[data]!== undefined && tmp[layer].achievements[data].unlocked" v-bind:class="{ [layer]: true, achievement: true, locked: !hasAchievement(layer, data), bought: hasAchievement(layer, data)}"
v-bind:tooltip="
hasAchievement(layer, data) ? (tmp[layer].achievements[data].doneTooltip ? tmp[layer].achievements[data].doneTooltip : 'You did it!')
: (tmp[layer].achievements[data].goalTooltip ? tmp[layer].achievements[data].goalTooltip : 'LOCKED')
"
v-bind:style="[(!tmp[layer].achievements[data].unlocked) ? {'visibility': 'hidden'} : {}, tmp[layer].achievements[data].style,]">
<span v-if= "tmp[layer].achievements[data].name"><br><h3 v-html="tmp[layer].achievements[data].name"></h3><br></span>
</div>
`
})
// NOT FOR USE IN STANDARD TAB FORMATTING // NOT FOR USE IN STANDARD TAB FORMATTING
Vue.component('tab-buttons', { Vue.component('tab-buttons', {

View file

@ -200,6 +200,20 @@ h1, h2, h3, b, input {
font-size: 10px; font-size: 10px;
} }
.achievement {
height: 90px;
width: 90px;
border-radius: 25%;
border: 2px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
font-size: 10px;
color: white;
text-shadow: 0px 0px 2px #000000;
}
.achievement:hover {
box-shadow: 0px 0px 10px var(--points);
}
.buyable { .buyable {
height: 200px; height: 200px;
width: 200px; width: 200px;
@ -541,6 +555,7 @@ ul {
.treeOverlay { .treeOverlay {
z-index: 200000; z-index: 200000;
pointer-events:none; pointer-events:none;
overflow:hidden;
} }
.overlayThing { .overlayThing {
@ -551,6 +566,6 @@ ul {
.sideLayers { .sideLayers {
pointer-events:auto; pointer-events:auto;
position: absolute; position: absolute;
right: 35px; right: 55px;
top: 55px; top: 65px;
} }