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
Yep 
 
LTIME PAINS 
How is ltime calculated? In otherwords, why would this code have two separate entities start their movement at different times? It's starting essentially func_trains (they're not really but they are MOVETYPE_PUSH and move the same way)

local float eltime;

eltime = self.ltime; //force ltime to be the same?

stemp = self;
self = self.target1;
self.ltime = eltime;
self.think = qtrain_start;
self.nextthink = (self.ltime + 0.100);
self = stemp;

stemp = self;
self = self.target2;
self.ltime = eltime;
self.think = qtrain_start;
self.nextthink = (self.ltime + 0.100);
self = stemp;

...
...
...

I have already tried the servertime trick with StartFrame, but they still start at different times. Apparently they aren't starting on the same frame??!? 
Ltime 
ltime is incremented for the number of seconds the door or whatever has been moving for. If the door is blocked for whatever reason, ltime will not change and the door will freeze in place.
this ensures that the think is scheduled for when it arrives at its destination rather than in true seconds.
the whole point about ltime is that its local to the entity in question, and is allowed to freeze as required. If you want your trains to work as a pair, you'll need to ensure that if one is blocked, the other also stops moving, and take extra care to re-sync them when they start moving again.

seriously, why has noone made a map where you have to lure monsters to a certain area to block a train to desync them so you can jump from one moving platform to another? :s 
Spike Is A Genious 
That's a cool idea! Secret area maybe?

Okay. So bprint debugging has confirmed that the nextthink's are infact the same for both entities. Does this mean that they will start at the same time though? Obviously no. Still no cookies.

So I'll step it through. Preach? Correct me if I'm wrong anyone.

Function sets think for two entities regardless of targetname (entities are stored as entities not targetname, target1 and target2 are .entities)

Function sets nextthink for two entities

Nextthinks are confirmed the same, bprint to console so I know.

Entities are set to start in nextthink seconds

This frame just set the nextthink time, so these entities will get called in the same frame in ltime+x. Well, theoretically they would. What's the tolerance for time when attempting to get two entities to start on the same frame in x seconds? + or - 1 frame? 10 frames?

These entities are not moving so ltime is not increased.

They never start then theoretically. But they start anyway just to confuse me.
1st one starts after about 2.5 secs
2nd one starts after about 4 secs

So how is ltime incremented while an object like a door is NOT moving???? 
Potential Reason 
Remember that the quake engine loops through entities and applies physics to them in a sequence. It can't process them "simultaneously". Imagine that we've already run physics on target1 this frame, but that we haven't run physics on target2. If we run your function at that moment, the two will be synced when the function ends, but by the end of the frame we'll have run an extra tick of physics on target2, desynchronising them again. Could that be what's occuring? 
 
a bmodel entity must have it's nextthing greater than ltime or it will not move. 
 
its a bit clumsy, as it tries to get the maths precise so that the train stops exactly where it needs to be even if its only a fraction of a frame away.
the ramifications of this is that ltime does not increase if self.ltime>=self.nextthink.

thus if you want a bsp object to move or spin, it MUST have a valid nextthink set.

remember that the timings are precise in that the movement covers the correct distance, rather than it arrives at the projected time. this isn't some national railway with precise time keeping. Its just trying to avoid moving through walls if you've got a low framerate.

If you have some third entity clearing everything out but the velocity, one pusher can still get blocked and ltime will not update while the other starts moving around freely.

An entire second and a half in 4 is too high for any precision issues. Are you sure its not getting blocked or anything? You're sure that you're using .ltime and not time when setting nextthink, etc? 
FIXED!! 
Turns out I had forgotten to recompile with ltime and it was still using time. However, even though with ltime it was reealllly close, I still had a discrepancy of about 0.8 to 1 game unit when the two entities started from the same point.

-Edit!-
------------------
I FIGURED IT OUT!!!!!!!

There is a latency, a lag, within a frame from one object to another when the physics are calculated. The two entities had been created several dozen entities apart in the map (about 15 min of mapping on another part of the map while I was thinking about what to do). This means that several dozen entities needed to be ran before the other entity would run, causing enough time lag for a small gap in start times.

The fix?

Duplicated lagging entity. Deleted original. Placed duplicate entity back in the same spot.

Done.

And that only took me 2 months to figure out.
Wait till you see what I use it for in my new map! 
Fish! 
An obscure bug with the monster that nobody cares about.

Basically, they don't move when scraping their heads against the top of the water volume they're in.

Anyone have a fix for this? I can trade you a fish model with a normal sized head when it dies... 
 
A simple way is to use pointcontents to check a point above the fish's head and if it is out of the water, reset the monster's z origin to the previous frame so that it can still move horizotally, but won't move upwards into a position where it will be stuck.

This has the small chance of making a fish get stuck in a wall, but you'd have to make a very specific shape of wall and water edge for it to happen. 
Bait On A Hook 
Banging their heads on the surface usually occurs when they're trying to swim up to the level of an .enemy who's higher up than them. You can create a dummy entity called worm. When you want a fish to swim, stash their proper enemy in a temporary variable, and set their .enemy variable to the worm. You can then position the worm to lead them where you want - place it in the direction of the real enemy, but at an appropriate height vertically. Remember to reset .enemy once you've called the ai movement functions. 
Aha 
The issue was obvious really - like what happens to a scrag when it hits ceiling and you're above it.

Thanks guys. 
Doing Things The Bad Way 
The above post is the good way to do things. But we don't like the proper way of doing things, we want to save an entity! The bad way to do things is to instead stash the old height of the fish's enemy, move THE REAL ENEMY down, then instantly restore that enemy's position when the movement function is done. If you're breaking the rules, you should go all the way and skip the call to setorigin - that would only cause actual collsions to occur when you want to avoid that happening. God help you if the fish collides with the enemy though... 
Field Pointers 
New QC article today - on field pointers. There are two - yes TWO - reasons to read today's article! One is if you'd like to learn what a field pointer is in QC and how to use them. They help you write generic code.

Even if you don't care about the technical knowledge, the article solves a practical problem: how do you code an entity which has templates? Templates here are where a mapper can set a single value on your entity and get a bunch of default values applied, and the mapper can selectively override these defaults.

http://tomeofpreach.wordpress.com/2013/08/07/field-pointers-in-qc/ 
 
As always a good read and I learned something new about function parameters. :)

I�m going to share a snippit of code from Quoth
You should one day publish that code, I imagine it would be awesome see how you organize and structure stuff. I would love to see how you implement features. 
Gleaming Spires 
Don't be daft, I'm showing off the good bits! The rest of it is all cludged together. Bit by bit I'll try to blog the how-to behind all the good bits, so that everyone can make a mod better than Quoth. 
 
Don't be daft... all the bad bits are me. :P 
Almost Forgot 
Here's the fixed death frame fish model:

https://www.dropbox.com/s/eluwi8hwa9ot0yd/fixed_fish.7z

It's also got some 'small' frames, if anyone wanted to make, say, a school of piranha. 
 
oh btw, that's a great post about the field-as-arguments. 
Nonsense Necros... 
I was considering posting an awful bit of Quoth code for the sake of comparison - and the function that sprang to mind immediately was the coop removal function, which I wrote entirely myself! 
More Field Pointers 
So here's a more thoughtful counterpoint to the lovely beautiful code stuff above. It's a second blog post about field pointers on the surface, but it's also about the battle writing clean code and battling the technical limits. There's also an exciting cliffhanger for part III...

http://tomeofpreach.wordpress.com/2013/08/09/going-overboard-with-field-pointers/

Since this is really a mapping forum, I'll give you guys the cuttings about how this stuff relates to maps. Yes, it is possible to overflow the stack if your triggers get too complicated, even in stock Quake. You're probably doing some serious neg!ke style map hacks if you get to that point. There's a workaround if you ever do hit the limits though: give one of your triggers a delay. The stack will be reset between the triggers which precede and follow the delayed trigger. You can think of this like giving the engine a chance to catch its breath! 
Revenge Of The Field Pointers 
http://tomeofpreach.wordpress.com/2013/08/23/getting-even-with-field-pointers/

Here's an article with two ways of fixing the problems from last time - using macros and using arrays.

I was going to go further, try and create a system where you could have an arbitrarily long chain of entities using each other without overflowing the stack. The plan was to split SUB_UseTargets in two, the first half would make a global linked list of targetted entities, and the second half would work through this list and call their use functions. If one of these use functions called SUB_UseTargets recursively, it would add the entities to the global list but then return, and let the original invocation call the use functions.

There are two problems with the plan though. One is that the order of which use functions start and complete has been changed by this transformation, which could break maps in some unanticipated way. The bigger problem can be found in this discussion:
http://www.celephais.net/board/view_thread.php?id=4&start=13098&end=13108
It's possible, if maybe unlikely, for an entity to legitimately be triggered recursively. This doesn't work well with the linked list. There might be ways to fix this, possibly by letting the function recurse in these places, but I'll have to give it some more thought. 
Chainlist = Findradius(origin,radius) 
It is the subtle things in life that cause the most problems ...

The findradius QC command can work differently in Darkplaces compared to Fitz engines. It may not seem like much of a difference but it can cause serious problem if you are not aware of it.

The Fitz engine ignores all entities with .solid=SOLID_NOT when creating a chain list for the findradius command. This is a good thing because certain types of entities (particles, delay spawns) can be excluded from this function easily and included when needed.

Darkplaces (older versions) does not seem to have any exceptions, it will gladly find ANY entity within range and create a chain list. The problem is the findradius command will freely 'corrupt' the chain field and if you use it for anything else then those lists will start to fall apart.

I recently switched over to a chain list for my particle system because it was crazy to keep creating/deleting them all the time. So I cycle round a chain list and it works fine in Fitz. In Darkplaces the lists were getting corrupted and I did not know why and then I finally found the problem, Findradius was finding the particles (my system can use sprites or models) and changing the chain field.

It seems Preach has run into this problem (post#962) but I could not find any other forum links. Apparently there is a sv_gameplayfix_blowupfallenzombies to fix this but the naming of it is not easily recognizable as something to do with the findradius command.

I tried to find a way for Darkplaces to exclude entities from the Findradius command. I set the model field to an empty string or "null" (dp documentation) and setting the bbox and size to zero, but nothing seems to make a difference.

I assume the latest version of Darkplaces fixes this by disabling the fallenzombie gamefix, but not everyone using DP updates all the time. It certainly easy to code for this change, but it is frustrating to find out about this the hard way. 
Compatibility Battles 
Yeah, the more experimental engines are a bit more of a hassle to keep combatible with. Perhaps we should try and create a little community config file which restores darkplaces etc. to standard quake compatibility. Then add it to quake.rc to execute between config.cfg and autoexec.cfg. Incidentally that's the best time to aply strongly recommended cvar defaults - it replaces whatever was autosaved in the config last time, but lets people who think they know what they're doing override them in the autoexec file.

In your specific predicament, I'd recommend creating an extra entity field, perhaps:

.entity p_chain;

...and use that to replace your code's use of chain
Could Also Contact LordHavoc 
He's usually on the ball with these types of fixes and happy to help out. 
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.