Tutorial for code structure
Eskarn's tutorial for code structure
This is an introduction on how to code in XStoryPlayer The coding language is based off C++
I strongly recommend using Notepad ++ its awesome
I am in no way a master of this stuff but the tutorial should help
There will be no pictures and I'm not going to focus on story's but rather the code involved
Where to start
Each character in the game has a run.dat
<*state_run> res = true; case (state.dyn.me.do.state) { [RUN0] run0_state(); [RUN1] run1_state(); [RUN2] run2_state(); [RUN3] run3_state(); [RUN4] run4_state(); } </state_run>
This file keeps running the set state in this case it will be [RUN0] until changed and that will happen when the story progresses
If it did not keep running the function we would not be able to do things like timers and the whole game would just stop well the characters atleast
So we are currently running run0_state which is in another file
run0_state.dat
<run0_state> <- opening code goes in here </run0_state> <- closing
i think these would be called functions im not to sure but im gonna call em functions
Everything between the opening function line and the closing function line will run when we tell it to
Now we move onto cases
Cases go in functions and give us more control
Cases can be called whatever you want past the do and every case needs an opening bracket and a closing bracket { }
This tells the engine that the case is between the brackets Without a opening bracket the case would never have anything in it Without a closing bracket the case would never end
Both ending in errors
case (state.dyn.me.do.whateveryouwanttocallit) { <- opening bracket } <- closing bracket
So we have a case what now
Now we can add in i really dont know what to call em but im gonna go with innercase
case (state.dyn.me.do.whateveryouwanttocallit) { <- opening bracket [NONE] { <- opening bracket } <- closing bracket } <- closing bracket
Now we still need an opening bracket and a closing bracket { } to tell the innercase what belongs in it
This is where having a organised system to your code helps This examples a little bit pointless because there's only 2 opening brackets and 2 closing brackets
But when you have 50 opeing and 49 closing brackets its really hard to find the missing one
This is my system and it works for me and this code ill explain that a bit later
Here is an example of non orgnised code It's chaos but again its a small scale example some of my dunegon files are 4k lines
But as you can see its hard to tell where the brackets are and what is happening So lets clean it up
case (state.dyn.me.do.orgsystem) { [NONE] { [more code bits] { bla bla bla code bla code [even more code] ya more code } [wow more code] { bla code } } [NEXT] { [more code bits] { bla bla bla code bla code [even more code] ya more code } [wow more code] { bla code } } }
My system is below it looks a lot more cleaner and all the brackets line up so you can see if you are missing one and using notepad ++ it shows the connecting brackets
Now im using tabs to move the code across and this is not the best practice because some programming languages do not recognize tabs or they do different things and break
My brother who is a coder uses 3 spaces instead of tab but because this programming language does not care i'm using tabs
Moral of the story is to use what you are comfortable with and what works with the current programming language you are working on
case (state.dyn.me.do.orgsystem) { [NONE] { [more code bits] { bla bla bla code bla code [even more code] { ya more code } } [wow more code] { bla code } } [NEXT] { [more code bits] { bla bla bla code bla code [even more code] { ya more code } } [wow more code] { bla code } } }
So by now you should know how to structure your brackets { }
Now onto moving between innercases
We can call the innercases whatever we want so long as they are all in capitals
We are at [NONE] and we want to move to [NEXT]
So we will use
state.dyn.me.do.changingcases = NEXT; state.dyn.me.do.changingcases = BOOBIES1234; case (state.dyn.me.do.changingcases) { [NONE] { } [NEXT] { } [BOOBIES1234] { } }
Now we have semicolons ;;;;;;;;;
These say that the current line has ended
state.dyn.me.do.changingcases = NEXT;
And thats it do this Without the ;
state.dyn.me.do.changingcases = NEXT
This line never ends and will result in an error
Now to actually change the case we would need to trigger something so lets introduce timers
case (state.dyn.me.do.timersarefun) { [NONE] { do_set_timer(6); state.dyn.me.do.timersarefun = START; } [START] { loc.ts = GetTs(); [loc.ts < state.dyn.me.do.ts] return; state.dyn.me.do.timersarefun = START2; } [START2] { } }
First part of the timers are setting the time duration
do_set_timer(6);
do_set_timer is a function in another file and we are giving it the infomation of (6) this is seconds
You can add in custom timers but i will not cover this because most people wont need them
loc.ts = GetTs(); loc.
stands for local which means this is only stored inside this innercase
So
loc.ts
loc.howyagoingm8
GetTs();
is a function in another file somewhere that gets the game time
So we are saying that
loc.ts
is whatever the current gametime is this next part is a condition
if
loc.ts
is less then the current time then go back up the top
[loc.ts < state.dyn.me.do.ts] return;
The inner case will stay the same but it will reset and run the
loc.ts
again and the time will update until
loc.ts
is greater then the time then it will no longer return back to the start
[loc.ts < state.dyn.me.do.ts] return;
Then it will run the code under it which in our case is
state.dyn.me.do.timersarefun = START2;
and that will move it to the next innercase
theres 2 main timers
do_set_timer(6); do_set_timer2(6); [loc.ts < state.dyn.me.do.ts] return; [loc.ts < state.dyn.me.do.ts2] return;
These timers dont work well with other timers but very raly will 2 timers be needed Now this is a bit more advanced but ill add it anyway
I found this works well if you need multiple timers running at the same time
[loc.ts >= state.dyn.me.do.ts] { }
Next is operators
[loc.ts == state.dyn.me.do.ts] if loc.ts is equal to ts [loc.ts != state.dyn.me.do.ts] if loc.ts is not equal to ts [loc.ts > state.dyn.me.do.ts] if loc.ts is greater then ts [loc.ts >= state.dyn.me.do.ts] if loc.ts is greater then or equal to ts [loc.ts <= state.dyn.me.do.ts]if loc.ts is less then or equal to ts
Then we can do some more
[loc.ts == state.dyn.me.do.ts & state.dyn.me.do.timersarefun == START2 ]
if loc.ts is equal to ts and timersarefun is equal to START2
[loc.ts == state.dyn.me.do.ts | state.dyn.me.do.timersarefun == START2 ]
if loc.ts is equal to ts or timersarefun is equal to START2
But wait theres even more
state.dyn.me.do.playwithvalues = 1;
This sets playwithvalues to whatever is set
state.dyn.me.do.playwithvalues += 1;
This adds a the value to the exsisting varaible
state.dyn.me.do.playwithvalues -= 1;
This subtracts the value to the exsisting varaible
state.dyn.me.do.playwithvalues *= 1;
This multiplys the value of the exsisting variable by whatever is set
state.dyn.me.do.playwithvalues /= 1;
This divides the value of the exsisting variable by whatever is set
state.dyn.me.do.playwithvalues ^= state.dyn.me.do.valuedoesnotexsist;
This ones a little bit diffrent it sets a value even if it does not exsist
We can even do things like this
state.dyn.me.do.playwithvalues = state.dyn.me.do.somevalue; state.dyn.me.do.playwithvalues += state.dyn.me.do.somevalue;
Ok lets move on to getting them to talk
This is the basic talk function
talk.s = "hello";
There are ways we can control this
talk.dur = 6000;//Time in milliseconds 1000 milliseconds is 1 second talk.delay = 4000;
We can even use a case to control what is said
Rnd(3);
This is a function to randomly select a value between the set number 0 is included so its 0 1 2 but (3)
loc.rnd = Rnd(3); case (loc.rnd) { [0] talk.s = "random0"; [1] talk.s = "random1"; [2] talk.s = "random2"; }
So what we are saying here is loc.rnd is a number between 0-2 and the case is what ever number that loc.rnd is then the whatever that number is the innercase will run
Here's another example but a bit more controlled
So this starts in start and set the count variable then make s 6 second timer and moves to START2
When it gets to START2 it waits till the 6 seconds are over then says say0
It adds 1 to the count variable and adds a 5 second timer and goes back up the top and will do this until we change the state which will happen when the count reaches 4 then the condition will be met and it will move to the next innercase
[START] { state.dyn.me.do.count = 0; do_set_timer(6); state.dyn.me.do.state2 = START2; } [START2] { loc.ts = GetTs(); [loc.ts < state.dyn.me.do.ts] return; case (state.dyn.me.do.count) { [0] talk.s = "say0"; [1] talk.s = "say1"; [2] talk.s = "say2"; [3] talk.s = "say3"; [4] talk.s = "say4"; } [state.dyn.me.do.count == 4] { state.dyn.me.do.state2 = NEXT; } state.dyn.me.do.count += 1; do_set_timer(5); } [NEXT] { }
Thats it for now the rest you will have to figure out on your own my suggestion is to open every game file you can and see how things are done
All i had was the tutorials that XPA did and the rest iv learned by either tinkering around or asking
If you need help feel free to ask on the forums but try to figure it out first you don't learn by people doing it for you