游戏开发实战(三):Python复刻「崩坏星穹铁道」嗷呜嗷呜事务所---源码级解析该小游戏背后的算法与设计模式【纯原创】

发布于:2025-05-21 ⋅ 阅读:(24) ⋅ 点赞:(0)

本博文是奇美拉项目的完结篇章
前两个篇连接如下:
游戏开发实战(一):Python复刻「崩坏星穹铁道」嗷呜嗷呜事务所—源码级解析该小游戏背后的算法与设计模式【纯原创】-CSDN博客
游戏开发实战(二):Python复刻「崩坏星穹铁道」嗷呜嗷呜事务所—源码级解析该小游戏背后的算法与设计模式【纯原创】-CSDN博客
项目Github: https://github.com/Hanachirusat/Chimeras

奇美拉类

摸鱼仔,负能量,真老实,小坏蛋,压力怪

这些奇美拉并没有技能,平平无奇。我们对所有奇美拉的攻击力快照设置了property(只读)。我们不希望可以随意更改奇美拉快照,而只有每回合开始时调用相应的方法来固定攻击力快照。

#**摸鱼仔【3,2】**:平平无奇
class Myz(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=3
        self.__hp=2
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否登场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #观察模式,2观察所有1观察自身外的所有0只观察自身
        self.__skill=False
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        pass

#**负能量【7,3】:**平平无奇
class Fnl(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=7
        self.__hp=3
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否登场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0
        self.__skill=False
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        pass

#**真老实【1,16】:**平平无奇
class Zls(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=1
        self.__hp=16
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否登场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0
        self.__skill=False
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        pass

#**小坏蛋【3,5】:**平平无奇
class Xhd(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=3
        self.__hp=5
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否登场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0
        self.__skill=False
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        pass

##**压力怪【5,3】**:平平无奇
class Ylg(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=5
        self.__hp=3
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否登场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0
        self.__skill=False
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        pass

治愈师

每回合开始,使前一格同伴体力+1

治愈师进观察自身,如果变化的属性不是自己则直接返回。代码中的new_value-old_value)==1是防御性编程。用于辅助发现episode相关的逻辑错误(每回合episode只能+1)

#**治愈师【温暖,2,5】:**==每回合开始==,使前一格同伴`体力+1`
class Zys(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=2
        self.__hp=5
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否登场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        if changed_attr=="episode" and (new_value-old_value)==1 and self.__on:
            position=self.queue.members_list.index(self)+1
            #如果前面一格有队友
            if position<=len(self.queue.members_list-1):
                self.queue.members_list[position].hp+=1

小团体

每回合开始时,使前两格同伴效率+1,其余同伴体力-1

小团体的实际技能生效情况和自身位置相关。我们先对它前面两格(直到队头奇美拉)的同伴效率+1。其余体力同伴-1可能会触发其他奇美拉的被动技能,因此我们先记住其余奇美拉对象,然后再对这些奇美拉对象的hp-1。

class Xtt(ChimerasEntity):
    #
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=3
        self.__hp=3
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否登场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        if changed_attr=="episode" and (new_value-old_value)==1 and self.__on:
            position=self.queue.members_list.index(self)
            l=len(self.queue.members_list)
            #前两格同伴效率+1
            for i in range(position+1,min(position+3,l)):
                self.queue.members_list[i].atk += 1
            # 其余同伴体力-1,体力-1可能触发其他同伴的技能导致位置发生变化
            # w我们先记录需要-1的同伴
            chi_hp_decrease=[]
            for i in range(position):
                chi_hp_decrease.append(self.queue.members_list[i])
            for chi in chi_hp_decrease:
                chi.hp -= 1

画饼王

登场技判断需要保证旧值为False。回合技能需要保证再第二回合触发。

# **画饼王【话术,2,7】:**==登场后==所有同伴`效率+8 `,==若自身在场,每回合==使所有同伴`效率-2`
class Hbw(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=2
        self.__hp=7
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #登场后,所有同伴效率+8
        if changed_attr=="on" and old_value==False and self.__on:
            for i in self.queue.members_list:
                if i!=self:
                    i.atk+=8
        # 若自身在场,每回合所有同伴效率-2,这个应该从第二回合开始算,第二回合的时候1-》2.old_value应该为1
        if changed_attr=="episode" and (new_value-old_value)==1 and old_value>0 and self.__on:
            for i in self.queue.members_list:
                if i!=self:
                    i.atk-=2

平凡王

先记录场上所有无特性同伴的效率和体力,之后进行截断(上限为25)。注意:这里单词的实际意思可能是单个奇美拉的意思,因为登场技能只会发动一次,因此不存在技能发动单次的含义

# **平凡王【联合,7,7】:**==登场后==,获得场上`所有无特性同伴100%的效率和体力`(单次上限均为25)
class Pfw(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=7
        self.__hp=7
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #登场后,获得场上所有无特性同伴100%的效率和体力
        if changed_attr=="on" and old_value==False and self.__on:
            a,h=0,0
            for i in self.queue.members_list:
                if i!=self:
                    a+=min(i.atk,25)
                    h+=min(i.hp,25)
            #单次上限均为25
            self.__atk+=a
            self.__hp+=h

坏脾气

只有当前奇美拉不是最后一个奇美拉时才会发动技能

# **坏脾气【发作,AT:2,HP:9】:**==自身工作时==:使`后一格`同伴`体力-1`
class Hpq(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=2
        self.__hp=9
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #自身工作时
        if changed_attr=="work_num" and new_value>old_value and self.__on:
            #获得当前位置
            position=self.queue.members_list.index(self)
            #如果后面有同伴,则使后面同伴hp-1
            if position>0:
                self.queue.members_list[position-1].hp-=1

抗压包

使得同伴效率降增加并不会实际触发技能,因此可以直接操作,并不需要先记录需要加buff的奇美拉对象

#**抗压包【熟练,2,5】:**==自身体力降低时==使`前后一格`同伴`效率+1`
class Kyb(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=2
        self.__hp=5
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #自身体力降低时
        if changed_attr=="hp" and new_value<old_value and self.__on:
            #获得当前位置
            position=self.queue.members_list.index(self)
            l=len(self.queue.members_list.index(self))
            position_before=position+1
            position_after=position-1

            if position_before>=0 and position_before<l:
                self.queue.members_list[position_before].atk+=1
            if position_after >= 0 and position_after < l:
                self.queue.members_list[position_after].atk += 1

请假狂

# **请假狂【装病,2,7】:**==自身体力降低后==,与`后一格`同伴`交换位置`,并使自身`效率+2`
class Qjk(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=2
        self.__hp=7
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #自身体力降低时
        if changed_attr=="hp" and new_value<old_value and self.__on:
            #获得当前位置
            position=self.queue.members_list.index(self)
            #如果不在队列最后,则与后一格同伴交换位置,并使自身体力+2
            if position>0:
                self.queue.members_list[position-1],self.queue.members_list[position]=self.queue.members_list[position],self.queue.members_list[position-1]
                self.atk += 2

请假王

# **请假王【开摆,6,3】**:==自身体力降低后==,与`后一格`同伴`交换位置`,并使自身`体力+3`
class Qjw(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=6
        self.__hp=3
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #自身体力降低时
        if changed_attr == "hp" and new_value < old_value and self.__on:
            # 获得当前位置
            position = self.queue.members_list.index(self)
            # 如果不在队列最后,则与后一格同伴交换位置
            if position > 0:
                self.queue.members_list[position - 1], self.queue.members_list[position] = self.queue.members_list[position], self.queue.members_list[position - 1]
                self.hp += 3

内卷王

# **内卷王【激励,3,8】:**==自身完成工作时==:获得`效率+2`,`体力+3`
class Njw(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=3
        self.__hp=8
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #自身完成工作时,
        if changed_attr=="complete_work_num" and new_value>old_value and self.__on:
            self.atk+=2
            self.hp+=3

受气包

# **受气包【道歉,2,5】:** ==自身体力降低时==,使全局同伴`效率+1`
class Sqb(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=2
        self.__hp=5
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #自身体力降低时
        if changed_attr == "hp" and new_value < old_value and self.__on:
            for i in self.queue.members_list:
                if i!=self:
                    i.atk+=1

跑路侠

跑路侠累到后和后一个同伴一起逃离工作,根据前面提到的“规则”,我们不直接修改后一个同伴的生命,而是修改on属性为False,在每回合结束后,会把所有on属性为False的奇美拉清理出队列。此外,为了确保跑路侠hp小于0后一定离场(后续其他奇美拉对他加hp也不起作用),我们增加了一个hp-100的语句,为了防止这个语句继续调用跑路侠的技能,跑路下的技能判断条件为,旧的hp>0,新的hp<1(<=0)。(如果你还记得我们之前提到过的,在eval中用下划线修改私有属性并不会通知观察者,那么这里你就可以优雅的用self.__hp+=100)

# **跑路侠【怂恿,1,1】**:==自身累倒时==,和后一格同伴一起`逃离工作`,并使其他同伴`体力+8`
class Plx(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=1
        self.__hp=1
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #只监控自身的属性变化从而保证每回合只触发一次被动技能
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #自身累倒时
        if changed_attr == "hp" and new_value < 1 and old_vale>0 and self.__on:
            slef.__hp-=100
            position=self.queue.members_list.index(self)
            # 使得后一格同伴离场,不修改后一格同伴的生命值
            if position>0:
                self.queue.members_list[position-1].on=False
            # 使得其他同伴体力+8
            for i in range(len(self.queue.members_list)):
                if i<(position-1) or (i> position):
                    self.queue.members_list[i].hp+=8
                  

看乐子

正如上文提到的,对于同一个奇美拉,看乐子可能触发两次被动。这也是有道理的,奇美拉生命值小于0表示累倒了。但是同伴可以给他加buff,使她的hp>0,此时又充满活力了,所以可以再次工作累倒。

# **看乐子【围观,3,3】**:==同伴累到后==,自身`效率+2`,`体力+2`
class Klz(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=3
        self.__hp=3
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴累倒后
        if changed_attr == "hp" and new_value < 1 and old_vale>0 and self.__on:
            self.atk+=2
            self.hp+=2

背锅侠

# **背锅侠【接锅,3 ,6:**==同伴累倒时==,使该同伴`体力+10`,自身逃离工作
class Bgx(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=3
        self.__hp=6
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴累倒后
        if changed_attr == "hp" and new_value < 1 and old_vale>0 and self.__on:
            #同伴累到后体力为hp,不会出现负体力,但这里不要用等于10
            #可能会出现其他奇美拉也给他加血,并且先于该奇美拉。
            print(f"\t给{changed_obj.name}加buff,hp+10!")
            changed_obj.hp+=10
            #注意此时不能修改self.queue.remove_member,因为我们无法保证此时弱引用集合已经遍历完毕。
            #如果直接修改,可能会会导致集合在遍历过程中被修改了,因此我们需要保证所有的技能都执行完,才移除on属性为False的奇美拉。也就是在每回合最后才移除奇美拉。
            self.on=False

抢功劳

这里需要保证work的hp>0,道理也很简单,如果工作已经被完成了,尘埃落定,如何抢功劳呢?由于追加工作可能触发其他奇美拉的被动,因此我们先判断是否完成工作,然后再修改complete_work_num属性。

# **抢功劳【独占,15,2:** ==同伴工作时==:若自身效率》=剩余工作进度,则进行`追加工作`完成该任务
class Qgl(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=15
        self.__hp=2
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴工作时,进行追加工作不算
        if changed_attr == "work_num" and (new_value-old_value)==1 and self.__on:
            #如果回合开始时的效率足以完成任务
            if self.__fixed_atk>=self.queue.work.hp and self.queue.work.hp>0:
                #进行一次追加攻击
                self.queue.work.hp-=self.__fixed_atk
                if self.queue.work.hp < 1:
                    #完成工作也可能触发其他同伴的技能,因此需要最后执行
                    self.complete_work_num += 1
                #追加攻击可能触发其他同伴的技能,导致继续工作一次,因此需要最后执行
                self.append_work_num+=1

急先锋

# **急先锋【带头,2,5】:**==同伴工作或追加工作时==,自身与前一格同伴`交换位置`,并且`体力+6`
class Jxf(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=2
        self.__hp=5
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴工作或追加工作时,
        if (changed_attr =="work_num" or changed_attr=="append_work_num") and (new_value-old_value)==1:
            position=self.queue.members_list.index(self)
            #如果当前奇美拉不是第一个奇美拉
            if position<len(self.queue.members_list)-1:
                print(f"\t{self.name}与前一格奇美拉交换位置")
                #与前一格同伴交换位置
                self.queue.members_list[position],self.queue.members_list[position+1]=\
                self.queue.members_list[position+1],self.queue.members_list[position]
                self.hp+=6

说怪话

# **说怪话【暗讽,14,1】:**==同伴完成工作时==,使该同伴`效率+4 `并发表自己的意见
class Sgh(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=14
        self.__hp=1
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴完成工作时
        if changed_attr == "complete_work_num" and (new_value-old_value)==1 and self.__on:
            #使该同伴效率+4
            changed_obj.atk+=4
            #发表锐评
            print(f"{self.name}{changed_obj.name}发表了锐评")

帮倒忙

# **帮倒忙【捣乱,-1,5】:**==同伴工作时==,自身进行一次`追加工作`
class Bdm(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=-1
        self.__hp=5
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴工作时候
        if changed_attr =="work_num" and (new_value-old_value)==1 and self.__on:
            #自身进行追加攻击,工作小于0的时候也可以继续追加攻击获得buff
            self.queue.work.hp-=self.__fixed_atk
            print(f"\t{self.name}进行一次追加攻击!")
            #追加攻击后立刻判断是否完成工作
            if self.queue.work.hp < 1:
                self.complete_work_num += 1
            self.append_work_num+=1

小夸夸

# **小夸夸【鼓励,3,3】:**==同伴工作或追加工作时==,若同伴效率》5使其`效率+2 `
class Xkk(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=3
        self.__hp=3
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴工作或追加工作时
        if (changed_attr == "work_num" or changed_attr== "append_work_num") and (new_value-old_value)==1 and self.__on:
            #如果同伴效率>5,使效率+2
            if changed_obj.atk>5:
                #小夸夸监控所有队友的所有属性,因此修改atk也会进入小夸夸的eval但是不会执行任何语句。
                print(f"\t给{changed_obj.name}加buff,atk+2!")
                changed_obj.atk+=2

工作狂

# **工作狂【争先 6,10】:**==同伴工作或追加工作时==,进行一次等于自身50%效率的`追加工作`
class Gzk(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=6
        self.__hp=10
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴工作或追加工作时
        if (changed_attr == "work_num" or changed_attr== "append_work_num") and (new_value-old_value)==1 and self.__on:
            #进行一次相当于自身50%效率的追加攻击
            self.queue.work.hp-=(self.__fixed_atk/2)
            print(f"\t{self.name}进行一次追加攻击!")
            if self.queue.work.hp<1:
                self.complete_work_num+=1
            self.append_work_num+=1

职业经理

# **职业经理【自我驱动】:**==登场后==:使全体奇美拉`效率+3`,`体力+3`
class Zyjl(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=6
        self.__hp=10
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=0  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self!=changed_obj):
            return
        #同伴工作或追加工作时
        if changed_attr == "on" and old_value==False and self.__on:
            #使所有成员效率和体力都+3
            for i in self.queue.members_list:
                i.hp+=3
                i.atk+=3

严酷恶魔

# **严酷恶魔【不准停!】:**奇美拉==完成工作后==,使其`效率+5`
class Ykem(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=6
        self.__hp=10
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=1  #监控自己以外的所有同伴
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):
        if(self==changed_obj):
            return
        #同伴完成工作后
        if changed_attr == "complete_work_num" and (new_value-old_value)==1 and self.__on:
            #使其效率+5
            changed_obj.atk+=5

职场清流

# **职场清流【抚慰之心】:**==登场后==:全体奇美拉`效率+2`。奇美==拉追加工作==后:使其`效率+1`
class Zcql(ChimerasEntity):
    __observed_attrs__ = ('atk', 'hp','episode','on','work_num','append_work_num','mode','skill','complete_work_num')
    def __init__(self, name):
        super().__init__(name)
        self.name=name
        self.__atk=6
        self.__hp=10
        self.__episode=0  #每回合开始都会先增加回合数
        self.__on=False  #是否在场,True为登场,刚开始的时候为False
        self.__work_num=0  #工作次数
        self.__append_work_num=0  #追加攻击次数
        self.__mode=2  #监控自己及其所有奇美拉
        self.__skill=True
        self.__complete_work_num = 0
        self.__fixed_atk=0
    @property
    def fixed_atk(self):
        return self.__fixed_atk
    def set_fix_atk(self):
        self.__fixed_atk=self.__atk
    def eval(self, changed_obj, changed_attr, old_value, new_value):

        #登场后,全体奇美拉效率+2
        if changed_attr == "on" and old_value==False and changed_obj==self and self.__on:
            for i in self.queue.members_list:
                print(f"\t给{i.name}加buff,atk+2!")
                i.atk+=2
        #同伴追加攻击后,使其效率+1
        if changed_attr == "append_work_num" and (new_value-old_value)==1 and changed_obj!=self and self.__on:
            print(f"\t给{changed_obj.name}加buff,{changed_attr} atk+1!")
            changed_obj.atk+=1

开始工作吧小奇美拉

我们把工作也定义成一个类,该类中仅有攻击力和生命值属性,同时设置这两个属性的property描述符。我们把所有的工作逻辑都集成在奇美拉队列类中,我们在该类中新增三个方法

  1. work_once,选定第一个在场并且hp>0的奇美拉,使其进行一次工作
  2. work_episode,执行一个工作回合,在该函数中处理每回合工作前和工作后的一些信息。例如工作前增加回合数,固定攻击力。工作后判断奇美拉是否应该离场
  3. begin_work,该方法接受一个工作对象,然后循环调用work_episode直到完成工作或者所有奇美拉都累到了

Task类

class Task():
    def __init__(self,atk=5,hp=150):
        self._atk = atk
        self._hp = hp

    @property
    def atk(self):
        return self._atk
    @atk.setter
    def atk(self,value):
        self._atk=value

    @property
    def hp(self):
        return self._hp
    @hp.setter
    def hp(self,value):
        self._hp=value

begin_work:

class InteractionQueue:
    """管理相互观察的队列"""
	#......此处参见本系列的上一篇博文
def begin_work(self,work):
  self.work=work
  print("开始工作...")
  #奇美拉登场
  for i in self.members_list:
    i.on=True

    self.leader.on=True
    #1 更新回合数,固定攻击力
    #2 第一个奇美拉攻击(立即判断工作是否被完成)
    #3 其他奇美拉发动技能)
    #4 判断奇美拉是否离场(循环内判断),或者工作是否被完成(循环条件判断)
    while(work.hp>0):
      live=self.work_episode()
      if not live and work.hp>0:
        print("工作未完成,奇美拉都累坏了~")
        print("工作已全部完成~")

work_episode

class InteractionQueue:
    """管理相互观察的队列"""
	#......此处参见本系列的上一篇博文
def work_episode(self):
  self.episode += 1
  print(f"===开始第{self.episode}回合的工作===")
  #先处理episode+1的逻辑,这时可能会触发被动,这个被动可能加攻击力
  for chi in self.members_list:
    chi.episode+=1
    #开始工作前的最后时刻固定攻击力
    for chi in self.members_list:
      # 固定奇美拉攻击力
      chi.set_fix_atk()
      print("\t工作前:")
      print("\t", end="")
      for chi in self.members_list:
        print(f"{chi.name}[atk:{chi.atk},hp:{chi.hp}]", end="")
        print("\n",end="")
        print(f"\tWork[atk:{self.work.atk},hp:{self.work.hp}]")
        # 奇美拉开始工作
        self.work_once()
        print("\t请脑部工作画面呀QwQ")
        # 根据奇美拉的生命值判断是否应该离场,并触发离场被动
        for chi in self.members_list:
          if chi.hp<1:
            chi.on=False
            self.remove_member(chi)
            #防御性编程
            if chi.on==False:
              self.remove_member(chi)
              print("\t工作后:")
              print("\t", end="")
              for chi in self.members_list:
                print(f"{chi.name}[atk:{chi.atk},hp:{chi.hp}]", end="")
                print("\n",end="")
                print(f"\tWork[atk:{self.work.atk},hp:{self.work.hp}]")
                #如果本回合结束后奇美拉都离场了
                if len(self.members_list)==0:
                  return False
                else:
                  return True

work_once

class InteractionQueue:
    """管理相互观察的队列"""
	#......此处参见本系列的上一篇博文
def work_once(self):
  before_work_hp=self.work.hp
  work_chi=None
  for chi in reversed(self.members_list):
    if chi.hp>0 and chi.on:
      work_chi=chi
      break
      #开始工作,这时进触发自己hp降低的被动
      self.work.hp-=work_chi.atk
      after_work_hp=self.work.hp

      #开始触发技能,自身 体力降低,或同伴累倒后
      work_chi.hp-=self.work.atk
      #触发工作时和完成工作时的被动
      work_chi.work_num+=1
      if before_work_hp>0 and after_work_hp<1:
        work_chi.complete_work_num+=1

最后,给出我们非常简单的main函数,完结撒花,★,°:.☆( ̄▽ ̄)/$:.°★ 。🌸🌸🌸

import time
import unittest
from observer import InteractionQueue, Task
from chimeras import *

if __name__=="__main__":
    queue=InteractionQueue()
    jxf=Jxf("急先锋")
    queue.add_member(jxf)
    gzk=Gzk("工作狂")
    queue.add_member(gzk)
    bdm=Bdm("帮倒忙")
    queue.add_member(bdm)
    xkk=Xkk("小夸夸")
    queue.add_member(xkk)
    bgx=Bgx("背锅侠")
    queue.add_member(bgx)
    zcql=Zcql("职场清流")
    queue.add_leader(zcql)
    work=Task(atk=5,hp=80)

    queue.begin_work(work)

没想到吧,我还在!(优化)

**不同的奇美拉应该监控特定属性:**回忆一下我们的元类和基类的实现,__observed_attrs__存放的更像是subject的主题,也就是其他奇美拉关注的属性。那么对于同一个奇美拉,不同的奇美拉可能观察该奇美拉的不同属性,这就需要属性分类。

我们可以把属性进行分类(或者说分级),相应的我们把observer也进行分类(和前面属性的分类是一一对应的)。在属性变化的时候,根据属性的级别,遍历不同的observer集合,调用被动技能和该属性有关的奇美拉的eval方法,而不是调用所有奇美的eval方法。

有序弱引用:我们在queue中保存所有对象的强引用(list保存),此时我们必须手动调用queue的remove函数。如果我们打算在成员的析构函数中调用queue的remove函数就会出现一个奇怪的循环。如果我们忘记调用queue的remove_member函数,我们del成员,但是由于queue中保留着成员的引用,所以不会执行成员的析构函数。由于不会执行析构函数,所以queue中一直保留着成员的引用。解决办法可以采用有序弱引用。

我们需要自己基于weakref来实现一个有序弱引用类。这里给出基于有序字典来实现的思路:我们创建一个key为id(递增,)value为弱引用对象的有序字典。每次我们手动递增id。添加弱引用的时候需要判断该弱引用对象是否已经存在地点内。


网站公告

今日签到

点亮在社区的每一天
去签到