MIN WORKS

HACKERS TEXT-BASED RPG

PRIMARY OBJECTIVE: PYTHON GAME DESIGN

It is the 80's. The synth is wavy. Michael Jackson is still alive (and still black). Thankfully, things are not like how Orwell anticipated. Life is actually pretty good.

But you have no time to lose. SKYNET, a rouge AI, is going to kill your mother. Fortunately, you are an expert hacker and is determined to save your mom from certain death. You need to hack into SKYNET's mainframe and disable their algorithms. You slip on your NES Powerglove. The fate of this world rests on your Commodore 64.

This is a game I made for my AP Computer Science Principles class in my junior year of high school. It's written purely in Python3 and while the game has some minor bugs, it is fully playable.

It is intended to be played on a fullscreen terminal. Python3 and pythondialog are required to run. Synthwave required for full immersive experience.

EDIT: as of 2019-12-30, the game no longer works on my computer. There seems to be an issue with the pythondialog module. I have tried everything to get my game working again to no avail.

EDIT: as of 2021-6-15, I bought a new Windows machine and it works flawlessly.
                
# HACKERS the turn-based RPG text game

# IMPORT ALL THE THINGS!
import locale
from time import *
from random import *
from dialog import Dialog

# rename Dialog module to make life a bit easier E.G. d.msgbox() instead of Dialogue.msgbox()
d = Dialog(dialog="dialog")

# Title screen
banner = """   
  (      c    0   x c   2 X   >   V   U     i   `   %   7      f    
  `      e    h q 4 -   k 0   T   )   t     V ; W   R   9   P      
  R      !    \ S Q N   ! -   x   r   7     ^ R {   X   o   O  e    
    ______  _________ _______________ ____________________ ________
    ___  / / /___    |__  ____/___  //_/___  ____/___  __ \__  ___/
    __  /_/ / __  /| |_  /     __  ,<   __  __/   __  /_/ /_____ \ 
    _  __  /  _  ___ |/ /___   _  /| |  _  /___   _  _, _/ ____/ / 
    /_/ /_/   /_/  |_|\____/   /_/ |_|  /_____/   /_/ |_|  /____/
    THE TURN-BASED RPG GAME WRITTEN IN PYTHON 3.7
  : T         B R \ r | G G   )   6   0     T b (   @   u   H , Q  
  E f         O N     - K u   l   U   ]     Q < ,   +   T   Z - P  
  r p         n &     e P ;   3   4   !     a   `   C   *   j : l  
  8 {    f    o /       3 .   L   ,   )     H   (   6   |   z Q t     
"""

# all the global variables for the game
RAM = 8
ROM = 128
CLOCK = 2.5
playerMove = ""#.................Player's move
playerHealth = 100#..............Player's health
compMove = ""#...................Computer's move
compHealth = 100#................Computer's health
effective = True#................Determines if a move is effective or not
pVirus = False#..................Determines if the player has a virus
cVirus = False#..................Determines if the computer has a virus
pThrottle = False#...............Determines if player's network is throttled
cThrottle = False#...............Determines if computer's network is throttled
pThrottleCount = 1#..............Throttle turn counter
cThrottleCount = 1#..............Throttle turn counter

def mainMenu():
    # initialize variables for fresh game
    playerMove = ""
    playerHealth = 100
    compMove = ""
    compHealth = 100
    effective = True
    pVirus = False
    cVirus = False
    pThrottle = False
    cThrottle = False
    pThrottleCount = 1
    cThrottleCount = 1
    code, mainMenuChoice = d.menu("MAIN MENU",
        choices=[("Game Rules", "Uhh... I guess, game rules?"),
                 ("Start Game", "Uhh... I guess, start the game?"),
                 ("Quit", "Uhh... I guess, quit the game?")])
    if code == d.OK:
        if mainMenuChoice == "Game Rules":
            gameRules()
        elif mainMenuChoice == "Start Game":
            game()
        elif mainMenuChoice == "Quit":
            exit()

# a status bar on top of the screen that reports the player and the computer's status and health.
# function is called after every turn.
def statusBar():
    global pVirus
    global pThrottle
    global cVirus
    global cThrottle
    d.set_background_title("PLAYER INFECTED: " + str(pVirus) + " / PLAYER THROTTLED: " + str(pThrottle) + " / SKYNET INFECTED: " + str(cVirus) + " / SKYNET THROTTLED: " + str(pThrottle) + " / PLAYER HEALTH: " + str(playerHealth) + " / SKYNET HEALTH: " + str(compHealth))

statusBar()

def playerDeath():
    d.msgbox("YOU DIED!")
    d.msgbox("Your computer suddenly explodes and you get hit with shrapnel. You slowly die without strength to do anything else... You failed to save the world from Skynet's evil schemes... The world slowly fades away as you hear distant cries and screams...", 15, 40)
    if d.yesno("Wanna play again?") == d.OK:
        mainMenu()
    else:
        d.msgbox("Hmph. Coward.")
        exit()

def compDeath():
    d.msgbox("YOU WON!")
    d.msgbox("You successfully save the world from Skynet's evil robots. You are hailed as a hero and get a bunch of cash from the president. Thank you for saving us!", 15, 40)
    if d.yesno("Wanna play again?") == d.OK:
        mainMenu()
    else:
        d.msgbox("Bye bye!")
        exit()

# following two functions are the game's second main algorithm, computes status effects and damage report

# This is really confusing but it basically goes like this:
#
# player's move = player, computer's move = computer for simplicity's sake
#
# if player is effective(15), computer is ineffective(5) to player and vice versa
# if player is none(10), computer is also none(10)
# if player is same(10), computer is also same(10)
# if player is status(2), computer is none(10) and vice versa
#
def showCompTurn():
    global playerMove
    global compMove
    global playerHealth
    global compHealth
    global effective
    global cVirus
    global pVirus
    global cThrottle
    global pThrottle
    global pThrottleCount
    global cThrottleCount
    
    d.msgbox("Skynet used " + compMove + "!")
    if cThrottle == True:
        if cThrottleCount == 5:
            cThrottle = False
            d.msgbox("Skynet's network healed itself!")
            cThrottleCount = 0
            showCompTurn()
        else:
            cThrottleCount += 1
            d.msgbox("Skynet's network is throttled! It might not be able to attack!")
            isThrottled = randint(1, 2)
        if isThrottled == 1:
            d.msgbox("Skynet's attack was too slow, it missed!")
            chooseCompMove()
            choosePlayerMove()
    if effective == "false":#................................If player's move is ineffective computer's move is effective
        d.msgbox("It's super effective!")
        damage = 20#.........................................So set damage dealt to 15 health points
    elif effective == "true":#...............................and if player's move is effective computer's move is ineffective
        d.msgbox("It had little effect...")
        damage = 10#..........................................So set damage dealt to 5 health points
    elif effective == "none":
        damage = 2
    elif effective == "same":#...............................since "same" message was already displayer in showPlayerTurn()                                                          no need to show it again here
        damage = 15
    elif effective == "status":
        damage = 15
    d.gauge_start("SYSTEM INTEGRITY")
    for i in range(playerHealth, playerHealth - damage, -1):
        d.gauge_update(i - 1)
        sleep(0.05)
    sleep(0.5)
    playerHealth = playerHealth - damage
    if pVirus == True:
        d.msgbox("You are hurt from the virus!")
        d.gauge_start("SYSTEM INTEGRITY", None, None, playerHealth)
        for i in range(playerHealth, playerHealth - 5, -1):
            d.gauge_update(i - 1)
            sleep(0.05)
        playerHealth = playerHealth - 5
    sleep(0.5)

    if playerHealth <= 0:
        playerDeath()
    elif compHealth <= 0:
        compDeath()

    chooseCompMove()
    choosePlayerMove()

#shows each player's turn and damage report
def showPlayerTurn():
    global playerMove
    global compMove
    global playerHealth
    global compHealth
    global effective
    global cVirus
    global pVirus
    global cThrottle
    global pThrottle
    global pThrottleCount
    global cThrottleCount

    d.msgbox("Skynet used " + compMove + "!")
    #first show player's move and computer's damage report
    if pThrottle == True:#......................................1/2 chance of player missing attack when throttled
        if pThrottleCount == 5:#................................Throttle lasts for 5 turns
            pThrottle = False
            d.msgbox("Your network healed itself!")#............Throttle goes away
            pThrottleCount = 0
            showPlayerTurn()
        else:
            pThrottleCount += 1#................................increments throttle counter
            d.msgbox("Your network is throttled! You might not be able to attack!")
            isThrottled = randint(1, 2)#........................Randomizer for attack miss or no
            if isThrottled == 1:#...............................Player misses attack if randomizer returns 1
                d.msgbox("Your attack was too slow, it missed!")
                showCompTurn()#.................................Player misses turn and game goes to computer's turn
    if effective == "true":
        d.msgbox("It's super effective!")#......................Critical damage
        damage = 20
    elif effective == "false":
        d.msgbox("It had little effect...")#....................Ineffective
        damage = 10
    elif effective == "none":#..................................Attacks are not effective nor ineffective
        damage = 15
    elif effective == "same":#..................................When both use the same attack; same is "none" but with
        d.msgbox("Both sides used the same attack!")#           a message telling so
        damage = 15
    elif effective == "status":#................................Status attacks like virus or throttle deal minimal damage
        damage = 2
    d.gauge_start("SKYNET SYSTEM INTEGRITY")#...................Damage report
    for i in range(compHealth, compHealth - damage, -1):
        d.gauge_update(i - 1)
        sleep(0.05)
    sleep(0.5)
    compHealth = compHealth - damage#...........................Update health value
    if cVirus == True:#.........................................Virus detection for opponent
        d.msgbox("Skynet is hurt from the virus!")
        d.gauge_start("SKYNET SYSTEM INTEGRITY", None, None, compHealth)
        for i in range(compHealth, compHealth - 5, -1):#........Extra damage done by virus
            d.gauge_update(i - 1)
            sleep(0.05)
        compHealth = compHealth - 5#............................Virus eats away 5 from health
    sleep(0.5)

    if playerHealth <= 0:
        playerDeath()
    elif compHealth <= 0:
        compDeath()

def showTurn():#...............................................Neatly packages two turn functions together into one
    statusBar()
    showPlayerTurn()
    statusBar()
    showCompTurn()
    statusBar()

# function where computation of player and computer's move happens
def battle():#.................................................Main algorithm, computes effectiveness and status effects
    global playerMove
    global compMove
    global playerHealth
    global compHealth
    global effective
    global cVirus
    global pVirus
    global cThrottle
    global pThrottle

    if playerHealth <= 0:
        playerDeath()
    elif compHealth <= 0:
        compDeath()

    if playerMove == "sql":#...................................If player's move is sql injection
        if compMove == "sql":#.................................If computer's move is sql injection
            effective = "same"#................................set effectiveness to "same"
        elif compMove == "ddos":
            effective = "true"#................................sql injections are super effective to ddos attacks
        elif compMove == "phishing":
            effective = "false"#...............................and weak against spear phishing attacks.
        elif compMove == "throttle":#..........................if computer uses network throttle
            pThrottle = True#..................................set player throttle status(pThrottle) to True
            effective = "none"#................................not effective, ineffective, nor same
        elif compMove == "virus":#.............................same thing as throttle pretty much
            pVirus = True
            effective = "none"

    elif playerMove == "ddos":
        if compMove == "sql":
            effective = "false"
        elif compMove == "ddos":
            effective = "same"
        elif compMove == "phishing":
            effective = "true"
        elif compMove == "throttle":
            pThrottle = True
            effective = "none"
        elif compMove == "virus":
            pVirus = True
            effective = "none"

    elif playerMove == "phishing":
        if compMove == "sql":
            effective = "true"
        elif compMove == "ddos":
            effective = "false"
        elif compMove == "phishing":
            effective = "same"
        elif compMove == "throttle":
            pThrottle = True
            effective = "none"
        elif compMove == "virus":
            pVirus = True
            effective = "none"

    elif playerMove == "throttle":#..........................If player uses throttle
        cThrottle = True#....................................set computer's throttle status(cThrottle) to True
        effective = "status"#................................Since this isn't an attack move set effectiveness as "status"
        if compMove == "sql":
            effective = "status"#............................set effectiveness to "status" for every move the computer does
        elif compMove == "ddos":
            effective = "status"
        elif compMove == "phishing":
            effective = "status"
        elif compMove == "throttle":
            pThrottle = True
            effective = "status"
        elif compMove == "virus":
            pVirus = True
            effective = "status"

    elif playerMove == "virus":
        cVirus = True
        effective = "status"
        if compMove == "sql":
            effective = "status"
        elif compMove == "ddos":
            effective = "status"
        elif compMove == "phishing":
            effective = "status"
        elif compMove == "throttle":
            pThrottle = True
            effective = "status"
        elif compMove == "virus":
            pVirus = True
            effective = "status"

    statusBar()
    showTurn()

def choosePlayerMove():#.......................................menu for player to choose his/her move
    statusBar()
    global playerMove
    global shopChoice
    code, playerMove = d.menu("CHOOSE MOVE", 18, 130,
        choices=[("SQL Injection", "A brutal wireless SQL injection attack. Works well against DDOS attacks."),
                 ("DDOS Attack", "A Distributed Denial-of-Service attack. Effective against Spear Phishing attacks."),
                 ("Spear Phishing", "A sneaky attack that allows access to sensitive data. Useful against SQL Injections."),
                 ("Network Throttling", "An annoying attack that heavily slows down network speeds of the target, reducing accuracy."),
                 ("Virus", "A cruel and effective virus that gradually takes over the computer."),])
                 #("Use Equipment", "Use your " + shopChoice + "."),])
    if code == d.OK:
        if playerMove == "SQL Injection":
            d.msgbox("You performed an SQL Injection!")
            playerMove = "sql"
            battle()

        elif playerMove == "DDOS Attack":
            d.msgbox("You unleashed a powerful DDOS Attack!")
            playerMove = "ddos"
            battle()

        elif playerMove == "Spear Phishing":
            d.msgbox("You used Spear Phishing!")
            playerMove = "phishing"
            battle()

        elif playerMove == "Network Throttling":
            d.msgbox("You throttled Skynet's network traffic!")
            playerMove = "throttle"
            battle()

        elif playerMove == "Virus":
            d.msgbox("You infected Skynet with a virus!")
            playerMove = "virus"
            battle()

    elif code == d.CANCEL:
        if d.yesno("You sure about that?") == d.OK:
            exit()
        else:
            choosePlayerMove()

def chooseCompMove():#...........................................randomly chooses one of 5 moves for the computer
    global compMove
    global pVirus
    global pThrottle
    if pVirus == True and pThrottle == False:#...................The next three if statements prevent the computer from
        randNum = randint(1, 4)#                                 selecting a status move when the status effect is already
        if randNum == 1:#                                        applied on the player.
            compMove = "sql"
        elif randNum == 2:
            compMove = "ddos"
        elif randNum == 3:
            compMove = "phishing"
        elif randNum == 4:
            compMove = "throttle"
    elif pVirus == False and pThrottle == True:
        randNum = randint(1, 4)
        if randNum == 1:
            compMove = "sql"
        elif randNum == 2:
            compMove = "ddos"
        elif randNum == 3:
            compMove = "phishing"
        elif randNum == 4:
            compMove = "virus"
    elif pVirus == True and pThrottle == True:
        randNum = randint(1, 3)
        if randNum == 1:
            compMove = "sql"
        elif randNum == 2:
            compMove = "ddos"
        elif randNum == 3:
            compMove = "phishing"
    else:
        randNum = randint(1, 5)
        if randNum == 1:
            compMove = "sql"
        elif randNum == 2:
            compMove = "ddos"
        elif randNum == 3:
            compMove = "phishing"
        elif randNum == 4:
            compMove = "throttle"
        elif randNum == 5:
            compMove = "virus"


def game():#......................................................ties the player and computer's game code together
    #shop()
    while playerHealth > 0 or compHealth > 0:
        chooseCompMove()
        choosePlayerMove()

def bootUp():#...................................................nothing really useful here besides exposition
    d.scrollbox(banner, 20, None)

    d.msgbox("""
    WELCOME.

    YOU HAVE BEEN GIVEN THE MISSION TO INFILTRATE SKYNET AND COLLECT CLASSIFIED DATA. USE THE TOOLS AVAILABLE TO COMPROMISE SKYNET'S SYSTEMS!
    """, 20, 40)

def gameRules():
    message = d.msgbox("If you ever played pokemon, you should get grips with this game pretty easy.It's just like rock-paper-scissors but with more bells and whistles. You have three attacks: SQL injection, DDOS attack, and Spear Phishing. Each one is effective against the one that comes after it and weak against the one that comes before it. Your other two moves are Network Throttling, which for five turns, gives the opponent a 50 percent chance of missing the target, and Virus, which slowly eats away the target's health. Do you get it now?", 20, 80)
    mainMenu()

#....................................................................KICKOFF!
bootUp()
mainMenu()