Jump to content
The Dark Mod Forums

Scripting: atdm:target_callObjectFunction and triggers and activating entities


Tels

Recommended Posts

I'm currently banging my head against the wall to solve a specific scripting problem. Maybe somebody else has some insight?

 

The setup:

 

* there is a door (pressure plate, trapdoor, whatever). It has a scripting object on it.

* I have a trigger_multi, that gets triggered when the player steps in it

 

The trigger should do some specific actions on the door, but they also depend on the state of the door (f.i. if already open, just stay open).

 

What I want: when triggered, the trigger calls a function on the scripting object of the door, passing ideally itself and whatever caused the triggern (player? crate?) along

 

If you link the trigger directly to the door, this does not work, as the trigger "activates" the door. It does so in the C++ code, which then calls ToggleOpen(). The upshot is that the scriptobject on the entities does not know about this. The only place I can set it up is with the "state_change_callback" function, however, in this case it is already too late. (for instance if I want to artificially not close the door again, the triggers "ToggleOpen()" will simple close the door again and then call the state_change_callback afterwards.

 

So, the current, somewhat working solution is to have an "atdm:target_callObjectFunction" hovering over the trigger. The trigger targets this entity. Every trigger than just causes one call to a selected function on all targets of this entity - in this case only the right door.

 

So far, so fine, but it means for every trap I need 3 entities (trigger, callObject and door). I've now tried to get away with only two objects.

 

* First idea (link trigger to door directly) does not work. I tried overriding the "Activate()" function in the script object, but that does not work (the c++ function is called, and the scripting side never sees the call).

* Second idea: link the trigger (actually, all triggers) to the same "activator" entity. This is of class atdm:target_callObjectFunction. Problem A: This entity cannot target itself (no entity can) so it cannot call a function on its own scripting object. Bummer.

 

Ok, so we link the central activator object to a central second activator object with a script object. This works. However, the scriptobject function is called with the following two parameters (if you use "passSelf" and "passActviator"):

 

Activator:Triggered activated from "activator" via "player1"

 

"actviator" is here the first central target entity. This, however, is useless, as we do not know which trigger triggered the action. (From this trigger we could figure out to which door it belongs).

 

It might be possible to find the target-door via:

 

* look at getOrigin()of player1 (or whatever triggered). Find all triggers nearby. Select the one closest, from this find the door it belongs to.

 

However, if you have a hundred triggers, this can become quite slow (plus, it is hard from the scripting side looping through entities).

 

Alternatively, for hundred traps one needs one hundred "intermidiate" entities that do basically nothing but relay a function call...

 

Has anybody any ideas how to solve this?

 

My solution would be to add another spawnarg to atdm:target_callObjectFunction like "passTrigger" and then the function gets called with one more argument. This also means it only works in TDM 2.03.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Aha!

 

There is a seemingly undocumented spawnarg on trigger multi called "triggerWithSelf". If set to 1, it relayes "self" instead of "actviator", so the function is called:

 

Activator:Triggered activated from activator via trigger_plate_0__576_96__100

 

Now we loose the info who triggered the trigger, but we know which trigger was triggered :)

 

That will work for now, but the abitlity to have both would surely come in handy.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

In a similar vein, I reckon target_callobjectfunctions and target_callscriptfunctions would benefit from spawnargs "pass" "whatever_value", "pass2" "some_other_value" etc, maybe with some helpful special values like $player1 or $entityname in general. They'd be useable for many more script events than they are now.

Link to comment
Share on other sites

Hm, yeah, but in my (or rather in this) special case it would be better if the trigger didn't need an extra entity just to call a function on a third entity.

 

A brief look in the code shows that this isn't possible with the current trigger_multi, tho, because the trigger will always activate it's targets (regardless of whatever) and it will always either pass along itself, or the entity that triggered it.

 

So you either get "player1", or "trigger" passed along, but never both, and get the target's "Activate()" called in the C++ code.

 

I'll try to write a patch for a new type of trigger_multi, that can instead just call a script function on any targeted entities, skippig the activation. Then you can decide wether this should be a new spawnclass, or rolled into the current trigger_multi via more spawnarg options (like "no_activate" "1", "event" "eventfunctiontocall" etc).

 

The system will still be not that flexible (as the current scripting intrrface does not allow functions with variable number of arguments), but it will be a bit better. :)

 

Making the callobjectfunctions or callscriptfunctions more versatile is orthogonal to that idea (and currently I don't need it :).

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Hm, seems I spoke to soon of it "working".

 

Sure, the trigger_multi responds to the player, and with "anyTouch" you can also make it respond to AI (or that is what I think, haven't tried). But it never responds to moveables like crates. I know this was already working, but I lost the prefabs that got it correct. :(

 

Sooo, I was looking into trigger_touch. While it does respond to "touches" from everything, it has much deeper problems, namely:

 

* the code looks really expensive. At every frame it compares every clipmodel in the entire map with its own clipmodel to see if they touch. Ouch. Imagine you have 500 moveables and 50 triggers...(easy for a large puzzle map nowadays)

* when something touches, it can only call a global function, passing the entity along. So we loose the info which trigger actually was triggered.

 

The second limit can be overcome by using a different function for every trigger (which my code actually could generate). However, this is quite ugly. Plus, the performance will really suck with a large map.

 

Ideally, trigger_touch would:

 

* use the physics system and only trigger if something collides with it (and be idle in between)

* be able to call an event function on it's own scriptobject (if it has any), the spawnarg should probably be "event" to be consistent, and pass it the entity that touched the trigger, plus call the same method on the scriptobject of its targets (if it has any targets and they have a scriptobject)

 

Than you could add a scriptobject to each trigger touch and it would know which trigger it belongs to and what entity touched the trigger.

 

Right now, trigger_touch is quite useless for my case.

 

So, after banging my head on the wall for the past 2 days, I've had it with triggers and tried looking "contacts" directly. (e.g. the pressure plate checks which entity is in "contact" with it and then acts accordingly).

 

There are, however, problems with that as well:

 

* the scripting interface has ActivateContacts(), but not "GetContacts()". So you can activate their physics, but not find out if you have any. Hrmpf.

* There is "touches(entity)", which could be used, but then each plate would run this against all other entities periodically, just to see if it touches. Hrmpf again.

 

Oh well...

 

Summa summarum, should I add bug tracker entries for the missing features and performance problems of triggers, as well as for entity.getContacts()?

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

getContacts() sounds like out could be useful. Or getNextContact(), if it's going to have to return several. Presumably it could use the same method of discovering them as activateContacts()

 

A couple of suggestions in the meantime: if you know the entity that triggered it, can you use getOrigin() to find out where it is and so which trigger has been hit?

 

A way to get contacts using the current script events could be to perform a box trace upward from the pressure plate.

 

On efficiency, is that clipmodel check how all triggers work, or is it just trigger_touch doing that? And when it cycles through all entities, is it doing a quick bounds check before it does a full interaction check? I think that's how the physics code detects collisions too by the way.

Link to comment
Share on other sites

getContacts() sounds like out could be useful. Or getNextContact(), if it's going to have to return several. Presumably it could use the same method of discovering them as activateContacts()

 

There are two ways, one is like the "getNextKey()" method that loops through things, the other one is (IIRC) the one used by targets, first a call like "GetNumTargets()", then "getTarget(X)" where X is 0 .. numTargets.

 

Either method would be fine, I guess.

 

And yeah, AFAIK each entity already knows the number of contacts, so activateContacts() just loops through them. (There is of course the secondary issue that two crates stack on top of each other count as only one contact to the underlying plate, but that can be ignored for now. I'd actually not even find out now what the contacts are, just that there are more than 0 :)

 

A couple of suggestions in the meantime: if you know the entity that triggered it, can you use getOrigin() to find out where it is and so which trigger has been hit?

 

Yes, although I need to somehow loop through all triggers, which is a bit cumbersome in scripting. (Technically, my code knows which triggers the map will have, so it can somewhere store the list. But looping through such a list and performing traces in scripting won't be fast, I think)

 

Or maybe it would work, afterall, one entity in a trigger would only hit one trigger and with the next point (trace) this might be doable.

 

A way to get contacts using the current script events could be to perform a box trace upward from the pressure plate.

 

Ah, yes, that might work, I forgot one can do traces in scripting. Thanx! :)

 

On efficiency, is that clipmodel check how all triggers work, or is it just trigger_touch doing that? And when it cycles through all entities, is it doing a quick bounds check before it does a full interaction check? I think that's how the physics code detects collisions too by the way.

 

It is only trigger_touch, it has some completely different code than all the other triggers. OTOH, all the other triggers do not react to a crate, only to players.

 

So basically, it is one of:

 

* do the check yourself (via trace in script, every 0.x seconds)

* use trigger_touch and hope it gets faster someday. Unfortunately, trigger_touch has a different, even more limited system for calling a script function (the doc is a bit inclear what it actually does, I need to check it)

 

I haven't actually measured performance, but I thought it might be best to use a system which will scale to a few hundred traps in one map.

 

Thanx for the ideas!

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Yes, although I need to somehow loop through all triggers, which is a bit cumbersome in scripting.

That'll be easier in 2.03 :)

 

I haven't actually measured performance, but I thought it might be best to use a system which will scale to a few hundred traps in one map.

 

I haven't looked at the trigger_touch code, but you *might* find it's better than it looks when you come to profile its performance. I shared your horror when I first looked at the collision code. Every mover's tracemodel is checked against the tracemodel of every moveable in the map to discover potential collisions every frame. But those checks use a very efficient short-circuit to get a quick answer in 99% of cases: a simple bounds check (average 2-3 int-sized comparisons). And it's probably the most efficient way to do it in a game that supports teleportation of movers. The more expensive precise checking gets used only for the very few entities each frame that pass that first check. Hopefully, target_touch does something similar?

 

Even so, it's a n-squared algorithm: count(movers) x count(moveables). So you're right, there'll no doubt be a limit after which adding more movers/moveables starts to get very painful very quickly if you exceed it.

Link to comment
Share on other sites

That'll be easier in 2.03 :)

 

Ah, yes, I look forward to that :)

 

Regarding new features in scripting a lot has happened between 1.08 and 2.0 and a lot of these were added by me or due to my feedback, and this immensly helped cutting down my scripting code. :)

 

My hope is now that if I find more of these cases quickly, the fixes can go into 2.03, instead of 2.04 and so are available to Swift Mazes sooner :)

 

I haven't looked at the trigger_touch code, but you *might* find it's better than it looks when you come to profile its performance. I shared your horror when I first looked at the collision code. Every mover's tracemodel is checked against the tracemodel of every moveable in the map to discover potential collisions every frame. But those checks use a very efficient short-circuit to get a quick answer in 99% of cases: a simple bounds check (average 2-3 int-sized comparisons). And it's probably the most efficient way to do it in a game that supports teleportation of movers. The more expensive precise checking gets used only for the very few entities each frame that pass that first check. Hopefully, target_touch does something similar?

 

Even so, it's a n-squared algorithm: count(movers) x count(moveables). So you're right, there'll no doubt be a limit after which adding more movers/moveables starts to get very painful very quickly if you exceed it.

 

Yeah, the check might be quick, but it stil ldoesn't scale. Back in the D3 days you could only have 2048 (or 4096? I forgot) entities per map, but we doubled the limit now at least twice.

 

It is now very common for maps having thousand movables, esp. since all loot entities are moveables (they usually do not move, because they vanish as soon as you pick them up. But they *could* move, f.i. if you shoot them with an arrow :).

 

Which means if you have a few hundred loot items, you suddenly have a few hundred movables more. Oops :)

 

Now, I can work around some of these issues, and some might never happen, but then, its better to be safe then sorry :)

 

As for trigger_touch, in a lot of cases in the past we have reduced work done by Not Doing Things So Often™. For instance, not doing traces every frame, but every second and so on.

 

For trigger_touch, it would be at least nice if you could tell it to wait between checks with a spawnarg. So it doesn't do all the checks 60 times a second when 20 times a second might be enough. :)

 

Likewise, there is no need to test entities that aren't in the same leaf than the trigger and so on. You are right that the physics engine can do this quickly, but trigger_touch uses a manual loop over all entities and blindly tests their clip models for collision... :rolleyes: (The trigger probably accounts for itself moving, too, but again, if neither trigger nor entity move, why test them again every frame?)

 

Edit: I forgot to mention that my biggest concern with trigger touch is that it calls a script function for every entity that it touches, every frame. Imagine you pile 50 items into such a trigger, that would probably bog down the system even quicker than the checks.

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

The good news is: I couldn't replicate the slowdown. :) Used Swift Mazes to build a testmap with 120 trigger_touch and about 1000 moveable entities. The map still runs at 60 FPS when I look at the opposite wall, even when 3 entities are thrown into triggers.

 

The bad news:

 

trigger_touch only allows calling a global function and passes it the entity that is inside the trigger. So for three entities in triggers, each frame the function is called three times, with three different entities.

 

As you start to throw things in the trigger, the game gets more and more laggy. A few dozend items in triggers and it still renders 60 frames, but the player movements become jerky. Whoops.

 

And while it would be possible to figure out which entity is in which trigger, this is quite expensive, and totally unnec. to redo every frame. A sort of "cache" might help (e.g. did I see the same entity at the same position? If so, re-use the trigger found already). That however is more developer work for me.

 

I might still go for the "manually do traces for each trigger", esp. as this will allow me to do away with the trigger entities completely, and also allows me to control the update frequency (e.g. only every 5 frames). Manual traces won't actually work, because the script will detect the first entity via the trace, but not the others. So it can never know that there are two objects sitting on it. This would really need a "getContacts()" method to ever work.

 

post-144-0-94845800-1408564189_thumb.jpg

post-144-0-55431600-1408564327_thumb.jpg

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Added bug reports with the wanted features for triggers and contacts:

 

http://bugs.thedarkmod.com/view.php?id=3823

 

http://bugs.thedarkmod.com/view.php?id=3824

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Hurray! It works!

 

And here is how:

  • each entity scriptobject registers itself with its calls at a central "brain" entity. For triggers, this class is simply "triggers".
  • when an entity touches a trigger_touch, a central function is called each frame (this is currently a bottleneck, but can hopefully be made better in 2.03)
  • the function swift_test_trigger( entity ) first checks the activator entity in question wether it already has worked on it in the last X seconds (to avoid doing it every frame)
  • swift_test_trigger( entity ) then calls brain.forEachEntity( "trigger", "activateOnTouch", activator );
  • brain.forEachEntity will cycle through all the registered entities of class "trigger" (it stores them in the spawnargs :)
  • for each trigger entity it will store the name of the activator in "swift_foreach_call" (as we cannot pass arguments to activateOnTouch), then calls trigger.activateOnTouch()
  • trigger.activateOnTouch() will see if it intersects with the activator entity (it cannot use touch(), as the trigger_touch entity is non-solid *sigh*)
  • When the two touch, the trigger will then fetch its target entity (or multiple) via swift_target (we cannot use "target" spawnargs, as this would automatically activate() them), and then call triggered( activator, trigger) on each target. On a pressure plate that sets bTriggered = true, which keeps the plate down as long as it stays true (checked via a second thread that repeatedly sets it to false and then waits a bit)

One optimization would also be to record on each entity which trigger it intersected last, and then testing this trigger first, instead of cycling through hundreds of triggers to find the same trigger again. The reason this would help is because entities left on a plate will cause endless triggering just to keep the plate down. (I have no real idea how to avoid this, tho. The only way would be to remove the trigger and somehow "watch" if the entity gets moved or removed from the plate. But this is more coding work.).

 

Anyway, the prefab is simply set up so that there is a very thin "trigger_touch" (1 unit high) floating directly over the pressure plate. And it is also bound to the pressure plate, so that when the plate is down, the trigger will move with it, and not hover over it. Otherwise very thin items might suddenly be below the trigger, causing a flip-flopping of the plate.

 

With only two entities and a big of scripting magic you can now throw a crate on the pressure plate, and it moves down and stays down. Remove the crate, and it pops up again.

 

All in all the entire adventure has cost me a few man-days stretched over nearly two weeks *phew*

 

Coming next: computing the weight of all entities on the plate and only triggering if "min_weight" is exceeded. :)

 

Edit: There is still a bug, one trigger causes all plates in the level to go down. Hopefully easy to find and fix :)

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Edit: There is still a bug, one trigger causes all plates in the level to go down. Hopefully easy to find and fix :)

 

Or not :( "trigger_touch" entities remove their clipmodel upon spawn and set clipModel(NULL) instead. So when you query their getMins() and getMaxs() via scripting, you end up with "0 0 0 0 0 0" *sigh*

 

Another day, another problem...

 

This was fixed by using the spawnarg "origin" instead of getOrigin() and by storing the mins/maxs manually as"swift_mins" and "swift_maxs" on the triger_touch entity. A bit cumbersome, but acceptable workaround, as each prefab needs to be done only once.

 

Now it works, but an unexpected side-effect of the thin, and moving-down trigger is that if you drop a crate edge on from some height (like a 1m),the crate causes the pressure plateto move down, bounces up a few cm, which makes it leave the trigger, which then makes the pressure plate come back again, until it finally settles down.

 

Looks realistic, but I think I'll add a certain delay into the plate before it triggers to make it less agile :D Otherwise it could cause double triggerings.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Or not :( "trigger_touch" entities remove their clipmodel upon spawn and set clipModel(NULL) instead. So when you query their getMins() and getMaxs() via scripting, you end up with "0 0 0 0 0 0" *sigh*

That's odd. I just took a look too. It basically removes itself from the world physics so that it can reimplement the collision code in its Think() method. There must be some small difference in behaviour that was wanted. Perhaps because it gets triggered by any moveable, not just those that call TouchTriggers().

 

Tempting to wonder whether it could do away with physics checks altogether and just do an origin check on moveables. But less drastically, a spawnarg to slow it down would help. Both the entity def and the wiki warn about its inefficiency. Only 3 maps use it including the training mission.

 

Now it works, but an unexpected side-effect of the thin, and moving-down trigger is that if you drop a crate edge on from some height (like a 1m),the crate causes the pressure plateto move down, bounces up a few cm, which makes it leave the trigger, which then makes the pressure plate come back again, until it finally settles down.

 

Looks realistic, but I think I'll add a certain delay into the plate before it triggers to make it less agile :D Otherwise it could cause double triggerings.

I love it when things work like that :) Even if you decide not to use it. Grats on getting your ingenious setup working.

Link to comment
Share on other sites

That's odd. I just took a look too. It basically removes itself from the world physics so that it can reimplement the collision code in its Think() method. There must be some small difference in behaviour that was wanted. Perhaps because it gets triggered by any moveable, not just those that call TouchTriggers().

 

Or maybe so that they are "invisible" to the physics system, so they can do check against moveables. The physics system either has entities non-solid (so they pass through), or solid, so they contact. But the trigger_touch wants a sort of "both". The current implementation also has the side-effect that each trigger is invisible to other triggers. However, sinceyou cannot move them..

 

Tempting to wonder whether it could do away with physics checks altogether and just do an origin check on moveables. But less drastically, a spawnarg to slow it down would help. Both the entity def and the wiki warn about its inefficiency. Only 3 maps use it including the training mission.

 

Which are the three maps? I'd like to lok at them.

 

The spawnarg would help dramatically. Even testing every 5th frame is more than enough for most cases.

 

Also helping would be to have a way to "suspend()" and "resume()" these triggers. Such a system could also be implemented with every entitiy, and every entity class would just honor a "suspended" flag. You could then temp. suspend triggers, doors etc. I'll file this as sep. tracker.

 

Yeah, it is a rather odd entity, and I think it stems from the original D3 days where it was used for teleporting or so - e.g. entities would always either destroy the trigger or remove themselves from it the very moment they touch it.

 

I love it when things work like that :) Even if you decide not to use it. Grats on getting your ingenious setup working.

 

Oh, yeah, I love it already, you can do so many things with it now. :wub:

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Hm, so I thought I'm clever by binding the trigger to the plate, and making it move down with the plate. However, that does not work as intended, because trigger_touch entities ignore any movement, binds or physics changes - as they store their original clipmodel and never change it at all.

 

This means if you drop a carrot on the plate, the plate and carrot move down, while the trigger stays. So the carrot leaves the trigger, and the plate and carrot move up again. This then repeats endlessly *sigh*

 

Guess that cannot be fixed without changes in the engine to trigger_touch's code base. :(

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Thanx for the list!

 

Yes, I think if the trigger is deeper, the plate will fire it. But I maybe I can make ignore it itself, tho. Another workaround is registering all trigger-activating entities with the plate and check if they still touch and if so, ignore the trigger-vanished-effect. This will be slower, tho.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Extending the trigger down a few units works beautiful. It does not get triggered by the door (not sure why, tho), but I have added the "don't allow to trigger yourself" safeguard anyway. :D

 

So far, everything seems to work, apples, carrots, crates, player. Except the small detail that flowerpots and orchids don't trigger the plate.. I have the hunch that "getMins()" and "getMaxs()" do not work for these, somehow.

 

Maybe because they are round? Investigating.... *sigh*

 

Edit: No, almost no moveable works if flipped onto its side. I guess it is because the origin for moveables is not at the center, but at the bottom. So my custom "boundsIntersect" routine does not compute the correct bounds of the entity to see if it is inside the trigger. Back to the drawing board then...

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Hm, yeah, the reason is the the origin for moveables is usually not at the center. I now remember that this also causes side-effects in the physics engine, because it assumes the center-of-mass is at the origin center. However, having the origin at the bottom makes for easier placement in DR.

 

Assuming the origin at the bottom is only half a fix, btw. In the following shot the blue entities have the origin at the floor, the others have it show where it is. So either I need to carry an offset list for every moveable, or find a better solution.

 

post-144-0-69434500-1408899215_thumb.jpg

 

However, for my code, actually I don't even need to find the intersection between the trigger and the object - the current code fails because none of the triggers intersect the activator-entity (which cannot be). But technically, just finding the closest trigger to the object would work. Rarely would two triggers be so close together that there could be a mismatch. And it's just a temp. solution to the "triggers don't pass themselves" which hopefully is fixed in 2.03, anyway :)

 

Edit: center-of-mass is apparently computed in the engine, but AFAICS it is always at the center of a clipmodel/tracemodel. (Which is better than "at the origin", but still not optimal, as you couldn't model "bowling pin" style entities).

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

So do getMins() and getMaxs() give an incorrect result for an model that's on its side? They're just reporting the physics bounds so if it's wrong then collision detection would be wrong too. Is that what you mean by side-effects in the physics engine? Can you see the wrong bounding boxes using g_showentityinfo?

Link to comment
Share on other sites

So do getMins() and getMaxs() give an incorrect result for an model that's on its side? They're just reporting the physics bounds so if it's wrong then collision detection would be wrong too. Is that what you mean by side-effects in the physics engine? Can you see the wrong bounding boxes using g_showentityinfo?

 

Sorry for the confusion, there are multiple issues going on at the same time:

  • Some moveables have the origin not at their center. Fixing the origins to be always at the center or floor for old entities, changing the origin cannot be done as it would break maps. However, if you want to find out the bounds of a moveable, you need to add getMins()/getMaxs to the origin, but you also need to know where the origin is in relation to the center. (Ugly long list of manual computed offsets :(

That is problem one, and only partial related to my problem. Afer some investigation, the following problems appeared:

  • trigger_touch gives wrong (empty) information for getOrigin(). (Strangely enough, getWorldOrigin() works. I'd have thought it would just give the same as getOrigin()
  • trigger_touch gives wrong (empty) information for getMins()/getMaxs()

I worked around that problem by trying to get the correct AABB of an entity via various tricks.

 

But finally, my script code had a bad assumption regarding the bounds: It did not correct for rotation. So if you take mins/maxs and add them to the origin (after you use the right values :), you still arrive at the situation that sometimes the trigger is triggered by an object, but the script does wrongly find that the trigger does not intersect the object.

 

I wanted to compute the real bounds of the moveables, but since there is not script event for it I found it to complicated to code the entire "rotate an AABB along the three axis" again in the scripting side. Would be doable, but I actually want to leave that feature behind me :)

 

So as a fallback I have written it now like so:

  1. For the entity, compare it to each trigger and take the one where the bounds intersect.
  2. If none intersect, take the closest trigger (origin <=> origin)

The entire scheme is still way to complicated, eats CPU like crazy, and still has the problem that one moveable can't be in two triggers at the same time (like a long plank), because the script code couldn't decide which trigger to use in this case. So even if I correctly find the "intersection" between a moveable and the trigger, it does not fix the underlying problems of the design.

 

But it works now for demonstration purposes.

 

Apart from the missing functionality (spawnargs on trigger_touch), there is nothing wrong in the engine per se. Did that make sense? :)

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Addendum: getMins()/getMaxs() do not return the true physics bounds, just the mins/max from the unrotated entity. AFAIK this info is not possible to be obtained via script (at least not easily).

 

Spent 10 minutes wondering why debugBounds does not work - it has to be sys.debugBounds() *sigh* Anyway, this image shows that the origin for the stool is at the botttom, and that the computed bounding box is wrong (you don't see it in the image, but the size of the bounding box does not change with rotation):

 

post-144-0-61455600-1408987160_thumb.jpg

 

A "getAABB()" script even would be really cool :) Or even better an Entity.intersects(vector mins, vector max) script event.

 

(The current trace() event you mentioned also suffers from the problem that the trace will always stop at the first entity, as far as I can see. So you could not find out if there are more than 1 entity in one box/line?)

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recent Status Updates

    • taffernicus

      i am so euphoric to see new FMs keep coming out and I am keen to try it out in my leisure time, then suddenly my PC is spouting a couple of S.M.A.R.T errors...
      tbf i cannot afford myself to miss my network emulator image file&progress, important ebooks, hyper-v checkpoint & hyper-v export and the precious thief & TDM gamesaves. Don't fall yourself into & lay your hands on crappy SSD
       
      · 3 replies
    • OrbWeaver

      Does anyone actually use the Normalise button in the Surface inspector? Even after looking at the code I'm not quite sure what it's for.
      · 7 replies
    • Ansome

      Turns out my 15th anniversary mission idea has already been done once or twice before! I've been beaten to the punch once again, but I suppose that's to be expected when there's over 170 FMs out there, eh? I'm not complaining though, I love learning new tricks and taking inspiration from past FMs. Best of luck on your own fan missions!
      · 4 replies
    • The Black Arrow

      I wanna play Doom 3, but fhDoom has much better features than dhewm3, yet fhDoom is old, outdated and probably not supported. Damn!
      Makes me think that TDM engine for Doom 3 itself would actually be perfect.
      · 6 replies
    • Petike the Taffer

      Maybe a bit of advice ? In the FM series I'm preparing, the two main characters have the given names Toby and Agnes (it's the protagonist and deuteragonist, respectively), I've been toying with the idea of giving them family names as well, since many of the FM series have named protagonists who have surnames. Toby's from a family who were usually farriers, though he eventually wound up working as a cobbler (this serves as a daylight "front" for his night time thieving). Would it make sense if the man's popularly accepted family name was Farrier ? It's an existing, though less common English surname, and it directly refers to the profession practiced by his relatives. Your suggestions ?
      · 9 replies
×
×
  • Create New...