home .. externals .. leech 2000 .. source code ..
LLL.py
# ######################################################################
# Copyright 2005 by Michael Alyn Miller.  All rights reserved.
# vi:et:sts=4
#

"""Code for the LLL BBS.

The Lame Local Leeches (LLL) BBS provides all sorts of tools for helping
users through the game.  Software and backup methods can be bought/sold
here, the user can gamble away their money (not wise) or even try and
hack the LLL bank (even less wise).

Various user-to-user interaction features are available on LLL as well.
Players can see the list of leeches registered with the game, look at
another leech's stats and post bulletins for all to see.
"""



# ######################################################################
# IMPORTS
#

# Python imports.
import time

# Hermes imports.
from hermes import *

# Leech imports.
import Leech



# ######################################################################
# LLL Constants
#

phoneNumber='5551212'



# ######################################################################
# LLL Menu
#

def menu():
    
"""Entry point for the LLL menu."""

    
# Call the LLL BBS.
    
clearScreen()
    
print PromptHeaderStyle('Dialing LLL...')
    
Leech.dialModem(phoneNumber, 2400)
    
print

    
# Run the LLL menu.  The run function will exit when the user
    
# has left the menu.
    
runMenu('LLL', clearOnFirstPrint=False)

    
# Disconnect from LLL.
    
print
    
print 'Thanks for calling the LLL BBS!  Call again soon...'
    
print
    
Leech.hangupModem()



# ######################################################################
# Backup Menu
#

def buySellBackup():
    
"""Lets users buy and sell backup methods."""

    
# List the backup methods.
    
listBackupMethods()

    
# Enter the backup menu loop.
    
while True:
        
# Print some information about the user's current backup method
        
# and their financial status.
        
print
        
print 'Current backup: %s' % (
            
Leech.Backups[user.data.backupType].name)
        
print PromptHeaderStyle(
            
'You have %s on you (%s in the bank).' % (
                
formatCurrency(user.data.money, 0),
                
formatCurrency(user.data.moneyInBank, 0)))

        
# Print the prompt and get their response.
        
cmd = multipleChoicePrompt(
            
'Backups Menu (B>uy, S>ell, L>ist, Q>uit): ',
            
defaultChar='L',
            
choiceList = {
                
'B': ('Buy Backup', buyBackup),
                
'S': ('Sell Backup', sellBackup),
                
'L': ('List Backup Methods', listBackupMethods),
                
'Q': ('Quit', None),
            
})

        
# We're done if the command is None.
        
if not cmd:
            
break

        
# Run the command.
        
cmd()


def buyBackup():
    
"""Lets the users buy a new backup method."""

    
# Figure out which backup method the user wants to buy.
    
newBackupType = numericPrompt(
        
'Which backup method do you want to buy? ',
        
minValue=1, maxValue=15, autoAccept=False, requireResponse=False)

    
# Do nothing if the user chose not to buy a backup method.
    
if newBackupType is None:
        
print 'Aborted.'
        
return

    
# Get the new backup method.  We have to decrement the value by one
    
# since we have the user enter 1..MAX values instead of the 0..MAX-1
    
# that we store internally.
    
newBackup = Leech.Backups[newBackupType - 1]

    
# See if the user can afford this backup method.
    
if newBackup.price > user.data.money:
        
print
        
print NoticeStyle('That backup method costs more than you have.')
        
return

    
# Buy the backup method.
    
user.data.money -= newBackup.price
    
user.data.backupType = newBackup.id

    
# Tell the user about their purchase.
    
print
    
print 'You bought %s.' % (newBackup.name)


def sellBackup():
    
"""Lets the user sell their current backup method."""

    
# Get the user's current backup method.
    
curBackup = Leech.Backups[user.data.backupType]

    
# See if the user wants to sell their backup method.
    
print
    
print 'You can sell your %s for %s.' % (
        
curBackup.name, curBackup.salePriceString)
    
if yesNoPrompt('Do you want to sell? '):
        
# Adjust the user's account.
        
user.data.money += curBackup.salePrice
        
user.data.backupType = 0

        
# Let them know that they sold it.
        
print 'Backup sold.'
    
else:
        
print 'Aborted.'


def listBackupMethods():
    
"""List the available backup methods.

    The user's current backup method is marked with an asterisk.
    """


    
# Clear the screen.
    
clearScreen()

    
# Print the header.
    
print Leech.HeaderStyle('=' * 78)
    
print 'BACKUP METHODS'
    
print Leech.HeaderStyle('-' * 78)

    
# Print the backup methods, ordered by id.
    
backupMethods = Leech.Backups.items()
    
backupMethods.sort(lambda a, b: a[1].id - b[1].id)
    
for id, item in backupMethods:
        
# Print an asterisk if this user has the current backup method.
        
if id == user.data.backupType:
            
print '*',
        
else:
            
print ' ',

        
# Print the stats about this backup method.
        
for name, value in (
                
('Number:',   '%2d'  % (id + 1)),
                
('Strength:', '%2d'  % item.strength),
                
('Price:',    '%11s' % item.priceString),
                
('Backup:',   '%s'   % item.name)):
            
print '%s %2s ' % (Leech.NameStyle(name), Leech.ValueStyle(value)),

        
# Finish this line.
        
print



# ######################################################################
# Software Menu
#

def buySellSoftware():
    
"""Lets users buy and sell software."""

    
# List the software.
    
listSoftware()

    
# Enter the software menu loop.
    
while True:
        
# Print some information about the user's current software and
        
# their financial status.
        
print
        
print 'Current software: %s' % (
            
Leech.Software[user.data.softwareType].name)
        
print PromptHeaderStyle(
            
'You have %s on you (%s in the bank).' % (
                
formatCurrency(user.data.money, 0),
                
formatCurrency(user.data.moneyInBank, 0)))

        
# Print the prompt and get their response.
        
cmd = multipleChoicePrompt(
            
'Software Menu (B>uy, S>ell, L>ist, Q>uit): ',
            
defaultChar='L',
            
choiceList = {
                
'B': ('Buy Software', buySoftware),
                
'S': ('Sell Software', sellSoftware),
                
'L': ('List Software', listSoftware),
                
'Q': ('Quit', None),
            
})

        
# We're done if the command is None.
        
if not cmd:
            
break

        
# Run the command.
        
cmd()


def buySoftware():
    
"""Lets the users buy new software."""

    
# Figure out which software the user wants to buy.
    
newSoftwareType = numericPrompt(
        
'Which software do you want to buy? ',
        
minValue=1, maxValue=15, autoAccept=False, requireResponse=False)

    
# Do nothing if the user chose not to buy a software.
    
if newSoftwareType is None:
        
print 'Aborted.'
        
return

    
# Get the new software.  We have to decrement the value by one since
    
# we have the user enter 1..MAX values instead of the 0..MAX-1 that
    
# we store internally.
    
newSoftware = Leech.Software[newSoftwareType - 1]

    
# See if the user can afford this software.
    
if newSoftware.price > user.data.money:
        
print
        
print NoticeStyle('That software costs more than you have.')
        
return

    
# Buy the software.
    
user.data.money -= newSoftware.price
    
user.data.softwareType = newSoftware.id

    
# Tell the user about their purchase.
    
print
    
print 'You bought %s.' % (newSoftware.name)


def sellSoftware():
    
"""Lets the user sell their current software."""

    
# Get the user's current software.
    
curSoftware = Leech.Software[user.data.softwareType]

    
# See if the user wants to sell their software.
    
print
    
print 'You can sell your %s for %s.' % (
        
curSoftware.name, curSoftware.salePriceString)
    
if yesNoPrompt('Do you want to sell? '):
        
# Adjust the user's account.
        
user.data.money += curSoftware.salePrice
        
user.data.softwareType = 0

        
# Let them know that they sold it.
        
print 'Software sold.'
    
else:
        
print 'Aborted.'


def listSoftware():
    
"""List the available software.

    The user's current software is marked with an asterisk.
    """


    
# Clear the screen.
    
clearScreen()

    
# Print the header.
    
print Leech.HeaderStyle('=' * 78)
    
print 'SOFTWARE PROGRAMS'
    
print Leech.HeaderStyle('-' * 78)

    
# Print the software, ordered by id.
    
software = Leech.Software.items()
    
software.sort(lambda a, b: a[1].id - b[1].id)
    
for id, item in software:
        
# Print an asterisk if this user has the current software.
        
if id == user.data.softwareType:
            
print '*',
        
else:
            
print ' ',

        
# Print the stats about this software.
        
for name, value in (
                
('Number:',   '%2d'  % (id + 1)),
                
('Strength:', '%2d'  % item.strength),
                
('Price:',    '%11s' % item.priceString),
                
('Software:', '%s'   % item.name)):
            
print '%s %2s ' % (Leech.NameStyle(name), Leech.ValueStyle(value)),

        
# Finish this line.
        
print



# ######################################################################
# Examine Leech
#

def examineLeech():
    
"""Lets the user take a look at another leech's stats.

    It costs $10 per level of the player to examine another leech's
    stats.  This is to increase the difficulty of the game as the leech
    increases in level.  We don't want high-level players to be able to
    pick off the n00bs.
    """


    
# Compute the cost of examining another user.
    
cost = user.data.level * 10

    
# Make sure that the user has enough money.
    
if cost > user.data.money:
        
print
        
print 'It costs %s to examine another leech; you only have %s.' % (
            
formatCurrency(cost, 0), formatCurrency(user.data.money, 0))
        
return

    
# Find out which leech the user wants to examine.
    
# TODO Should use a userPrompt here.
    
print
    
name = textPrompt(
        
'Enter the name of the leech to examine: ', 32, forceUppercase=True)

    
# Nothing to do if we were not given a name.
    
if not name:
        
print 'Aborted.'
        
return


    
# Find the requested user.
    
try:
        
u = bbs.users[name]
    
except KeyError:
        
print 'Unknown user.'
        
return

    
# See if that user is playing Leech.
    
if not u.data:
        
print 'That user is not playing Leech!'
        
return


    
# Make sure the user is aware of the cost associated with examining
    
# another user.
    
if not yesNoPrompt(
            
'It costs %s to examine a user.  Still want to do it? ' % (
                
formatCurrency(cost, 0))):
        
print 'Aborted.'
        
return

    
# Deduct the cost from the user's account.
    
user.data.money -= cost

    
# Print the user.
    
Leech.printStats(u)



# ######################################################################
# Gamble
#

def gamble():
    
"""Gives the user a chance to gamble away all of their money, or as
    much as they are willing to part with.

    The user has a 20% chance of success.  Not exactly great odds, but
    what do you expect, the LLL BBS is run by leeches.
    """


    
# Make sure that the user has money to gamble with.
    
if user.data.money <= 0:
        
# Print the message.
        
print
        
print 'You do not have any money.  What were you planning on'
        
print 'gambling with?'

        
# Go back to the LLL menu.
        
return


    
# See how much the user wants to gamble.
    
print
    
print PromptHeaderStyle(
        
'You have %s on you.' % formatCurrency(user.data.money, 0))
    
amount = numericPrompt(
        
'How much do you want to gamble? ',
        
minValue=0, maxValue=user.data.money,
        
autoAccept=False, requireResponse=False)
    
print

    
# Don't gamble if the user did not give us a response or if they did
    
# not want to gamble anything.
    
if not amount:
        
print 'Nothing at all?  Pretty safe bet...'
        
return


    
# The user has a 1-in-5 chance of success.  Their gambling attempt
    
# fails if they get any number other than zero.
    
if instance.randrange(5):
        
# Decrease the user's cash.
        
user.data.money -= amount

        
# Print the message.
        
print NoticeStyle('You lost!  Better luck next time...')

        
# Go back to the LLL menu.
        
return

    
# The user won!  Give them their money.
    
user.data.money += amount

    
# Let the know about the win.
    
print Style(fgGreen)('You won %s!' % formatCurrency(amount, 0))



# ######################################################################
# Hack LLL Bank
#

def hackBank():
    
"""Offers the user the chance to steal all of the money in the LLL
    bank; at very high risk, of course.

    The hack's chance of success is based on the ratio of the number of
    drives the user has left to hack before they get to the next level
    and their software power.  Users with incredibly powerful software
    and very few drives to crack will have a much better chance at
    hacking the bank.

    If the hack fails, then we increase their drives-to-next-level
    counter by five times the user's level.  So although powerful users
    are more likely to be successful at hacking the bank (they tend to
    have more powerful software, for example), the risks to them for
    failing are very high.

    If the hack succeeds, the user gets all of the money in the bank.
    """


    
# Make sure that this user hasn't tried to hack the bank too many
    
# times already.
    
if instance.data.bankHacks >= prefs.data.bankHacksPerPlay:
        
print
        
print 'Sorry, you can only try to hack the bank %d times per play.' % (
            
prefs.data.bankHacksPerPlay)
        
return


    
# Print the warning.
    
print
    
print 'The LLL will be *MAD* if they catch you hacking their bank...'

    
# Make sure that the user really wants to hack bank.
    
if not yesNoPrompt('Are you sure you want to try and hack the bank? '):
        
print 'Probably a good choice...'
        
return


    
# The user still wants to hack the bank.  Increment their attempts
    
# counter and print out the hacking message.
    
instance.data.bankHacks += 1

    
# Print the hacking message.
    
print
    
print 'Trying to hack the LLL bank',
    
for i in range(5):
        
print '.',
        
time.sleep(0.5)
    
print


    
# Let's see if the user is able to hack the bank.  We look at the
    
# number of drives that they have left to crash versus the power of
    
# their software.  We then create a random number based on that
    
# value.  If the resulting number is 1, then the hack is successful.
    
# Best case, the user has a 1-in-3 chance of success (because of the
    
# fact that rangeMax cannot be less than 2).  softwareType has to be
    
# increased by one since our type indexes are zero-based.
    
rangeMax = (user.data.drivesLeftToNextLevel * 10) \
                    
/ (user.data.softwareType+1)
    
if rangeMax < 2:
        
rangeMax = 2
    
result = instance.randrange(rangeMax)


    
# If the value is anything other than zero, then the user failed to
    
# hack the bank.
    
if result:
        
# Increase the number of drives they have to crash.
        
extraHardDrives = user.data.level * 5
        
user.data.drivesLeftToNextLevel += extraHardDrives

        
# Print the message.
        
print NoticeStyle(
            
'You were caught!  The LLL now requires you to crash an\n'
            
'extra %d hard drives to make it to the next level.' % (
                
extraHardDrives))

        
# Go back to the LLL menu.
        
return


    
# Success!  The user stole all of the money in the bank.  Total up
    
# all of the money in the bank, clearing everyone's accounts as we
    
# go.
    
moneyInAllAccounts = 0
    
for u in external.users:
        
# Add this user's bank account balance to our total.
        
moneyInAllAccounts += u.data.moneyInBank

        
# Clear out the user's bank account.
        
u.data.moneyInBank = 0

        
# TODO Make a note in this user's account to let them know that
        
# their bank account was stolen (and by whom).  Then tell them
        
# next time they logged in about the theft.

    
# Give this money to the user.
    
user.data.moneyInBank = moneyInAllAccounts

    
# Let the user know about their success.
    
print Style(fgGreen)(
        
"You successfully hacked the LLL bank!  Everyone's\n"
        
"money is now in your account.  You now have %s in"
        
"the LLL bank." % formatCurrency(user.data.moneyInBank, 0))



# ######################################################################
# View/Change LLL Bulletin
#

def viewChangeBulletin():
    
"""Displays the current LLL bulletin and lets the user change it."""

    
# Print out the current bulletin.
    
clearScreen()
    
Leech.printBulletin()


    
# See if the user wants to write their own bulletin.
    
print
    
if not yesNoPrompt('Do you want to change the bulletin? '):
        
return

    
# Prompt the user for the bulletin.
    
print
    
print PromptStyle('Enter four lines of text:')

    
#TODO use a multilinePrompt
    
line1 = textPrompt('1: ', 72, inputStyle=InputLineStyle)
    
line2 = textPrompt('2: ', 72, inputStyle=InputLineStyle)
    
line3 = textPrompt('3: ', 72, inputStyle=InputLineStyle)
    
line4 = textPrompt('4: ', 72, inputStyle=InputLineStyle)


    
# Build the new bulletin.
    
newBulletin = '%s\n%s\n%s\n%s' % (line1, line2, line3, line4)

    
# Show the user their bulletin.
    
print
    
print PromptHeaderStyle('Here is how your bulletin will look:')
    
print
    
Leech.printBulletin(user.name, newBulletin)

    
# Confirm that they want to save this bulletin.
    
print
    
if not yesNoPrompt('Do you want to post this bulletin? '):
        
print 'Bulletin cancelled.'
        
return

    
# Save the bulletin.
    
bbs.data.lllBulletin = newBulletin
    
bbs.data.lllBulletinByline = user.name


    
# Let the user know that their bulletin was saved.
    
print 'Your bulletin was saved.'