(Oops, sorry, as I started writing this it grew more and more into something closer to a guide to writing an add-on, than any specific code. Hope it works better that way, though, by explaining the "why" behind the code)
(Oh, and with no idea about how much you already know, I wrote it assuming no previous programming knowledge at all, so it might seem overly simplistic in some areas. Then again, that means that just about anyone can learn the basics from it...)
It depends.
One of the better ways to make a vehicle fly is to use a datablock specifically designed for flying vehicles, however, the horse is actually a player (or really, a bot), so that won't be an option.
Probably the easiest thing to do is keep launching it upwards as long as you hold the jump key. Doing so would require learning about datablocks, packaging, and the SomePlayerDatablockOrWhatever::onTrigger function.
Beware, though, it's not an easy place to start, so unless you already have experience working with Blockland add-ons, you might find it a bit too much to learn all at once.
Oh, and, very importantly, you can create a new <some type of datablock>, that is just like <some existing datablock> but different. If you open up the default Jump Jet player, you'll find that all it does is this: (Copied directly from Player_JumpJet.cs as an example)
//super human, just enough jet for a big leap
datablock PlayerData(PlayerJumpJet : PlayerStandardArmor)
{
minJetEnergy = 10;
jetEnergyDrain = 10;
canJet = 1;
rechargeRate = 3.0;
uiName = "Jump-Jet Player";
showEnergyBar = false;
};
The "datablock PlayerData(PlayerJumpJet : PlayerStandardArmor)" means "I want to create a new PlayerData datablock. Call it PlayerJumpJet, and make it exactly like PlayerStandardArmor, except for a few teeeny tiny changes...".
So. Flying horse. Well, opening up the horse vehicle, there is a rather large collection of code. Most of it doubtlessly confusing unless you fully understand what you are looking at. Fortunately, I'll spare you the confusion, and point out that the interesting line is
datablock PlayerData(HorseArmor)
While there, it is also important to note the
uiName = "Horse";
since that will certainly need to be changed.
Now, using the earlier "just like that but different" thing, these things can be put together to get
datablock PlayerData(FlyingHorseArmor : HorseArmor)
{
uiName = "Flying Horse!1!!11oneoneeleven"; //No, wait, just use "Flying Horse". Everyone will prefer it better that way.
//Oh, by the way, in case you didn't know, everything after a //, until the end of the line, is completely ignored!
//Perfect for leaving notes to yourself.
};
Except there are two problems there... One, you just have an exact copy of the horse, except it has a different name. Two, what happens if someone unchecked the regular horse, but left yours in? Then it wouldn't have the regular horse to copy from, and would have errors!
Fortunately, Ephialtes took the time to ask Kalphiter to explain how to solve that second problem
over here.
To fix the first problem, well, there are countless ways to do so, so I'll go over one of the quicker ones, though surely one of the less correct ones.
Why not make holding the jump button keep launching you upwards? Well, there are a few reasons not to (mainly: it's not a smooth motion, it jumps around a bit, especially for other players in multiplayer)
Well, this is somewhat complicated...
//You are not expected to understand most of this. If you do, that's great!
//Warning: Dark magic ahead.
function FlyingHorseArmor::onTrigger(%datablock, %player, %triggerNumber, %isTriggered)
{
parent::::onTrigger(%datablock, %player, %triggerNumber, %isTriggered);
//The parent:: line means "do what you would normally do", so in this case, "do what a horse would do here"
if(%triggerNumber == 2) //2 means "jumping". == means "is the thing on the left the same number as the thing on the right"
{
if(%isTriggered)
{
%datablock.flyLoop(%player);
}
else
{
cancel(%player.flyLoopSchedule);
}
}
}
function FlyingHorseArmor::flyLoop(%datablock, %player)
{
cancel(%player.flyLoopSchedule);
%currentVel = %player.getVelocity();
%newVel = vectorAdd(%currentVel, "0 0 2");
%player.setVelocity(%newVel);
%player.flyLoopSchedule = %datablock.schedule(250, "flyLoop", %player);
}
Well, things are looking almost useful! Let's try it out in-game. Uh oh, how do we do that?
Well, all that you really need is a folder in add-ons, let's call it Vehicle_FlyingHorse. Then you create a file in it called description.txt. You can leave it blank for now. Then you need a file called server.cs in there, as well. Now, following what Kalphiter wrote, lets put this in it...
%error = ForceRequiredAddOn("Vehicle_Horse");
if(%error == $Error::AddOn_Disabled)
{
//A bit of a hack:
// we just forced the jeep to load, but the user had it disabled
// so lets make it so they can't select it
HorseArmor.uiName = "";
}
if(%error == $Error::AddOn_NotFound)
{
error("ERROR: Vehicle_FlyingHorse - required add-on Vehicle_Jeep not found");
}
else
{
exec("./FlyingHorse.cs");
}
Then, still in that folder you just created, add yet
another file, and call it FlyingHorse.cs, and then, in it, finally, we can put all that great (or not so great...) code!
datablock PlayerData(FlyingHorseArmor : HorseArmor)
{
uiName = "Flying Horse!1!!11oneoneeleven"; //No, wait, just use "Flying Horse". Everyone will prefer it better that way.
//Oh, by the way, in case you didn't know, everything after a //, until the end of the line, is completely ignored!
//Perfect for leaving notes to yourself.
};
//You are not expected to understand most of this. If you do, that's great!
//Warning: Dark magic ahead.
function FlyingHorseArmor::onTrigger(%datablock, %player, %triggerNumber, %isTriggered)
{
//The parent:: line means "do what you would normally do", so in this case, "do what a horse would do here"
parent::onTrigger(%datablock, %player, %triggerNumber, %isTriggered);
if(%triggerNumber == 2) //2 means "jumping". == means "is the thing on the left the same number as the thing on the right"
{
if(%isTriggered)
{
%datablock.flyLoop(%player);
}
else
{
cancel(%player.flyLoopSchedule);
}
}
}
function FlyingHorseArmor::flyLoop(%datablock, %player)
{
cancel(%player.flyLoopSchedule);
%currentVel = %player.getVelocity();
%newVel = vectorAdd(%currentVel, "0 0 2");
%player.setVelocity(%newVel);
%player.flyLoopSchedule = %datablock.schedule(250, "flyLoop", %player);
}
And now, to test it out...
What?!? It didn't work! That horse is only barely jumping higher than normal!
You lied. It was supposed to fly.
Well, the thing is, to fly, you need to go up faster than gravity is pulling you down. So, lets go change a few things. First, oops, forgot to correct that name. Nobody likes excessive punctuation, especially when it isn't punctuation...
So, make things nicer by changing
uiName = "Flying Horse!1!!11oneoneeleven"; //No, wait, just use "Flying Horse". Everyone will prefer it better that way.
into
uiName = "Flying Horse";
And now onto the problem of flight. There are two things that can be done, launch the horse up more often, and do it with more force. Lets do both!
function FlyingHorseArmor::flyLoop(%datablock, %player)
{
cancel(%player.flyLoopSchedule);
%currentVel = %player.getVelocity();
%newVel = vectorAdd(%currentVel, "0 0 3");
%player.setVelocity(%newVel);
%player.flyLoopSchedule = %datablock.schedule(200, "flyLoop", %player);
}
So, 5 times per second rather than four, and going up 3 faster instead of 2 faster. Small change, really. Now, lets see what that did...
Well, that horse sure ain't flying yet, but it seems to be getting close, like it's on the moon. So, lets up it one more time...
%newVel = vectorAdd(%currentVel, "0 0 3");
becomes
%newVel = vectorAdd(%currentVel, "0 0 4");
Well, now it jumps to the bedroom roof, and if you hold space, glides down gracefully. But. Not. Good. Enough. So, lets try 5...
Hooray! It works... Of course, there are still a few things that could be improved, and that description.txt is missing a few lines, but hey! A flying horse!