fearyourself
Wanderer
[Tutorial] First Quest -> Bunny Attack
Hi all, I didn't know about this sub-forum so I posted this rant in the Script forum, I'll put it here so that more people can find it
Any optimizations are welcome, I'm only a noob...
Introduction
Ok, I've only been looking through the UO scripting system since yesterday and yeah I've started C# pretty much at the same time. But I tried yesterday to play around with the system and it took me a while to centralize all the information needed to make even the most basic quest...
So I'm starting this thread so that you can tell me what I'm doing wrong or what I'm doing right
Take this thread as a learning rant by my part, tell me where I'm wrong or what I've done badly that way I can correct myself
Hopefully the moderators won't be too annoyed but I learnt a bit, wasted lots of time trying to gather in one place this information. If a moderator prefers to delete this thread, don't worry I won't mind
Story of the quest :
Basically this is an excuse to play around with the scripting language : Bob is a mobile we'll be creating and he'll give you the opportunity to have fun killing 10 rabbits (cruel world isn't it?).
If you say yes then 10 rabbits are created next to Bob, if you say no then he'll just ignore you after that...
Now since we're in a MMORPG, it's possible that another player comes around and tries to kill the rabbits, we'll have to handle that later and I'll show two techniques I found to do it
Oh, and I forgot, the reward you get for killing the 10 rabbits is a nice black bear
Ok, so that's for the excuse of the quest, now there are only fourfiles that are going to be attached to this quest.
The file explaining who's Bob, the behaviour of Bob's rabbits and the gump you'll get when you talk to bob for the first time and of course a special marker to handle the quest's stage.
Configuration
First things first, in your RunUO directory, go in the Scripts subdirectory and create a Custom subdirectory. When you'll launch RunUO, it goes through the directories and compiles everything that's new (At least I assume it doesn't try to compile everything from scratch when there's a change...)
In this new Custom directory, we'll add a BunnyAttack directory. Thanks to Rosey1's way of putting the files, I made a mobiles, items and gumps directory. I assume it's a classical way of separating types of elements and it does make sense so go ahead and make those three directories.
Let's start with the easiest file : BobsQuestMarker.cs that I'll put in a new directory named markers, itself put in the items folder.
A marker is basically an invisible object that will help the NPCs (and bunnies) to know where the player is up to in the quest. This marker is like any other item in the game, it inherits from the base class Item and, of course, you can add as many variables that you will need to handle your quest.
Some people like to have a certain number of markers to handle their quests. That's ok too but generally it becomes more a hassle to remember what marker is what. Centralizing everything (though you'll be using slightly more memory) is, I think, a good idea...
Code : BobsQuestMarker.cs
Anyhow, here goes ! The beginning of this file just takes care of the includes, defines what namespace we're in (we're in Server.Items because a marker is an item) and we define our new class BobsQuestMarker.
Since our quest deals with killing a certain number of rabbits, we'll need a counter to keep track of the number of rabbits the player killed. And since a quest is mainly a state thing (the quest is starting, it's finished, it was refused, etc...) we'll add a enum :
A enum is basically just constants that we'll use. You could say that the variable pm_state is an integer and decide yourself that 0 means the quest is starting, 22 is that the player can go get the reward and 4321 means that the quest is finished. But to have an nice easily readable code, enums is the place to go...
Here, we define 4 states : START means the quest is started, GETREWARD that the player did what was necessary and can get the reward, FINISHED means the quest is finished and IGNORE is the state when the NPC should ignore the player (if the player refused the quest for example)... Of course, you can add states or remove a few depending on your needs...
Now if you notice, I declared the variables private because, as all Object oriented language, we don't like it when people can just change the values. What if someone tries to change the state to a value not handled ? The solution is to define get/set methods and in C# it's done this way :
This looks complicated but actually it's quite easy... What we have here is basically an easy way to modify the value of the counter or the state. Let's say we had an object BobsQuestMarker named bqm and we wanted to decrement the counter. All we have to do is this :
What's the difference with putting the p_counter variable public ? In this case, nothing really except that we can change the way the counter is decremented, check when it is decremented, decide NOT to decrement by changing the get/set functions. And of course the rest of the code using this class doesn't have (and doesn't need) to know about it... So it's always a good idea to put your class members private and use these get/set functions...
Anyway, next comes the constructor of this object :
Ok, as you can see here : http://www.runuo.com/forums/showthread.php?t=40091, we pass to the super class base the ItemID to say what graphics to use. Here we use a set of keys sprite. But that doesn't really matter because we set the object's Visible parameter to false. This means that the object is not visible to the normal user (admins will be able to see it).
Of course, the element is not movable, it's blessed so that it doesn't degrade, the counter is put to 0 and the state is the beginning of the quest. We put the weight to 0 and set a name. The name could be a parameter of the constructor to define different markers that have the same kind of information needs (a counter and a state...).
Finally we have the serialization and deserialization methods that need to store and load the counter and the state, this is how we can do it :
Notice how we have to cast the enum as an integer, I don't know if there other ways to do it but this is generally done so I just followed the flow .
As you can see, nothing really complicated here but we now have a really nice marker that can count the number of Rabbits we kill and Bob can see where we are at in the quest by looking in this marker. That is already something quite sweet (at least I think so...)
If I've done anything wrong, please tell me about it, as I've said, this is my first attempt so I might be doing things the wrong way...
The full code is here :
**edit daat99: removed quote**
Hi all, I didn't know about this sub-forum so I posted this rant in the Script forum, I'll put it here so that more people can find it
Any optimizations are welcome, I'm only a noob...
Introduction
Ok, I've only been looking through the UO scripting system since yesterday and yeah I've started C# pretty much at the same time. But I tried yesterday to play around with the system and it took me a while to centralize all the information needed to make even the most basic quest...
So I'm starting this thread so that you can tell me what I'm doing wrong or what I'm doing right
Take this thread as a learning rant by my part, tell me where I'm wrong or what I've done badly that way I can correct myself
Hopefully the moderators won't be too annoyed but I learnt a bit, wasted lots of time trying to gather in one place this information. If a moderator prefers to delete this thread, don't worry I won't mind
Story of the quest :
Basically this is an excuse to play around with the scripting language : Bob is a mobile we'll be creating and he'll give you the opportunity to have fun killing 10 rabbits (cruel world isn't it?).
If you say yes then 10 rabbits are created next to Bob, if you say no then he'll just ignore you after that...
Now since we're in a MMORPG, it's possible that another player comes around and tries to kill the rabbits, we'll have to handle that later and I'll show two techniques I found to do it
Oh, and I forgot, the reward you get for killing the 10 rabbits is a nice black bear
Ok, so that's for the excuse of the quest, now there are only fourfiles that are going to be attached to this quest.
The file explaining who's Bob, the behaviour of Bob's rabbits and the gump you'll get when you talk to bob for the first time and of course a special marker to handle the quest's stage.
Configuration
First things first, in your RunUO directory, go in the Scripts subdirectory and create a Custom subdirectory. When you'll launch RunUO, it goes through the directories and compiles everything that's new (At least I assume it doesn't try to compile everything from scratch when there's a change...)
In this new Custom directory, we'll add a BunnyAttack directory. Thanks to Rosey1's way of putting the files, I made a mobiles, items and gumps directory. I assume it's a classical way of separating types of elements and it does make sense so go ahead and make those three directories.
Let's start with the easiest file : BobsQuestMarker.cs that I'll put in a new directory named markers, itself put in the items folder.
A marker is basically an invisible object that will help the NPCs (and bunnies) to know where the player is up to in the quest. This marker is like any other item in the game, it inherits from the base class Item and, of course, you can add as many variables that you will need to handle your quest.
Some people like to have a certain number of markers to handle their quests. That's ok too but generally it becomes more a hassle to remember what marker is what. Centralizing everything (though you'll be using slightly more memory) is, I think, a good idea...
Code : BobsQuestMarker.cs
Anyhow, here goes ! The beginning of this file just takes care of the includes, defines what namespace we're in (we're in Server.Items because a marker is an item) and we define our new class BobsQuestMarker.
Code:
using System;
using Server.Items;
namespace Server.Items
{
public class BobsQuestMarker : Item
{
Since our quest deals with killing a certain number of rabbits, we'll need a counter to keep track of the number of rabbits the player killed. And since a quest is mainly a state thing (the quest is starting, it's finished, it was refused, etc...) we'll add a enum :
Code:
public enum BobsQuestState
{
START,
GETREWARD,
FINISHED,
IGNORE
}
private int pm_counter;
private BobsQuestState pm_state;
A enum is basically just constants that we'll use. You could say that the variable pm_state is an integer and decide yourself that 0 means the quest is starting, 22 is that the player can go get the reward and 4321 means that the quest is finished. But to have an nice easily readable code, enums is the place to go...
Here, we define 4 states : START means the quest is started, GETREWARD that the player did what was necessary and can get the reward, FINISHED means the quest is finished and IGNORE is the state when the NPC should ignore the player (if the player refused the quest for example)... Of course, you can add states or remove a few depending on your needs...
Now if you notice, I declared the variables private because, as all Object oriented language, we don't like it when people can just change the values. What if someone tries to change the state to a value not handled ? The solution is to define get/set methods and in C# it's done this way :
Code:
public int Counter
{
get {return pm_counter;}
set {pm_counter = value;}
}
public BobsQuestState QuestState
{
get { return pm_state; }
set { pm_state = value; }
}
This looks complicated but actually it's quite easy... What we have here is basically an easy way to modify the value of the counter or the state. Let's say we had an object BobsQuestMarker named bqm and we wanted to decrement the counter. All we have to do is this :
Code:
//Decrement counter
bqm.Counter--;
What's the difference with putting the p_counter variable public ? In this case, nothing really except that we can change the way the counter is decremented, check when it is decremented, decide NOT to decrement by changing the get/set functions. And of course the rest of the code using this class doesn't have (and doesn't need) to know about it... So it's always a good idea to put your class members private and use these get/set functions...
Anyway, next comes the constructor of this object :
Code:
public BobsQuestMarker()
: base(0x176B)
{
Weight = 0;
Name = "BobsQuestMarker";
Hue = 0;
pm_counter = 0;
pm_state = BobsQuestState.START;
LootType = LootType.Blessed;
Movable = false;
Visible = false;
}
Ok, as you can see here : http://www.runuo.com/forums/showthread.php?t=40091, we pass to the super class base the ItemID to say what graphics to use. Here we use a set of keys sprite. But that doesn't really matter because we set the object's Visible parameter to false. This means that the object is not visible to the normal user (admins will be able to see it).
Of course, the element is not movable, it's blessed so that it doesn't degrade, the counter is put to 0 and the state is the beginning of the quest. We put the weight to 0 and set a name. The name could be a parameter of the constructor to define different markers that have the same kind of information needs (a counter and a state...).
Finally we have the serialization and deserialization methods that need to store and load the counter and the state, this is how we can do it :
Code:
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int) 0);
writer.Write(pm_counter);
writer.Write((int) pm_state);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
pm_counter = reader.ReadInt();
pm_state = (BobsQuestState) reader.ReadInt();
}
Notice how we have to cast the enum as an integer, I don't know if there other ways to do it but this is generally done so I just followed the flow .
As you can see, nothing really complicated here but we now have a really nice marker that can count the number of Rabbits we kill and Bob can see where we are at in the quest by looking in this marker. That is already something quite sweet (at least I think so...)
If I've done anything wrong, please tell me about it, as I've said, this is my first attempt so I might be doing things the wrong way...
The full code is here :
Code:
using System;
using Server.Items;
namespace Server.Items
{
public class BobsQuestMarker : Item
{
public enum BobsQuestState
{
START,
GETREWARD,
FINISHED,
IGNORE
}
private int pm_counter;
private BobsQuestState pm_state;
public int Counter
{
get {return pm_counter;}
set {pm_counter = value;}
}
public BobsQuestState QuestState
{
get { return pm_state; }
set { pm_state = value; }
}
public BobsQuestMarker()
: base(0x176B)
{
Weight = 0;
Name = "BobsQuestMarker";
Hue = 0;
pm_counter = 0;
pm_state = BobsQuestState.START;
LootType = LootType.Blessed;
Movable = false;
Visible = false;
}
public BobsQuestMarker(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int) 0);
writer.Write(pm_counter);
writer.Write((int) pm_state);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
pm_counter = reader.ReadInt();
pm_state = (BobsQuestState) reader.ReadInt();
}
}
}
Jc