There are tons of solutions for how to handle this. But one of my soap-boxes is that game data should be defined in a textual format, and be easy to edit (either by editing the text format, or with an easy UI tool).
For Halcyon, I decided to use a YAML format for enemy definitions. YAML is a data definition language, similar to XML or JSON. If you read about it, there's all sorts of reasons people say you shouldn't use it. But it has one important thing going for it: it's really easy to read and write. Which is what I need for this game.
- name: topgunner hp: 4 width: 16 height: 16 dmg: 1 on-collide: NORMAL on-offscreen: KILL update: enemyb_topgunnerUpdate animation: anim_topgunner frames: frames_topgunner init: enemyb_topgunnerInit - name: topgunnerBullet hp: 4 width: 4 height: 4 dmg: 1 on-collide: NORMAL on-offscreen: KILL on-shoot: NONE update: enemyb_8wayBulletUpdate animation: anim_shooterBullet frames: frames_shooterBullet flags: BULLET
I define my enemies in a YAML format, specifying both their numeric stats, the name of some animation data to display them, as well as various routines and properties to define their behavior. My build script then converts this YAML at compile-time into an assembly language file that defines arrays of data to represent each enemy. Symbol names (like enemyb_8wayBulletUpdate) get injected right into the generated assembly code, and the linker knows how to translate those into the addresses of the functions that they refer to.