diff --git a/.gitignore b/.gitignore index 7e99e36..60d0160 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -*.pyc \ No newline at end of file +*.pyc +original** +test** +__pycache__** diff --git a/audio/beep.wav b/audio/beep.wav new file mode 100644 index 0000000..4e57d75 Binary files /dev/null and b/audio/beep.wav differ diff --git a/audio/correctpass.wav b/audio/correctpass.wav new file mode 100644 index 0000000..999f440 Binary files /dev/null and b/audio/correctpass.wav differ diff --git a/audio/keyenter.wav b/audio/keyenter.wav new file mode 100644 index 0000000..3e11d7a Binary files /dev/null and b/audio/keyenter.wav differ diff --git a/audio/poweroff.wav b/audio/poweroff.wav new file mode 100644 index 0000000..3a005a0 Binary files /dev/null and b/audio/poweroff.wav differ diff --git a/audio/poweron.wav b/audio/poweron.wav new file mode 100644 index 0000000..e9c2360 Binary files /dev/null and b/audio/poweron.wav differ diff --git a/audio/wrongpass.wav b/audio/wrongpass.wav new file mode 100644 index 0000000..3a85b47 Binary files /dev/null and b/audio/wrongpass.wav differ diff --git a/fallout.py b/fallout.py index 193af61..80435bc 100755 --- a/fallout.py +++ b/fallout.py @@ -4,17 +4,80 @@ import fallout_boot as boot import fallout_locked as locked import fallout_hack as hack import fallout_selection as select +#import fallout_things as things +import fallout_data as data +from fallout_functions import soundTest, isQueueEmpty, addSound +from time import sleep import sys hard = False -if len(sys.argv) == 2 and sys.argv[1].lower() == 'hard': +if '--hard' in sys.argv: hard = True -if boot.beginBoot(hard): - pwd = hack.beginLogin() - if pwd != None: - login.beginLogin(hard, 'ADMIN', pwd) - print(select.beginSelection()) - else: - locked.beginLocked() - print('Login failed') +skip = False +if '--skip' in sys.argv: + skip = True + +if '--skip-preload' not in sys.argv: + soundTest() + +try: + addSound("poweron") + while not isQueueEmpty(): + sleep(0.2) + if skip or boot.beginBoot(hard): + while not isQueueEmpty(): + sleep(0.2) + pwd = None + if not skip: + pwd = hack.beginLogin() + else: + pwd = "VERYVERYSECUREPASSWORD" + if pwd != None: + login.beginLogin(hard, 'ADMIN', pwd) + while not isQueueEmpty(): + sleep(0.2) + sel = 0 + while sel != 3: + sel = select.beginSelection(data.ROBCO_HEADERS, data.SOFT_HEADERS, data.MAIN_MENU) + while not isQueueEmpty(): + sleep(0.2) + if sel == 0: + loc = 0 + while loc != 1: + MENU = [data.LOCK_MENU[tog][data.LOCK_CONDS[tog]] for tog in data.LOCK_CONDS.keys()] + MSGS = [data.LOCK_MSGS[tog][data.LOCK_CONDS[tog]] for tog in data.LOCK_CONDS.keys()] + loc = select.beginSelection(data.ROBCO_HEADERS, data.SOFT_HEADERS, MENU, MSGS) + if loc == 0: + if data.LOCK_CONDS['LOCKED']: + data.LOCK_CONDS['LOCKED'] = 0 + else: + data.LOCK_CONDS['LOCKED'] = 1 + elif sel == 1: + tur = 0 + while tur != 2: + MENU = [data.TURRET_MENU[tog][data.TURRET_CONDS[tog]] for tog in data.TURRET_CONDS.keys()] + MSGS = [data.TURRET_MSGS[tog][data.TURRET_CONDS[tog]] for tog in data.TURRET_CONDS.keys()] + tur = select.beginSelection(data.TURRET_HEADERS, data.TURRET_HEADERS2, MENU, MSGS) + if tur == 0: + data.TURRET_CONDS['TARGETING'] = 1 + elif tur == 1: + if data.TURRET_CONDS['ENABLED']: + data.TURRET_CONDS['ENABLED'] = 0 + else: + data.TURRET_CONDS['ENABLED'] = 1 + elif sel == 2: + log = 0 + while log != data.LOG_RET_ID: + log = select.beginSelection(data.ROBCO_HEADERS, data.SOFT_HEADERS, data.LOG_NAMES) + if log != data.LOG_RET_ID: + select.beginSelection(data.ROBCO_HEADERS, data.LOGS[data.LOG_NAMES[log]].split("\n"), ["Return"]) + else: + locked.beginLocked() + print('Login failed') +except KeyboardInterrupt: + pass + +addSound("poweroff") +while not isQueueEmpty(): + sleep(0.2) diff --git a/fallout_boot.py b/fallout_boot.py index 2f393f2..2464e27 100644 --- a/fallout_boot.py +++ b/fallout_boot.py @@ -58,7 +58,7 @@ def runBoot(scr, hardMode): # input is entered for them slowWrite(scr, '>') curses.napms(INPUT_PAUSE) - slowWrite(scr, ENTRY_1 + '\n', TYPE_DELAY) + slowWrite(scr, ENTRY_1 + '\n', TYPE_DELAY, True) slowWrite(scr, '\n' + MESSAGE_2 + '\n\n') @@ -73,10 +73,10 @@ def runBoot(scr, hardMode): else: slowWrite(scr, '>') curses.napms(INPUT_PAUSE) - slowWrite(scr, ENTRY_2 + '\n', TYPE_DELAY) + slowWrite(scr, ENTRY_2 + '\n', TYPE_DELAY, True) slowWrite(scr, '>') curses.napms(INPUT_PAUSE) - slowWrite(scr, ENTRY_3 + '\n', TYPE_DELAY) + slowWrite(scr, ENTRY_3 + '\n', TYPE_DELAY, True) slowWrite(scr, '\n' + MESSAGE_3 + '\n\n') @@ -88,7 +88,7 @@ def runBoot(scr, hardMode): else: slowWrite(scr, '>') curses.napms(INPUT_PAUSE) - slowWrite(scr, ENTRY_4 + '\n', TYPE_DELAY) + slowWrite(scr, ENTRY_4 + '\n', TYPE_DELAY, True) curses.napms(INPUT_PAUSE) return True diff --git a/fallout_data.py b/fallout_data.py new file mode 100644 index 0000000..c2f1068 --- /dev/null +++ b/fallout_data.py @@ -0,0 +1,147 @@ +####################### text strings ######################## + +ROBCO_HEADERS = ( + 'ROBCO INDUSTRIES UNIFIED OPERATING SYSTEM', + 'COPYRIGHT 2075-2077 ROBCO INDUSTRIES', + '-SERVER 6-', + '' +) + +TURRET_HEADERS = ( + '-RobCo Trespasser Management System-', + '====================================', + '' +) + +TURRET_HEADERS2 = ( + 'RobcOS v.85', + '(C)2076 RobCo', + '========================', + '| User Log:', + '| >> Administrator (RobCoID 2398-H)', + '| >> New_Admin: FIRE PUP', + '| Welcome new user, FIRE PUP', + '| >> New_Targeting_Param:', + '| >>> FIRE PUP_userGroup', + '' +) + +SOFT_HEADERS = ( + '\tSoftLock Solutions, Inc', + '"Your Security is Our Security"', + '>\\ Welcome, USER', + '' +) + +MAIN_MENU = ( + 'Lock Control', + 'Turret Control', + 'Read Log', + 'Logoff Terminal' +) + +LOCK_MENU = { + 'LOCKED': ["Disengage Lock", "Engage Lock"], + 'RETURN': ["Return"] +} + + +TURRET_MENU = { + 'TARGETING': ["Re-configure Targeting Parameters", "WARNING: No Targeting Data"], + 'ENABLED': ["Deactivate Turret System", "Activate Turret System"], + 'RETURN': ["Return"] +} + +TURRET_MSGS = { + 'TARGETING': ['Target Data Cleared. Exercise Caution.', 'Please Exercise Caution'], + 'ENABLED': ['Powering Down...', 'Booting...'], + 'RETURN': [''] +} + +LOCK_MSGS = { + 'LOCKED': ['Clearance granted, Unlocking...', 'Clearance granted, Locking...'], + 'RETURN': [''] +} + +TURRET_CONDS = { + 'TARGETING': 0, + 'ENABLED': 0, + 'RETURN': 0 +} + +LOCK_CONDS = { + 'LOCKED': 0, + 'RETURN': 0 +} + +LOGS = { + 'Megaton - Afterword': """USER: MOIRA BROWN +PASS: *********** + +Date: 09.10.2297 + +Welcome, MOIRA. It is another lovely day for science! + +DAILY LOG: + +Experiment Reports: +Irradiated Agriculture—Good response from mutfruit, strangely aggressive response to baseline sample. Purified water very helpful. +Deathclaw Communication—No language that I can discern, unless "Mutilating assistants" counts. What would they have to say, anyway? +Jefferson Purifier - Guards still refuse to allow access to see how the Purifier works. They say it's to prevent sabotage, but I think they aren't entirely certain. + +Personal Note: +Just shipped out another crate of survival guides, and the caravans just can't seem to get enough of them. Sheriff Simms says they've really put Megaton on the map—pretty ironic, since I've had to redraw that map about a million times since the first edition came out 20 years ago. At least all the attention means there's no shortage of assistants, but I'm never getting another assistant like the Lone Wanderer who stumbled into my shop so long ago. + +Just about everyone in the Capital Wasteland has a story about the Lone Wanderer, even though precious few ever really knew him. But that doesn't stop them from telling crazy tall tales about how he saved their lives, or blew up a mountain, or ate a car or something. Heck, if you get Simms drunk, he'll tell you that his dad died because of the Wanderer, even though he saved the town. People can't even agree on whether the Wanderer was a man or a woman, much less a saint or a monster. But they all agree on one thing: the Lone Wanderer changed the Capital Wasteland. + +Of course, that's why I'm working on the new book, compiling the best and most useful tales of the Lone Wanderer for the next generation. It's not easy sorting out all the conflicting stories, but that'll be half of the fun for the readers. More importantly, between all of those crazy stories of bravery, barbarity, and everything in-between, we can all find a reason to keep on fighting our war for survival. + +I guess some things never change, huh?""", + 'ERR - Ikkm00:Mvkz6x1ml:Nqtm': "[ERROR HX40-399: invalid decry.key]", + 'Nuka Cola - Quantum Progress Report 0041': """Isotope CE770 has proven to be a disastrous failure. All of the test subjects suffered severe internal organ failures and died within three days of ingestion. We recommend the immediate destruction of container A32 in the production rooms and suggest switching to isotope CE772. Please send standard "Nuka Condolences" Fruit and Cheese Packages to test group member's families.""", + 'Nuka Cola - Quantum Progress Report 0055': "Isotope CE772 has proven too damaging to the initial test group which now needs to be disbanded due to their reluctance to continue in our program. This group has suffered 4 fatalities, 12 major internal organ failures and 32 internal radiation burns. This is an unacceptable number of issues in a given test group and recommend we switch to an alternate isotope (such as CE774 or UR993).", + 'Nuka Cola - Quantum Progress Report 0067': "Test subjects in the Nuka-Cola Quantum program are responding well to the reconfigured taste and the new isotope. The only listed side effects from the group are: 3 cases of dizziness, 1 case of nausea and 1 case of impaired vision. We find from a sampling of 50 that this is an acceptable number of cases and approve this product for production.", + 'Nuka Cola - Company Announcement': "The Nuka-Cola Corporation is pleased to announce to all it's employees that the first shipments of our Nuka-Cola Quantum® are on their way to retailers in the Washington D.C. area! This flagship test market program is the culmination of a three year research program to bring the refreshing taste of Quantum to market. Congratulations to all employees on a job well done!", + 'Nuka Cola - New Flavor Coming!': """Attention all Nuka-Cola Corporation Employees + +We are very proud to announce that R&D has been completed on Nuka-Cola Clear! With an only minimal loss of life, we've been able to modify the look of Nuka-Cola but give it the same great taste. We will be submitting the product to Marketing shortly for bottle design and advertising strategies. From all of us in the Research Department, thanks for the support!""", + 'Nuka Cola - From: Marketing': """The following locations have been accepted into the flagship Nuka-Cola Quantum test program. Please ensure that 1 (one) crate of Quantum is included with their regular deliveries along with the advertising package provided by our Marketing Department. + +1. Paradise Falls Shopping Mart +2. Super Duper Mart +3. Old Olney Grocery""", + 'Nuka Cola - Packing Line Instructions': """Welcome to the Nuka-Cola Packing Line Operator's Station! You are now instrumental in getting Nuka-Cola from our factory and to the public, so please read the simple instructions below. If you need assistance, please call x347 and ask for your Line Supervisor. + +1. Access the Packing Terminal and choose desired inventory to load into Sorting Units. +2. Select "Activate Packing Line." +3. Monitor the Packing Line by listening for things such as mechanical screeching, explosions and/or human cries for help. +4. If there is an emergency, DO NOT PANIC! Simply call x347 and ask for your Line Supervisor. The Packing Line will automatically shut down in the event of an issue. + +Remember, only you can prevent inventory loss by being attentive and vigilant!""", + 'Nuka Cola - Packing Line Notice': """Attention all Packing Line employees! + +Due to an oversight by the design department, the new Nuka-Cola Quantum bottles are slightly heavier than the standard Nuka-Cola bottles. As a result, the Packing Line is prone to jams and may cause damage to the equipment. Please DO NOT load Nuka-Cola Quantum bottles into the Sorting Units until further notice. All test samples of Quantum will be packaged by hand until a solution is reached.""", + 'Nuka Cola - Stage One': """Stage One of the Nuka-Cola Quantum marketing will include: 2 (two) 15 (fifteen)-second television commercials, 4 (four) 10 (ten)-second radio commercials and a highway billboard campaign. + +The spots on TV and radio will tease the consumer with the blue glow of the new drink, never showing the bottle in an illuminated environment. The billboard will show the bottle's blue silhouette on a black background. + +The tag line will be "Try something new... Go Blue!". The name will not be revealed until Stage Two.""", + 'Nuka Cola - Stage Two': """Stage Two of the Nuka-Cola Quantum marketing will include: 2 (two) 30 (thirty)-second television commercials, 4 (four) 15 (fifteen)-second radio commercials and a highway billboard campaign. + +In this stage, the name "Quantum" will be revealed and the bottle shown in full view. We will emphasize the drink's new energy content and flavor. + +The tag line will be "Take the leap... enjoy a Quantum!" """, + 'Nuka Cola - Stage Three': """Stage Three of the Nuka-Cola Quantum marketing will include: 4 (four) 30 (thirty)-second television commercials, 4 (four) 15 (fifteen)-second radio commercials and a highway billboard campaign. + +In this final stage we will aggressively compare the drink to other competitors and show their inferiority using hired actors at "taste tests". The actors will read pre-written scripts approved by our department. The text should give an authentic "on the spot" impression but still clearly point out Quantum's strengths. + +The tag line will remain: "Take the leap... enjoy a Quantum!" """, + + + + +} + +LOG_NAMES = list(LOGS.keys()) +LOG_NAMES.extend(["Return"]) +LOG_RET_ID = len(LOG_NAMES) - 1 diff --git a/fallout_functions.py b/fallout_functions.py index 0e86be2..e65856c 100644 --- a/fallout_functions.py +++ b/fallout_functions.py @@ -1,6 +1,11 @@ import curses import time import sys +from pathlib import Path as _Path +from playsound import playsound as _play +from threading import Thread +from time import sleep + LETTER_PAUSE = 5 @@ -12,16 +17,29 @@ HIDDEN_MASK = '*' NEWLINE = 10 -DELETE = 127 +DELETE = 330 -def slowWrite(window, text, pause = LETTER_PAUSE): +BACKSPACE = 263 + +_playing = False + +_soundQueue = [] + +global _queueRunning +_queueRunning = False + +def slowWrite(window, text, pause = LETTER_PAUSE, fake_user = False): """ wrapper for curses.addstr() which writes the text slowely """ + if not fake_user: + addSound("beep") for i in range(len(text)): window.addstr(text[i]) window.refresh() curses.napms(pause) + if fake_user: + _playSound("keyenter", True) def upperInput(window, hidden = False, can_newline = True): """ @@ -39,8 +57,9 @@ def upperInput(window, hidden = False, can_newline = True): if inchar > 96 and inchar < 123: inchar -= 32 # deal with backspace - if inchar == DELETE: + if inchar in [DELETE, BACKSPACE]: if len(instr) > 0: + #addSound("keyenter") instr = instr[:-1] cur = window.getyx() window.move(cur[0], cur[1] - 1) @@ -51,12 +70,14 @@ def upperInput(window, hidden = False, can_newline = True): continue # output the character elif inchar != NEWLINE: + #addSound("keyenter") instr += chr(inchar) if hidden: window.addch(HIDDEN_MASK) else: window.addch(inchar) elif can_newline: + #addSound("keyenter") window.addch(NEWLINE) return instr @@ -67,3 +88,73 @@ def centeredWrite(window, text, pause = LETTER_PAUSE): width = window.getmaxyx()[1] window.move(window.getyx()[0], int(width / 2 - len(text) / 2)) slowWrite(window, text, pause) + + +def _soundCheck(): + """ + Internal use - Checks if the user explicity disabled sound or not + """ + return '--no-sound' not in sys.argv + + +def _playSound(file, block = False): + """ + Internal use - plays a specific sound from the current directory + """ + if _soundCheck(): + _play(str(_Path.cwd() / f'audio/{file}.wav'), block) + + +def _playQueue(): + """ + Internal use - play sounds from a queue + """ + global _queueRunning + global _soundQueue + if _queueRunning: + return + _queueRunning = True + while 1: + if len(_soundQueue) > 0: + _playSound(_soundQueue[0], True) + _soundQueue.pop(0) + else: + sleep(0.02) + + +def isQueueEmpty(): + """ + Check if the sound queue is empty + """ + return len(_soundQueue) == 0 + + +def soundTest(): + addSound("beep") + addSound("beep") + addSound("keyenter") + addSound("keyenter") + addSound("wrongpass") + addSound("wrongpass") + addSound("correctpass") + addSound("correctpass") + addSound("poweron") + addSound("poweron") + addSound("poweroff") + addSound("poweroff") + global _soundQueue + while not isQueueEmpty(): + sleep(0.2) + + +def addSound(file): + """ + Add sounds to the queue + """ + global _soundQueue + _soundQueue.extend([file]) + + +_queueMgr = Thread(target=_playQueue) +_queueMgr.daemon = True +_queueMgr.start() diff --git a/fallout_hack.py b/fallout_hack.py index 0ad6e4e..0205ba9 100644 --- a/fallout_hack.py +++ b/fallout_hack.py @@ -2,8 +2,7 @@ import curses import random import time import os -from fallout_functions import slowWrite -from fallout_functions import upperInput +from fallout_functions import slowWrite, upperInput, addSound ################## text strings ###################### @@ -23,7 +22,7 @@ LOGIN_ATTEMPTS = 4 HEADER_LINES = 5 # amount of time to pause after correct password input -LOGIN_PAUSE = 3000 +LOGIN_PAUSE = 3 # starting number for hex generation START_HEX = 0xf650 @@ -212,6 +211,8 @@ def userInput(scr, passwords): # user got password right if guess.upper() == pwd.upper(): + + addSound("correctpass") inputPad.addstr('>Exact match!\n') inputPad.addstr('>Please wait\n') inputPad.addstr('>while system\n') @@ -219,7 +220,7 @@ def userInput(scr, passwords): moveInput(scr, inputPad) - curses.napms(LOGIN_PAUSE) + time.sleep(LOGIN_PAUSE) return pwd # wrong password @@ -233,6 +234,7 @@ def userInput(scr, passwords): except IndexError: pass # user did not enter enough letters + addSound("wrongpass") inputPad.addstr('>Entry denied\n') inputPad.addstr('>' + str(matched) + '/' + str(pwdLen) + ' correct.\n') diff --git a/fallout_login.py b/fallout_login.py index 363d807..721149b 100644 --- a/fallout_login.py +++ b/fallout_login.py @@ -47,7 +47,7 @@ def runLogin(scr, hardMode, username, password): # input is entered for them slowWrite(scr, '> ') curses.napms(INPUT_PAUSE) - slowWrite(scr, ENTRY + username.upper() + '\n', TYPE_DELAY) + slowWrite(scr, ENTRY + username.upper() + '\n', TYPE_DELAY, True) slowWrite(scr, '\n' + PASSWORD_PROMPT + '\n\n') @@ -65,7 +65,7 @@ def runLogin(scr, hardMode, username, password): slowWrite(scr, '> ') curses.napms(INPUT_PAUSE) password_stars = HIDDEN_MASK * len(password) - slowWrite(scr, password_stars + '\n', TYPE_DELAY) + slowWrite(scr, password_stars + '\n', TYPE_DELAY, True) curses.napms(500) diff --git a/fallout_selection.py b/fallout_selection.py index c9596a2..0e26609 100644 --- a/fallout_selection.py +++ b/fallout_selection.py @@ -1,33 +1,10 @@ import curses -from fallout_functions import slowWrite -from fallout_functions import centeredWrite -from fallout_functions import NEWLINE - -####################### text strings ######################## - -CENTERED_HEADERS = ( - 'ROBCO INDUSTRIES UNIFIED OPERATING SYSTEM', - 'COPYRIGHT 2075-2077 ROBCO INDUSTRIES', - '-SERVER 6-', - '' -) - -OTHER_HEADERS = ( - '\tSoftLock Solutions, Inc', - '"Your Security is Our Security"', - '>\\ Welcome, USER', - '' -) - -SELECTIONS = ( - 'Disengage Lock', - 'Deactivate Turrets', - 'Read Log' -) +from time import sleep +from fallout_functions import slowWrite, centeredWrite, NEWLINE, addSound ###################### Functions ############################ -def makeSelection(scr): +def makeSelection(scr, SELECTIONS, MSGS): """ ALlow the user to select an option Returns the line number of the users selection starting at 0 @@ -37,7 +14,7 @@ def makeSelection(scr): selection_count = len(SELECTIONS) selection_start_y = scr.getyx()[0] width = scr.getmaxyx()[1] - + while inchar != NEWLINE: # move to start of selections and hightlight current selection scr.move(selection_start_y, 0) @@ -46,7 +23,7 @@ def makeSelection(scr): whole_line = '> ' + SELECTIONS[line] space = width - len(whole_line) % width whole_line += ' ' * space - + if line == selection: scr.addstr(whole_line, curses.A_REVERSE) else: @@ -59,13 +36,23 @@ def makeSelection(scr): # move up and down if inchar == curses.KEY_UP and selection > 0: selection -= 1 + addSound("keyenter") elif inchar == curses.KEY_DOWN and selection < selection_count - 1: selection += 1 - + addSound("keyenter") + if MSGS and MSGS[selection]: + whole_line = '> ' + MSGS[selection] + space = width - len(whole_line) % width + whole_line += ' ' * space + scr.addstr(' ' * width) + scr.addstr(whole_line) + scr.refresh() + sleep(2) + addSound("keyenter") return selection - -def runSelection(scr): + +def runSelection(scr, CENTERED_HEADERS, OTHER_HEADERS, OPTIONS, MESSAGES): """ Print the selections and allow the user to select one """ @@ -87,11 +74,11 @@ def runSelection(scr): scr.addch(curses.ACS_BSBS) scr.refresh() - return makeSelection(scr) + return makeSelection(scr, OPTIONS, MESSAGES) -def beginSelection(): +def beginSelection(center, other, options, messages = []): """ Initialize curses and start the boot process """ - res = curses.wrapper(runSelection) + res = curses.wrapper(runSelection, center, other, options, messages) return res diff --git a/fallout_things.py b/fallout_things.py new file mode 100644 index 0000000..e69de29 diff --git a/play-test.py b/play-test.py new file mode 100644 index 0000000..34c5d69 --- /dev/null +++ b/play-test.py @@ -0,0 +1,33 @@ +"""PyAudio Example: Play a wave file.""" + +import wave +import sys + +import pyaudio + + +CHUNK = 1024 + +if len(sys.argv) < 2: + print(f'Plays a wave file. Usage: {sys.argv[0]} filename.wav') + sys.exit(-1) + +with wave.open(sys.argv[1], 'rb') as wf: + # Instantiate PyAudio and initialize PortAudio system resources (1) + p = pyaudio.PyAudio() + + # Open stream (2) + stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), + channels=wf.getnchannels(), + rate=wf.getframerate(), + output=True) + + # Play samples from the wave file (3) + while len(data := wf.readframes(CHUNK)): # Requires Python 3.8+ for := + stream.write(data) + + # Close stream (4) + stream.close() + + # Release PortAudio system resources (5) + p.terminate()