Tutorial for code structure

From XStoryPlayer Wiki
Jump to: navigation, search

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


PDF VERSION PDF versions do not get updated as regularly


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
is a varaible we are setting it could be
loc.howyagoingm8
but ts is eaiser because we know what it means timeset or something like that


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