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

"""Utility functions, constants and style definitions for the Leech
game."""



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

# Python imports.
import time

# Hermes imports.
from hermes import *



# ######################################################################
# STYLE DEFINITIONS
#

# Used when displaying item lists and user stats.
HeaderStyle = Style(fgYellow)
NameStyle = Style(fgGreen)
ValueStyle = Style(fgMagenta)

# Used when displaying the BBS list.
BbsListNumberStyle = Style(fgLtGray)
BbsListNameStyle = Style(fgMagenta)

# Used for ``modem text'' when calling the LLL and other BBSs.
ModemStyle = Style(fgCyan)



# ######################################################################
# CONSTANTS
#

# --------------------------------------
# Software Types and Backup Methods.
#

class _LeechItem:
    
def __init__(self, name, value):
        
# Decode and store the name.
        
self.id = int(name)

        
# Decode and store the value.
        
s, p, n = value.split(',')
        
self.strength = int(s)
        
self.price = int(p)
        
self.name = n.strip()

        
# Populate a few other attributes.
        
self.priceString = formatCurrency(self.price, 0)
        
self.salePrice = self.price / 2
        
self.salePriceString = formatCurrency(self.salePrice, 0)

Backups = loadStringList(
    
'BackupTypes',
    
nameDecoder=lambda n: int(n),
    
valueDecoder=lambda n,v: _LeechItem(n, v))

Software = loadStringList(
    
'SoftwareTypes',
    
nameDecoder=lambda n: int(n),
    
valueDecoder=lambda n,v: _LeechItem(n, v))


# --------------------------------------
# Leech BBS List.
#

class _LeechBbs:
    
def __init__(self, value):
        
# Decode and store the value.
        
pn, n = value.split(',', 1)
        
self.phoneNumber = pn.strip()
        
self.name = n.strip()

BbsList = loadStringList(
    
'BbsList',
    
nameDecoder=lambda n: int(n),
    
valueDecoder=lambda n,v: _LeechBbs(v))



# ######################################################################
# DISPLAY FUNCTIONS
#

def printBulletin(byline=None, text=None):
    
# Use the system bulletin if we were not given other data to
    
# display.
    
if byline is None:
        
byline = bbs.data.lllBulletinByline
    
if text is None:
        
text = bbs.data.lllBulletin

    
# Print out the bulletin.
    
print HeaderStyle('=' * 72)
    
print 'LLL Bulletin by %s' % (byline)
    
print HeaderStyle('=' * 72)
    
print
    
print text


def printTopScores():
    
# Clear the screen.
    
clearScreen()

    
# Print the top scores.
    
print HeaderStyle('=' * 72)
    
print 'Top Scores'
    
print HeaderStyle('=' * 72)
    
print

    
if bbs.data.highScores:
        
# Print the most recent 10 entries in the high scores list.
        
for name, date in bbs.data.highScores[-10:]:
            
print '  %s won on %02d/%02d/%04d' % (
                
name, date[1], date[2], date[0])
    
else:
        
print '  No one has won yet.  Maybe you will be the first?'

    
print


def printStats(u=user):
    
"""Print the given leech's statistics.

    This function will not print `private' statistics if the requested
    leech is not the currently playing leech.

    @param u: The leech to print.
    """


    
# Figure out how many megs left the requested user has.  The current
    
# user's value is based on their instance stats.  Everyone else is
    
# at their full value.
    
if u == user:
        
megsLeft = instance.data.megsLeft
    
else:
        
megsLeft = u.data.totalMegs

    
# Build the list of stats names and values.
    
stats = [
        
('Level:', u.data.level),
        
('Rank:', getUserRank(u)),
        
('Software:', Software[u.data.softwareType].name),
        
('Backup:', Backups[u.data.backupType].name),
        
('Megs left:', '%d (%d)' % (megsLeft, u.data.totalMegs)),
        
('Money:', formatCurrency(u.data.money, 0)),
    
]

    
# Add additional stats if we are examining the current leech.  The
    
# information below is private, so it will not be displayed when
    
# leeches examine each other on the LLL BBS.
    
if u == user:
        
# Add the user's private stats.
        
stats = stats + [
            
('Money in LLL account:', formatCurrency(u.data.moneyInBank, 0)),
            
('Calls to BBSs left:',
                
prefs.data.bbsCallsPerPlay - instance.data.bbsCalls),
            
('Calls to leeches left:',
                
prefs.data.leechCallsPerPlay - instance.data.leechCalls),
            
('Drives left to crash:', u.data.drivesLeftToNextLevel),
            
('Fast Backups left:', u.data.fastBackups),
            
('Trojan Horses left:', u.data.trojanHorses),
        
]

        
# Add the number of `games left today' stat, inserting the
        
# appropriate value if this user gets unlimited plays.
        
if u.sysop and prefs.data.unlimitedSysopPlay:
            
stats.append(
                
('Games left today:',
                    
'Unlimited'))
        
else:
            
stats.append(
                
('Games left today:',
                    
prefs.data.playsPerDay - u.data.playsToday))

    
# Clear the screen.
    
clearScreen()

    
# Display our stats header.
    
print HeaderStyle('=' * 72)
    
print u.name
    
print HeaderStyle('=' * 72)

    
# Display all of our stats.
    
for name, value in stats:
        
print '%s %s' % (
            
NameStyle('%-24s' % name), ValueStyle(str(value)))


def listLeeches():
    
"""Prints a list of all of the leeches registered with the game.

    The leeches are ordered by rank.  A pause is displayed every
    screenful and the header is redrawn with each new screen.
    """


    
# Get the list of leeches.
    
leechList = []
    
for u in external.users.values():
        
leechList.append({
            
'rank': getUserRank(u),
            
'name': u.name,
            
'level': u.data.level,
            
'last': u.data.lastTimePlayed,
        
})

    
# Sort the list by rank.
    
leechList.sort(lambda a, b: a['rank'] - b['rank'])

    
# List each of the users.
    
for index, leech in enumerate(leechList):
        
# Draw the header at the start of the list and every
        
# screenHeight-3 (2 for the header and 1 for the pause prompt)
        
# leeches after that.
        
if index % (user.screenHeight - 3) == 0:
            
# Pause if this is not the first list of leeches.
            
if index > 0:
                
pausePrompt()

            
# Clear the screen.
            
clearScreen()

            
# Print the header.
            
print Style(fgLtGray)(
                
'Rank Leech Name                       Lvl Last Play ')
            
print HeaderStyle(
                
'==== ================================ === ==========')

        
# Print this leech.
        
print Style(fgLtGray)('#%(rank)3d' % leech),
        
print Style(fgCyan)('%(name)-32s' % leech),
        
print Style(fgCyan)('%(level)3d' % leech),
        
print Style(fgMagenta)('%02d/%02d/%04d' % (
            
leech['last'][1], leech['last'][2], leech['last'][0]))


def dialModem(phoneNumber, baudRate):
    
"""Pretends to dial the Leech Virtual Modem."""

    
# Simulate dialing the phone number and connecting at the giving
    
# baud rate.
    
print ModemStyle('ATDT%s' % (phoneNumber))
    
time.sleep(0.75)
    
print ModemStyle('<authentic modem sounds>')
    
time.sleep(0.75)
    
print ModemStyle('CONNECT %d' % (baudRate))

def hangupModem():
    
"""Terminates the call on our Leech Virtual Modem."""

    
# Delay for a bit (disconnects are not instantaneous), then hangup.
    
time.sleep(0.5)
    
print ModemStyle('NO CARRIER')



# ######################################################################
# UTILITY FUNCTIONS
#

def compareUsersByRank(u1, u2):
    
# Compare by level.
    
levelDiff = u1.data.level - u2.data.level
    
if levelDiff:
        
return levelDiff

    
# Compare by software type.
    
softwareDiff = u1.data.softwareType - u2.data.softwareType
    
if softwareDiff:
        
return softwareDiff

    
# Compare by backup type.
    
backupDiff = u1.data.backupType - u2.data.backupType
    
if backupDiff:
        
return backupDiff

    
# Compare by total amount of money.
    
totalMoneyDiff \
        
= (u1.data.money + u1.data.moneyInBank) \
        
- (u2.data.money + u2.data.moneyInBank)
    
if totalMoneyDiff:
        
return totalMoneyDiff

    
# Everything is the same, so whoever played first is the winner.
    
return cmp(u1.data.firstTimePlayed, u2.data.firstTimePlayed)


def getUserRank(u=user):
    
# Get the list of users currently playing Leech.
    
users = external.users.values()

    
# Reverse-sort the list by level, then software type, then backup type.
    
users.sort(compareUsersByRank)
    
users.reverse()

    
# The user's rank is their position in the list plus one (because
    
# list indexes are zero-indexed).
    
rank = users.index(u) + 1

    
# Return the rank.
    
return rank