Example #1
0
class MonsterActor:
    _anim_warnings = collections.defaultdict(set)
    _ANIMS = None
    _ANIM_FILE = 'models/golem_animations.bam'

    def __init__(self, form, parent_node=None, weapon=None):
        self.form = form

        mesh = self.form.mesh

        if hasattr(builtins, 'base'):
            if self._ANIMS is None:
                anim_root = base.loader.load_model(self._ANIM_FILE)
                self.__class__._ANIMS = p3d.NodePath('anims')
                for bundle in anim_root.find_all_matches('**/+AnimBundleNode'):
                    bundle.reparent_to(self._ANIMS)
            model = base.loader.load_model('models/{}.bam'.format(
                mesh['bam_file']))
            root_node = model.find('**/{}'.format(mesh['root_node']))
            if root_node.is_empty():
                print(f"Warning: root node ({mesh['root_node']}) not found in "
                      f"bam_file ({mesh['bam_file']}) for {form.id}")
            else:
                self._ANIMS.instance_to(root_node)
            self._path = Actor(root_node)
            if weapon:
                self.update_weapon(weapon)
            self.play_anim('idle', loop=True)
            if parent_node:
                self._path.reparent_to(parent_node)
        else:
            self._path = Actor()

    def update_weapon(self, weapon):
        if isinstance(weapon, str):
            gdb = gamedb.get_instance()
            weapon = gdb['weapons'][weapon]

        meshname = weapon.mesh['root_node']
        if meshname == '':
            return
        weapon_joint = self._path.expose_joint(None, 'modelRoot', 'weapon')
        modelroot = base.loader.load_model('models/{}.bam'.format(
            weapon.mesh['bam_file']))
        mesh = modelroot.find(f'**/{meshname}')
        if mesh.is_empty():
            print(f'Warning: could not find weapon {meshname}')
            modelroot.ls()
            return
        elif weapon_joint is None:
            print(f'Warning: could not find weapon joint on {self.form.name}')
            return

        # Update weapon transform
        weaponxf = self.form.weapon_offset
        pos, hpr, scale = weaponxf['position'][:], weaponxf[
            'hpr'][:], weaponxf['scale'][:]
        pos = [i * 0.1 for i in pos]
        pos[1] += 0.4
        mesh.set_pos(mesh.get_pos() + p3d.LVector3(*pos))
        mesh.set_hpr(*hpr)
        scale = [
            scale[idx] / inv
            for idx, inv in enumerate(weapon_joint.get_scale())
        ]
        mesh.set_scale(*scale)
        mesh.instance_to(weapon_joint)

    def __getattr__(self, name):
        return getattr(self._path, name)

    def __estattr__(self, name, value):
        return setattr(self._path, name, value)

    @property
    def as_nodepath(self):
        return self._path

    def _anim_warning(self, anim):
        if isinstance(anim, str):
            baseanim = anim
        else:
            baseanim = anim[-1]
        if baseanim not in self._anim_warnings[self.form.id]:
            print(f'Warning: {self.form.name} is missing an animation: {anim}')
            self._anim_warnings[self.form.id].add(baseanim)

    def play_anim(self, anim, *, loop=False):
        self._path.stop()
        mapped_anim = self.get_anim(anim)
        if mapped_anim is None:
            self._anim_warning(anim)
            return
        if loop:
            self._path.loop(mapped_anim)
        else:
            self._path.play(mapped_anim)

    def get_anim(self, anims):
        if isinstance(anims, str):
            anims = [anims]

        for anim in anims:
            if anim in self._path.get_anim_names():
                return anim
            if anim in self.form.anim_map:
                return self.form.anim_map[anim]

        return None

    def actor_interval(self, anim):
        mapped_anim = self.get_anim(anim)
        if mapped_anim is None:
            self._anim_warning(anim)
            return intervals.Sequence()
        return self._path.actor_interval(mapped_anim)