| #charset "us-ascii" | |
| #include "adv3.h" | |
| /* | |
| Tads-3 OccludingConnector | |
| Steve Breslin, 2004 | |
| email: versim@hotmail.com | |
| ==== | |
| Licence: | |
| Everyone is free to use this, but please alert me to your findings, and | |
| provide me with all improvements and modifications. | |
| ==== | |
| About this module: | |
| This module models a sense connector, like a window, which occludes some | |
| objects that might otherwise be sensed through it. If you're looking | |
| through a window, for example, it's a good bet that you can't see | |
| everything in the room you're looking into. The OccludingConnector | |
| allows you to model this, by defining what objects cannot be seen | |
| through the connector. | |
| This module formalizes a discussion on v-space over July and August | |
| 2004. (The curious reader is encouraged to read the 'occlusion' threads | |
| for these months.) | |
| We unanimously agreed that the behavior of the then-called | |
| OccludingConnector was counter-untuitive in that it didn't filter by | |
| sense-path, and didn't otherwise have any conceptual tie to | |
| sense-connection (despite the fact that it was initially designed for | |
| the purpose described above). | |
| As of Tads-3 release 3.0.8, this class has been renamed Occluder, which | |
| more accurately describes what it does: the Occluder makes a final pass | |
| over objects in sense-scope, and filters that table of objects | |
| irrespective of their sense-path to the POV. | |
| This new OccludingConnector, by contrast, filters an object only if it | |
| it is directly connected to the POV through the OccludingConnector in | |
| question. | |
| Brendan Barnwell proposed a solution roughly along the lines we adopt in | |
| this module. That preliminary solution added significant computation | |
| overhead to core processes. This module revises Brendan's initial | |
| solution, and mitigates almost entirely the computation overhead. | |
| In fact, if OccludingConnectors are used in a game, this module is | |
| somewhat faster (on average) than the library's current Occluder | |
| mechanism (Occluder.finishSensePath()), not only because it is directed | |
| only at potentially relevant objects in the sense tree, and not at the | |
| entire sense table, but also because it has the virtue, as Brendan | |
| wrote, of occluding "pieces of the actual sense tree, not just | |
| individual objects [and therefore] ensures that, if some object is | |
| invisible, all objects 'under' it in the sense tree are also invisible." | |
| ==== | |
| The algorithm is fairly simple: | |
| 1) when the sense-path building mechanism passes through an | |
| OccludingConnector, that connector is recorded as the | |
| libGlobal.curOccludingConnector. | |
| 2) when the sense-path building mechanism passes through a | |
| SenseConnector, the curOccludingConnector is reset to nil (or if it's | |
| also an OccludingConnector, reset to itself). | |
| This is because the object filtered by one occluding connector should be | |
| able to be added to the path through another connector downpath | |
| (provided the latter connector is not itself filtered by the occluding | |
| connector). This means that occluding connectors don't indirectly | |
| occlude, which seems slightly more intuitive and desirable. | |
| 3) The previous curOccludingConnector is recorded as | |
| oldOccCon, which is used to restore curOccludingConnector when the | |
| current connector is finished adding its connections. | |
| 4) An object is not added to the sense-path if it is occluded by the | |
| current occluding connector; also, the occluded object doesn't allow | |
| its containment children or containment parents to be added to the | |
| sense-path. (That is, objects further down the sense-path are also | |
| occluded.) | |
| Note: objects (and their containment children and containment parents) | |
| can be added to the sense-path through other connections, either | |
| indirectly downpath of the occluding connector, or on a separate path | |
| from the occluding connector. | |
| */ | |
| OccludingConnector: SenseConnector | |
| /* Do we occlude the given object in the given sense? | |
| * This returns true if the object is to be filtered, nil if not. | |
| */ | |
| checkOcclusion(obj, sense) | |
| { | |
| /* by default, we don't occlude anything. Here's where you | |
| * override on your instance of the OccludingConnector to | |
| * occlude certain objects. E.g.: | |
| *. if (obj == bookCase) | |
| *. return true; | |
| *. return nil; | |
| */ | |
| return nil; | |
| } | |
| ; | |
| modify libGlobal | |
| /* curOccludingConnector is the current OccludingConnector. It is | |
| * used to filter out any objects (and their containment children) | |
| * from the connector's locations (outward from the POV). | |
| */ | |
| curOccludingConnector = nil | |
| ; | |
| /* We modify Thing to check for the current occluding connector, if one | |
| * currently exists, so we don't add to the sense path objects which | |
| * are occluded by the occluding connector. | |
| * | |
| * Thus we modify sensePathToLoc(), sensePathWithin(), and | |
| * sensePathWithout(). Note that we don't have to modify | |
| * sensePathToContents(), because that's only an intermediary step, and | |
| * doesn't update sense path information itself. | |
| */ | |
| modify Thing | |
| sensePathToLoc(sense, trans, obs, fill) | |
| { | |
| if (location != nil) | |
| { | |
| /* check if my location is being filtered by the current | |
| * occluding connector. if it is, we do nothing. otherwise, | |
| * we proceed as usual. | |
| */ | |
| if (!libGlobal.curOccludingConnector | |
| || !libGlobal.curOccludingConnector. | |
| checkOcclusion(location, sense)) | |
| { | |
| location.sensePathFromWithin(self, sense, trans, obs, fill); | |
| } | |
| } | |
| } | |
| sensePathFromWithin(fromChild, sense, trans, obs, fill) | |
| { | |
| /* if I'm occluded by the current occluding connector, I don't | |
| * add myself (or my contents) to the sense path. | |
| */ | |
| if (libGlobal.curOccludingConnector | |
| && libGlobal.curOccludingConnector.checkOcclusion(self, sense)) | |
| return; | |
| /* otherwise, proceed as usual. */ | |
| inherited(fromChild, sense, trans, obs, fill); | |
| } | |
| sensePathFromWithout(fromParent, sense, trans, obs, fill) | |
| { | |
| /* if I'm occluded by the current occluding connector, I don't | |
| * add myself (or my contents) to the sense path. | |
| */ | |
| if (libGlobal.curOccludingConnector | |
| && libGlobal.curOccludingConnector.checkOcclusion(self, sense)) | |
| return; | |
| /* otherwise, proceed as usual. */ | |
| inherited(fromParent, sense, trans, obs, fill); | |
| } | |
| ; | |
| modify SenseConnector | |
| /* | |
| * Build a sense path from a container to me | |
| */ | |
| sensePathFromWithout(fromParent, sense, trans, obs, fill) | |
| { | |
| /* if I'm occluded by the current occluding connector, I don't | |
| * add myself (or my other containers (in my locationList)) to | |
| * the sense path. | |
| */ | |
| if (libGlobal.curOccludingConnector | |
| && libGlobal.curOccludingConnector.checkOcclusion(self, sense)) | |
| return; | |
| /* | |
| * if there's better transparency along this path than along any | |
| * previous path we've used to visit this item, take this path | |
| */ | |
| if (transparencyCompare(trans, tmpTrans_) > 0) | |
| { | |
| local transThru; | |
| /* remember the new path to this point */ | |
| tmpTrans_ = trans; | |
| tmpObstructor_ = obs; | |
| /* we're coming to this object from outside */ | |
| tmpPathIsIn_ = true; | |
| /* transmit to my contents */ | |
| sensePathToContents(sense, trans, obs, fill); | |
| /* | |
| * We must transmit this energy to each of our other | |
| * parents, possibly reduced for traversing our connector. | |
| * Calculate the new level after traversing our connector. | |
| */ | |
| transThru = transparencyAdd(trans, transSensingThru(sense)); | |
| /* if we changed the transparency, we're the obstructor */ | |
| if (transThru != trans) | |
| obs = self; | |
| /* | |
| * if there's anything left, transmit it to the other | |
| * containers | |
| */ | |
| if (transThru != opaque) | |
| { | |
| /* remember the old occluding connector. */ | |
| local oldOccCon = libGlobal.curOccludingConnector; | |
| /* in the rare case that we exit unexpectedly from the | |
| * following code block, we want definitely to restore | |
| * the libGlobal.curOccludingConnector. So we use a | |
| * try/finally structure to ensure this. | |
| */ | |
| try | |
| { | |
| /* reset the current occluding connector. */ | |
| libGlobal.curOccludingConnector = | |
| (ofKind(OccludingConnector) ? self : nil); | |
| /* transmit to each container except the source */ | |
| foreach (local cur in locationList) | |
| { | |
| /* if this isn't the sender, transmit to it */ | |
| if (cur != fromParent) | |
| cur.sensePathFromWithin(self, sense, | |
| transThru, obs, fill); | |
| } | |
| } | |
| finally | |
| { | |
| /* restore the old occluding connector */ | |
| libGlobal.curOccludingConnector = oldOccCon; | |
| } | |
| } | |
| } | |
| } | |
| ; | |
Xet Storage Details
- Size:
- 9.43 kB
- Xet hash:
- 28184dbd82ab6220e08f159a07721361ece8aebb12641bc37c7e6afbefb029de
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.