# Game commands and socials

from util import *
import gzip,event,core

cmds = []
helpd = {}

def D(**data): # register command with data/flags
    def d(f): cmds.append((f.func_name,f,data)); return f
    return d

C = lambda f: D()(f)  # short form for no args

# if this decorator is used, l is parsed into v
def arg(f,req=1,w=0):
    def z(S,l):
        v = 0
        if l or req:
            if req and not l: return S.sendl(cap(f.func_name) + " what?")
            v = S.find(l,w)
            if not v: return S.sendl("You don't see that here.")
        f(S,v)
    z.orig = f
    z.func_name = f.func_name
    return z

optarg = lambda f: arg(f,0)
warg = lambda w: lambda f: arg(f,1,w) # ugly, eh?

# handle a command
def do(a,l):
    if not l: return
    if l[0] == ",": # special handling for abbreviated form of say
        if a.foe: l = "say %s, %s" % (a.foe.name,l[1:].lstrip())
        else: l = "say %s" % l[1:].lstrip()

    x = l.split(" ", 1)
    for c,f,d in cmds:
        if c.startswith(x[0]):
            if "nabbr" in d and c != x[0]:
                if "hide" in d: continue
                return a.sendl("Must spell out %s." % c)
            if "admin" in d and not a.admin:
                return a.sendl("Must be admin.")
            f(a, (len(x) > 1 and x[1]) or "")
            return
    else: a.sendl("Huh?")

# commands

@C
def help(S,l):
    if l:
        if l in helpd: S.write(helpd[l])
        else: S.sendl("No such topic.")
    else:
        S.sendl('Also try typing "commands".\r\n\r\nHelp topics:')
        for t in sorted(helpd): S.sendl("  "+t)

# soc = if true, show socials, otherwise show commands
def clist(S,soc):
    i=0
    for c,f,d in sorted(cmds):
        if soc ^ ("soc" in d) or "hide" in d: continue
        x = "%s%s" % (c, ("admin" in d and "*" or ""))
        S.write("%-18s" % x)
        if i % 4 == 3: S.sendl()
        i += 1
    S.sendl()

@C
def north(S,l): S.go("north")

@C
def south(S,l): S.go("south")

@C
def west(S,l): S.go("west")

@C
def east(S,l): S.go("east")

@C
def up(S,l): S.go("up")

@C
def down(S,l): S.go("down")

@D(nabbr=1)
def quit(S,l):
    if S.foe: return S.sendl("Not during combat.")
    else: S.c.handle_close()
@C
def commands(S,l): clist(S, 0)

@C
def socials(S,l): clist(S, 1)

@C
@optarg
def look(S,v):
    if v: examine.orig(S,v)
    else:
        S.sendl(S.room.descTo(S))
        S.sendl("{GExits{x: "+', '.join(S.room.ex))

@C
@arg
def examine(S,v):
    Act(S,v).subj("You look at $VN.").vict("$SN looks at you.")
    S.sendl(v.desc)

    if is_a(v,core.Actor):
        S.sendl("\r\n"+v.status())
        if v.worn:
            S.sendl("%s is wearing %s."%(cap(v.SE)," and ".join([x.name for x in v.worn.values()])))

@C
@warg(1) # room only
def get(S,v): v.on_get(S)

@C
@warg(2) # inv only
def drop(S,v): v.on_drop(S)

@C
@warg(2) # inv only
def wear(S,v):
    v.on_wear(S)

@C
@warg(2) # inv only
def remove(S,v):
    v.on_remove(S)

@C
def give(S,l):
    lv,lto = fit(xarg(l))
    if not lv or not lto or lto=="to": return S.sendl("Syntax: give <item> <person>")
    v,to = S.find(lv),S.find(lto)
    if not v: S.sendl("No such item here.")
    elif not to: S.sendl("No such person here.")
    else: v.on_give(S,to)

@C
def lists(S,l):
    ss = S.room.shops
    if not ss: S.sendl("No shops here.")
    for s in ss: s.show(S)

@C
def buy(S,l):
    if not l: return lists(S,l)
    for s in S.room.shops:
        z = s.find(l)
        if z: break
    else: return S.sendl("Not for sale here.")

    S.buy(s,z)

@C
@arg
def drink(S,v):
    v.on_drink(S)

@C # throw something
def throw(S,l):
    lv,lat = fit(xarg(l))
    if not lv or lat=="at": return S.sendl("Syntax: throw <item> [victim]")
    v,at = S.find(lv,2),S.find(lat)
    if not v: S.sendl("No such item.")
    elif lat and not at: S.sendl("No such person here.")
    else:
        if not S.foe: S.ks = 1
        if not S.fight(at): return
        if not S.ks: return S.sendl("You're not ready to attack yet.")
        v.on_throw(S,S.foe)

@C
def who(S,l):
    S.sendl("{YOnline players:{x")
    for p in cplayers():
        ip = (S.admin and p.c.addr[0]) or ""
        S.sendl("  %-15s %s" % (p.name,ip))

@C
def say(S,l):
    if not l: return S.sendl("Say what?")
    Act(S, 0, cap(l)).subj('You say, "{m$OT{x"').room('$SN says, "{m$OT{x"')

    # battle insults
    if S.foe and find(l.split(',')[0].lower(),[S.foe]): # UGLY but safer
        if not S.ks:
            Act(S).subj("{BYou botched the insult due to bad timing.{x").room(
"$SN botched the insult to bad timing.")
            return

        # This would be a good place to check for lame/awesome insults
        # namely, check for shortness, repeats, or unpronounceable nonsense
        # conversely, would it be a good idea to reward rhyming insults?
        S.rhit(8,'insult')

@C
def ooc(S,l):
    Act(S, 0, l).subj('You OOC: {M$OT{x').world('$SN OOCs: {M$OT{x')

@C
def mydesc(S,l):
    def cb(t): S.desc = t
    from mud import EditUI
    S.c.ui = EditUI(S.c, S.desc, cb)

@C
def tell(S,l):
    lv,l = fit(l.split(None,1))
    if not lv: return S.sendl("Tell whom what?")
    for p in cplayers():
        if p.name.startswith(fcap(lv)): break
    else: return S.sendl("No such player online.")
    Act(S,p,l).subj("You tell $VN: {c$OT{x").vict("$SN tells you: {c$OT{x")

@C
@arg
def fight(S,v):
    S.fight(v)

@C
@optarg
def hit(S,v):
    if not S.foe: S.ks = 1  # one free attack
    if not S.fight(v): return
    if not v and S.foe: v = S.foe

    if not S.ks: return S.sendl("You're not ready to attack yet.")

    x = S.worn.get("in grasp",S.FISTS)  # weapon (if available) or fists
    Act(S,x,v).subj("You swing $VN at $ON.").vict("$SN swings $VN at you.").room("$SN swings $VN at $ON.")
    S.hit(x.dam)

@C
def flee(S,l):
    Act(S).subj("You try to flee!").room("$SN tries to flee!")
    if S.foe:
        S.rpc -= S.rpm/10
        S.checklose()
    if randint(0,5) < 3: S.flee()

@C
def rname(S,l):
    if not S.room.try_edit(S): return
    if not l: return S.sendl("Set room name to what?")
    S.room.name = l
    S.sendl("Room name changed to " + l)

@C
def rdesc(S,l):
    if not S.room.try_edit(S): return
    def cb(t): S.room.desc = t
    from mud import EditUI
    S.c.ui = EditUI(S.c, S.room.desc, cb)

ztypes = {"smbiz":["lemonade"],"biz":["coffee"],"night":["beer"],"res-high":["apartment"]}

@C
def rent(S,l):
    b = S.room.biz
    if b.owner: return S.sendl("Not available for rent.")
    dep = b.rent * 3 + 10

    if not l:
        S.sendl('Land for rent (type "rent apply <type>" to rent)'+
"\r\nZoning: %s (valid types: %s)" % (b.zone,ztypes[b.zone])+
"\r\nDeposit+fee: $%d" % dep+
"\r\nHourly rent: $%d" % b.rent)

        return

    la,l = xarg(l)
    if not l or la != "apply": return S.sendl("rent apply <type>")

    if dep > S.cash: return S.sendl("Not enough cash.")
    if l not in ztypes[b.zone]: return S.sendl("Wrong type.")

    S.cash -= dep
    b.owner = S.name
    b.type = l

    S.sendl("You rent the property. Rent will be taken from your cash hourly.")

@C
def shop(S,l):
    biz = S.room.biz
    if biz.owner != S.name:
        return S.sendl("Not your property.")
    o,l = fit(xarg(l))
    if not o: return S.sendl("Options: new, add <flavors>, sample <flavor>, price '<item>' <price>, restock")
    shop = S.room.shop
    if o == "new":
        if shop: S.sendl("Already got one.")
        else: S.room.shop = core.BevShop(biz.type); S.sendl("Created. Sales revenue paid hourly.")
        return
    if not shop: return S.sendl('Create a shop first.')
    if o == "add":
        import world
        fls = sorted(sxarg(l))
        if not l or not set(fls) <= set(shop.fls): # shop has chosen flavors?
            return S.sendl("Valid flavors: "+' '.join(shop.fls))
        x = world.Drink()
        x.fls = fls+[shop.type]
        x.name = "a glass of "+l+" "+shop.type
        shop.inv += [[x,1,10]]
        S.sendl("Added %s." % x.name)
    if o == "sample":
        x = S.find(l,2)
        if not x: return S.sendl("You don't have that.")
        if not is_a(x,core.Sample): return S.sendl("Not a flavor sample.")
        shop.fls += [x.fl]
        S.sendl("Sampled %s." % x.fl)
        x.goto(0)
    if o == "restock":
        S.sendl("Restocked at a cost of $%s." % shop.restock())
    if o == "price":
        l,tpr = fit(l.rsplit(None,1))
        x = shop.find(l)
        if not x: return S.sendl("No such item in shop.")
        if not tpr.isdigit() or int(tpr) <= 0: return S.sendl("Price must be a positive number.")
        x[2] = int(tpr)
        S.sendl("Price set.")

@C
def inventory(S,l):
    S.sendl("You're carrying:")
    for x in S.inv:
        w = (x.wloc and x.worn and " (worn %s)" % x.wloc) or ""
        S.sendl("  %s%s" % (x.name, w))

@C
def prompt(S,l):
    if l == "reset": del S.pro
    elif l: S.pro = l; S.sendl("Prompt set.")
    else:
        S.sendl("{cCurrent prompt{x: "+S.pro.replace("{","{{"))
        S.sendl('Type "{Wprompt reset{x" to restore the default.')

@C
def save(S,l): S.save(); S.sendl("Saved.")

@D(admin=1)
def rdig(S,l):
    if not l in core.dirs: return S.sendl("Bad dir.")
    r = core.Room()
    S.room.ex[l] = r
    r.ex[core.rdirs[core.dirs.index(l)]] = S.room
    S.sendl("Dug.")

@D(admin=1)
def rlink(S,l):
    sr = S.room
    rd = core.rdirs[core.dirs.index(d)]   # reverse direction

    d,l = fit(xarg(l))
    if not l or not d in core.dirs: return S.sendl("rlink <dir> [<dest>|none]")
    if l == "none":  # DOES NOT REMOVE OTHER EXIT
        if d in sr.ex: del sr.ex[d]; return

    dest = find(l,[r for r in core.Thing.all if is_a(r,core.Room)])
    if not dest: return S.sendl("Room not found.")
    sr.ex[d] = dest
    dest.ex[core.rdirs[rd]] = sr
    S.sendl("Linked.")

# reloads this module, updating commands and socials
@D(admin=1)
def creload(S,l):
    import cmd
    reload(cmd)
    S.sendl("cmd.py reloaded.")

@D(admin=1)
def goto(S,l):
    v = find(l,core.Thing.all)
    if not v: S.sendl("Not found.")
    elif not v.room: S.sendl("Goes nowhere.")
    else:
        if S.foe: S.unfight()
        Act(S).room("{W$SN{x vanishes in a *{Wfoop{x*.")
        S.goto(v.room)
        Act(S).subj("{WPoof{x.").room("{W$SN{x appears in a *{Wpoof{x*.")
        look(S,"")

@D(admin=1)
def lease(S,l):
    o,l = fit(xarg(l))

    if not o: return S.sendl("Options: new, evict, condemn, rent <$>, zone <type>")

    if o == "new": S.room.biz = core.Biz(); return

    biz = S.room.biz
    if biz == core.CITY: return S.sendl("No. City property.")

    if o == "rent":    # Set hourly rent
        if not l.isdigit(): S.sendl("In dollars.")
        else: biz.rent = int(l)
    elif o == "zone":  # Set room zoning
        if l not in ztypes: S.sendl("Valid zones: "+' '.join(ztypes))
        else: biz.zone = l
    elif o == "evict": biz.owner = "" # kick out owner and make rentable again
    elif o == "condemn": biz.owner = "CITY" # return property to city ownership
    else: S.sendl("Bad option.")

# Another admin command for misc things
@D(admin=1)
def frotz(S,l):
    if l == '$': S.cash += 5000                 # gives you money
    elif l == "wsave": core.wsave()                # save the world
    else: S.sendl("Options: $, wsave")

@D(nabbr=1,hide=1)
@optarg
def xyzzy(S,v): # makes you an admin
    if not S.admin and S.c.addr[0] != '127.0.0.1':
        return S.sendl("Nothing happens.")

    if v and v.npc: return S.sendl("Players only.")

    x = v or S
    x.admin ^= 1

    t = x.admin and "now" or "no longer"
    if v: S.sendl("%s is %s an admin." % (x.name,t))
    x.sendl("You are %s an admin." % t)

# quick and dirty shutdown!
@D(nabbr=1,admin=1)
def shutdown(S, l):
    Act().world("{RSHUTTING DOWN.{x")
    for p in cplayers(): p.save()
    frotz(S,'wsave')
    import asyncore; asyncore.loop(0.1, 1, count=1); asyncore.close_all()
    import sys; sys.exit(1)

# adds a social to the command table
# args: subj sans target, room sans target, subj/room/vict with a target
def addsoc(n,ts,tr,tds,tdv,tdr):
    def f(S, l):
        v = 0
        if l:
            v = S.find(l)
            if not v: return S.sendl("No such person.")

        if not v and S.foe: v = S.foe  # in combat, auto-target foe

        if v and v == S.foe and not S.ks:
            return S.sendl("You're not ready yet.")

        a = Act(S,v)
        if v: a.subj(tds); a.vict(tdv); a.room(tdr)
        else: a.subj(ts); a.room(tr)

        if v and v == S.foe:  # a (potentially) insulting attack
            S.rhit(6,n)

    cmds.append((n,f,{"soc":1}))

def load_soc():
    x = []
    for l in gzip.open("socials.txt.gz", "r"):
        l = l.strip()
        if l == '~': # end of a social
            addsoc(*fit(x,6,'')); x = []
        else: x += [l]

def load_help():
    x = n = ""
    f = gzip.open("help.txt.gz","r")
    for l in f:
        l = l.rstrip()
        if not n: n = l; continue
        if l == "~":
            helpd[n] = x
            x = ""; n = 0
        else: x += l+"\r\n"

load_soc()
load_help()