News | Forum | People | FAQ | Links | Search | Register | Log in
Coding Help
This is a counterpart to the "Mapping Help" thread. If you need help with QuakeC coding, or questions about how to do some engine modification, this is the place for you! We've got a few coders here on the forum and hopefully someone knows the answer.
First | Previous | Next | Last
Possible Version 
Here's what I'd do, roughly:

//convert current velocity into speed and direction
speed = vlen(self.velocity);
olddir = normalise(self.velocity);

//convert angles into a normalised heading
makevectors(self.y_angle);

//add a small amount of the heading to the current direction of travel
newdir = normalise( v_forward*frametime + olddir*(1-frametime));

//preserve the speed we had, but change the direction
self.velocity = newdir * speed;

//reduce the strength of gravity based on how level we are currently flying
self.gravity = 0.1 + 0.9*abs(v_forward*'0 0 1');


Some of the values might need tuning here and there, but that should get you started. Player movetype should be left as default, because we want gravity to apply (subject to the last line reducing how fast we fall. Haven't tested this, so it may be rubbish... 
Correction 
makevectors(self.y_angle);

should of course be

makevectors(self.v_angle);
 
 
use dotproducts:
upspeed = self.velocity * v_up;
fwdspeed = self.velocity * v_forward;
rightspeed = self.velocity * v_right;
(vec*vec is a dotproduct).

You can then get back to the original velocity vector with:
self.velocity = v_up*upspeed + v_forward*fwdspeed + v_right*rightspeed;

Of course, if you change some fraction of the force exerted on the underside of your glider before reforming the velocity then you'll reduce the effects of gravity while horizontal, and return to normal when facing vertically down.
Of course, that energy should normally go somewhere - if you add to fwdspeed anything you subtract from upspeed then you should get something a bit glidery.
you'll also need to include some friction somewhere, in order to get some terminal velocity. and yeah, the conversion should probably also not be 100% efficient either, nor should it convert ALL the energy.

but yeah, dotproducts are great if you want to compare two directions. trying to do stuff like this without using them is just going to leave it feeling really hacky.
Unfortunately there's no real way to control the camera from ssqc, but csqc can override the camera angles without changing the client's 'desired' angles. you should be able to implement roll that way, somehow. 
Notes After Testing The Suggested Code 
Had a go with the code from my post on Saturday, it worked pretty well for a first attempt. I did find that it wasn't responsive enough until I doubled frametime everywhere it appeared. One big tip for testing it - use e1m8. Firstly because its one of the few ID maps with enough vertical and horizontal space to glide in properly, secondly because it's low gravity so it's much more fun!

It's got most of the effects you'd want to see in glider physics - you can dive down to gain speed then level off and carry the momentum forward, and if you look up you'll gain height but bleed forward speed. You can look straight up, and you'll quickly stall then crash to the ground. Also if you collide with walls etc your speed is lost which tends to end the flight pretty quick.

One advanced trick: you can accelerate in Quake while in the air. If you're gliding, strafe left while turning in an anticlockwise circle and you'll gain speed (same principle as bunnyhopping works on). You can use this to offset the loss of speed while climbing, and gain height by moving in a big spiral. 
Preach’s Code Test 
Hi Preach, yesterday I arrived to my PC and I got an error after compiling your code. May be I have done something wrong. Could you please post a full file with your code so I could test it out? I think that would be useful for all the community. 
 
Maybe if you post the error you're getting we can help you out ... 
Skeleton Code 
I tried to post it as skeleton code so that you'd get some practice hooking it up. One thing I did unintentionally was to misspell (well, anglicize) a built-in function name, so make sure you change the spelling to normalize. If that wasn't the error that's stopped you, post it like c0burn says and we can suggest fixes. 
 
Still no result. I try to use this code if no flag on_ground is false but nothing works. 
New Issue 
Hi Reyond. It sounds like it's a bit further on if the compiler error has gone away. What function are you running the code in? I ran it in PlayerPreThink so that it would happen every frame. You're right to disable the code when the player is on the ground, but from your description I don't know whether the code you have is right.

I used: if(self.flags & FL_ONGROUND) to see if the player was on the ground. I also turned the code off while the player was in water using the check if(self.waterlevel) (although that's a detail to add once the basics work.

If that doesn't help, post the whole text of the code you're using and maybe we can spot what else it could be... 
Tut From InsideQc 
Found an old tutorial from Legion with an Xman version of a FlySimulator. Maybe helpfull as a guide. 
 
Allright, I found an error. Here is how I use the code:
else // airborn
{
vector fly_direction; vector new_dir; float fly_speed;
float fly_velocity; '
float koef;
/*
if (wishspeed < 30)
f = wishspeed - (self.velocity * wishdir);
else
f = 30 - (self.velocity * wishdir);
if (f > 0)
self.velocity - self.velocity + wishdir * (min(f, sv_accelerate) * wishspeed * frametime);
*/
fly_speed = vlen(self.velocity);
fly_direction = normalize(self.velocity);
makevectors(self.v_angle);
new_dir = normalize(v_forward*frametime +fly_direction*(1-frametime)); self.velocity = new_dir * speed;
 
Yes, actually it looks a little bit like gliding but I can’t understand how the code works. Why do I use frametime if this function works every frame? How this code affects the gravity (in-game gravity is 100 now). And how can I improve this code to make it work better?

Also, I would like to make it works without changing the standart “800” value of the sv_gravity variable. Is that possible?

Also, thanks to Preach and to everyone who helps me in making player gliding in quake. 
Try It Out 
Have a go with the second-to-last line changed to

new_dir = v_forward;

This leaves out the bits with frametime. What you should notice is that while gliding, you instantly start moving in exactly the direction you are facing. It's very easy to control, but it's not very realistic for a glider - you can 180 turn instantly at any time.

The idea behind the frametime code is to add the v_forward part more slowly, so that your direction of travel doesn't change instantly. Instead we add the v_forward in a bit at a time. Because the function runs every frame, we need the pieces we add to be smaller when the framerate is higher, otherwise people with higher framerates could execute tighter turns, which would be unfair.

I found that in practice the rate of change was too slow, and that it was better to change that line to:

new_dir = normalize(v_forward*2*frametime +fly_direction*(1-2*frametime));

By making the piece we add each frame twice as large, we get a better trade-off between smooth turning and reaction time.

In terms of making the glider float, I noticed that you don't have the last line from the code I posted:

self.gravity = 0.1 + 0.9*abs(v_forward*'0 0 1');

I expect you were getting a compiler error from this line that you didn't know how to fix. The secret is to add the following to the bottom of defs.qc:

.float gravity;

This warns the compiler that you'd like to be able to store a float called "gravity" on all the entities. The engine uses this to change the gravity just for that entity. So if you are flying in normal 800 strength gravity, but the player has self.gravity = 0.1, for that player gravity will be 80 instead (because 800 * 0.1 = 80). All other entities are unaffected, try firing a grenade while gliding and notice that it falls normally.

Give it a go, see what comes of it! Any more questions, just ask... 
 
Error: unknown value “abs”. 
To Reyond 
its fabs i think 
Yup 
Teach me to copy-paste from the sketchy untested post rather than the working code, use fabs and it'll work. 
 
Finally, we have a nice result!

Watch this:
https://youtu.be/vuXm64C_xso

Thanks to Preach for the code and to st1x51, Baker, madfox, metlslime, Qmaster, Spike, mankrip, c0burn and to all who helped me with this.

Here you can get progs.dat file to check it out:
https://drive.google.com/open?id=1U1Qb6nN3nTc9zwpRYZotGen_weFsBjIQ

And here is the source code (use dpextensions.qc or fteextensions.qc to compile it and add .float gravity in to the defs.qc):
https://drive.google.com/open?id=1Mck52dhvC7KKKOkC2N3QzGcgiBqQJtQU 
Reyond 
Tried in on E1M7 in FTE and it works nice.

Congrats! 
Improving The Code 
Is there any way to slow down flight? I have to make huge maps to make flight works properly.

And is there any way to change the value of horizontal speed loss?

Is there any way to make this work in standard quake without ising fork engines extensions? 
Improving The Code 
Is there any way to slow down flight? I have to make huge maps to make flight works properly.

And is there any way to change the value of horizontal speed loss?

Is there any way to make this work in standard quake without ising fork engines extensions? 
Generic Engines 
In theory, if you set the player's movetype to movetype_none in PlayerPostThink, and back to movetype_walk in PlayerPreThink, then you can insert a call to your (renamed) SV_PlayerPhysics at the start of PlayerPreThink and get a similar result.

In practice, doing so would leave you with no way to determine the user's intents (read: the .movement extension). You can work around this by using movetype_noclip instead of the movetype_none above, and to then try to guess the intents according to how the player's velocity accelerated between postthink->prethink (hint: dotproducts, see my earlier post). You'll also need to unwind those noclip-based velocity changes which will require some kind of shadow field, and clearing the velocity field in playerpostthink will make your .movement guessing logic more reliable.
Either way, it'll screw over prediction and get confused by knockbacks.

.gravity exists in all quake engines since hipnotic. You should be able to knock up a min/max function easily enough. I don't think there's anything else in there that's a problem. 
Engine Neutral 
My code runs fine in standard quake engines (once you define the .gravity field to enable that feature). You just run it in playerprethink, it uses standard player physics but then just rewrites the velocity each frame.

To slow it down, I guess you could do a number of things. One thing would be to reduce gravity further, even while facing down, so that you can't build up speed so well. Another would be to add a cap to fly_velocity after you calculate it:

if(fly_velocity > MAX_FLY_VELOCITY)
fly_velocity = MAX_FLY_VELOCITY;

Without this, the player can achieve speeds up to the global maximum speed an entity can reach, which defaults to 2000! 
Quake1_test 
On Quake Wiki I read the Trivial of the HellKnight.
The intention was to let Rangers spawn when the Hellknight is hit.

The same applies to the Axe_Ogre, which I managed to give some extra postures.
In this case, the Ogre should laugh when someone dies.
A more peculiar fact was pissing on the player's body.

If I were to start coding ... where should I start?
Something like pissing on the player would mean something to the player.qc and ai.qc.
Laughing when someone dies is a more general topic. 
Take A Leak 
PlayerDeathThink in client.qc, add a while loop that use

spot = find (world,classname,"monster_ogreaxe");
if ( spot ) {

spot.think = ai_leak1;
spot.leakentity = self;

}

Then set goalentity to the leakentity in ai_leak1 and self.enemy to self.leakentity. Do some ai_run()s inside your ai_leak1, 2, 3, etc. check for distance using vlen and start ai_leaking1 when close to player. In ai_leaking1, 2, 3 do your leak anim frames and play a sound effect maybe. 
Of Course 
You may need to add a .float to act as a bool so that you only check for axeogres in PlayerDeathThink once, something like

if(!foundleakers) {
foundkeakers = 1;
//while loop in here
}

And then in respawn() maybe add a foundleakers = 0; call. You might need to get fancy with coop though. 
First | Previous | Next | Last
You must be logged in to post in this thread.
Website copyright © 2002-2024 John Fitzgibbons. All posts are copyright their respective authors.