2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/engfeat/simpdmg.cpp,v 1.30 2000/02/05 19:37:27 adurant Exp $
49 // Must be last header
52 ////////////////////////////////////////////////////////////
53 // cSimpleDamageModel implementation
57 #define max(a,b) ((a) > (b) ? (a) : (b))
60 F_DECLARE_INTERFACE(IPropertyManager
);
61 F_DECLARE_INTERFACE(ILinkManager
);
63 static struct sRelativeConstraint Constraints
[] =
65 { kConstrainAfter
, &IID_IPropertyManager
},
66 { kConstrainAfter
, &IID_ILinkManager
},
80 //------------------------------------------------------------
81 // Construction/destruction
84 cSimpleDamageModel
* cSimpleDamageModel::TheDamageModel
= NULL
;
86 cSimpleDamageModel::cSimpleDamageModel(IUnknown
* pOuter
)
87 : cBaseDamageModel(pOuter
,Constraints
),mpCorpses(NULL
),mpFlinders(NULL
)
89 TheDamageModel
= this;
92 cSimpleDamageModel::~cSimpleDamageModel()
97 //------------------------------------------------------------
98 // IDamageModel Methods
101 STDMETHODIMP_(eDamageResult
) cSimpleDamageModel::HandleImpact(ObjID victim
, ObjID culprit
, sImpact
* impact
, sChainedEvent
* cause
)
103 // @TBD: this is a tad crude, so we should think about how to handle
104 // it better. Basically, we need to make sure that we don't spuriously
105 // damage avatars, because that damage can propagate back to the
106 // player. Players are responsible for their own damage. It would be
107 // nice to handle this with fewer side-effects, though.
108 // @NOTE: Removed 10/19/99, since Thief actively doesn't want this
109 // and Shock now deals with it at a better level...
111 //if (IsAPlayer(victim) && !(victim == PlayerObject())) {
112 // return kDamageNoOpinion;
115 sDamageMsgData msgdata
= { kDamageMsgImpact
, victim
, culprit
, impact
};
116 sDamageMsg
msg(kEventKindImpact
,&msgdata
,cause
);
118 eDamageResult result
= SendMessage(&msg
);
120 if (result
> kDamageSlay
)
121 return ApplyResult(victim
,culprit
,result
,&msg
);
123 if (culprit
!= OBJ_NULL
&& victim
!= OBJ_NULL
&& ObjHasWeaponDamage(culprit
))
126 int kind
= kDamageKindImpact
;
128 damage
.amount
= ObjGetWeaponDamage(culprit
);
129 ObjGetWeaponType(culprit
,&kind
);
132 eDamageResult opinion
= DamageObject(victim
,culprit
,&damage
,&msg
);
133 result
= max(opinion
,result
);
139 ////////////////////////////////////////
141 STDMETHODIMP_(eDamageResult
) cSimpleDamageModel::DamageObject(ObjID victim
, ObjID culprit
, sDamage
* damage
, sChainedEvent
* cause
, BOOL allowzero
)
143 eDamageResult result
= ApplyFilters(victim
,culprit
,damage
);
145 // don't take zero damage
146 if ((damage
->amount
== 0) && (!allowzero
))
147 return kDamageStatusQuo
;
149 // don't damage dead people
150 if (AlreadySentMsg(kDamageMsgSlay
,victim
))
151 return kDamageStatusQuo
;
154 if (ObjGetHitPoints(victim
,&hp
))
156 hp
-= damage
->amount
;
158 result
= max(result
,kDamageSlay
);
159 ObjSetHitPoints(victim
,hp
);
162 sDamageMsgData msgdata
= { kDamageMsgDamage
, victim
, culprit
, damage
};
163 sDamageMsg
msg(kEventKindDamage
,&msgdata
,cause
);
165 ConfigSpew("damage_spew",("%d was damaged by %d with %d of kind %d\n",victim
,culprit
,damage
->amount
,damage
->kind
));
167 eDamageResult opinion
= SendMessage(&msg
);
168 result
= max(opinion
,result
);
170 result
= ApplyResult(victim
,culprit
,result
,&msg
);
174 ////////////////////////////////////////
177 static eDamageResult slay_result_map
[] =
178 { kDamageNoOpinion
, kDamageStatusQuo
, kDamageTerminate
, kDamageDestroy
};
180 STDMETHODIMP_(eDamageResult
) cSimpleDamageModel::SlayObject(ObjID victim
, ObjID culprit
, sChainedEvent
* cause
)
182 #ifdef NEW_NETWORK_ENABLED
183 // If we're in multiplayer, don't kill things we don't own:
184 AutoAppIPtr(ObjectNetworking
);
185 if (pObjectNetworking
->ObjIsProxy(victim
))
186 return kDamageNoOpinion
;
189 tDamageKind kind
= 0;
190 // grovel through history looking for damage
193 sDamageMsg
* msg
= (sDamageMsg
*)cause
->Find(kEventKindDamage
);
195 kind
= msg
->data
.damage
->kind
;
198 sDamageMsgData msgdata
= { kDamageMsgSlay
, victim
, culprit
, (void*)kind
};
199 sDamageMsg
msg(kEventKindSlay
,&msgdata
,cause
);
201 eDamageResult result
= SendMessage(&msg
);
202 if (result
== kDamageNoOpinion
)
203 result
= slay_result_map
[ObjGetSlayResult(victim
)];
204 if (result
== kDamageNoOpinion
&& victim
!= PlayerObject())
205 result
= kDamageTerminate
;
206 if (result
> kDamageSlay
)
208 sDamage dmg
= { 0, kind
};
209 result
= ApplyResult(victim
,culprit
,result
,&msg
);
212 result
= kDamageSlay
;
216 ////////////////////////////////////////
218 STDMETHODIMP_(eDamageResult
) cSimpleDamageModel::TerminateObject(ObjID victim
, sChainedEvent
* cause
)
220 sDamageMsgData msgdata
= { kDamageMsgTerminate
, victim
, OBJ_NULL
, NULL
};
221 sDamageMsg
msg(kEventKindTerminate
,&msgdata
,cause
);
223 eDamageResult result
= SendMessage(&msg
);
225 if (result
<= kDamageTerminate
)
228 if (result
== kDamageNoOpinion
)
229 result
= kDamageDestroy
;
231 result
= kDamageTerminate
;
233 #ifdef NEW_NETWORK_ENABLED
234 AutoAppIPtr(NetManager
);
235 AutoAppIPtr(ObjectNetworking
);
236 BOOL isLocalOnly
= pObjectNetworking
->ObjLocalOnly(victim
);
239 cCorpseIter
corpseIter(victim
, mpCorpses
);
240 while (!corpseIter
.Finished())
242 result
= kDamageDestroy
;
244 // Replace the victim with a corpse
245 ObjPos pos
= *ObjPosGet(victim
);
247 // Actually instantiate the corpse
248 #ifdef NEW_NETWORK_ENABLED
249 // If the victim was a local-only object, then the corpse should
250 // be local-only as well. By suspending messaging, we tell object
251 // networking to do this.
254 pNetManager
->SuspendMessaging();
257 ObjID corpse
= mpObjSys
->BeginCreate(corpseIter
.Get(),kObjectConcrete
);
258 #ifdef NEW_NETWORK_ENABLED
261 pNetManager
->ResumeMessaging();
264 ObjPosUpdate(corpse
,&pos
.loc
.vec
,&pos
.fac
); // position it correctly
266 PropagateCulpability(victim
,corpse
,kCulpTransitive
);
268 if (BOOL(corpseIter
.GetData()))
271 if (g_pSourceScaleProperty
->Get(victim
, &scale
))
272 g_pSourceScaleProperty
->Set(corpse
, scale
);
275 mpObjSys
->EndCreate(corpse
);
280 cCorpseIter
flinderIter(victim
, mpFlinders
);
281 if (!flinderIter
.Finished())
282 result
= kDamageDestroy
;
284 while (!flinderIter
.Finished())
286 sFlinder
*pFlinder
= (sFlinder
*)flinderIter
.GetData();
287 CreateFlinders(victim
, flinderIter
.Get(), pFlinder
->count
, pFlinder
->scatter
, pFlinder
->impulse
, pFlinder
->offset
);
293 if (result
> kDamageTerminate
)
294 result
= ApplyResult(victim
,OBJ_NULL
,result
,&msg
);
299 ////////////////////////////////////////
301 STDMETHODIMP_(eDamageResult
) cSimpleDamageModel::ResurrectObject(ObjID victim
, ObjID culprit
, sChainedEvent
* cause
)
304 ObjGetHitPoints(victim
,&hp
);
306 ObjGetMaxHitPoints(victim
,&maxhp
);
307 if (maxhp
<= 0) // can't resurrect a guy who's max hitpoints is zero or fewer
308 return kDamageStatusQuo
;
311 ObjSetHitPoints(victim
,maxhp
);
313 return cBaseDamageModel::ResurrectObject(victim
,culprit
,cause
);
316 ////////////////////////////////////////
318 STDMETHODIMP
cSimpleDamageModel::Init()
320 cBaseDamageModel::Init();
322 mpTraitMan
= AppGetObj(ITraitManager
);
323 CreateCorpseRelation();
324 CreateFlinderizeRelation();
330 STDMETHODIMP
cSimpleDamageModel::End()
332 SafeRelease(mpTraitMan
);
334 cBaseDamageModel::End();
335 SafeRelease(mpCorpses
);
336 SafeRelease(mpCulpable
);
337 SafeRelease(mpCulpableFor
);
342 ////////////////////////////////////////
346 static sRelationDesc corpse_rdesc
=
351 static sRelationDataDesc corpse_ddesc
= {"Propagate Source Scale?", sizeof(BOOL
),};
353 static sFieldDesc corpse_fdesc
=
354 { "Propagate Source Scale?", kFieldTypeBool
, sizeof(BOOL
), 0, 0 };
355 static sStructDesc corpse_sdesc
=
356 { "Propagate Source Scale?", sizeof(BOOL
), kStructFlagNone
, 1, &corpse_fdesc
};
358 void cSimpleDamageModel::CreateCorpseRelation()
360 mpCorpses
= CreateStandardRelation(&corpse_rdesc
,&corpse_ddesc
,kQCaseSetSourceKnown
|kQCaseSetBothKnown
);
361 StructDescRegister(&corpse_sdesc
);
364 ////////////////////////////////////////
366 // Fliderize Relation
368 static sRelationDesc flinderize_rdesc
=
373 static sRelationDataDesc flinderize_ddesc
= LINK_DATA_DESC_FLAGS(sFlinder
, kRelationDataAutoCreate
);
375 static sFieldDesc flinderize_data_fields
[] =
377 {"Count", kFieldTypeInt
, FieldLocation(sFlinder
, count
), kFieldFlagNone
},
378 {"Impulse", kFieldTypeFloat
, FieldLocation(sFlinder
, impulse
), kFieldFlagNone
},
379 {"Scatter?", kFieldTypeBool
, FieldLocation(sFlinder
, scatter
), kFieldFlagNone
},
380 {"Offset", kFieldTypeVector
, FieldLocation(sFlinder
, offset
), kFieldFlagNone
},
383 static sStructDesc flinderize_sdesc
= StructDescBuild(sFlinder
, kStructFlagNone
, flinderize_data_fields
);
385 void cSimpleDamageModel::CreateFlinderizeRelation()
387 mpFlinders
= CreateStandardRelation(&flinderize_rdesc
, &flinderize_ddesc
, kQCaseSetSourceKnown
);
388 StructDescRegister(&flinderize_sdesc
);
391 //////////////////////////////////////////////////////////////
396 void cSimpleDamageModel::PropagateCulpability(ObjID from
, ObjID to
, ulong flags
)
398 // can't be culpable for an archetype
399 if (OBJ_IS_ABSTRACT(to
))
401 ConfigSpew("culpable_spew",("Trying to spread culpability from %d to %d\n",from
,to
));
405 ObjID src
= OBJ_NULL
;
406 BOOL culpable
= FALSE
;
408 // if the "to" object can be culpable, then culpability doesn't propagate
410 if (mpCulpable
->Get(to
,&culpable
) && culpable
)
414 mpCulpable
->Get(from
,&culpable
);
416 // If I can be culpable, link straight to me
419 else if (flags
& kCulpTransitive
)
421 // is anyone culpable for me?
422 LinkID culplink
= mpCulpableFor
->GetSingleLink(LINKOBJ_WILDCARD
,from
);
423 if (culplink
!= NULL
)
426 mpCulpableFor
->Get(culplink
,&link
);
434 // remove any old link that might exist
435 LinkID link
= mpCulpableFor
->GetSingleLink(LINKOBJ_WILDCARD
,to
);
436 if (link
!= LINKID_NULL
)
437 mpCulpableFor
->Remove(link
);
438 mpCulpableFor
->Add(src
,to
);
442 ////////////////////////////////////////
444 ObjID
cSimpleDamageModel::GetRealCulprit(ObjID culprit
)
446 if (culprit
== OBJ_NULL
)
449 LinkID id
= mpCulpableFor
->GetSingleLink(LINKOBJ_WILDCARD
,culprit
);
450 if (id
== LINKID_NULL
)
454 mpCulpableFor
->Get(id
,&link
);
459 ////////////////////////////////////////
461 static sRelationDesc culp_rdesc
=
464 // For timing reasons, we have to allow proxies to delete
465 // CulpableFor links:
466 kRelationNetProxyChangeable
,
469 static sPropertyDesc culp_pdesc
=
475 { "Game: Damage Model", "Culpable" }
479 void cSimpleDamageModel::CreateCulpability()
481 // create the property and relation
482 mpCulpable
= CreateBoolProperty(&culp_pdesc
,kPropertyImplSparseHash
);
483 sRelationDataDesc ddesc
= LINK_NO_DATA
;
484 mpCulpableFor
= CreateStandardRelation(&culp_rdesc
,&ddesc
,kQCaseSetDestKnown
);
487 ////////////////////////////////////////
489 // Corpse iterator - so we can have multiple corpses/victim
492 cCorpseIter::cCorpseIter(ObjID objID
, IRelation
*pRelation
):
495 AutoAppIPtr(TraitManager
);
496 // @TODO: use trait cache
497 IObjectQuery
* donors
= pTraitManager
->Query(objID
, kTraitQueryAllDonors
);
498 cLinkQueryFactory
* factory
= CreateSourceSetQueryFactory(pRelation
, LINKOBJ_WILDCARD
);
500 m_pQuery
= CreateObjSetLinkQuery(donors
,factory
);
504 cCorpseIter::~cCorpseIter(void)
506 SafeRelease(m_pQuery
);
509 ObjID
cCorpseIter::Get(void)
511 ObjID result
= OBJ_NULL
;
513 Assert_(m_pQuery
!= NULL
);
517 m_pQuery
->Link(&link
);
519 // set start object if not already set
520 if (m_startObj
== OBJ_NULL
)
521 m_startObj
= link
.source
;
526 void *cCorpseIter::GetData(void)
530 Assert_(m_pQuery
!= NULL
);
532 result
= m_pQuery
->Data();
537 BOOL
cCorpseIter::Finished(void)
539 Assert_(m_pQuery
!= NULL
);
540 if (!m_pQuery
->Done())
542 // if query not done, check that we are still looking at links
543 // from the initial object (to ensure that we don't return links
544 // from archetypes higher up in the hierarchy)
545 if (m_startObj
== OBJ_NULL
)
548 m_pQuery
->Link(&link
);
549 return (m_startObj
!= link
.source
);
554 void cCorpseIter::Next(void)
556 Assert_(m_pQuery
!= NULL
);
560 ////////////////////////////////////////
561 void InitSimpleDamageModel(void)
563 AutoAppIPtr(Unknown
);
564 new cSimpleDamageModel(pUnknown
);
567 ////////////////////////////////////////////////////////////
571 void PropagateCulpability(ObjID from
, ObjID to
, ulong flags
)
573 cSimpleDamageModel::TheDamageModel
->PropagateCulpability(from
,to
,flags
);
576 ObjID
GetRealCulprit(ObjID culprit
)
578 return cSimpleDamageModel::TheDamageModel
->GetRealCulprit(culprit
);
581 ////////////////////////////////////////
583 static void LGAPI
culp_link_listener(sRelationListenMsg
* msg
, RelationListenerData data
)
585 ulong flags
= (ulong
)data
;
586 PropagateCulpability(msg
->link
.source
,msg
->link
.dest
,flags
);
591 void AddCulpabilityRelation(IRelation
* pRel
, ulong flags
)
593 pRel
->Listen(kListenLinkBirth
,culp_link_listener
,(void*)flags
);
596 #endif // __SIMPDMG_H