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)