Chapter - Speeding up Concealed Possessions Checking [The "deciding the concealed possessions of something" activity is a huge performance bottleneck when parsing, even if the actual concealed possessions rulebook is empty, simply because it is called such a ridiculous number of times when parsing a complex verb's syntax list. With these modificiations, the activity need run just once for every object in scope before the parsing proper begins.] [We define a new property, "scope_concealed," and give it to every thing and direction in the game. I don't think it's possible for any other objects to come into scope, although it's very possible someone will come up with an example to prove me wrong. (There is of course the "any" adjective, but the parser deals with that in a completely different way.)] Include (- with scope_concealed, -) when defining a thing. Include (- with scope_concealed, -) when defining a direction. Include (- ! The following two functions run just before the parsing phase begins. They loop over each object in scope and run the "testing concealed possessions of" activity on it. The result is stored in that object's scope_concealed property. Now we need only reference that property to determine concealment while parsing the command proper. [ CheckScopeConcealed obj; obj.scope_concealed=TestConcealment(parent(obj),obj); ]; [ LoopOverScopeConcealed routine act x y a al; x = parser_one; y = scope_reason; a = actor; al = actors_location; parser_one = routine; if (act == 0) actor = player; else actor = act; actors_location = ScopeCeiling(actor); scope_reason = LOOPOVERSCOPE_REASON; SearchScopeConcealed(actors_location, actor, 0); parser_one = x; scope_reason = y; actor = a; actors_location = al; ]; ! SearchScopeConcealed is actually the original version of the SearchScope function. It is now used only before parsing proper begins, called from LoopOverScopeConcealed. ! Yes, it might have been cleaner to implement conditionals within these functions instead of making a whole new set, but I'm very concerned about reducing overhead to a bare minimum. I'm happy to trade bytes and (perhaps) a bit of elegance for a bit more speed. [ SearchScopeConcealed domain1 domain2 context i; if (domain1 == 0) return; ! (a) if (scope_token) { scope_stage = 2; #Ifdef DEBUG; if (parser_trace >= 3) print " [Scope routine called at stage 2]^"; #Endif; if (indirect(scope_token) ~= 0) rtrue; } ! (b) BeginActivity(DECIDING_SCOPE_ACT, actor); if (ForActivity(DECIDING_SCOPE_ACT, actor) == false) { ! (c.1) if ((scope_reason == PARSING_REASON) && (context == MULTIINSIDE_TOKEN) && (advance_warning ~= -1)) { if (IsSeeThrough(advance_warning) == 1){ ScopeWithinConcealed(advance_warning, 0, context); } } else { ! (c.2) if ((scope_reason == PARSING_REASON) && (context ~= CREATURE_TOKEN) && (indef_mode == 0) && (domain1 == actors_location)){ ScopeWithinConcealed(compass); } ! (c.3) if (domain1 has supporter or container) DoScopeAction(domain1); ScopeWithinConcealed(domain1, domain2, context); ! (c.4) if (domain2) { if (domain2 has supporter or container) DoScopeAction(domain2); ScopeWithinConcealed(domain2, 0, context); } } ! (c.5) if (thedark == domain1 or domain2) { DoScopeActionAndRecurseConcealed(actor, actor, context); if (parent(actor) has supporter or container) DoScopeActionAndRecurseConcealed(parent(actor), parent(actor), context); } } EndActivity(DECIDING_SCOPE_ACT, actor); ]; ! Again, this is actually the original version of the ScopeWithin function. [ ScopeWithinConcealed domain nosearch context obj next_obj; if (domain == 0) rtrue; ! Look through the objects in the domain, avoiding "objectloop" in case ! movements occur. obj = child(domain); while (obj) { next_obj = sibling(obj); if ((domain == actor) || (TestConcealment(domain, obj) == false)){ DoScopeActionAndRecurseConcealed(obj, nosearch, context); } obj = next_obj; } ]; ! And the original of DoScopeActionAndRecurse. [ DoScopeActionAndRecurseConcealed domain nosearch context i ad n obj next_obj; DoScopeAction(domain); ! (a) if ((domain ~= nosearch) && ((domain ofclass K1_room or K8_person) || (IsSeeThrough(domain) == 1))) { obj = child(domain); while (obj) { next_obj = sibling(obj); if ((domain == actor) || (TestConcealment(domain, obj) == false)) DoScopeActionAndRecurseConcealed(obj, nosearch, context); obj = next_obj; } } ! (b) if (domain provides component_child) { obj = domain.component_child; while (obj) { next_obj = obj.component_sibling; if ((domain == actor) || (TestConcealment(domain, obj) == false)) DoScopeActionAndRecurseConcealed(obj, 0, context); obj = next_obj; } } ! (c) ad = domain.&add_to_scope; if (ad ~= 0) { ! Test if the property value is not an object. #Ifdef TARGET_ZCODE; i = (UnsignedCompare(ad-->0, top_object) > 0); #Ifnot; ! TARGET_GLULX i = (((ad-->0)->0) ~= $70); #Endif; ! TARGET_ if (i) { ats_flag = 2+context; RunRoutines(domain, add_to_scope); ats_flag = 0; } else { n = domain.#add_to_scope; for (i=0 : (WORDSIZE*i)i) DoScopeActionAndRecurseConcealed(ad-->i, 0, context); } } ]; -) after "Parser.i6t". [The ScopeWithin called from the parser proper now just references the scope_concealed properties of the objects in question, rather than running the activity.] Include (- [ ScopeWithin domain nosearch context obj next_obj; if (domain == 0) rtrue; ! Look through the objects in the domain, avoiding "objectloop" in case ! movements occur. obj = child(domain); while (obj) { next_obj = sibling(obj); if ((domain == actor) || (~obj.scope_concealed)){ DoScopeActionAndRecurse(obj, nosearch, context); } obj = next_obj; } ]; -) instead of "ScopeWithin" in "Parser.i6t". [A new DoScopeActionAndRecurse, same drill as above.] Include (- [ DoScopeActionAndRecurse domain nosearch context i ad n obj next_obj; DoScopeAction(domain); ! (a) if ((domain ~= nosearch) && ((domain ofclass K1_room or K8_person) || (IsSeeThrough(domain) == 1))) { obj = child(domain); while (obj) { next_obj = sibling(obj); if ((domain == actor) || (~obj.scope_concealed)) DoScopeActionAndRecurse(obj, nosearch, context); obj = next_obj; } } ! (b) if (domain provides component_child) { obj = domain.component_child; while (obj) { next_obj = obj.component_sibling; if ((domain == actor) || (~obj.scope_concealed)) DoScopeActionAndRecurse(obj, 0, context); obj = next_obj; } } ! (c) ad = domain.&add_to_scope; if (ad ~= 0) { ! Test if the property value is not an object. #Ifdef TARGET_ZCODE; i = (UnsignedCompare(ad-->0, top_object) > 0); #Ifnot; ! TARGET_GLULX i = (((ad-->0)->0) ~= $70); #Endif; ! TARGET_ if (i) { ats_flag = 2+context; RunRoutines(domain, add_to_scope); ats_flag = 0; } else { n = domain.#add_to_scope; for (i=0 : (WORDSIZE*i)i) DoScopeActionAndRecurse(ad-->i, 0, context); } } ]; -) instead of "DoScopeActionAndRecurse" in "Parser.i6t". [We run through objects in scope just once now, right after reading a command, to check concealment. After that, the parser just references the scope_concealed properties of objects in question.] To record concealment: (- LoopOverScopeConcealed(CheckScopeConcealed); -). After reading a command: record concealment.