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
Oh Also 
Preach that is an excellent post. 
Exclusion Zone 
I think once you accept that the monsters aren't gonna transition with you through a loading zone, you'd have to start with a recommendation to mappers that loading zones be designed to be isolated, free of items and largely inaccessible to monsters. You could enforce this somewhat on the mod side using code that runs with save game detection. When you notice that the player just loaded a saved game, findradius all the monsters near the player, and reset them back to their spawn location, optionally non-alert too.

That might be a neat idea to do with some of the further away monsters too, perhaps just the ones with patrol routes so that there's a limit to how much a sneaky player can exploit it. That would make it a bit more like Metal Gear Solid style zone transitions, which I think is a better model to have in mind than the Half-Life level continuity which is too hard to achieve. Mimicking the fade-to-black cutscene entering the loading zone and fade up on the other side would mask some of these "defensive moves"... 
 
http://quakewiki.org/wiki/FRIK_FILE

i don't know much about it, but i think it was used in that top-down RPG mod... which I am blanking on the name right now. 
Prydon Gate? 
well googling "frik file" gives me almost no more information other than that page you linked, and that Darkplaces supports it, so I'm guessing it's probably something that was supported in that one engine? 
 
well, yeah, in theory, any engine that supports the FRIK_FILE extension, but afaik, it's just DP.

it is a powerful system though, you could, in theory, transfer player info with a text file between savegame loading as well as use it to transfer over monsters in the trigger zone so that it behaved like half life. (although you might run into trouble if you brought over monsters that were not precached in the map originally). 
Transferring Monsters 
You might have to work really hard to get monsters which weren't present before loaded in the level, but I think it might be possible. The thing you have going for you is that you can notice early enough that you need to run extra precaches, as the transfer data is available on the initial frame.

The thing you have to contend with is the modelindex field in the saved game you're loading (assuming the player has already visited the level) - if you precache extra models you'll probably invalidate the existing modelindex values, especially since you'd likely have to do all of this extra precaching during worldspawn. Given the choice we would prefer to do all the precaches after the other spawn-functions, where they couldn't wreck things.

So what we'd have to do is lots of clean-up when we get our onload function running. We could start with the idea that we need to do setmodel(self, self.model) on all the entities at this stage, and then try and work out all the exceptions we need to flag. Should be possible to get that working on anything that isn't a modelindex entity hack, and you know, you can't always save that kind of thing in a mod.

I did get excited for a little bit when I thought about how much storage you'd need for a monster, and figured I could get it down to 34 bits a monster. For a second I was excited as the parms provide enough space for 9 such monsters plus a player! Then I remembered we didn't have the parms and was saddened... 
Quick Question 
Is it possible to remove (or at least, hide) the "Saving to..." line that appears as a console message on top left of the screen which displays the full path to .sav file, by modifying the progs file? Lately I noticed that longish message distracts me greatly. 
Not Really 
It's something that the engine does so in general you can't modify it away from the progs. If you had game saves which were being triggered from inside the progs (like an autosave feature or a hub system thing) you could mask it by appending ";echo \n\n\n\n" to the command string, but it's a bit hacky, you're wiping the whole notification area like that and making a bit of a mess of the console log.

If you wanted to adapt your favourite engine to not do it then it's a pretty easy change. Grab the source code for the engine, open host_cmd.c and comment out the following line:

Con_Printf ("Saving game to %s...\n", name);
 
 
Bind your save key to "con_notifylines 0; save quick.sav; con_notifylines 4" 
Not Worthy! *takes Hat Off And Bows At Preach* 
Like anyone can grab the source code for an engine, pop it into an IDE and hit compile and not get a gajillion errors.

fteqccgui.exe = easy QuakeC
<insert typical IDE here> = ERRORS!! The C++ Lord laughs at you and says "You're not supposed to be here!" 
@Spirit 
That only works in JoeQuake-like engines.

I don't think Quakespasm supports it nor FitzQuake. DarkPlaces uses "con_notify". 
 
@qmaster
that's more of a dependancy thingm combined with thesystem header files cahanging significantly with every single new ide version (especially between vendors).
qc just has NO dependancies - just compiler and vm. many qc mods still break to engine incompatabilities or compiler bugs. :P

@spirit
con_notifytime 0
set it back to 3 after 3 seconds have elapsed, or increase it over time.
there's a more portable solution for you. :) 
 
The con_notifytime 0 does appear for a split second. Probably 1 frame.

(Which is why the non-standard con_notifylines 0 is nice) 
Thinking Fast 
Somewhere on func_ recently there was discussion on 20fps animation in Quake, and one of the obstacles was that the QC might cope badly with them if the engine dropped below that framerate. Here in partial answer to that problem is a blog post:

https://tomeofpreach.wordpress.com/2015/03/28/thinking-fast/

It's also me trying to rewrite a much older post I made on func_ trying to describe exactly how the Quake engine deals with time and think functions. I hope the new approach taken makes it a bit easier to understand. 
QuakeWorld 
aka sv_gameplayfix_multiplethinks (exists as a cvar in both fte+dp)
vanilla quakeworld already performs multiple thinks per second if the real framerate drops too low somehow.
it'll just keep running think functions until it catches up with the actual time.
(yes, this means that really low think intervals is just wasteful). 
 
how does it behave if you set nextthink = 1? 
 
gives up because it didn't progress at all.
there's a lot of mods that use self.nextthink = time;
the time global will presumably stick one frame behind.
its probably also worth mentioning that quakeworld really doesn't do the whole fixed interval thing at all, favouring latency over smoothness (vanilla doesn't support interpolation at all).
it doesn't exactly do the order thing either, if you abuse newmis 
 
gives up because it didn't progress at all.

does it think exactly once though? otherwise, it is another compatibility break with winquake. :(
i use nextthink = 1 when I need one single think update on the very next frame. 
 
@necros
vanilla quakeworld: that's an infinite loop
fte: exactly what you want: exactly one think per physics frame if you set nextthink <= time like that.

@Preach
'If the drop to 10fps was only temporary then the entity may �catch up� once the framerate increases, but this will result in strange extra-fast behaviour in the meantime'
This is incorrect. time will be clamped to the start of the frame, even in vanilla nq.
This means that while an entity may run slowly in nq, it will NOT catch up / run fast once framerates increase.
(the nextthink = 1 example should give the time value that was correct at the start of the frame in every engine, due to this clamping - it will still be meaningful for the thinks of other entities although it'll happen about half a frame sooner than it otherwise would). 
Thanks Spike 
I've updated the article, although now I'm a bit worried that I have the way frametime increases the wrong way round. From double-checking the source (what I should have done rather than writing the article from memory!) it looks like thinks are actually triggered when they're less than the true time plus the next frame length. This means the startframe function has the true time, but it's actually earlier than all the times thinks will occur this frame. The engine still lies about what time it is - but lies that it's the future rather than the past.

When it's morning and I'm less prone to making a mistake I'll put a second amendment in the article and fix all the tables 
 
depends how you define it.
startframe gets the time at the start of the frame. once the frame is done, time is updated to starttime + frametime. ie: the physics frame covers a block of time rather than a single absolute timestamp. Its still lying about being in the past (time should be starttime+frametime), but logically it somewhat IS in the past, as its trying to update those entities that still have thinks pending since the last frame. 
Truth, Justice And All That 
Yeah, the problem is that the article is taking the position that any function not told that time = starttime + frametime is being lied to (and if you're lied to, you can't successfully simulate multiple thinks). Under that definition, the startframe function is also lied to, since it is told time = starttime. However, it can always reason about which think functions should run this frame from this position, so I think it would be better to redefine what's lying etc... 
 
'but lies that it's the future rather than the past'
startframe is told its in the past, not the future.
think times are unordered, but always within the time period defined by the frame.

if you want to update the entities to the _current_ time then you should be using while(ent.nextthink<=time+frametime)
just make sure you don't do this on movetype_push entities. 
Projectiles 
I'm trying to obtain a kind of a flamethrower for a model.

I'm a bad coder, everything just happens on the experience of addding to an excisting code.

To get the explod2.spr working I made a flame script that looks like this:


$frame 0 1 2 3 4 5 6 7 8 9 10 11

void() flam_stand1 =[ 0, flam_stand2 ] {};
void() flam_stand2 =[ 1, flam_stand3 ] {};
void() flam_stand3 =[ 2, flam_stand4 ] {};
void() flam_stand4 =[ 3, flam_stand5 ] {};
void() flam_stand5 =[ 4, flam_stand6 ] {};
void() flam_stand6 =[ 5, flam_stand7 ] {};
void() flam_stand7 =[ 6, flam_stand8 ] {};
void() flam_stand8 =[ 7, flam_stand9 ] {};
void() flam_stand9 =[ 8, flam_stand10 ] {};
void() flam_stand10 =[ 9, flam_stand11 ] {};
void() flam_stand11 =[ 10, flam_stand12 ] {};
void() flam_stand12 =[ 11, flam_stand13 ] {};
void() flam_stand13 =[ 12, flam_stand1 ] {};



void() model_flam =
{
precache_model ("progs/explod2.spr");
precache_sound ("ambience/waterfal.wav");
ambientsound (self.origin, "ambience/waterfal.wav", 0.5, ATTN_NORM);

self.solid = SOLID_BBOX;
self.movetype = MOVETYPE_NONE;

setmodel (self, "progs/explod2.spr");

setsize (self, '16 16 16', '24 24 24');
self.think = flam_stand1;
self.nextthink = time + 0.1;
};


void() FlameExplode =
{
// T_RadiusDamage (self, self.owner, 120, world);

// WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
// WriteByte (MSG_BROADCAST, TE_EXPLOSION);
// WriteCoord (MSG_BROADCAST, self.origin_x);
// WriteCoord (MSG_BROADCAST, self.origin_y);
// WriteCoord (MSG_BROADCAST, self.origin_z);

// BecomeFExplosion ();

T_RadiusDamage (self, self.owner, 40, world);
sound (self, CHAN_VOICE, "weapons/r_exp3.wav", 1, ATTN_NORM);

WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
WriteCoord (MSG_BROADCAST, self.origin_x);
WriteCoord (MSG_BROADCAST, self.origin_y);
WriteCoord (MSG_BROADCAST, self.origin_z);

self.velocity = '0 0 0';
self.touch = SUB_Null;
setmodel (self, "progs/explod2.spr");
self.solid = SOLID_NOT;
s_explode1 ();

};

void() FlameTouch =
{
if (other == self.owner)
return; // don't explode on owner
if (other.takedamage == DAMAGE_AIM)
{
FlameExplode();
return;
}

sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM); // bounce sound

if (self.velocity == '0 0 0')
self.avelocity = '0 0 0';
};

/*
================
W_FireFlame
================
*/
void() W_FireFlame =
{
local entity missile;

self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

self.punchangle_x = -2;

missile = spawn ();
missile.owner = self;
missile.movetype = MOVETYPE_BOUNCE;
missile.solid = SOLID_BBOX;
missile.classname = "flame";

// set missile speed

makevectors (self.v_angle);

if (self.v_angle_x)
missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;


else
{
missile.velocity = aim(self, 10000);
missile.velocity = missile.velocity * 600;
missile.velocity_z = 200;
}

missile.avelocity = '300 300 300';

missile.angles = vectoangles(missile.velocity);

missile.touch = FlameTouch;

// set missile duration
missile.nextthink = time + 2.5;
missile.think = FlameExplode;

setmodel (missile, "progs/explod2.spr");
setsize (missile, '0 0 0', '0 0 0');
setorigin (missile, self.origin);
};


I compared the sprite with the grenade code of the ogre.
If I can stop it tumbling and make a straight foreward replacement of the sprite
it could give an idea of a flame thrower.

The result of this is, that the sprite only gives one frame, and all projectiles head east.

What should I do to prevent this? 
Madfox 
You aren't running the model_flam function anywhere, which partly explains why you aren't getting an animation. 
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.