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)