import re,math,copy,gzip,cPickle as pk
from util import *
from event import *
prop = property
dirs = ["north","south","east","west","up","down"]
rdirs = ["south","north","west","east","down","up"]
class Thing(object):
all = {} # track of instances of this class
names = ()
name = ""
c = 0 # no connection
SE = prop(lambda S: ["it","he","she"][S.sex])
SS = prop(lambda S: ["its","his","her"][S.sex])
SM = prop(lambda S: ["it","him","her"][S.sex])
desc = "Fairly ordinary."
ldesc = "is here."
loc = 0 # location
sex = 0
shop = 0
nsave = [] # attributes that shouldn't be saved
room = prop(lambda S: S.loc and S.loc.room)
def __init__(S):
Thing.all[S] = 0
def __getstate__(S):
d = S.__dict__.copy()
# forget these values
for x in S.nsave:
if x in d: del d[x]
return d
def __setstate__(S,s):
S.__dict__.update(s)
Thing.all[S] = 0
def travel(S,dest):
p = S.room.route(dest,{})
if p:
S.path = p
adde(1,4,tup,[S])
def goto(S,dest):
if S.loc: S.loc.inv.remove(S)
S.loc = dest
if dest: dest.inv.append(S)
def on_get(S,by): by.sendl("You can't get that.")
def on_drop(S,by): by.sendl("You can't drop that.")
def on_give(S,by,to): by.sendl("You can't give that away.")
def recv(S,x,fr): by.sendl("It's not interested.")
def on_wear(S,by): by.sendl("You can't wear that.")
def on_remove(S,by): by.sendl("Not wearable.")
def on_drink(S,by): by.sendl("You can't drink that.")
def sendl(S,t): pass # ignore
write = sendl # ditto
class Container(Thing):
maxc = 5 # max num of items this can carry
def __init__(S):
super(Container, S).__init__()
S.inv = []
class Biz(object):
owner = ""
rent = 1
zone = "smbiz"
type = ""
CITY = Biz() # the city owns all public property
CITY.owner = "CITY"
class Room(Container):
name = "Somewhere"
biz = CITY
room = prop(lambda S: S)
def __init__(S):
super(Room, S).__init__()
S.ex = {}
def __getstate__(S):
d = S.__dict__.copy()
d["inv"] = [x for x in d["inv"] if not is_a(x,Player)]
return d
def try_edit(S, a):
if a.admin or a.name == S.biz.owner: return 1
a.sendl("You can only modify property that you own or rent.")
def descTo(S, v):
r = ""
if not S.biz.owner: r = " ({gFor Rent{x)"
elif S.biz.owner != 'CITY': r = " ({yRun by %s{x)" % S.biz.owner
d = "{B%s{x%s\r\n %s\r\n{x" % (S.name, r, S.desc)
if len(S.inv) > 1: d += NL
for x in S.people+S.items:
if x is not v: d += "%s %s\r\n" % (cap(x.name), x.ldesc)
return d
# Finds a path to a destination room
# Uses a really stupid DFS that does not attempt to pick the shortest path
# But hey, at least this is dead simple when every byte counts
def route(S,dest,vi):
if S in vi: return
vi[S] = 0
for d,r in S.ex.items():
if r == dest: return [d]
x = r.route(dest,vi)
if x: return x+[d]
crowd = prop(lambda S: [p for p in S.people if p.npc])
people = prop(lambda S: [o for o in S.inv if is_a(o,Actor)])
items = prop(lambda S: [o for o in S.inv if not is_a(o,Actor)])
shops = prop(lambda S: [x.shop for x in S.inv+[S] if x.shop])
# Items are carryable objects
class Item(Thing):
dam = 5 # weapon damage
wloc = 0 # wear location
# is worn?
worn = prop(lambda S: S.loc and is_a(S.loc, Actor) and
S.loc.worn.get(S.wloc) == S)
def on_get(S,by):
if S.loc == by: return by.sendl("You already have that.")
if len(by.inv) > by.maxc: return by.sendl("Your inventory is full.")
Act(by,S).subj("You get $VN.").room("$SN gets $VN.")
S.goto(by)
def on_drop(S,by):
if S.worn: return by.sendl("You're wearing that.")
Act(by,S).subj("You drop $VN.").room("$SN drops $VN.")
S.goto(by.room)
def on_give(S,by,to):
if S.worn: return by.sendl("You're wearing that.")
if len(to.inv) > to.maxc:
return Act(by,to).subj("$VE can't carry any more items.")
to.recv(S,by)
def on_throw(S,by,v):
Act(by,S,v).subj("You throw $VN at $ON!").vict(
"$SN throws $VN at you!").room("$SN throws $VN at $ON!")
S.on_splat(v,by)
# default splat is more like a *BONK*
def on_splat(v,by):
by.hit(S.dam*1.5)
S.goto(v.room) # fall into room
class Actor(Container):
cash = 200
npc = 1
hpc = 50 # hp
hpm = 50 # max hp
rpc = 100 # respect
rpm = 100 # max respect
foe = 0 # fighting against this person
ks = 0 # number of attacks available
FISTS = Item() # magical default fist weapon
FISTS.name = "a pair of fists"
FISTS.dam = 5
nsave = ["foe"]
path = []
def __init__(S):
super(Actor, S).__init__()
S.worn = {}
def do(S, l): import cmd; cmd.do(S, l)
def recv(S,x,fr):
Act(fr,x,S).subj("You give $VN to $ON.").obj("$SN gives you $VN."
).room("$SN gives $VN to $ON.")
x.goto(S)
# look for something in the current room (1) or my inv (2) or both (0)
def find(S,n,w=0):
return find(n,[S.room.inv+S.inv,S.room.inv,S.inv][w])
# move in a direction
def go(S, dir):
if S.foe: return S.sendl("You're fighting!")
dest = S.room.ex.get(dir)
if dest:
Act(S).room("$SN leaves to the %s." % dir)
S.goto(dest)
S.do("l")
Act(S).room("$SN arrives from the %s." % rdirs[ dirs.index(dir) ])
else:
S.sendl("You can't go that way.")
def flee(S):
S.unfight()
if S.room.ex: S.go(choice(S.room.ex.keys()))
def fight(S,v):
if S.foe:
if v and v != S.foe: S.sendl("You're already fighting someone.")
else: return 1
elif v == S: S.sendl("Hah, no.")
elif not v: S.sendl("Not fighting anyone yet.")
elif not is_a(v,Actor): S.sendl("No use fighting that.")
elif v.foe and v.foe != S: S.sendl(v.name+" is busy.")
else:
Act(S,v).subj("You prepare to fight $VN.").vict(
"$SN prepares to fight you!").room("$SN glares dangerously at $VN.")
S.foe = v
v.foe = S
v.ks = 0
adde(3,2,fup,(S,))
adde(6,2,fup,(v,))
adde(10,5,fam,(S,v))
return 1
def unfight(S):
if S.foe: S.foe.foe = 0
S.foe = 0
def buy(S,s,z):
x,q,pr = z
if q < 1: S.sendl("Out of stock.")
elif pr > S.cash: S.sendl("Can't afford it.")
else:
y = copy.copy(x)
y.goto(S)
Act(S,y).subj("You buy $VN.").room("$SN buys $VN.")
S.cash -= pr
z[1] -= 1 # quantity in-stock decreases
s.rev += pr
return 1
status = lambda S: "{W%s{x appears %s and %s." % (S.name,scale(rscale,S.rpc),
scale(hscale,100*S.hpc/S.hpm))
# checks the chance that the *attacker* hits
# warning, returns a float
def checkhit(S,bdam,chance=80):
# If hurt, there's a penalty (gradually increasing up to 15%)
chance -= 30*((1.5**(1-1.*S.hpc/S.hpm))-1)
if randint(0,100) < chance: return gauss(bdam,bdam/4.)
return 0
def checklose(S):
import world
if S.hpc > 0 and S.rpc > 0: return
if S.hpc < 1: # out of HP
Act(S).subj("{YYou {Rcollapse{Y and black out.{x").room(
"{Y$SN collapses and is carried away by paramedics.{x")
elif S.rpc < 1: # out of RP
Act(S).subj("{YUtterly disgraced, you run home.{x").room(
"{Y$SN runs away in total disgrace!{x")
S.goto(world.start)
S.hpc = S.hpm; S.rpc = S.rpm
S.path = 0
def hit(S,dam): # physical attack
dam = int(S.checkhit(dam))
S.ks -= 1 # one less attack
# People don't like it if you use violence against your opponent
# But it matters a lot less if you don't have any respect left anyway
rloss = S.pmult(dam/2.+max(100*(1.12**(1.*S.rpc/S.rpm)-1),3))
foe = S.foe
S.sendl("{BYou lost %d respect for your actions, but %s lost %d health.{x"
% (rloss,foe.name,dam))
foe.sendl("{RYou lost %d health from the attack, but %s lost %d respect.{x"
% (dam,S.name,rloss))
S.rpc -= rloss # violence penalty
foe.hpc -= dam # ouch
S.sendl(foe.status())
foe.checklose(); S.checklose() # yes, both sides can be eliminated
def pmult(S,dam): # modify damage for number of people watching
y = len(S.room.crowd)
return int(round(dam * math.log(max(y,1)+1)))
def rhit(S,dam,t): # dissin'
foe = S.foe
dam = S.checkhit(dam)
S.ks -= 1 # one less attack
if not dam: # failure
Act(S,t).subj("{BYour $VT didn't have quite the devastating finesse you wanted.{x").room(
"$SN's poorly executed $VT fell flat.")
return
# factor in number of people watching
dam = S.pmult(dam)
S.sendl("{BYour %s causes %s to lose %s respect.{x" % (t,S.foe.name,dam))
foe.sendl("{RYou lose %d respect to that %s!{x" % (dam,t))
foe.rpc -= dam
S.sendl(foe.status())
foe.checklose()
class Shop(object):
rev = 0 # revenue
def __init__(S):
S.inv = []
def show(S,v):
v.sendl("{BShop:{x")
for x,q,pr in S.inv:
v.sendl(" {g${x%-7.2f %-30s {B[{x%3d in stock{B]{x" % (pr,x.name,q))
def find(S, n):
return find(n,S.inv,lambda x:x[0])
# Restocks a shop, naively.
# You should add support for player-settable quantities and varying
# supply costs.
# Returns how much the restocking costed
def restock(S):
e = 0
for z in S.inv:
n = 10
z[1] += n # increase item stock by a fixed amount
e += n/2 # fixed cost of $0.50 to restock each item
return e
class BevShop(Shop):
def __init__(S,type):
super(BevShop,S).__init__()
S.type = type # type, e.g. lemonade, coffee, etc
S.fls = ['plain'] # flavors
class Sample(Item): # sample of a flavor, e.g. a cherry or a chocolate bar
fl = "" # flavor
# Clothes make you look nice and respectable
# Although there's no penalty for mismatched outfits ... yet
class Clothing(Item):
rpm = 0
def on_wear(S,by):
old = by.worn.get(S.wloc)
if old: old.on_remove(by)
Act(by,S).subj("You wear $VN.").room("$SN wears $VN.")
by.worn[S.wloc] = S
by.rpm += S.rpm; by.rpc += S.rpm # makes you look nicer
def on_remove(S,by):
if by.worn.get(S.wloc) != S: return by.sendl("Not wearing that.")
Act(by,S).subj("You take off $VN.").room("$SN takes off $VN.")
del by.worn[S.wloc]
by.rpm -= S.rpm; by.rpc -= S.rpm
by.checklose()
class Player(Actor):
nsave = ["foe","c","loc"]
npc = 0
admin = 0
pro = "$nl<$hpc{D/{x${hpm}{Dhp{x $rpc{D/{x${rpm}{Drp{x {g$${x$cash> "
def __init__(S, n):
super(Player, S).__init__()
S.name = n
S.names = n.lower(),
def sendl(S, t=""): S.c.sendl(t)
def write(S, t): S.c.write(t)
def save(S):
f = file("players/"+S.name,"w")
pk.dump(S,f)
def pload(n): # load a player
try:
f = file("players/"+n)
return pk.load(f)
except: pass
def wsave(): # save world
import world
f = gzip.open("data.gz","w")
pk.dump(world.start,f,-1)
def wload(): # load world
import world
f = gzip.open("data.gz")
return pk.load(f)