ID:102625
 
Keywords: programming
Well guys, sorry for my little "hiatus". I'm back now though! Still looking for ideas for future HTP posts! I will be scouring the forums, but feel free to page me, email me([email protected]), or post in the suggestion topic, informing me of any common problems you see that need explained!

Now, on to the entry.




Ever started programming a game, stop, then come back to it? How many times has it happened, and you have to start over, because you just cannot sort out your unorganized code? I know it happened a lot to me when I was just starting out. There is an easy fix to this, though. Make all of your functions like you are making a library.

In this post, I will show you a simple example of using a series of procedure to achieve one(1) result; all controlled by initiating one(1) procedure.

All right. In this example, we will cause a simple player-NPC conversation to happen. We will be using three(3) procedures, not counting the pre-defined Login procedure.

Let's start off by defining our name as "You" and make a NPC mob thats name is "NPC". Simple, right?
mob
name = "You"
NPC
name="NPC"

Right.

Now, lets start defining our procedures. I will start by making a Start_Talking procedure.

mob
name = "You"
NPC
name="NPC"
mob
proc
Start_Talking(mob/user,mob/_user, list/user_text, interval)

Simple enough. We have it made, and have some arguments. Now, what do these arguments do?
user: This is the client.
_user: This is the NPC.
user_text: This is a list that will have text associated with numbers. This will be explained more later.
interval: This is the time, in ticks, it will take before the next message is displayed.

Now, lets do some checking to avoid critical errors. Checking for simple things like this is highly recommended, for a small mistake could cause a whole game to crash, yet not give an error in the compiler.
mob
name = "You"
NPC
name="NPC"
mob
proc
Start_Talking(mob/user,mob/_user, list/user_text, interval)
ASSERT(user_text.len >= 1)
ASSERT(isnum(interval))

The ASSERT procedure does this job well. These to ASSERTs will check to make sure that there is something in the user_text list, and that the interval argument is a number. We cant use sleep with text, now can we?

Alright. Now, lets make another procedure to handle displaying the text. Let's call it Talk().
mob
name = "You"
NPC
name="NPC"
mob
proc
Start_Talking(mob/user,mob/_user, list/user_text, interval)
ASSERT(user_text.len >= 1)
ASSERT(isnum(interval))

Talk(mob/user,txt)
ASSERT(istext(txt))
world << "[user]: [txt]"

Alright. Two(2) arguments in this one. Pretty simple.
user: This is the person talking.
txt: This is what the user says.

Alright. This procedure just makes sure that txt is a text value, and then displays it, with the user's name.

Now, back to the Start_Talking procedure. To make the speech automated, we will use a for loop procedure.
mob
name = "You"
NPC
name="NPC"
mob
proc
Start_Talking(mob/user,mob/_user, list/user_text, interval)
ASSERT(user_text.len >= 1)
ASSERT(isnum(interval))
for(var/speech=1,speech<= user_text.len, speech++)

Talk(mob/user,txt)
ASSERT(istext(txt))
world << "[user]: [txt]"

The for loop can be a confusing concept at first, but once you get the order down, it is easy to understand.

It tests things in a set order; It uses the first argument, and checks if the second one is true. If it is true, then it executes everything indented under the loop. After it finishes that, it executes the last argument, then starts over until the second argument is not true.

In this case, it uses the variable, speech, which is set to 1. It checks to see if speech is less than or equal to the length of the user_text list. We will define things that go under this next.

Ok, I added a new procedure and a few lines under the for loop in this next piece.
mob
name = "You"
NPC
name="NPC"
mob
proc
Start_Talking(mob/user,mob/_user, list/user_text, interval)
ASSERT(user_text.len >= 1)
ASSERT(isnum(interval))
for(var/speech=1,speech<= user_text.len, speech++)
if(!Is_Even(speech))
Talk(user,user_text["[speech]"])
else Talk(_user,user_text["[speech]"])
sleep(interval)
del(user)

Talk(mob/user,txt)
ASSERT(istext(txt))
world << "[user]: [txt]"

Is_Even(number as num)
var/_number = number/2
_number = num2text(_number)
if(findtext(_number,"."))
return 0
else return 1

Alright, now to explain it all.

I added a Is_Even procedure. This just checks to see if a number is an even number, by dividing it by 2. It then converts the number to a text format, and looks for a decimal. If there is one, it returns 0, saying that the number was not even. If there isn't, it returns 1, saying that it is even.

Now, under the for loop, I used this procedure. It uses Is_Even to check speech. If it is not even, it calls Talk in the way that shows that You said it. If it is even, it calls Talk as if it was the NPC that said it.

Then, it waits for the interval(In this case, 10), then the for loop executes again. Once it finishes, it deletes the user(client).

Alright, now that we have this all set up, it is time to use it.
mob
Login()
. = ..()
var/mob/NPC=new /mob/NPC
var/list/user_text=list("1"="Hey there, how are you?",\
"2"="Fine, how about you?","3"="I'm fine. Weather's nice, huh?",\
"4"="Yup.","5"="Well, I am going to get out of here. Bye!","6"="Alright, bye!")
Start_Talking(src,NPC,user_text,10)

When the player logs in, it creates a new NPC. Then, it defines a list, using numbers associated with messages. Then, it calls Start_Talking, using the appropriate mob arguments, the list defined before, and an interval of 10(1 second). After that, Start Talking handles everything.

Use this code to make your own similar example, and watch what happens!




In case you don't get the point of this post, I will elaborate. Don't underestimate the power of multiple, strung together procedures! A few procedures, created by your and/or pre-defined, can produce amazing results when strung together, sometimes able to be handled with one call of a procedure!



"Also, strictly speaking on Code... Try to develop your systems to be independent. What I mean by this is try to create your systems as if they were Plug'N'Play or Libraries. This will serve good use if you ever make another project or if you want to add/remove a system in your current project with ease."
- Maximus_Alex2003
Good guide.
Yea+
Nice+
Your isEven procedure is rather flawed.
If you just want to parse integers as seems the concept right now, you'd be better off with a simple return (number + 1) % 2, after checking if number is a valid number [isnum(number)] and is an integer [round(number) == number].

You mention and are checking a few essential conditions, but forget others. For example you never check if user is actually set.

Btw. explaining that an instance of mob would be a client is wrong and misleading.

Have you considered a more flexible design of conversation where more people are involved and where somebody can have two lines of text in progression? You'd have to provide a parse-able reference the talking person instead of a sheer number in the associative list.
looks good better and shorter then the way i would have done it thats for sure
Schnitzelnagler wrote:
Your isEven procedure is rather flawed.
If you just want to parse integers as seems the concept right now, you'd be better off with a simple return (number + 1) % 2, after checking if number is a valid number [isnum(number)] and is an integer [round(number) == number].

You mention and are checking a few essential conditions, but forget others. For example you never check if user is actually set.

Btw. explaining that an instance of mob would be a client is wrong and misleading.

Have you considered a more flexible design of conversation where more people are involved and where somebody can have two lines of text in progression? You'd have to provide a parse-able reference the talking person instead of a sheer number in the associative list.

While all of these suggestions are good, this was made for the community, and they can experiment with it. I just wanted to get a point across.

Thanks again, though.
this is very helpful