ID:180902
 
I decided to forget about that kill thing, but i have a couple of questions:

1. How can i make an npc that will walk randomly until it sees a person, which will then follow and attack the person?
2. How can i make an experience system that will give a person experience when he kills someone, and at a certain amount of experience he will level up, making some stats go up?

If you need my code to see how my things work, please give me your email. Thank You.
I'm overdue to get some sleep, but hopefully this will start you off...

1. How can i make an npc that will walk randomly until it sees a person, which will then follow and attack the person?

When the mob is created, spawn a proc that runs forever, and sleeps for a few ticks between each loop.

To walk around, an easy way to do it is to use one of the built-in functions like walk_rand, step_to, etc. There are whole families of "walk" and "step" procs... just check the reference and do some experimenting to see which effect you like the best.

Also, allow me to plug the latest release of Deadron's library; I haven't tried it yet, but from the description, it offers some *very* nice, and simple-to-use, movement functions.

To decide whether or not to attack, just look for a player in view(src). If one is found, make that player your current target.

//Caution: this is *not* complete code... just an outline to give you ideas!
mob
var/mob/player/victim

New()
. = ..()
spawn Wander()

proc/Wander()
var/mob/player/M

while(src)
if(victim)
walk_to(src, victim)
else
walk_rand(src)
for(M in view(src))
victim = M
break

sleep(4)

proc/Bump(mob/player/M)
if(istype(M)) Attack(M)

proc/Attack(mob/M)
M.health -= 5


2. How can i make an experience system that will give a person experience when he kills someone, and at a certain amount of experience he will level up, making some stats go up?

//More untested code!
var/list/fighterLevels = list(1000, 2000, 4000, 8000, 16000)

mob
var/list/expLevelList
var/exp
var/level

fighter
expLevelList = fighterLevels
level = 1

proc/AddExperience(myAmount)
exp += myAmount
if(exp >= expLevelList[level])
src << "You are now level [++level]!"
someStat += 1
In response to Guy T.
On 10/6/00 11:35 pm Guy T. wrote:
I'm overdue to get some sleep, but hopefully this will start you off...

1. How can i make an npc that will walk randomly until it sees a person, which will then follow and attack the person?

When the mob is created, spawn a proc that runs forever, and sleeps for a few ticks between each loop.

To walk around, an easy way to do it is to use one of the built-in functions like walk_rand, step_to, etc. There are whole families of "walk" and "step" procs... just check the reference and do some experimenting to see which effect you like the best.

Also, allow me to plug the latest release of Deadron's library; I haven't tried it yet, but from the description, it offers some *very* nice, and simple-to-use, movement functions.

To decide whether or not to attack, just look for a player in view(src). If one is found, make that player your current target.

//Caution: this is *not* complete code... just an outline to give you ideas!
mob
var/mob/player/victim

New()
. = ..()
spawn Wander()

proc/Wander()
var/mob/player/M

while(src)
if(victim)
walk_to(src, victim)
else
walk_rand(src)
for(M in view(src))
victim = M
break

sleep(4)

proc/Bump(mob/player/M)
if(istype(M)) Attack(M)

proc/Attack(mob/M)
M.health -= 5


2. How can i make an experience system that will give a person experience when he kills someone, and at a certain amount of experience he will level up, making some stats go up?

//More untested code!
var/list/fighterLevels = list(1000, 2000, 4000, 8000, 16000)

mob
var/list/expLevelList
var/exp
var/level

fighter
expLevelList = fighterLevels
level = 1

proc/AddExperience(myAmount)
exp += myAmount
if(exp >= expLevelList[level])
src << "You are now level [++level]!"
someStat += 1

whoa, this stuff is kinda confusing for me (even though i read the whole byond book). whenever you have plenty of time, could you add some comments to explain that?
In response to Cinnom
Here you go! Comments appear right after the lines they pertain to. By the way, I'm glad you read the BYOND book! Not only is it educational, it's often funny... (Just had to throw in a plug for anyone else who might read this.)

//Caution: this is *not* complete code... just an outline to give you ideas!
mob
var/mob/player/victim

"victim" will be a pointer to whomever you're pursuing.

New()

New() is called whenever an object is created. This gives you the chance to initialize it however you like. In this case, when we create the new mob, we want to kick off a continuing process that will keep the mob thinking every so often.

. = ..()

This is probably not even necessary in this case. You don't need it unless you've already defined New() somewhere else and you want to make sure that code gets called too. For example, I might have one section of code that says new policemen always get a pistol. And I might have another section of code that says new mobs of any type are always announced to the world. Well, if the policeman's New() proc also calls ..(), we'll be sure that he will get his pistol and be announced to the world.

"." is the value that the proc will return. So these two examples behave the same:

example 1:
return 5

example 2:
. = 5
return

spawn Wander()

"spawn" will set off a separate process without interrupting the current one. Since Wander() will run for the lifetime of the mob, I used spawn here; otherwise New() would never return until the mob dies!

proc/Wander()
var/mob/player/M

M will be used below. I just defined it here because if I did it within the "while" loop, a new variable would need to be created every time the loop executes. But it probably wouldn't make any noticeable difference in the speed of the program.

while(src)

This means "while the caller of this procedure still exists." It's kind of meaningless in this case, because this spawned process will stop running as soon as src is deleted. I could just as easily have written "while(1)".

if(victim)
walk_to(src, victim)

If we've already found a mob to pick on, chase him. This code is relying on the Bump() command to handle the actual attack... if you try to move and run into something, Bump is called automatically. However, it's just a convenience... it isn't defined to do anything unless you override it (as I do below).

Also, you might need to experiment with walk_to and walk_towards. One of them will move right up to the victim but never try to bump it. I can't remember which is which right now though!

else
walk_rand(src)

If we don't have a mob to chase, then move around. You could replace this line (and the walk_to above, too) with a similar function from the Deadron Library.

for(M in view(src))
victim = M

Since M is declared to be a specific type, this will only find objects of the /mob/player type (and lower types like /mob/player/policeman, etc.). If no objects are available in the view list, the lines in the "for" loop won't even be executed. So, if the "victim = M" line is executed, it means we found one, and...

break

...it also means we can stop looking now, so "break" will exit the "for" loop without bothering to look over the rest of the view() list.

sleep(4)

This will wait four-tenths of a second before executing the next line. In this case there is no next line... but the proc will not return, because remember, we have that "while" loop up there. So your mob will be moving and looking for victims every four-tenths of a second until it's deleted.

proc/Bump(mob/player/M)
if(istype(M)) Attack(M)

This is called whenever the mob runs into something. BYOND will automatically let the proc know what you ran into, but we have to name it. I named it M.

However, there are other things to run into beside players, so I have to check with istype(). I don't want to attack a wall! If you use one argument with istype(), it checks to see if the argument is indeed the type it was declared as. So in this case, these two statements are equivalent:
if(istype(M)) Attack(M)
if(istype(M, /mob/player)) Attack(M)

proc/Attack(mob/M)
M.health -= 5

This does the damage. It assumes mobs have a health var declared.


//More untested code!
var/list/fighterLevels = list(1000, 2000, 4000, 8000, 16000)

This declares a global list. Since it isn't under /mob or /area or anything else, this variable can be used anywhere in the program. The example assumes that you might want different experience progressions for different mob types-- for example, thieves might need 1150 experience to reach second level, instead of 1000.

mob
var/list/expLevelList

This will be the list that tells us how much experience the mob needs for each level. It will actually just be a pointer to one of our global lists.

var/exp
var/level

Current experience points and level.

fighter
expLevelList = fighterLevels

Make the "experience level chart" point to the proper list for fighters.

level = 1

proc/AddExperience(myAmount)
exp += myAmount

Whenever the mob does something worthy of experience, like killing a monster, you call this proc and let it know how much experience was added. "exp += myAmount" means the same thing as "exp = exp + myAmount".

if(exp >= expLevelList[level])

Check the new experience score versus the level chart. expLevelList[1] = 1000 in this case. That means he has to get at least 1000 exp to move beyond level 1.

src << "You are now level [++level]!"

Inform the player. "++level" is shorthand for "level = level + 1".

someStat += 1

Enhance the stat scores however you like. The mob probably wouldn't actually have an attribute called "someStat". :)
In response to Guy T.
Thanks :)

Now that brings up another question of mine (similar to when i wanted a kill count).
How can I make the game know who killed a certain person(or monster)? (I think the kill count code you gave me was "incompatible" with my code, so once again, if you need to look at my code, just give me your email)
In response to Cinnom
Now that brings up another question of mine (similar to when i wanted a kill count).
How can I make the game know who killed a certain person(or monster)? (I think the kill count code you gave me was "incompatible" with my code, so once again, if you need to look at my code, just give me your email)

My email is [email protected]. As for the new question: it depends on what you're using it for! The reason is, you could store the information as pointers to mobs, but if you ever delete the mobs, then the pointers will become null and you can't retrieve the information anymore. (This is actually a minor bug in my current game: if you've logged into a computer terminal and you die, the computer magically knows that you've died and won't let anyone else use the terminal until they log in! This is because I store the current user information as a pointer to a mob rather than storing a text string, like the mob's tag or name or key.)

Anyway, email me or post more details here, and we'll see what happens!

In response to Guy T.
sorry for not replying in a while. anyways, i didn't exactly understand what you just said... Well, while you're "translating" that for stupid little me, here's another question for you:
How do i give a verb to a player while playing? (i.e. A player talks to a person and gets a new attack)

oh yea, and what exactly is that BYOND Hub thing?
In response to Cinnom
How do i give a verb to a player while playing? (i.e. A player talks to a person and gets a new attack)

Well, I'll just have to be snooty and steal some of Guy T's thunder! You can add a verb in a special way. Here's an example:

mob
var
level = 1
health = 20
max_health = 20
verbs_mob
proc
advanced_attack(mob/M)
M.health -= 20
player
verb
beginner_attack(mob/M)
M.health -= 5

proc
LevelUp()
src.level += 1
src.max_health += 10
if(src.level == 5)
src.verbs += /mob/verbs_mob/proc/advanced_attack
usr << "You learn a new attack method; type 'advanced attack' to use it against someone!"
usr << "You gain a level!

To add verbs, in other words, you define it as a proc (or verb, it doesn't matter) somewhere else, and then add it to the mob variable mob.verbs. You can remove it in the same way, by specifying the path to the verb's original location with a -= instead of a +=. You could also look at the "Verbs Packages" snippet in the DM InfoCenter over yonder.

When a mob reaches level 5 in this example, it adds the /mob/verbs_mob proc 'advanced_attack' to the mob's repertoire, enhancing its combat abilities. Note that the verbs_mob still can't use the 'advanced_attack' ability itself, because it isn't a verb, it is a proc there. When you add it to a mob's verbs, however, it becomes a full fledged verb that can be typed in.


oh yea, and what exactly is that BYOND Hub thing?

It is intended to be a type of universal directory that lets you look at other people's "BYOND Homes" when those players are running it. However, the Hub isn't quite fully functioning; there's a release of a new version coming soon.
In response to Cinnom
sorry for not replying in a while. anyways, i didn't exactly understand what you just said...

Okay, here's the basic idea: the suggestion I was making involved setting a pointer to a mob. This is just a variable whose value is set to point to an object, like so:

verb/befriend(M as mob)
usr.friend = M
usr << "[M] is now your best friend!"

Now, as long as M exists in the world, the user's mob will have a pointer to M (at least until the user decides to befriend someone else).

But if M dies, usr.friend will suddenly equal null.

Let me put it this way: a "pointer" (a.k.a. "reference") tells the system where in memory an object is stored. For the computer, it's kind of like the hyperlinks we humans use on the World Wide Web. If you see a cool web site, you don't have to copy the whole thing and send it to me; you can just tell me "Hey, visit byond.com for all the information." Similarly, when you reference usr.friend, the computer looks and see it's set to M (which, to the computer, is actually just a number-- that is, a pointer). The computer realizes it needs to look at such-and-such a place in its memory to get all the details about M. (Note that you can't ever print out the magic number assigned to M. The computer knows the difference between an ordinary number, like 65535, and a pointer that refers to memory location 65535.)

Here's a tip, for what it's worth: don't be afraid to steal ideas from existing programs. Many, many times, I've needed to do something, and gotten through the situation simply by finding a piece of code that did something similar, looking it over, making as much sense of it as I could, and then changing things here and there until it worked. There's no substitute for experience!


Well, while you're "translating" that for stupid little me, here's another question for you:
How do i give a verb to a player while playing? (i.e. A player talks to a person and gets a new attack)

There's a sample of adding verbs in the Code and Demos section. See DM InfoCenter -> Snippets -> Runtime Verbs. (This example may be a little more complicated than you actually need; just pay special attention to the "verbs" var of an object. You can add things to it, or remove things from it, and that's all you need to know!)


oh yea, and what exactly is that BYOND Hub thing?

You mean you haven't tried the Hub yet? We'll have to remedy that. Make sure you're connected to the Internet (like you probably are now, if you're reading this :) and choose "open location" from the leftmost menu. Enter this text:

dantom#hub

...and you will magically be transported to a land of adventure! Just right-click on an icon and select "go" to visit a world.

But maybe you mean that link to the left. That's the New Hub (or at least the Newest Hub, for those of us who have been around a while). It's a clever system; however, it may be a little too technical if you're just starting out. For now, stick with dantom#hub and you'll be fine.

In response to Spuzzum
Well, I'll just have to be snooty and steal some of Guy T's thunder!

You... uh... you... thunder-thief!
In response to Guy T.
Okie dokie, now i get what you're saying. Now, i'm guessing, by the way you've said things, that there is a better alternative to using "pointers". What would that be?

Another good question i conjured up: How could i make a statpanel to display verbs (kinda like another verb panel)?

:edit: I think Dantom should make another book for all this stuff they didn't cover in The Dream Maker (if they did, i'd buy it :)
In response to Cinnom
On 10/15/00 1:25 pm Cinnom wrote:
Okie dokie, now i get what you're saying. Now, i'm guessing, by the way you've said things, that there is a better alternative to using "pointers". What would that be?

Not sure. Wait for Slugworth to get back to you on that one. =)

Another good question i conjured up: How could i make a statpanel to display verbs (kinda like another verb panel)?

Just set the verb's category:

mob
verb
foo()
set category = "Attack"
bar()
set category = "Defend"

They'll get their own special panel dedicated to them, and any verbs with that category will show up in it.
In response to Cinnom
Okie dokie, now i get what you're saying. Now, i'm guessing, by the way you've said things, that there is a better alternative to using "pointers". What would that be?

Well, the only catch with pointers is that if the mob is deleted, every variable that points to a mob becomes null. Look at this code:

myMob = M
del M
usr << "myMob is [myMob]"

This would just display

myMob is

...because myMob pointed to M, and M is gone now, so myMob is null.

For alternatives... well, there are a couple. Here are some ideas.

* If you only care about tracking actual live players, then you could store mob.key instead of the mob itself. This works because the key is just a string of text, like "Gughunter" or "Cinnom", and keys are also unique. Well, so Dantom says. Haven't run into any duplicates yet. :)

* You could do something like this.

proc/GetNewTagID()
var/static/currentID = 0

++currentID
return "_[currentID]"

mob
New()
tag = "[type][GetNewTagID()]"

"Tag" is a built-in variable that's meant to give you a chance to give each object a unique name. In this case, creating a new mob would give you a tag like this:

"/mob/player/human_1"

The reason you want each tag to be unique is that it can help you use locate() to find the mob fast. So you could do something like this:

proc/ShowNumberOfDeaths(whateverTag)
// note: this assumes you've been keeping some list of how many times each tag has died!
var/myMob
myMob = locate(whateverTag)
if(myMob)
usr << "[myMob] died [deaths[whateverTag]] times."
else
usr << "Some deleted mob died [deaths[whateverTag]] times."