Game content and rules are all in my mind while I'm designing the engine and the systems accompanying it. I always make sure my classes can work just fine in and of themselves regardless of what the data is.
A script is one example. It is designed for flexibility and ease of use. Your system should be robust enough so that it allows the game designer to 'create the rules'. Your engine is just running one of the systems that you designed. The 'rules' come from how the designer uses the system to accomplish the actual game logic.
So the more robust your script, the more control you hand to the designer and the less you have to hard-code the 'rules'.
I follow this method and it is very similar to Novalogic's WAC system.
1. Very limited amount of language elements.
2. Each function in the script is created to do exactly one thing and do it well.
3. Variables are allowed via specific script functions/keywords.
4. Functions usually require 2 params or 3 at most.
Check out Novalogic's NILE editor and Black Hawk Down (BHD) editors. The WAC language is awesome and quite easy to use and write for.
'This is a comment
'Move unit with ID to waypoint list 1
'Turn unit 90 degrees
'Set alert status to green
'Do a random one-time effect
'if this is true 1 out of 20 times
'and Variable at slot 1 is not 1
'and this event has never executed before
'do a flash of lighting
'rain to 100% in 20 seconds
'Set clouds to 100% in 60 seconds
if (Random(20) and VarNE(1,1) and Never())
'Do a random repeating effect
'If this is true 1 out of 30 times
'and Variable at slot 1 (rain in our script) is 1
'do 2 lightning flashes
'Doesn't make sense to lightning when no rain
if (Random(30) and VarEQ(1,1))
You can copy it at your leisure so long as you don't steal their stuff verbatim. But it's a good way to learn how to do this.
For the most part your game engine is ignorant of the game until you supply the data and the script. If the AI looks stupid, it's because it is stupid and only does basic reactions until the level editor breathes more life into it. This puts a lot of control into the hands of the designers and story people which is right where it should be.
My variable system is just a class that handles an array of 65535 values (DWORDS).
Some of the commands are similar to assembly language:
VarNE(A,B) - variable A NOT EQUAL to value B
VarEQ(A,B) - variable A EQUAL TO value B
VarA(A,B) - variable A ABOVE value B
VarB(A,B) - variable A BELOW value B
VarAE(A,B) - variable A ABOVE OR EQUAL to value B
VarBE(A,B) - variable A BELOW OR EQUAL to value B
VarSET(A,B) - variable A is set to value B
VarASSIGN(A,B) - variable A is set to value of variable B