Ejemplo n.º 1
0
    def recover(self, update: Update, context: CallbackContext) -> bool:
        """将重伤患者的状态恢复。使用回复或者`@username`作为参数来选择对象恢复。
        回复的优先级高于参数。"""

        kp = self.forcegetplayer(update)
        gp = self.forcegetgroup(update)

        if gp.game is None:
            return self.errorInfo("没有进行中的游戏", True)

        if kp != gp.kp:
            return self.errorInfo("没有权限", True)

        rppl = self.getreplyplayer(update)
        if rppl is None:
            if len(context.args) == 0:
                return self.errorInfo("使用回复或@username指定恢复者")
            if not isint(context.args[0]) or int(context.args[0]) < 0:
                return self.errorInfo("参数无效", True)

            rppl = self.getplayer(int(context.args[0]))
            if rppl is None:
                return self.errorInfo("玩家无效")

        card = self.findcardfromgame(gp.game, rppl)
        if card is None:
            return self.errorInfo("找不到该玩家的卡。")

        if card.status != STATUS_SERIOUSLYWOUNDED:
            return self.errorInfo("该角色没有重伤")

        card.status = STATUS_ALIVE
        self.reply("角色已恢复")
        card.write()
        return True
Ejemplo n.º 2
0
    def textpassjob(self, update: Update) -> bool:
        t = update.message.text.split()
        if getmsgfromid(update) != ADMIN_ID or (t[0] != "jobcomfirm"
                                                and t[0] != "jobreject"):
            self.botchat(update)
            return

        if len(t) < 2 or not isint(t[1]):
            return self.errorInfo("参数无效")

        plid = int(t[1])
        if plid not in self.addjobrequest:
            return self.errorInfo("没有该id的职业新增申请")

        self.popOP(ADMIN_ID)
        if t[0] == "jobcomfirm":
            self.joblist[self.addjobrequest[plid]
                         [0]] = self.addjobrequest[plid][1]

            with open(PATH_JOBDICT, 'w', encoding='utf-8') as f:
                json.dump(self.joblist, f, indent=4, ensure_ascii=False)

            self.reply(plid, "您的新增职业申请已通过。")

        else:
            self.reply(plid, "您的新增职业申请没有通过。")

        return True
Ejemplo n.º 3
0
    def kill(self, update: Update, context: CallbackContext) -> bool:
        """使角色死亡。使用回复或者`@username`作为参数来选择对象撕卡。
        回复的优先级高于参数。"""

        kp = self.forcegetplayer(update)
        gp = self.forcegetgroup(update)

        if gp.game is None:
            return self.errorInfo("没有进行中的游戏", True)

        if kp != gp.kp:
            return self.errorInfo("没有权限", True)

        rppl = self.getreplyplayer(update)
        if rppl is None:
            if len(context.args) == 0:
                return self.errorInfo("使用回复或@username指定对象")
            if not isint(context.args[0]) or int(context.args[0]) < 0:
                return self.errorInfo("参数无效", True)

            rppl = self.getplayer(int(context.args[0]))
            if rppl is None:
                return self.errorInfo("玩家无效")

        card = self.findcardfromgame(gp.game, rppl)
        if card is None:
            return self.errorInfo("找不到该玩家的卡。")

        if card.status == STATUS_DEAD:
            return self.errorInfo("角色已死亡")

        card.status = STATUS_DEAD
        self.reply("已撕卡")
        card.write()
        return True
Ejemplo n.º 4
0
    def modify(self, update: Update, context: CallbackContext) -> bool:
        """强制修改某张卡某个属性的值。
        需要注意可能出现的问题,使用该指令前,请三思。

        `/modify --cardid --arg --value (game)`: 修改id为cardid的卡的value,要修改的参数是arg。
        带game时修改的是游戏内卡片数据,不指明时默认游戏外
        (对于游戏中与游戏外卡片区别,参见 `/help startgame`)。
        修改对应卡片的信息必须要有对应的KP权限,或者是BOT的管理者。
        如果要修改主要技能点和兴趣技能点,请使用`mainpoints`, `intpoints`作为`arg`,而不要使用points。
        id, playerid, groupid这三个属性不可以修改。
        想要修改id,请使用指令
        `/changeid --cardid --newid`
        (参考`/help changeid`)。
        想要修改所属群,使用指令
        `/changegroup --cardid --newgroupid`
        (参考`/help changegroup`)。"""

        pl = self.forcegetplayer(update)
        if not self.searchifkp(pl) and pl.id != ADMIN_ID:
            return self.errorInfo("没有权限", True)

        # need 3 args, first: card id, second: attrname, third: value
        if len(context.args) < 3:
            return self.errorInfo("需要至少3个参数", True)

        card_id = context.args[0]
        if not isint(card_id) or int(card_id) < 0:
            return self.errorInfo("无效ID", True)

        card_id = int(card_id)
        if len(context.args) > 3 and context.args[3] == "game":
            card = self.getgamecard(card_id)
            rttext = "修改了游戏内的卡片:\n"
        else:
            card = self.getcard(card_id)
            rttext = "修改了游戏外的卡片:\n"

        if card is None:
            return self.errorInfo("找不到这张卡")

        if not self.checkaccess(pl, card) & CANMODIFY:
            return self.errorInfo("没有权限", True)

        try:
            if context.args[1] == "mainpoints":
                ans, ok = card.skill.modify("points", context.args[2])
            elif context.args[1] == "intpoints":
                ans, ok = card.interest.modify("points", context.args[2])
            else:
                ans, ok = card.modify(context.args[1], context.args[2])
        except TypeError as e:
            return self.errorInfo(str(e))

        if not ok:
            return self.errorInfo("修改失败。" + ans)

        rttext += context.args[1] + "从" + ans + "变为" + context.args[2]
        self.reply(rttext)
        return True
Ejemplo n.º 5
0
    def switch(self, update: Update, context: CallbackContext):
        """切换目前操作的卡。
        注意,这不是指kp在游戏中的多张卡之间切换,如果kp要切换游戏中骰骰子的卡,请参见指令`/switchgamecard`。
        玩家只能修改目前操作的卡的基本信息,例如:年龄、性别、背景、技能点数等。
        `/switch`:生成按钮来切换卡。
        `/switch --cdid`切换至id为`cdid`的卡。"""

        if isgroup(update):
            return self.errorInfo("对bot私聊来切换卡。")

        pl = self.forcegetplayer(update)

        if len(pl.cards) == 0:
            return self.errorInfo("你没有任何卡。")

        if len(pl.cards) == 1:
            if pl.controlling is not None:
                return self.errorInfo("你只有一张卡,无需切换。")

            for card in pl.cards.values():
                pl.controlling = card
                break
            pl.write()

            self.reply(f"你只有一张卡,自动控制这张卡。现在操作的卡:{pl.controlling.getname()}")
            return True

        if len(context.args) > 0:
            if not isint(context.args[0]):
                return self.errorInfo("输入无效。")
            cdid = int(context.args[0])
            if cdid < 0:
                return self.errorInfo("卡片id为正数。")
            if cdid not in pl.cards:
                return self.errorInfo("找不到这个id的卡。")

            pl.controlling = pl.cards[cdid]
            pl.write()

            self.reply(f"现在操作的卡:{pl.controlling.getname()}")
            return True

        # 多个选项。创建按钮
        rtbuttons = [[]]
        for card in pl.cards.values():
            if len(rtbuttons[len(rtbuttons) - 1]) == 4:
                rtbuttons.append([])

            rtbuttons[len(rtbuttons) - 1].append(
                InlineKeyboardButton(card.getname(),
                                     callback_data="switch " + str(card.id)))
        rp_markup = InlineKeyboardMarkup(rtbuttons)
        self.reply("请选择要切换控制的卡:", reply_markup=rp_markup)
        self.workingMethod[self.lastchat] = BUTTON_SWITCH
        # 交给按钮来完成
        return True
Ejemplo n.º 6
0
    def textsetage(self, update: Update) -> bool:
        text = update.message.text
        plid = getchatid(update)
        if not isint(text):
            return self.errorInfo("输入无效,请重新输入")
        cardi = self.findcard(plid)
        if not cardi:
            self.popOP(plid)
            return self.errorInfo("找不到卡")

        return (bool(self.popOP(plid)) or True) if self.cardsetage(
            update, cardi, int(text)) else False
Ejemplo n.º 7
0
    def cardtransfer(self, update: Update, context: CallbackContext) -> bool:
        """转移卡片所有者。格式为
        `/cardtransfer --cardid --playerid`:将卡转移给playerid。
        回复某人`/cardtransfer --cardid`:将卡转移给被回复的人。要求参数有且仅有一个。
        只有卡片拥有者或者KP有权使用该指令。
        如果对方不是KP且对方已经在本群有卡,则无法转移。"""

        if len(context.args) == 0:
            return self.errorInfo("需要参数", True)
        if len(context.args) == 1 and self.getreplyplayer(update) is None:
            return self.errorInfo("参数不足", True)
        if not isint(context.args[0]) or (len(context.args) > 1
                                          and not isint(context.args[1])):
            return self.errorInfo("参数无效", True)
        if int(context.args[0]) < 0 or (len(context.args) > 1
                                        and int(context.args[1]) < 0):
            return self.errorInfo("负数参数无效", True)

        cdid = int(context.args[0])
        card = self.getcard(cdid)
        if card is None:
            return self.errorInfo("找不到这张卡")

        operationer = self.forcegetplayer(update)
        if len(context.args) == 1:
            tpl: Player = self.getreplyplayer(update)
        else:
            tpl = self.forcegetplayer(int(context.args[1]))

        if not self.checkaccess(operationer, card) & (OWNCARD | CANMODIFY):
            return self.errorInfo("没有权限", True)

        if tpl != card.group.kp:
            for c in tpl.cards.values():
                if c.group == card.group:
                    return self.errorInfo("目标玩家已经在对应群有一张卡了")

        # 开始处理
        self.atcardtransfer(update.message, cdid, tpl)
        return True
Ejemplo n.º 8
0
    def textnewcard(self, update: Update, cdid: int = -1) -> bool:
        text = update.message.text
        pl = self.forcegetplayer(update)

        if not isint(text) or int(text) >= 0:
            return self.errorInfo("无效群id。如果你不知道群id,在群里发送 /getid 获取群id。")
        gpid = int(text)
        self.popOP(pl.id)

        if self.hascard(pl.id, gpid) and self.forcegetgroup(gpid).kp != pl:
            return self.errorInfo("你在这个群已经有一张卡了!")

        return self.getnewcard(update.message.message_id, gpid, pl.id, cdid)
Ejemplo n.º 9
0
    def deletemsg(self, update: Update, context: CallbackContext) -> bool:
        """用于删除消息,清空当前对话框中没有用的消息。
        bot可以删除任意私聊消息,无论是来自用户还是bot。
        如果是群内使用该指令,需要管理员或KP权限,
        以及bot是管理员,此时可以删除群内的任意消息。

        当因为各种操作产生了过多冗杂消息的时候,使用
        `/delmsg --msgnumber`将会删除:delmsg指令的消息
        以及该指令上面的msgnumber条消息。例如:
        `/delmsg 2`将删除包含delmsg指令在内的3条消息。
        没有参数的时候,`/delmsg`默认删除指令和指令的上一条消息。

        因为要进行连续的删除请求,删除的时间会稍微有些滞后,
        请不要重复发送该指令,否则可能造成有用的消息丢失。
        如果感觉删除没有完成,请先随意发送一条消息来拉取删除情况,
        而不是继续用`/delmsg`删除。"""

        delnum = 1
        chatid = getchatid(update)

        if isgroup(update) and not self.isadmin(self.lastchat, BOT_ID):
            return self.errorInfo("Bot没有管理权限")

        if isgroup(update) and self.checkaccess(self.forcegetplayer(update), self.forcegetgroup(update)) & (GROUPKP | GROUPADMIN) == 0:
            return self.errorInfo("没有权限", True)

        if len(context.args) >= 1:
            if not isint(context.args[0]) or int(context.args[0]) <= 0:
                return self.errorInfo("参数错误", True)
            delnum = int(context.args[0])
            if delnum > 10:
                return self.errorInfo("一次最多删除10条消息")

        lastmsgid = self.lastmsgid
        while delnum >= 0:  # 这是因为要连同delmsg指令的消息也要删掉
            if lastmsgid < -100:
                break
            try:
                context.bot.delete_message(
                    chat_id=chatid, message_id=lastmsgid)
            except Exception as e:
                if str(e).find("can't be deleted for everyone") != -1:
                    self.errorInfo("消息删除失败,发送时间较久的消息无法删除")
                    break
                lastmsgid -= 1
            else:
                delnum -= 1
                lastmsgid -= 1

        update.effective_chat.send_message("删除完成").delete()
        return True
Ejemplo n.º 10
0
    def changeid(self, update: Update, context: CallbackContext) -> bool:
        """修改卡片id。卡片的所有者或者KP均有使用该指令的权限。

        指令格式:
        `/changeid --cardid --newid`

        如果`newid`已经被占用,则指令无效。
        这一行为将同时改变游戏内以及游戏外的卡id。"""

        if len(context.args) < 2:
            return self.errorInfo("至少需要两个参数。")

        if not isint(context.args[0]) or not isint(context.args[1]):
            return self.errorInfo("参数无效", True)

        oldid = int(context.args[0])
        newid = int(context.args[1])

        if newid < 0:
            return self.errorInfo("卡id不能为负数", True)
        if newid == oldid:
            return self.errorInfo("前后id相同", True)
        if newid in self.allids:
            return self.errorInfo("该ID已经被占用")

        card = self.getcard(oldid)
        if card is None:
            return self.errorInfo("找不到该ID对应的卡")

        pl = self.forcegetplayer(update)
        if not self.checkaccess(pl, card) & (OWNCARD | CANMODIFY):
            return self.errorInfo("没有权限")

        # 开始处理
        self.atidchanging(update.message, oldid, newid)
        return True
Ejemplo n.º 11
0
    def transferkp(self, update: Update, context: CallbackContext) -> bool:
        """转移KP权限,只有群管理员可以使用这个指令。
        当前群没有KP时或当前群KP为管理员时,无法使用。

        `/transferkp --kpid`:将当前群KP权限转移到某个群成员。
        如果指定的`kpid`不在群内则无法设定。

        `/transferkp`:将当前群KP权限转移到自身。

        `/trasferkp`(reply to someone):将kp权限转移给被回复者。"""
        if isprivate(update):
            return self.errorInfo("发送群消息强制转移KP权限")

        gp = self.getgp(update)
        pl = self.getplayer(update)
        f = self.checkaccess(pl, gp)

        if not f & GROUPADMIN:
            return self.errorInfo("没有权限", True)

        if gp.kp is None:
            return self.errorInfo("没有KP", True)

        if self.checkaccess(gp.kp, gp) & GROUPADMIN:
            return self.errorInfo("KP是管理员,无法转移")

        # 获取newkp
        newkpid: int
        if len(context.args) != 0:
            if not isint(context.args[0]):
                return self.errorInfo("参数需要是整数", True)
            newkp = self.forcegetplayer(int(context.args[0]))
        else:
            t = self.getreplyplayer(update)
            newkp = t if t is not None else self.forcegetplayer(update)

        if newkp == gp.kp:
            return self.errorInfo("原KP和新KP相同", True)

        if not self.changeKP(gp, newkp):
            return self.errorInfo("程序错误:不符合添加KP要求,请检查代码")  # 不应触发

        return True
Ejemplo n.º 12
0
    def setage(self, update: Update, context: CallbackContext):
        if isgroup(update):
            return self.errorInfo("发送私聊消息设置年龄。", True)

        pl = self.forcegetplayer(update)
        card = pl.controlling
        if card is None:
            return self.errorInfo("找不到卡。")

        if card.info.age >= 17 and card.info.age <= 99:
            return self.errorInfo("已经设置过年龄了。")

        if len(context.args) == 0:
            self.reply("请输入年龄:")
            self.addOP(getchatid(update), "setage")
            return True

        age = context.args[0]
        if not isint(age):
            return self.errorInfo("输入无效")

        age = int(age)
        return self.cardsetage(update, card, age)
Ejemplo n.º 13
0
    def delcard(self, update: Update, context: CallbackContext) -> bool:
        """KP才能使用该指令,删除一张卡片。一次只能删除一张卡。
        `/delcard --cardid`:删除id为cardid的卡。"""

        if len(context.args) == 0:
            return self.errorInfo("需要卡id作为参数", True)
        if not isint(context.args[0]) or int(context.args[0]) < 0:
            return self.errorInfo("参数无效", True)

        cdid = int(context.args[0])
        card = self.getcard(cdid)

        if card is None:
            return self.errorInfo("找不到对应id的卡")

        kp = self.forcegetplayer(update)
        if not self.checkaccess(kp, card) & CANMODIFY:
            return self.errorInfo("没有权限", True)

        # 开始处理
        self.reply(
            f"请确认是否删除卡片\n姓名:{card.getname()}\n如果确认删除,请回复:确认。否则,请回复其他任何文字。")
        self.addOP(getchatid(update), "delcard "+context.args[0])
        return True
Ejemplo n.º 14
0
    def show(self, update: Update, context: CallbackContext) -> bool:
        """显示目前操作中的卡片的信息。私聊时默认显示游戏外的卡,群聊时优先显示游戏内的卡。
        (如果有多张卡,用`/switch`切换目前操作的卡。)
        `/show`:显示最基础的卡片信息;
        `/show card`:显示当前操作的整张卡片的信息;
        `/show --attrname`:显示卡片的某项具体属性。
        (回复某人消息)`/show card或--attrname`:同上,但显示的是被回复者的卡片的信息。

        例如,`/show skill`显示主要技能,
        `/show interest`显示兴趣技能。
        如果要显示主要技能点和兴趣技能点,请使用`mainpoints`, `intpoints`作为`arg`,而不要使用points。
        如果当前卡中没有这个属性,则无法显示。
        可以显示的属性例子:
        `STR`,`description`,`SAN`,`MAGIC`,`name`,`item`,`job`"""

        pl = self.forcegetplayer(update)
        rppl = self.getreplyplayer(update)
        rpcard: Optional[GameCard] = None

        if rppl is None and len(context.args) > 0:
            if isint(context.args[0]):
                rppl = self.getplayer(int(context.args[0]))
            if rppl is not None:
                context.args = context.args[1:]

        if rppl is not None:
            gp = self.forcegetgroup(update)
            rpcard = self.findcardfromgroup(rppl, gp)
            if rpcard is None:
                return self.errorInfo("该玩家在本群没有卡")

        card = rpcard if rpcard is not None else None
        if card is None:
            if isgroup(update):
                gp = self.forcegetgroup(update)
                card = self.findcardfromgroup(pl, gp)
                if card is None:
                    return self.errorInfo("请先在本群创建卡")
            else:
                card = pl.controlling
                if card is None:
                    return self.errorInfo("请先创建卡,或者使用 /switch 选中一张卡")

        game = card.group.game if card.group.game is not None else card.group.pausedgame

        rttext = ""

        if game is not None and isgroup(update):
            if card.id in game.cards:
                rttext = "显示游戏中的卡:\n"
                card = game.cards[card.id]

        if rttext == "":
            rttext = "显示游戏外的卡:\n"

        if not self.checkaccess(pl, card) & CANREAD:
            return self.errorInfo("没有权限")

        if card.type != PLTYPE and isgroup(update):
            return self.errorInfo("非玩家卡片不可以在群内显示")

        if len(context.args) == 0:
            self.reply(card.basicinfo())
            return True

        if context.args[0] == "card":
            self.reply(str(card))
            return True

        if context.args[0] == "mainpoints":
            ans = card.skill.show("points")
        elif context.args[0] == "intpoints":
            ans = card.interest.show("points")
        elif context.args[0] == "points":
            return self.errorInfo("请用mainpoints或intpoints来显示")
        else:
            ans = card.show(context.args[0])

        if ans == "找不到该属性":
            return self.errorInfo("找不到该属性")

        if ans == "":
            self.reply(rttext + "无")
        else:
            self.reply(rttext + ans)
        return True
Ejemplo n.º 15
0
    def setrule(self, update: Update, context: CallbackContext) -> bool:
        """设置游戏的规则。
        一个群里游戏有自动生成的默认规则,使用本指令可以修改这些规则。

        `/setrule --args`修改规则。`--args`格式如下:

        `rulename1:str --rules1:List[int] rulename2:str --rule2:List[int] ...`

        一次可以修改多项规则。
        有可能会出现部分规则设置成功,但部分规则设置失败的情况,
        查看返回的信息可以知道哪些部分已经成功修改。

        规则的详细说明:

        skillmax:接收长度为3的数组,记为r。`r[0]`是一般技能上限,
        `r[1]`是个别技能的上限,`r[2]`表示个别技能的个数。

        skillmaxAged:年龄得到的技能上限增加设定。
        接收长度为4的数组,记为r。`r[0]`至`r[2]`同上,
        但仅仅在年龄大于`r[3]`时开启该设定。`r[3]`等于100代表不开启该设定。

        skillcost:技能点数分配时的消耗。接收长度为偶数的数组,记为r。
        若i为偶数(或0),`r[i]`表示技能点小于`r[i+1]`时,
        需要分配`r[i]`点点数来获得1点技能点。r的最后一项必须是100。
        例如:`r=[1, 80, 2, 100]`,则从10点升至90点需要花费`1*70+2*10=90`点数。

        greatsuccess:大成功范围。接收长度为4的数组,记为r。
        `r[0]-r[1]`为检定大于等于50时大成功范围,否则是`r[2]-r[3]`。

        greatfail:大失败范围。同上。"""

        if isprivate(update):
            return self.errorInfo("请在群内用该指令设置规则")

        gp = self.forcegetgroup(update)

        if not self.isfromkp(update):
            return self.errorInfo("没有权限", True)

        if len(context.args) == 0:
            return self.errorInfo("需要参数", True)

        gprule = gp.rule

        ruledict: Dict[str, List[int]] = {}

        i = 0
        while i < len(context.args):
            j = i+1
            tplist: List[int] = []
            while j < len(context.args):
                if isint(context.args[j]):
                    tplist.append(int(context.args[j]))
                    j += 1
                else:
                    break
            ruledict[context.args[i]] = tplist
            i = j
        del i, j

        msg, ok = gprule.changeRules(ruledict)
        if not ok:
            return self.errorInfo(msg)

        self.reply(msg)
        return True
Ejemplo n.º 16
0
    def changegroup(self, update: Update, context: CallbackContext) -> bool:
        """修改卡片的所属群。
        一般只用于卡片创建时输入了错误的群id。
        比较特殊的情形:
        如果需要将某个群的所有卡片全部转移到另一个群,
        第一个参数写为负数的`groupid`即可。这一操作需要原群的kp权限。
        在原群进行游戏时,这个指令无效。

        指令格式:
        `/changegroup --groupid/--cardid --newgroupid`
        """

        if len(context.args) < 2:
            return self.errorInfo("至少需要2个参数", True)
        if not isint(context.args[0]) or not isint(context.args[1]):
            return self.errorInfo("参数无效", True)

        newgpid = int(context.args[1])
        if newgpid >= 0:
            return self.errorInfo("转移的目标群id应该是负数", True)

        if int(context.args[0]) < 0:  # 转移全部群卡片
            ogpid = int(context.args[0])

            oldgp = self.getgp(ogpid)
            if oldgp is None or len(oldgp.cards) == 0:
                return self.errorInfo("该群没有卡")

            newgp = self.forcegetgroup(newgpid)
            kp = self.forcegetgroup(update)
            if ((kp != oldgp.kp and oldgp.id != -1)
                    or kp != newgp.kp) and kp.id != ADMIN_ID:
                return self.errorInfo("没有权限", True)

            if oldgp.getexistgame() is not None:
                return self.errorInfo("游戏进行中,无法转移")

            # 检查权限通过
            numofcards = len(oldgp.cards)
            self.changecardgpid(ogpid, newgpid)
            self.reply("操作成功,已经将" + str(numofcards) + "张卡片从群:" + str(ogpid) +
                       "移动到群:" + str(newgpid))
            return True

        # 转移一张卡片
        cdid = int(context.args[0])
        card = self.getcard(cdid)
        if card is None:
            return self.errorInfo("找不到这个id的卡片", True)

        oldgp = card.group
        if oldgp.getexistgame():
            return self.errorInfo("游戏正在进行,无法转移")

        pl = self.forcegetplayer(update)
        if not self.checkaccess(pl, card) & (OWNCARD | CANMODIFY):
            return self.errorInfo("没有权限")

        # 开始执行
        card = self.popcard(cdid)
        self.addcardoverride(card, newgpid)
        cardname = card.getname()
        self.reply("操作成功,已经将卡片" + cardname + "从群:" + str(oldgp.id) + "移动到群:" +
                   str(newgpid))
        return True
Ejemplo n.º 17
0
def checknonnegativeint(update, context: 'CallbackContext', *args,
                        **kwargs) -> bool:
    index = kwargs['index']
    return len(context.args) > index and isint(
        context.args[index]) and int(context.args[index] >= 0)
Ejemplo n.º 18
0
    def roll(self, update: Update, context: CallbackContext):
        """基本的骰子功能。

        只接受第一个空格前的参数`dicename`。
        `dicename`可能是技能名、属性名(仅限游戏中),可能是`3d6`,可能是`1d4+2d10`。
        骰子环境可能是游戏中,游戏外。

        `/roll`:默认1d100。
        `/roll --mdn`骰一个mdn的骰子。
        `/roll --test`仅限游戏中可以使用。对`test`进行一次检定。
        例如,`/roll 力量`会进行一次STR检定。
        `/roll 射击`进行一次射击检定。
        检定心理学时结果只会发送给kp。
        如果要进行一个暗骰,可以输入
        `/roll 暗骰`进行一次检定为50的暗骰,或者
        `/roll 暗骰60`进行一次检定为60的暗骰。"""

        if len(context.args) == 0:
            self.reply(commondice("1d100"))  # 骰1d100
            return True

        dicename = context.args[0]

        if isprivate(update):
            self.reply(commondice(dicename))
            return True

        gp = self.forcegetgroup(update)

        # 检查输入参数是不是一个基础骰子,如果是则直接计算骰子
        if gp.game is None or dicename.find('d') >= 0 or isint(dicename):
            if isint(dicename) and int(dicename) > 0:
                dicename = "1d" + dicename
            rttext = commondice(dicename)
            if rttext == "Invalid input.":
                return self.errorInfo("输入无效")
            self.reply(rttext)
            return True

        if gp.game is None:
            return self.errorInfo("输入无效")
        # 确认不是基础骰子的计算,转到卡检定
        # 获取临时检定
        tpcheck, gp.game.tpcheck = gp.game.tpcheck, 0
        if tpcheck != 0:
            gp.write()

        pl = self.forcegetplayer(update)

        # 获取卡
        if pl != gp.kp:
            gamecard = self.findcardfromgame(gp.game, pl)
        else:
            gamecard = gp.game.kpctrl
            if gamecard is None:
                return self.errorInfo("请用 /switchgamecard 切换kp要用的卡")
        if not gamecard:
            return self.errorInfo("找不到游戏中的卡。")
        if gamecard.status == STATUS_DEAD:
            return self.errorInfo("角色已死亡")
        if gamecard.status == STATUS_PERMANENTINSANE:
            return self.errorInfo("角色已永久疯狂")

        if dicename.encode('utf-8').isalpha():
            dicename = dicename.upper()

        # 找卡完成,开始检定
        test = 0
        if dicename == "侦察":
            dicename = "侦查"
        if dicename in gamecard.skill.allskills():
            test = gamecard.skill.get(dicename)
        elif dicename in gamecard.interest.allskills():
            test = gamecard.interest.get(dicename)
        elif dicename == "母语":
            test = gamecard.data.EDU
        elif dicename == "闪避":
            test = gamecard.data.DEX // 2

        elif dicename in gamecard.data.alldatanames:
            test = gamecard.data.__dict__[dicename]
        elif dicename == "力量":
            dicename = "STR"
            test = gamecard.data.STR
        elif dicename == "体质":
            dicename = "CON"
            test = gamecard.data.CON
        elif dicename == "体型":
            dicename = "SIZ"
            test = gamecard.data.SIZ
        elif dicename == "敏捷":
            dicename = "DEX"
            test = gamecard.data.DEX
        elif dicename == "外貌":
            dicename = "APP"
            test = gamecard.data.APP
        elif dicename == "智力" or dicename == "灵感":
            dicename = "INT"
            test = gamecard.data.INT
        elif dicename == "意志":
            dicename = "POW"
            test = gamecard.data.POW
        elif dicename == "教育":
            dicename = "EDU"
            test = gamecard.data.EDU
        elif dicename == "幸运":
            dicename = "LUCK"
            test = gamecard.data.LUCK

        elif dicename in self.skilllist:
            test = self.skilllist[dicename]

        elif dicename[:2] == "暗骰" and (isint(dicename[2:])
                                       or len(dicename) == 2):
            if len(dicename) != 2:
                test = int(dicename[2:])
            else:
                test = 50

        else:  # HIT BAD TRAP
            return self.errorInfo("输入无效")

        # 将所有检定修正相加
        test += gamecard.tempstatus.GLOBAL
        if gamecard.hasstatus(dicename):
            test += gamecard.getstatus(dicename)
        test += tpcheck

        if test < 1:
            test = 1
        testval = dicemdn(1, 100)[0]
        rttext = dicename + " 检定/出目:" + str(test) + "/" + str(testval) + " "

        greatsuccessrule = gp.rule.greatsuccess
        greatfailrule = gp.rule.greatfail

        if (test < 50 and testval >= greatfailrule[2]
                and testval <= greatfailrule[3]) or (
                    test >= 50 and testval >= greatfailrule[0]
                    and testval <= greatfailrule[1]):
            rttext += "大失败"
        elif (test < 50 and testval >= greatsuccessrule[2]
              and testval <= greatsuccessrule[3]) or (
                  test >= 50 and testval >= greatsuccessrule[0]
                  and testval <= greatsuccessrule[1]):
            rttext += "大成功"
        elif testval > test:
            rttext += "失败"
        elif testval > test // 2:
            rttext += "普通成功"
        elif testval > test // 5:
            rttext += "困难成功"
        else:
            rttext += "极难成功"

        if dicename == "心理学" or dicename[:2] == "暗骰":
            if gp.kp is None:
                return self.errorInfo("本群没有KP,请先添加一个KP再试!")

            self.reply(dicename + " 检定/出目:" + str(test) + "/???")
            self.sendto(gp.kp, rttext)
        else:
            self.reply(rttext)

        return True
Ejemplo n.º 19
0
    def sancheck(self, update: Update, context: CallbackContext) -> bool:
        """进行一次sancheck,格式如下:
        `/sancheck checkpass/checkfail`"""

        if isprivate(update):
            return self.errorInfo("在游戏中才能进行sancheck。")

        if len(context.args) == 0:
            return self.errorInfo("需要参数", True)

        checkname = context.args[0]
        if checkname.find("/") == -1:
            return self.errorInfo("将成功和失败的扣除点数用/分开。")

        checkpass, checkfail = checkname.split(sep='/', maxsplit=1)
        if not isadicename(checkpass) or not isadicename(checkfail):
            return self.errorInfo("无效输入")

        gp = self.forcegetgroup(update)

        if gp.game is None:
            return self.errorInfo("找不到游戏", True)

        pl = self.forcegetplayer(update)
        # KP 进行
        if pl == gp.kp:
            card1 = gp.game.kpctrl
            if card1 is None:
                return self.errorInfo("请先用 /switchgamecard 切换到你的卡")
        else:  # 玩家进行
            card1 = self.findcardfromgame(gp.game, pl)
            if card1 is None:
                return self.errorInfo("找不到卡。")

        rttext = "理智:检定/出目 "
        sanity = card1.attr.SAN
        check = dicemdn(1, 100)[0]
        rttext += str(sanity) + "/" + str(check) + " "
        greatfailrule = gp.rule.greatfail
        if (sanity < 50 and check >= greatfailrule[2]
                and check <= greatfailrule[3]) or (
                    sanity >= 50 and check >= greatfailrule[0]
                    and check <= greatfailrule[1]):  # 大失败
            rttext += "大失败"
            anstype = "大失败"
        elif check > sanity:  # check fail
            rttext += "失败"
            anstype = "失败"
        else:
            rttext += "成功"
            anstype = ""

        rttext += "\n损失理智:"
        sanloss, m, n = 0, 0, 0

        if anstype == "大失败":
            if isint(checkfail):
                sanloss = int(checkfail)
            else:
                t = checkfail.split("+")
                for tt in t:
                    if isint(tt):
                        sanloss += int(tt)
                    else:
                        ttt = tt.split('d')
                        sanloss += int(ttt[0]) * int(ttt[1])

        elif anstype == "失败":
            if isint(checkfail):
                sanloss = int(checkfail)
            else:
                m, n = checkfail.split("d", maxsplit=1)
                m, n = int(m), int(n)
                sanloss = int(sum(dicemdn(m, n)))

        else:
            if isint(checkpass):
                sanloss = int(checkpass)
            else:
                m, n = checkpass.split("d", maxsplit=1)
                m, n = int(m), int(n)
                sanloss = int(sum(dicemdn(m, n)))

        card1.attr.SAN -= sanloss
        rttext += str(sanloss) + "\n"
        if card1.attr.SAN <= 0:
            card1.attr.SAN = 0
            card1.status = STATUS_PERMANENTINSANE
            rttext += "陷入永久疯狂,快乐撕卡~\n"

        elif sanloss > (card1.attr.SAN + sanloss) // 5:
            rttext += "一次损失五分之一以上理智,进入不定性疯狂状态。\n"
            # TODO 处理角色的疯狂状态
        elif sanloss >= 5:
            rttext += "一次损失5点或以上理智,可能需要进行智力(灵感)检定。\n"

        self.reply(rttext)
        card1.write()
        return True
Ejemplo n.º 20
0
    def discard(self, update: Update, context: CallbackContext) -> bool:
        """该指令用于删除角色卡。
        通过识别卡中`discard`是否为`True`来判断是否可以删除这张卡。
        如果`discard`为`False`,需要玩家向KP申请,让KP修改`discard`属性为`True`。

        指令格式如下:
        `/discard (--groupid_1/--cardid_1 --groupid_2/--cardid_2 ...)`。
        可以一次输入多个群或卡id来批量删除。

        无参数时,如果只有一张卡可以删除,自动删除那张卡。
        否则,会创建一组按钮来让玩家选择要删除哪张卡。

        有参数时,
        若其中一个参数为群id(负数),则删除该群内所有可删除的卡。
        若其中一个参数为卡id,删除对应的那张卡。
        找不到参数对应的卡时,该参数会被忽略。"""

        if isgroup(update):
            return self.errorInfo("发送私聊消息删除卡。")

        pl = self.getplayer(update)  # 发送者
        if pl is None:
            return self.errorInfo("找不到可删除的卡。")

        if len(context.args) > 0:
            # 先处理context.args
            if any(not isint(x) for x in context.args):
                return self.errorInfo("参数需要是整数")
            nargs = list(map(int, context.args))

            discards = self.findDiscardCardsWithGpidCdid(pl, nargs)

            # 求args提供的卡id与可删除的卡id的交集

            if len(discards) == 0:  # 交集为空集
                return self.errorInfo("输入的(群/卡片)ID均无效。")

            if len(discards) == 1:
                card = discards[0]
                rttext = "删除卡:"+str(card.getname())
                rttext += "删除操作不可逆。"
                self.reply(rttext)
            else:
                self.reply(
                    "删除了"+str(len(discards))+"张卡片。\n删除操作不可逆。")

            for card in discards:
                self.cardpop(card)
            return True

        # 计算可以discard的卡有多少
        discardgpcdTupleList = self.findAllDiscardCards(pl)
        if len(discardgpcdTupleList) > 1:  # 创建按钮,接下来交给按钮完成
            rtbuttons: List[List[InlineKeyboardButton]] = [[]]

            for card in discardgpcdTupleList:
                if len(rtbuttons[len(rtbuttons)-1]) == 4:
                    rtbuttons.append([])
                cardname = card.getname()
                rtbuttons[len(rtbuttons)-1].append(InlineKeyboardButton(cardname,
                                                                        callback_data="discard "+str(card.id)))

            rp_markup = InlineKeyboardMarkup(rtbuttons)

            self.reply("请点击要删除的卡片:", reply_markup=rp_markup)
            self.workingMethod[self.lastchat] = BUTTON_DISCARD
            return True

        if len(discardgpcdTupleList) == 1:
            card = discardgpcdTupleList[0]

            rttext = "删除卡:"+card.getname()
            rttext += "\n删除操作不可逆。"
            self.reply(rttext)

            self.cardpop(card)
            return True

        # 没有可删除的卡
        return self.errorInfo("找不到可删除的卡。")
Ejemplo n.º 21
0
    def showcard(self, update: Update, context: CallbackContext) -> bool:
        """显示某张卡的信息。

        `/showcard --cardid (card/--attrname)`: 显示卡id为`cardid`的卡片的信息。
        如果第二个参数是`card`,显示整张卡;否则,显示这一项数据。
        如果第二个参数不存在,显示卡片基本信息。
        群聊时使用该指令,优先查看游戏内的卡片。

        显示前会检查发送者是否有权限显示这张卡。在这些情况下,无法显示卡:

        群聊环境:显示非本群的卡片,或者显示本群的type不为PL的卡片;

        私聊环境:显示没有查看权限的卡片。"""

        if len(context.args) == 0:
            return self.errorInfo("需要参数")
        if not isint(context.args[0]) or int(context.args[0]) < 0:
            return self.errorInfo("卡id参数无效", True)
        cdid = int(context.args[0])

        rttext: str = ""
        cardi: Optional[GameCard] = None

        if isgroup(update):
            cardi = self.getgamecard(cdid)
            if cardi is not None:
                rttext = "显示游戏内的卡片\n"

        if cardi is None:
            cardi = self.getcard(cdid)

            if cardi is None:
                return self.errorInfo("找不到这张卡")

        if rttext == "":
            rttext = "显示游戏外的卡片\n"

        # 检查是否有权限
        if isprivate(update):

            pl = self.forcegetplayer(update)

            if self.checkaccess(pl, cardi) & CANREAD == 0:
                return self.errorInfo("没有权限")
        else:
            if (cardi.groupid != -1 and cardi.group !=
                    self.forcegetgroup(update)) or cardi.type != PLTYPE:
                return self.errorInfo("没有权限", True)

        # 开始处理
        if len(context.args) >= 2:
            if context.args[1] == "card":
                self.reply(rttext + str(cardi))
            else:
                ans = cardi.show(context.args[1])
                if ans == "找不到该属性":
                    return self.errorInfo(ans)

                self.reply(rttext + ans)
            return True

        # 显示基本属性
        self.reply(rttext + cardi.basicinfo())
        return True
Ejemplo n.º 22
0
    def showkp(self, update: Update, context: CallbackContext) -> bool:
        """这一指令是为KP设计的。不能在群聊中使用。

        `/showkp game --groupid`: 显示发送者在某个群主持的游戏中所有的卡
        `/showkp card`: 显示发送者作为KP控制的所有卡
        `/showkp group --groupid`: 显示发送者是KP的某个群内的所有卡"""

        if isgroup(update):
            return self.errorInfo("使用该指令请发送私聊消息", True)

        if len(context.args) == 0:
            return self.errorInfo("需要参数")

        arg = context.args[0]
        if arg == "group":
            kp = self.forcegetplayer(update)
            # args[1] should be group id
            if len(context.args) < 2:
                return self.errorInfo("需要群ID")
            gpid = context.args[1]
            if not isint(gpid) or int(gpid) >= 0:
                return self.errorInfo("无效ID")

            gpid = int(gpid)
            if gpid < 0 or self.getgp(gpid) is None or self.getgp(
                    gpid).kp != kp:
                return self.errorInfo("这个群没有卡或没有权限")

            gp: Group = self.getgp(gpid)
            ans: List[GameCard] = []
            for card in kp.cards.values():
                if card.group != gp:
                    continue
                ans.append(card)

            if len(ans) == 0:
                return self.errorInfo("该群没有你的卡")

            for i in ans:
                self.reply(str(i))
                time.sleep(0.2)
            return True

        if arg == "game":
            kp = self.forcegetplayer(update)

            if len(context.args) < 2:
                return self.errorInfo("需要群ID")
            gpid = context.args[1]
            if not isint(gpid) or int(gpid) >= 0:
                return self.errorInfo("无效群ID")

            gp = self.getgp(gpid)
            if gp is None or (gp.game is None and gp.pausedgame is None):
                return self.errorInfo("没有找到游戏")

            if gp.kp != kp:
                return self.errorInfo("你不是这个群的kp")

            game = gp.game if gp.game is not None else gp.pausedgame

            hascard = False
            for i in game.cards.values():
                if i.player != kp:
                    continue
                hascard = True
                self.reply(str(i))
                time.sleep(0.2)

            return True if hascard else self.errorInfo("你没有控制的游戏中的卡")

        if arg == "card":
            kp = self.forcegetplayer(update)

            hascard = False
            for card in kp.cards.values():
                if card.group.kp != kp:
                    continue
                hascard = True
                self.reply(str(card))
                time.sleep(0.2)

            return True if hascard else self.errorInfo("你没有控制NPC卡片")

        return self.errorInfo("无法识别的参数")
Ejemplo n.º 23
0
    def addcard(self, update: Update, context: CallbackContext) -> bool:
        """使用已有信息添加一张卡片,模板使用的是NPC/怪物模板。指令格式如下:

        `/addcard --attr_1 --val_1 --attr_2 --val_2 …… --attr_n -- val_n`,
        其中`attr`是卡的直接属性或子属性。

        卡的属性只有三种类型的值:`int`, `str`, `bool`,其他类型暂不支持用本指令。
        函数会自动判断对应的属性是什么类型,其中`bool`类型`attr`对应的`val`只能是`true`, `True`, `false`, `False`之一。

        不可以直接添加tempstatus这个属性。

        如果需要添加主要技能点数,用mainpoints作为`attr`,兴趣技能点则用intpoints,清不要使用points。

        如果要添加特殊技能,比如怪物的技能,请令`attr`为`specialskill`,`val`为`特殊技能名:技能值`。
        技能值是正整数,技能名和技能值用英文冒号分开。

        `name`和背景信息不支持空格,如果要设置这一项信息,需要之后用`/setbkg`来修改,所以尽量不要用该指令设置背景信息。

        如果遇到无法识别的属性,将无法创建卡片。
        参数中,必须的`attr`之一为`groupid`,如果找不到`groupid`将无法添加卡片。
        `playerid`会自动识别为发送者,无需填写`playerid`。
        指令使用者是KP的情况下,才可以指定`playerid`这个属性,否则卡片无效。
        给定`id`属性的话,在指定的卡id已经被占用的时候,会重新自动选取。"""
        if isgroup(update):
            return self.errorInfo("向我发送私聊消息来添加卡", True)
        if len(context.args) == 0:
            return self.errorInfo("需要参数")
        if (len(context.args)//2)*2 != len(context.args):
            self.reply("参数长度应该是偶数")

        t = templateNewCard()
        # 遍历args获取attr和val
        mem: List[str] = []
        for i in range(0, len(context.args), 2):
            argname: str = context.args[i]
            if argname in mem:
                return self.errorInfo(argname+"属性重复赋值")
            mem.append(argname)
            argval = context.args[i+1]

            if argname == "specialskill":
                skillname, skillval = argval.split(":")
                if not isint(skillval) or int(skillval) <= 0:
                    return self.errorInfo("技能值应该是正整数")
                t["skill"]["skills"][skillname] = int(skillval)
                continue

            if argname == "points":
                return self.errorInfo("points应指明是mainpoints还是intpoints")

            if argname == "mainpoints":
                argname = "points"
                dt = t["skill"]
            elif argname == "intpoints":
                argname = "points"
                dt = t["interest"]

            dt = findattrindict(t, argname)
            if not dt:  # 可能是技能,否则返回
                if argname in self.skilllist or argname == "母语" or argname == "闪避":
                    if not isint(argval) or int(argval) <= 0:
                        return self.errorInfo("技能值应该是正整数")

                    dt: dict = t["skill"]["skills"]
                    dt[argname] = 0  # 这一行是为了防止之后判断类型报错
                else:
                    return self.errorInfo("属性 "+argname+" 在角色卡模板中没有找到")

            if isinstance(dt[argname], dict):
                return self.errorInfo(argname+"是dict类型,不可直接赋值")

            if type(dt[argname]) is bool:
                if argval == "false" or argval == "False":
                    argval = False
                elif argval == "true" or argval == "True":
                    argval = True
                if not type(argval) is bool:
                    return self.errorInfo(argname+"应该为bool类型")
                dt[argname] = argval

            elif type(dt[argname]) is int:
                if not isint(argval):
                    return self.errorInfo(argname+"应该为int类型")
                dt[argname] = int(argval)

            else:
                dt[argname] = argval
        # 参数写入完成
        # 检查groupid是否输入了
        if t["groupid"] == 0:
            return self.errorInfo("需要groupid!")

        # 检查是否输入了以及是否有权限输入playerid
        pl = self.forcegetplayer(update)
        if not self.searchifkp(pl):
            if t["playerid"] != 0 and t["playerid"] != pl.id:
                return self.errorInfo("没有权限设置非自己的playerid")
            t["playerid"] = getchatid(update)
        else:
            if t["groupid"] not in pl.kpgroups and t["playerid"] != 0 and t["playerid"] != pl.id:
                return self.errorInfo("没有权限设置非自己的playerid")
            if t["playerid"] == 0:
                t["playerid"] = pl.id

        # 生成成功
        card1 = GameCard(t)
        # 添加id

        if "id" not in context.args or card1.id < 0 or card1.id in self.allids:
            self.reply("输入了已被占用的id,或id未设置,或id无效。自动获取id")
            card1.id = self.getoneid()
        # 生成衍生数值
        card1.generateOtherAttributes()
        # 卡检查
        rttext = card1.check()
        if rttext != "":
            self.reply(
                "卡片添加成功,但没有通过开始游戏的检查。")
            self.reply(rttext)
        else:
            self.reply("卡片添加成功")

        return True if self.addonecard(card1) else self.errorInfo("卡id重复")
Ejemplo n.º 24
0
    def newcard(self, update: Update, context: CallbackContext) -> bool:
        """随机生成一张新的角色卡。需要一个群id作为参数。
        只接受私聊消息。

        如果发送者不是KP,那么只能在一个群内拥有最多一张角色卡。

        如果不知道群id,请先发送`/getid`到群里获取id。

        `/newcard`提交创建卡请求,bot会等待你输入`groupid`。
        `/newcard --groupid`新建一张卡片,绑定到`groupid`对应的群。
        `/newcard --cardid`新建一张卡片,将卡片id设置为`cardid`,`cardid`必须是非负整数。
        `/newcard --groupid --cardid`新建一张卡片,绑定到`groupid`对应的群的同时,将卡片id设置为`cardid`。

        当指定的卡id已经被别的卡占用的时候,将自动获取未被占用的id。

        当生成时有至少三项基础属性低于50时,可以使用`/discard`来放弃并删除这张角色卡。
        创建新卡之后,当前控制卡片会自动切换到新卡,详情参见
        `/help switch`。

        角色卡说明
        一张角色卡具有:
        `groupid`,`id`,`playerid`基本信息。
        STR,CON,SIZ,DEX,APP,INT,EDU,LUCK基本属性;
        职业、姓名、性别、年龄;
        技能信息;
        背景故事(描述,重要之人,重要之地,珍视之物,特质,受过的伤,恐惧之物,神秘学物品,第三类接触);
        检定修正值;
        物品,财产;
        角色类型(PL,NPC);
        是否可以被删除;
        状态(存活,死亡,疯狂等)。"""

        gpid: int = None
        gp: Optional[Group] = None
        newcdid: Optional[int] = None

        if isgroup(update):
            # 先检查是否有该玩家信息
            rtbutton = [[InlineKeyboardButton(
                text="跳转到私聊", callback_data="None", url="t.me/"+self.bot.username)]]
            rp_markup = InlineKeyboardMarkup(rtbutton)
            if self.getplayer(update) is None:
                self.reply("请先开启与bot的私聊", reply_markup=rp_markup)
                return True

            if len(context.args) > 0:
                if not isint(context.args[0]) or int(context.args[0]) < 0:
                    return self.errorInfo("参数无效")

            gpid = getchatid(update)
            gp = self.forcegetgroup(gpid)
            if len(context.args) > 0:
                newcdid = int(context.args[0])

        elif len(context.args) > 0:
            msg = context.args[0]

            if not isint(msg):
                return self.errorInfo("输入无效")

            if int(msg) >= 0:
                newcdid = int(msg)
            else:
                gpid = int(msg)
                gp = self.forcegetgroup(gpid)
                if len(context.args) > 1:
                    if not isint(context.args[1]) or int(context.args[1]) < 0:
                        return self.errorInfo("输入无效")
                    newcdid = int(context.args[1])

        if gp is None:
            self.reply(
                "准备创建新卡。\n如果你不知道群id,在群里发送 /getid 即可创建角色卡。\n你也可以选择手动输入群id,请发送群id:")
            if newcdid is None:
                self.addOP(getchatid(update), "newcard " +
                           str(update.message.message_id))
            else:
                self.addOP(getchatid(update), "newcard " +
                           str(update.message.message_id)+" "+str(newcdid))
            return True

        # 检查(pl)是否已经有卡
        pl = self.forcegetplayer(update)
        plid = pl.id
        if self.hascard(plid, gpid) and pl != gp.kp:
            return self.errorInfo("你在这个群已经有一张卡了!")

        # 符合建卡条件,生成新卡
        # gp is not None
        assert(gpid is not None)

        remsgid = None
        if isprivate(update):
            remsgid = update.message.message_id
        else:
            assert rp_markup
            self.reply("建卡信息已经私聊发送", reply_markup=rp_markup)

        return self.getnewcard(remsgid, gpid, plid, newcdid)
Ejemplo n.º 25
0
    def switchgamecard(self, update: Update, context: CallbackContext):
        """用于KP切换游戏中进行对抗时使用的NPC卡片。

        (仅限私聊时)`/switchgamecard --groupid`:创建按钮,让KP选择要用的卡。
        (私聊群聊皆可)`/switchgamecard --cardid`:切换到id为cardid的卡并控制。"""

        if len(context.args) == 0:
            return self.errorInfo("需要参数")

        if not isint(context.args[0]):
            return self.errorInfo("参数无效")

        pl = self.forcegetplayer(update)
        iid = int(context.args[0])
        if iid >= 0:
            cdid = iid
            if cdid not in pl.gamecards:
                return self.errorInfo("你没有这个id的游戏中的卡")

            card = pl.gamecards[cdid]
            game: GroupGame = card.group.game if card.group.game is not None else card.group.pausedgame
            assert (game is not None)
            if game.kp != pl:
                return self.errorInfo("你不是该卡对应群的kp")
            game.kpctrl = card
            self.reply("切换成功")
            game.write()
            return True

        gpid = iid

        if isgroup(update):
            return self.errorInfo("请直接指定要切换的卡id,或者向bot发送私聊消息切换卡!")

        gp = self.getgp(gpid)
        if gp is None:
            return self.errorInfo("找不到该群")

        game = gp.game if gp.game is not None else gp.pausedgame
        if game is None:
            return self.errorInfo("该群没有在进行游戏")
        if game.kp != pl:
            return self.errorInfo("你不是kp")

        rtbuttons = [[]]
        for card in game.cards.values():
            if card.player != pl:
                continue

            if len(rtbuttons[len(rtbuttons) - 1]) == 4:
                rtbuttons.append([])

            rtbuttons[len(rtbuttons) - 1].append(
                InlineKeyboardButton(card.getname(),
                                     callback_data="switchgamecard " +
                                     str(card.id)))

        rp_markup = InlineKeyboardMarkup(rtbuttons)
        self.reply("请选择要切换控制的卡:", reply_markup=rp_markup)
        self.workingMethod[self.lastchat] = BUTTON_SWITCHGAMECARD
        # 交给按钮来完成
        return True
Ejemplo n.º 26
0
    def hp(self, update: Update, context: CallbackContext) -> bool:
        """修改HP。KP通过回复某位PL消息并在回复消息中使用本指令即可修改对方卡片的HP。
        回复自己的消息,则修改kp当前选中的游戏卡。
        或者,也可以使用@用户名以及用玩家id的方法选中某名PL,但请不要同时使用回复和用户名。
        使用范例:
        `/hp +1d3`:恢复1d3点HP。
        `/hp -2`:扣除2点HP。
        `/hp 10`:将HP设置为10。
        `/hp @username 12`:将用户名为username的玩家HP设为12。
        下面的例子是无效输入:
        `/hp 1d3`:无法将HP设置为一个骰子的结果,恢复1d3生命请在参数前加上符号`+`,扣除同理。
        在生命变动的情况下,角色状态也会同步地变动。"""

        if isprivate(update):
            return self.errorInfo("游戏中才可以修改HP。")
        gp = self.forcegetgroup(update)
        kp = self.forcegetplayer(update)
        if gp.kp != kp:
            return self.errorInfo("没有权限", True)

        if len(context.args) == 0:
            return self.errorInfo("需要指定扣除的HP", True)

        chp: str = context.args[0]
        game = gp.game
        if game is None:
            return self.errorInfo("找不到进行中的游戏", True)

        rppl = self.getreplyplayer(update)
        if update.message.reply_to_message is not None:
            rpmsgid = update.message.reply_to_message.message_id
        else:
            rpmsgid = update.message.message_id

        if rppl is None:
            if len(context.args) < 2:
                return self.errorInfo("请用回复或@用户名的方式来选择玩家改变HP")
            if not isint(context.args[0]) or int(context.args[0]) < 0:
                return self.errorInfo("参数无效")
            rppl = self.getplayer(int(context.args[0]))
            if rppl is None:
                return self.errorInfo("指定的用户无效")
            chp = context.args[1]

        if rppl != kp:
            cardi = self.findcardfromgame(game, rppl)
        else:
            cardi = game.kpctrl

        if cardi is None:
            return self.errorInfo("找不到这名玩家的卡。")

        if chp[0] == "+" or chp[0] == "-":
            if len(chp) == 1:
                return self.errorInfo("参数无效", True)

            # 由dicecalculator()处理。减法时,检查可能的括号导致的输入错误
            if chp[0] == '-' and chp[1] != '(' and (chp[1:].find('+') != -1 or chp[1:].find('-') != -1):
                return self.errorInfo("当第一个减号的后面是可计算的骰子,且存在加减法时,请在第一个符号之后使用括号")

            try:
                diceans = dicecalculator(chp[1:])
            except Exception:
                return self.errorInfo("参数无效", True)

            if diceans < 0:
                return self.errorInfo("骰子的结果为0,生命值不修改")

            chp = chp[0]+str(diceans)
        else:
            # 直接修改生命为目标值的情形。不支持dicecalculator(),仅支持整数
            if not isint(chp) or int(chp) > 100 or int(chp) < 0:
                return self.errorInfo("参数无效", True)

        if cardi.status == STATUS_DEAD:
            return self.errorInfo("该角色已死亡")

        originhp = cardi.attr.HP
        if chp[0] == "+":
            cardi.attr.HP += int(chp[1:])
        elif chp[0] == "-":
            cardi.attr.HP -= int(chp[1:])
        else:
            cardi.attr.HP = int(chp)

        hpdiff = cardi.attr.HP - originhp
        if hpdiff == 0:
            return self.errorInfo("HP不变,目前HP:"+str(cardi.attr.HP))

        if hpdiff < 0:
            # 承受伤害描述。分类为三种状态
            takedmg = -hpdiff
            if takedmg < cardi.attr.MAXHP//2:
                # 轻伤,若生命不降到0,不做任何事
                if takedmg >= originhp:
                    self.reply(
                        text="HP归0,角色昏迷", reply_to_message_id=rpmsgid)
            elif takedmg > cardi.attr.MAXHP:
                self.reply(
                    text="致死性伤害,角色死亡", reply_to_message_id=rpmsgid)
                cardi.status = STATUS_DEAD
            else:
                self.reply(text="角色受到重伤,请进行体质检定以维持清醒",
                           reply_to_message_id=rpmsgid)
                cardi.status = STATUS_SERIOUSLYWOUNDED
                if originhp <= takedmg:
                    self.reply(
                        text="HP归0,进入濒死状态", reply_to_message_id=rpmsgid)
                    cardi.status = STATUS_NEARDEATH

            if cardi.attr.HP < 0:
                cardi.attr.HP = 0

        else:
            # 恢复生命,可能脱离某种状态
            if cardi.attr.HP >= cardi.attr.MAXHP:
                cardi.attr.HP = cardi.attr.MAXHP
                self.reply(text="HP达到最大值", reply_to_message_id=rpmsgid)

            if hpdiff > 1 and originhp <= 1 and cardi.status == STATUS_NEARDEATH:
                self.reply(text="脱离濒死状态", reply_to_message_id=rpmsgid)
                cardi.status = STATUS_SERIOUSLYWOUNDED
        cardi.write()

        self.reply(text="生命值从"+str(originhp)+"修改为" +
                   str(cardi.attr.HP), reply_to_message_id=rpmsgid)
        return True