2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/object/trait.cpp,v 1.16 1999/05/19 16:05:39 mahk Exp $
22 // Must be last header
25 IMPLEMENT_UNAGGREGATABLE_SELF_DELETE(cBaseTrait
,ITrait
);
27 ////////////////////////////////////////////////////////////
32 STDMETHODIMP_(BOOL
) cUninheritedTrait::PossessedBy(ObjID obj
)
34 return IntrinsicTo(obj
);
37 ////////////////////////////////////////
39 STDMETHODIMP_(ObjID
) cUninheritedTrait::GetDonor(ObjID
)
44 ////////////////////////////////////////
46 STDMETHODIMP_(IObjectQuery
*) cUninheritedTrait::GetAllDonors(ObjID obj
)
49 return CreateSingleObjectQuery(obj
);
51 return CreateEmptyObjectQuery();
54 ////////////////////////////////////////
56 STDMETHODIMP_(IObjectQuery
*) cUninheritedTrait::GetAllHeirs(ObjID
, eObjConcreteness
)
58 return CreateEmptyObjectQuery();
61 //------------------------------------------------------------
65 class cTraitFilterQuery
: public cFilterObjectQuery
69 BOOL
Filter(ObjID obj
) { return Trait
->IntrinsicTo(obj
);};
72 cTraitFilterQuery(IObjectQuery
* q
, ITrait
* trait
)
73 : cFilterObjectQuery(q
),Trait(trait
)
79 ~cTraitFilterQuery() { SafeRelease(Trait
);};
82 ////////////////////////////////////////////////////////////
87 STDMETHODIMP_(BOOL
) cInheritedTrait::PossessedBy(ObjID obj
)
92 return GetDonor(obj
) != OBJ_NULL
;
95 ////////////////////////////////////////
97 STDMETHODIMP_(ObjID
) cInheritedTrait::GetDonor(ObjID obj
)
99 ObjID result
= OBJ_NULL
;
100 IObjectQuery
* query
= GetAllDonors(obj
);
101 if (!query
->Done()) result
= query
->Object();
105 result
= (query
->Done()) ? OBJ_NULL
:query
->Object();
111 ////////////////////////////////////////
113 STDMETHODIMP_(IObjectQuery
*) cInheritedTrait::GetAllDonors(ObjID obj
)
115 IObjectQuery
* inner
= TraitMan
->Query(obj
,kTraitQueryAllDonors
);
116 IObjectQuery
* outer
= new cTraitFilterQuery(inner
,this);
121 ////////////////////////////////////////
123 STDMETHODIMP_(IObjectQuery
*) cInheritedTrait::GetAllHeirs(ObjID obj
, eObjConcreteness which
)
125 // @TODO: make this actually stop when another donor overshadows the property
126 IObjectQuery
* inner
= TraitMan
->Query(obj
,kTraitQueryAllDescendents
);
127 IObjectQuery
* outer
= new cConcretenessFilterQuery(inner
,which
);
133 ////////////////////////////////////////////////////////////
135 // Cached trait query
137 // A total rework of the depth first query
141 // Helper class for iterating across metaprop links
144 class cChildren
: public MetaPropLinks::cIter
147 typedef MetaPropLinks::cIter cParent
;
148 cChildren(const cParent
& p
)
157 MetaPropLinks
* links
= GetObjectMetaPropLinks(obj
);
159 *this = links
->Iter();
171 // Search stack frame
181 ObjID donor
; // current opinion of donor
182 ObjID through
; // where we get donor through
184 sStackFrame(ObjID n
= OBJ_NULL
,
186 IObjectQuery
* c
= NULL
,
203 class cSearchStack
: public cSimpleStack
<sStackFrame
>
211 class cCachedTraitQuery
: public cBaseObjectQuery
214 cAutoIPtr
<ITrait
> Trait
;
221 #define trSpew(x) if (do_spew) mprintf x
226 static IDonorCache
* Cache
;
227 static ITraitManager
* TraitMan
;
229 typedef cBaseObjectQuery cParent
;
234 // Given an object, build a query of successors, using cache where possible.
236 BOOL
Expand(sStackFrame
* frame
)
238 ObjID obj
= frame
->node
;
245 trSpew(("Expanding past %d\n",obj
));
250 // Look for us in the cache
251 if (Cache
->GetDonor(obj
,ID
,&donor
,&through
))
253 trSpew(("Cache: %d has %d through %d\n",obj
,donor
,through
));
254 if (donor
== OBJ_NULL
)
256 frame
->children
= cChildren();
260 frame
->donor
= donor
;
261 frame
->through
= through
;
263 if (frame
->donor
!= SkipMe
)
270 // fast forward the donor query past our "through"
272 for (; !rest
.Done(); rest
.Next())
273 if (rest
.Object() == through
)
276 frame
->children
= rest
;
282 frame
->children
= cChildren(obj
);
284 return !frame
->children
.Done();
291 // Open a stack frame for an object
295 sStackFrame
frame(obj
);
296 frame
.intrinsic
= Trait
->IntrinsicTo(obj
);
297 trSpew(("Opening %d intrinsic %d\n",obj
,frame
.intrinsic
));
302 void PropagateDonor(const sStackFrame
& finished
)
304 // Now, set the donor of the current top, based on what we already knew.
305 if (Stack
.Top().donor
== OBJ_NULL
) // if we don't already know
307 ObjID donor
= finished
.donor
;
308 if (finished
.intrinsic
)
309 donor
= finished
.node
;
310 Stack
.Top().donor
= donor
;
311 Stack
.Top().through
= finished
.node
;
316 // Unwind the stack to the next upward branch
321 if (Stack
.Top().node
== OBJ_NULL
)
324 while (Stack
.Top().children
.Done())
326 sStackFrame
& finished
= Stack
.Top();
327 // This node is finished. Cache it.
331 trSpew(("Closing %d Donor: %d through %d Intrinsic: %d\n",
335 finished
.intrinsic
));
338 Cache
->SetDonor(finished
.node
,ID
,finished
.donor
,finished
.through
);
342 // If we hit bottom, we're done.
343 if (Stack
.Top().node
== OBJ_NULL
)
346 // propagate the donor backward
347 PropagateDonor(finished
);
348 Stack
.Top().children
.Next();
354 // Go to the next object in the search, before filtering out non-donors
358 if (!Expand(&Stack
.Top()))
364 Open(Stack
.Top().children
.Object());
370 && YieldMe
== OBJ_NULL
371 && !(Stack
.Top().intrinsic
&& Stack
.Top().node
!= SkipMe
))
373 trSpew(("Skipping %d\n",Stack
.Top().node
));
379 // @TODO: Release these interfaces one day
383 Cache
= AppGetObj(IDonorCache
);
385 if (TraitMan
== NULL
)
386 TraitMan
= AppGetObj(ITraitManager
);
392 cCachedTraitQuery(ObjID obj
, ITrait
* trait
, TraitID id
)
400 do_spew
= config_is_defined("trait_cache_spew");
401 trSpew(("Start %s search on %d\n",Trait
->Describe()->name
,obj
));
414 while (Stack
.Top().node
!= OBJ_NULL
)
416 sStackFrame frame
= Stack
.Pop();
418 // if we got valid cache data, store it.
419 trSpew(("Unwinding %d. ",frame
.node
));
420 if (frame
.donor
!= OBJ_NULL
) // valid donor data
422 trSpew(("Caching donor %d through %d",frame
.donor
,frame
.through
));
423 Cache
->SetDonor(frame
.node
,ID
,frame
.donor
,frame
.through
);
425 PropagateDonor(frame
);
429 trSpew(("End search.\n"));
440 STDMETHOD_(BOOL
,Done
)() { return Stack
.Top().node
== OBJ_NULL
;};
441 STDMETHOD_(ObjID
,Object
)()
443 if (YieldMe
!= OBJ_NULL
)
445 return Stack
.Top().node
;
451 ITraitManager
* cCachedTraitQuery::TraitMan
= NULL
;
452 IDonorCache
* cCachedTraitQuery::Cache
= NULL
;
456 ////////////////////////////////////////////////////////////
463 cCachedTrait::cCachedTrait(const sTraitDesc
& desc
, const sTraitPredicate
& pred
)
464 : cInheritedTrait(desc
,pred
),
465 pCache(AppGetObj(IDonorCache
))
467 ID
= pCache
->NewTrait(&Desc
);
469 ConfigSpew("trait_id_spew",("Trait %s has id %d\n",desc
.name
,ID
));
474 cCachedTrait::~cCachedTrait()
480 // Go ahead and do as much of the first level of the search as you
481 // can, using the cache. This is particularly useful in the case
482 // of a concrete that is not in the cache
484 // TRUE means the out param is the donor you're looking for
486 // FALSE means donor is the first donor for search
489 BOOL
cCachedTrait::get_cached_donor(ObjID obj
, ObjID
* donor
)
493 MetaPropLinks
* links
= GetObjectMetaPropLinks(obj
);
497 MetaPropLinks::cIter iter
;
498 for (iter
= links
->Iter(); !iter
.Done(); iter
.Next())
500 ObjID qobj
= iter
.Value().dest
;
505 if (IntrinsicTo(qobj
))
507 pCache
->SetDonor(obj
,ID
,qobj
,qobj
);
512 if (pCache
->GetDonor(qobj
,ID
,&cachedonor
,&through
))
514 if (cachedonor
== OBJ_NULL
)
519 pCache
->SetDonor(obj
,ID
,*donor
,qobj
);
529 pCache
->SetDonor(obj
,ID
,OBJ_NULL
,OBJ_NULL
);
533 STDMETHODIMP_(ObjID
) cCachedTrait::GetDonor(ObjID obj
)
535 // check the cache first
536 ObjID cachedonor
,through
;
537 if (pCache
->GetDonor(obj
,ID
,&cachedonor
,&through
))
540 // Now check the first level
542 if (get_cached_donor(obj
,&donor
))
545 // If that fails, do the search
546 ObjID result
= OBJ_NULL
;
548 IObjectQuery
* query
= GetAllDonors(obj
);
549 if (!query
->Done()) result
= query
->Object();
553 result
= (query
->Done()) ? OBJ_NULL
:query
->Object();
559 STDMETHODIMP_(IObjectQuery
*) cCachedTrait::GetAllDonors(ObjID obj
)
561 return new cCachedTraitQuery(obj
,this,ID
);
564 STDMETHODIMP
cCachedTrait::Touch(ObjID obj
)
567 pCache
->Flush(FLUSH_ALL_OBJS
,ID
);
570 IObjectQuery
* objs
= TraitMan
->Query(obj
,kTraitQueryAllDescendents
);
571 pCache
->FlushObjSet(objs
,ID
);