This is a snippet from the code used to control an enemy. If you have any expertise in computer science concepts, you can probably recognize this as implementing a finite state machine (FSM). Lots of repetitive code. This is why enemies and NPCs in video games often use domain-specific langauges.
I did it the hard way.
def setup_state(self,*args,**kwargs): if self.state == "looking": self.to_feet() self.role = acting.HoldStill(self) elif self.state == "returning": self.to_feet() self.role = acting.MoveToPoint(self,*self.path.get_pointvec(0.0)) elif self.state == "patrolling": self.to_path(self.path,0.0) self.role = acting.Pace(self) elif self.state == "charging": self.to_feet() self.role = acting.Chase(self,game.lance,speed=10.0,nearthresh=0.5) elif self.state == "swatting": self.to_feet() self.role = acting.MeleeAttack(self,self.swat_choreo,"goblin_swat",duration=0.667) elif self.state == "recoiling": self.to_feet() self.role = acting.Recoil(self,*args,**kwargs) elif self.state == "dying": self.to_feet() self.role = acting.Shift(self,self.supine_choreo,0.5) self.weakness.targetable = False else: raise ValueError("unknown state %s" % self.state) def check_transitions(self): strikedir = self.strike self.strike = None if self.state == "looking": if self.life <= 0: self.transition("dying") elif strikedir is not None: self.transition("recoiling",strikedir) elif self.timer >= 0.4 and self.hero_is_swattable(): self.transition("swatting") elif self.timer >= 0.4 and self.hero_is_visible(): self.transition("charging") elif self.timer > 2.0: self.transition("returning") elif self.state == "returning": if self.life <= 0: self.transition("dying") elif strikedir is not None: self.transition("recoiling",strikedir) elif self.hero_is_swattable(): self.transition("swatting") elif self.hero_is_visible(): self.transition("charging") elif self.role.completed: self.transition("patrolling") elif self.state == "patrolling": if self.life <= 0: self.transition("dying") elif strikedir is not None: self.transition("recoiling",strikedir) elif self.hero_is_swattable(): self.transition("swatting") elif self.hero_is_visible(): self.transition("charging") elif self.state == "charging": if self.life <= 0: self.transition("dying") elif game.register["lance.life"] <= 0: self.transition("returning") elif strikedir is not None: self.transition("recoiling",strikedir) elif self.hero_is_swattable(): self.transition("swatting") elif self.state == "swatting": if self.life <= 0: self.transition("dying") elif strikedir is not None: self.transition("recoiling",strikedir) elif self.role.completed: self.transition("looking") elif self.state == "recoiling": if self.life <= 0: self.transition("dying") elif strikedir is not None: self.transition("recoiling",strikedir) elif self.role.completed: self.transition("looking") elif self.state == "dying": if self.timer > 5.0: game.scene.remove_object(self)
Edit: June 28, 2018: As I reread this I realized that it’s not actually a finite state machine: one of the states (“recoil”) is parameterized with a 3-D real-valued vector, which means it’s an infinite number of states. Though techincally speaking, since real numbers are discretized, it still has a finite number of states, but it’s not really an FSM in spirit.