Add "Respec and load" option (#3270)

* Add rudimentary "Eternity and load" function to studystringmodal

* Add "Eternity and load" Button to tt shop

* Make it clearer when eternity and load option won't eternity

* make StudyStringPreview more accurate for eternity and load

* Fix eternity and load bug

* Rename "eternity and load" to "respec and load"

* Address PR feedback (Respec and load)
This commit is contained in:
Dys 2023-01-17 13:22:59 +08:00 committed by GitHub
parent b0956163fb
commit 392a2934b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 253 additions and 59 deletions

View File

@ -8,10 +8,12 @@ export function animateAndDilate() {
setTimeout(startDilatedEternity, 1000);
}
export function animateAndUndilate() {
// eslint-disable-next-line no-empty-function
export function animateAndUndilate(callback = () => {}) {
FullScreenAnimationHandler.display("a-undilate", 2);
setTimeout(() => {
eternity(false, false, { switchingDilation: true });
callback();
}, 1000);
}

View File

@ -137,22 +137,28 @@ export function eternity(force, auto, specialConditions = {}) {
return true;
}
export function animateAndEternity() {
if (!Player.canEternity) return;
// eslint-disable-next-line no-empty-function
export function animateAndEternity(callback = () => {}) {
if (!Player.canEternity) return false;
const hasAnimation = !FullScreenAnimationHandler.isDisplaying &&
((player.dilation.active && player.options.animations.dilation) ||
(!player.dilation.active && player.options.animations.eternity));
if (hasAnimation) {
if (player.dilation.active) {
animateAndUndilate();
animateAndUndilate(callback);
} else {
eternityAnimation();
setTimeout(eternity, 2250);
setTimeout(() => {
eternity();
callback();
}, 2250);
}
} else {
eternity();
callback();
}
return hasAnimation;
}
export function initializeChallengeCompletions(isReality) {

View File

@ -551,16 +551,6 @@ button:focus {
padding: 0.2rem;
}
.l-tt-save-load-btn__menu-item {
padding: 0.25rem 1rem;
cursor: pointer;
}
.c-tt-save-load-btn__menu-item:hover {
color: black;
background: white;
}
.c-tt-autobuyer-toggle {
color: initial;
}
@ -1461,8 +1451,8 @@ br {
background-color: var(--color-background);
border: var(--var-border-width, 0.2rem) solid var(--color-text);
border-radius: var(--var-border-radius, 0.5rem);
padding: 0.5rem;
margin-top: -0.4rem;
padding: 0.5rem;
transition-duration: 0.3s;
pointer-events: none;
}
@ -1485,11 +1475,11 @@ br {
position: absolute;
top: 2.8rem;
right: 100%;
border-right: 0 solid black;
border-top: 0.6rem solid transparent;
border-right: 0 solid black;
border-bottom: 0.6rem solid transparent;
transform: translateY(-50%);
margin-right: 0.1rem;
transform: translateY(-50%);
transition-duration: 0.3s;
}
@ -5634,10 +5624,10 @@ properly on certain themes. */
}
.c-modal-away-progress__resources div {
min-width: 55rem;
border-bottom: 0.1rem solid var(--color-text);
margin-bottom: 0.2rem;
padding-bottom: 0.2rem;
min-width: 55rem;
cursor: pointer;
}
@ -8888,12 +8878,14 @@ kbd {
.c-reality-upgrade-btn--possible {
color: black;
background: repeating-linear-gradient(
-45deg,
#979729,
#979729 3rem,
#777722 3rem,
#777722 6rem);
background:
repeating-linear-gradient(
-45deg,
#979729,
#979729 3rem,
#777722 3rem,
#777722 6rem
);
cursor: default;
}
@ -8906,12 +8898,12 @@ kbd {
color: black;
background: #954040;
background-image:
linear-gradient(45deg, #a55252 25%, transparent 25%),
linear-gradient(135deg, #a55252 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #a55252 75%),
linear-gradient(135deg, transparent 75%, #a55252 75%);
background-size: 4rem 4rem;
linear-gradient(45deg, #a55252 25%, transparent 25%),
linear-gradient(135deg, #a55252 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #a55252 75%),
linear-gradient(135deg, transparent 75%, #a55252 75%);
background-position: 0 0, 2rem 0, 2rem -2rem, 0 2rem;
background-size: 4rem 4rem;
cursor: default;
}
@ -9214,13 +9206,13 @@ input.o-automator-block-input {
.l-h2p-modal {
display: flex;
overflow: hidden;
flex-direction: column;
/* stylelint-disable-next-line unit-allowed-list */
width: calc(100vw - 20vh);
/* stylelint-disable-next-line unit-allowed-list */
height: calc(100vh - 20vh);
margin: 0.5rem;
overflow: hidden;
}
.l-h2p-container {

View File

@ -32,6 +32,8 @@ export default {
return {
input: "",
name: "",
respecAndLoad: false,
canEternity: false
};
},
computed: {
@ -41,12 +43,14 @@ export default {
},
// This represents the state reached from importing into an empty tree
importedTree() {
if (!this.inputIsValidTree) return false;
if (!this.inputIsValidTree) return {};
const importedTree = new TimeStudyTree(this.truncatedInput);
const newStudiesArray = importedTree.purchasedStudies.map(s => this.studyString(s));
return {
timeTheorems: importedTree.spentTheorems[0],
spaceTheorems: importedTree.spentTheorems[1],
newStudies: makeEnumeration(importedTree.purchasedStudies.map(s => this.studyString(s))),
newStudies: makeEnumeration(newStudiesArray),
newStudiesArray,
invalidStudies: importedTree.invalidStudies,
firstPaths: makeEnumeration(importedTree.dimensionPaths),
secondPaths: makeEnumeration(importedTree.pacePaths),
@ -57,14 +61,16 @@ export default {
// This is only shown when importing; when modifying a preset we assume that generally the current state of the
// tree is irrelevant because if it mattered then the player would simply import instead
combinedTree() {
if (!this.inputIsValidTree) return false;
if (!this.inputIsValidTree) return {};
const currentStudyTree = GameCache.currentStudyTree.value;
const combinedTree = this.combinedTreeObject;
const newStudiesArray = combinedTree.purchasedStudies
.filter(s => !currentStudyTree.purchasedStudies.includes(s)).map(s => this.studyString(s));
return {
timeTheorems: combinedTree.spentTheorems[0] - currentStudyTree.spentTheorems[0],
spaceTheorems: combinedTree.spentTheorems[1] - currentStudyTree.spentTheorems[1],
newStudies: makeEnumeration(combinedTree.purchasedStudies
.filter(s => !currentStudyTree.purchasedStudies.includes(s)).map(s => this.studyString(s))),
newStudies: makeEnumeration(newStudiesArray),
newStudiesArray,
firstPaths: makeEnumeration(combinedTree.dimensionPaths),
secondPaths: makeEnumeration(combinedTree.pacePaths),
ec: combinedTree.ec,
@ -141,10 +147,23 @@ export default {
this.$refs.input.select();
},
methods: {
update() {
this.canEternity = Player.canEternity;
},
confirm() {
if (this.deleting) this.deletePreset();
else if (this.isImporting) this.importTree();
else this.savePreset();
if (this.deleting) {
this.deletePreset();
} else if (this.isImporting) {
if (this.respecAndLoad && Player.canEternity) {
player.respec = true;
const studies = new TimeStudyTree(this.truncatedInput).purchasedStudies;
animateAndEternity(() => TimeStudyTree.commitToGameState(studies, false));
return;
}
this.importTree();
} else {
this.savePreset();
}
},
convertInputShorthands() {
this.input = TimeStudyTree.formatStudyList(this.input);
@ -237,7 +256,9 @@ export default {
<StudyStringPreview
v-if="!deleting"
:show-preview="inputIsValidTree"
:tree-status="combinedTree"
:new-studies="!isImporting || (canEternity && respecAndLoad) ? importedTree.newStudiesArray
: combinedTree.newStudiesArray"
:disregard-current-studies="!isImporting || (canEternity && respecAndLoad)"
/>
<div v-else-if="hasInput">
Not a valid tree
@ -253,6 +274,35 @@ export default {
Format Preset Text
</PrimaryButton>
</div>
<span v-if="isImporting">
<br>
<div
v-tooltip="canEternity ? '' : 'You are currently unable to eternity, so this will only do a normal load.'"
class="c-modal__confirmation-toggle"
@click="respecAndLoad = !respecAndLoad"
>
<div
:class="{
'c-modal__confirmation-toggle__checkbox': true,
'c-modal__confirmation-toggle__checkbox--active': respecAndLoad,
}"
>
<span
v-if="respecAndLoad"
class="fas fa-check"
/>
</div>
<span class="c-modal__confirmation-toggle__text">
Also respec tree and eternity
<span
v-if="!canEternity"
class="c-modal__confirmation-toggle__warning"
>
!
</span>
</span>
</div>
</span>
<template #confirm-text>
{{ confirmText }}
</template>
@ -266,4 +316,22 @@ export default {
pointer-events: none;
user-select: none;
}
.c-modal__confirmation-toggle__text {
opacity: 1;
}
.c-modal__confirmation-toggle__warning {
display: inline-flex;
/* stylelint-disable-next-line unit-allowed-list */
width: 1em;
/* stylelint-disable-next-line unit-allowed-list */
height: 1em;
justify-content: center;
align-items: center;
color: #332222;
background: var(--color-bad);
border-radius: 100%;
margin-left: 0.3rem;
}
</style>

View File

@ -1,4 +1,6 @@
<script>
import { ForceBoughtState } from "./StudyStringPreview";
export default {
name: "PseudoTimeStudyButton",
props: {
@ -6,6 +8,10 @@ export default {
type: Object,
required: true
},
forceIsBought: {
type: Number,
default: 1
},
isNewFromImport: {
type: Boolean,
default: false
@ -81,7 +87,7 @@ export default {
update() {
const study = this.study;
this.isUseless = Pelle.uselessTimeStudies.includes(this.study.id) && Pelle.isDoomed;
this.isBought = study.isBought;
this.isBought = ForceBoughtState.getState(this.forceIsBought, study.isBought);
this.doomedRealityStudy = study.type === TIME_STUDY_TYPE.DILATION && study.id === 6 && Pelle.isDoomed;
},
}

View File

@ -1,11 +1,17 @@
<script>
import { ForceBoughtState } from "./StudyStringPreview";
export default {
name: "TimeStudyConnection",
name: "PseudoTimeStudyConnection",
props: {
setup: {
type: Object,
required: true
}
},
forceIsBought: {
type: Number,
default: 1
},
},
data() {
return {
@ -55,7 +61,7 @@ export default {
methods: {
update() {
this.isOverridden = this.setup.connection.isOverridden;
this.isBought = this.setup.isBought;
this.isBought = ForceBoughtState.getState(this.forceIsBought, this.setup.isBought);
},
percents(value) {
return `${value * 100}%`;

View File

@ -4,6 +4,24 @@ import PseudoTimeStudyConnection from "./PseudoTimeStudyConnection";
import { STUDY_TREE_LAYOUT_TYPE, TimeStudyTreeLayout } from "@/components/tabs/time-studies/time-study-tree-layout";
export const ForceBoughtState = {
notBought: 0,
unspecified: 1,
bought: 2,
getState(forceState, currentState) {
switch (forceState) {
case this.notBought:
return false;
case this.unspecified:
return currentState;
case this.bought:
return true;
}
return currentState;
}
};
export default {
name: "TimeStudiesTab",
components: {
@ -11,9 +29,13 @@ export default {
PseudoTimeStudyConnection,
},
props: {
treeStatus: {
type: [Object, Boolean],
required: true
disregardCurrentStudies: {
type: Boolean,
default: false
},
newStudies: {
required: true,
validator: newStudies => Array.isArray(newStudies) || newStudies === undefined,
},
showPreview: {
type: Boolean,
@ -36,10 +58,6 @@ export default {
studies() {
return this.layout.studies;
},
newStudies() {
// NewStudies is formatted using makeEnumeration so we need to convert it back to an array
return this.treeStatus.newStudies.replace(", and ", ", ").replace(" and ", ", ").split(", ");
},
connections() {
return this.layout.connections;
},
@ -83,6 +101,17 @@ export default {
case TIME_STUDY_TYPE.ETERNITY_CHALLENGE: return `EC${study.id}`;
}
return "Dilation Study";
},
getStudyForceBoughtState(studyStr) {
if (!this.disregardCurrentStudies) return ForceBoughtState.unspecified;
return this.newStudies.includes(studyStr) ? ForceBoughtState.bought : ForceBoughtState.notBought;
},
getConnectionForceBoughtState(setup) {
if (!this.disregardCurrentStudies) return ForceBoughtState.unspecified;
return (this.newStudies.includes(this.studyString(setup.connection.to)) &&
this.newStudies.includes(this.studyString(setup.connection.from)))
? ForceBoughtState.bought
: ForceBoughtState.notBought;
}
}
};
@ -99,7 +128,8 @@ export default {
v-for="setup in studies"
:key="setup.study.type.toString() + setup.study.id.toString()"
:setup="setup"
:is-new-from-import="newStudies.includes(studyString(setup.study))"
:force-is-bought="getStudyForceBoughtState(studyString(setup.study))"
:is-new-from-import="!disregardCurrentStudies && newStudies.includes(studyString(setup.study))"
/>
<svg
:style="treeStyleObject"
@ -108,6 +138,7 @@ export default {
<PseudoTimeStudyConnection
v-for="(setup, index) in connections"
:key="'connection' + index"
:force-is-bought="getConnectionForceBoughtState(setup)"
:setup="setup"
/>
</svg>

View File

@ -16,6 +16,7 @@ export default {
return {
name: "",
displayName: "",
canEternity: false
};
},
computed: {
@ -27,6 +28,7 @@ export default {
update() {
this.name = player.timestudy.presets[this.saveslot - 1].name;
this.displayName = this.name === "" ? this.saveslot : this.name;
this.canEternity = Player.canEternity;
},
nicknameBlur(event) {
const newName = event.target.value.slice(0, 4).trim();
@ -65,6 +67,14 @@ export default {
Modal.message.show("This Time Study list currently contains no Time Studies.");
}
},
respecAndLoad() {
if (Player.canEternity) {
player.respec = true;
const newTree = new TimeStudyTree();
newTree.attemptBuyArray(newTree.parseStudyImport(this.preset.studies));
animateAndEternity(() => TimeStudyTree.commitToGameState(newTree.purchasedStudies, false));
}
},
deletePreset() {
this.hideContextMenu();
if (this.preset.studies) Modal.studyString.show({ id: this.saveslot - 1, deleting: true });
@ -125,11 +135,24 @@ export default {
>
Save
</div>
<div
class="l-tt-save-load-btn__menu-item c-tt-save-load-btn__menu-item"
@click="load"
>
Load
<div class="l-tt-save-load-btn__menu-item">
<div
class="c-tt-save-load-btn__menu-item"
@click="load"
>
Load
</div>
<div class="c-tt-save-load-btn__menu-item__hover-options">
<div
:class="{
'c-tt-save-load-btn__menu-item__hover-option': true,
'c-tt-save-load-btn__menu-item__hover-option--disabled': !canEternity,
}"
@click="respecAndLoad"
>
Respec and Load
</div>
</div>
</div>
<div
class="l-tt-save-load-btn__menu-item c-tt-save-load-btn__menu-item"
@ -196,12 +219,72 @@ export default {
}
.l-tt-save-load-btn__menu-item {
padding: 0.25rem 1rem;
position: relative;
cursor: pointer;
}
.c-tt-save-load-btn__menu-item {
text-align: left;
padding: 0.25rem 1rem;
}
.c-tt-save-load-btn__menu-item:hover {
color: black;
background: white;
}
.c-tt-save-load-btn__menu-item__hover-options {
visibility: hidden;
width: fit-content;
position: absolute;
top: 0;
left: 100%;
opacity: 0;
color: white;
background: black;
border: 0.1rem solid black;
border-radius: var(--var-border-width, 0.5rem);
transform: translateX(0.5rem);
transition: visibility 0.2s, opacity 0.2s;
transition-delay: 0.5s;
cursor: pointer;
}
.c-tt-save-load-btn__menu-item__hover-option {
white-space: nowrap;
padding: 0.25rem 1rem;
}
.c-tt-save-load-btn__menu-item__hover-options::after {
content: "";
position: absolute;
/* A single menu item is 26px tall, minus 5px from the border */
top: 0.8rem;
right: 100%;
border-top: 0.5rem solid transparent;
border-right: 0.5rem solid black;
border-bottom: 0.5rem solid transparent;
}
.c-tt-save-load-btn__menu-item:hover,
.c-tt-save-load-btn__menu-item__hover-option:hover {
color: black;
background: white;
}
.l-tt-save-load-btn__menu-item:hover .c-tt-save-load-btn__menu-item__hover-options {
visibility: visible;
opacity: 1;
transition-delay: 0s;
}
.c-tt-save-load-btn__menu-item__hover-option--disabled {
opacity: 0.7;
cursor: default;
}
.c-tt-save-load-btn__menu-item__hover-option--disabled:hover {
color: white;
background: transparent;
}
</style>

View File

@ -230,8 +230,8 @@ export default {
.l-load-tree-area {
display: flex;
flex-direction: column;
align-items: left;
width: 50%;
align-items: left;
}
.l-tree-load-button-wrapper {