convert line ends
[canaan.git] / prj / cam / src / engfeat / simpdmg.cpp
blobcf1b152df405366b95c22db9b8d049b56f75ef2c
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/engfeat/simpdmg.cpp,v 1.30 2000/02/05 19:37:27 adurant Exp $
8 #ifndef __SIMPDMG_H
9 #define __SIMPDMG_H
11 #include <simpdmg_.h>
12 #include <objhp.h>
13 #include <dmgbase.h>
14 #include <collprop.h>
15 #include <dmgprop.h>
17 #include <sdesc.h>
18 #include <sdesbase.h>
20 #include <linkman.h>
21 #include <linkbase.h>
22 #include <relation.h>
23 #include <lnkquery.h>
24 #include <osetlnkq.h>
25 #include <propbase.h>
27 #include <traitman.h>
28 #include <traitbas.h>
30 #include <iobjsys.h>
31 #include <objpos.h>
32 #include <osystype.h>
33 #include <osysbase.h>
34 #include <wrtype.h>
35 #include <flinder.h>
37 #include <config.h>
38 #include <cfgdbg.h>
40 #include <chevkind.h>
42 #include <playrobj.h>
44 #include <netman.h>
45 #include <iobjnet.h>
47 #include <stimprop.h>
49 // Must be last header
50 #include <dbmem.h>
52 ////////////////////////////////////////////////////////////
53 // cSimpleDamageModel implementation
56 #ifndef max
57 #define max(a,b) ((a) > (b) ? (a) : (b))
58 #endif
60 F_DECLARE_INTERFACE(IPropertyManager);
61 F_DECLARE_INTERFACE(ILinkManager);
63 static struct sRelativeConstraint Constraints[] =
65 { kConstrainAfter, &IID_IPropertyManager },
66 { kConstrainAfter, &IID_ILinkManager },
68 { kNullConstraint, },
71 struct sFlinder
73 int count;
74 float impulse;
76 BOOL scatter;
77 mxs_vector offset;
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))
125 sDamage damage;
126 int kind = kDamageKindImpact;
128 damage.amount = ObjGetWeaponDamage(culprit);
129 ObjGetWeaponType(culprit,&kind);
130 damage.kind = kind;
132 eDamageResult opinion = DamageObject(victim,culprit,&damage,&msg);
133 result = max(opinion,result);
136 return 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;
153 int hp;
154 if (ObjGetHitPoints(victim,&hp))
156 hp -= damage->amount;
157 if (hp <= 0)
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);
171 return result;
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;
187 #endif
189 tDamageKind kind = 0;
190 // grovel through history looking for damage
191 if (cause)
193 sDamageMsg* msg = (sDamageMsg*)cause->Find(kEventKindDamage);
194 if (msg)
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);
211 else
212 result = kDamageSlay;
213 return result;
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;
230 else
231 result = kDamageTerminate;
233 #ifdef NEW_NETWORK_ENABLED
234 AutoAppIPtr(NetManager);
235 AutoAppIPtr(ObjectNetworking);
236 BOOL isLocalOnly = pObjectNetworking->ObjLocalOnly(victim);
237 #endif
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.
252 if (isLocalOnly)
254 pNetManager->SuspendMessaging();
256 #endif
257 ObjID corpse = mpObjSys->BeginCreate(corpseIter.Get(),kObjectConcrete);
258 #ifdef NEW_NETWORK_ENABLED
259 if (isLocalOnly)
261 pNetManager->ResumeMessaging();
263 #endif
264 ObjPosUpdate(corpse,&pos.loc.vec,&pos.fac); // position it correctly
266 PropagateCulpability(victim,corpse,kCulpTransitive);
268 if (BOOL(corpseIter.GetData()))
270 float scale;
271 if (g_pSourceScaleProperty->Get(victim, &scale))
272 g_pSourceScaleProperty->Set(corpse, scale);
275 mpObjSys->EndCreate(corpse);
277 corpseIter.Next();
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);
289 flinderIter.Next();
293 if (result > kDamageTerminate)
294 result = ApplyResult(victim,OBJ_NULL,result,&msg);
296 return result;
299 ////////////////////////////////////////
301 STDMETHODIMP_(eDamageResult) cSimpleDamageModel::ResurrectObject(ObjID victim, ObjID culprit, sChainedEvent* cause)
303 int hp;
304 ObjGetHitPoints(victim,&hp);
305 int maxhp = 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;
310 if (hp < maxhp)
311 ObjSetHitPoints(victim,maxhp);
313 return cBaseDamageModel::ResurrectObject(victim,culprit,cause);
316 ////////////////////////////////////////
318 STDMETHODIMP cSimpleDamageModel::Init()
320 cBaseDamageModel::Init();
321 InitDamageProps();
322 mpTraitMan = AppGetObj(ITraitManager);
323 CreateCorpseRelation();
324 CreateFlinderizeRelation();
325 CreateCulpability();
326 return S_OK;
330 STDMETHODIMP cSimpleDamageModel::End()
332 SafeRelease(mpTraitMan);
333 TermDamageProps();
334 cBaseDamageModel::End();
335 SafeRelease(mpCorpses);
336 SafeRelease(mpCulpable);
337 SafeRelease(mpCulpableFor);
339 return S_OK;
342 ////////////////////////////////////////
344 // Corpse Relation
346 static sRelationDesc corpse_rdesc =
348 "Corpse",
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 =
370 "Flinderize",
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 //////////////////////////////////////////////////////////////
393 // Culpability
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));
402 return ;
405 ObjID src = OBJ_NULL;
406 BOOL culpable = FALSE;
408 // if the "to" object can be culpable, then culpability doesn't propagate
409 // to it.
410 if (mpCulpable->Get(to,&culpable) && culpable)
411 return ;
413 culpable = FALSE;
414 mpCulpable->Get(from,&culpable);
416 // If I can be culpable, link straight to me
417 if (culpable)
418 src = from;
419 else if (flags & kCulpTransitive)
421 // is anyone culpable for me?
422 LinkID culplink = mpCulpableFor->GetSingleLink(LINKOBJ_WILDCARD,from);
423 if (culplink != NULL)
425 sLink link;
426 mpCulpableFor->Get(culplink,&link);
427 src = link.source;
431 // add a new link
432 if (src != OBJ_NULL)
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)
447 return culprit;
449 LinkID id = mpCulpableFor->GetSingleLink(LINKOBJ_WILDCARD,culprit);
450 if (id == LINKID_NULL)
451 return culprit;
453 sLink link;
454 mpCulpableFor->Get(id,&link);
456 return link.source;
459 ////////////////////////////////////////
461 static sRelationDesc culp_rdesc =
463 "CulpableFor",
464 // For timing reasons, we have to allow proxies to delete
465 // CulpableFor links:
466 kRelationNetProxyChangeable,
469 static sPropertyDesc culp_pdesc =
471 "Culpable",
472 0, // flags
473 NULL,
474 0, 0,
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):
493 m_startObj(OBJ_NULL)
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);
501 SafeRelease(donors);
504 cCorpseIter::~cCorpseIter(void)
506 SafeRelease(m_pQuery);
509 ObjID cCorpseIter::Get(void)
511 ObjID result = OBJ_NULL;
513 Assert_(m_pQuery != NULL);
514 if (!Finished())
516 sLink link;
517 m_pQuery->Link(&link);
518 result = link.dest;
519 // set start object if not already set
520 if (m_startObj == OBJ_NULL)
521 m_startObj = link.source;
523 return result;
526 void *cCorpseIter::GetData(void)
528 void *result = NULL;
530 Assert_(m_pQuery != NULL);
531 if (!Finished())
532 result = m_pQuery->Data();
534 return result;
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)
546 return FALSE;
547 sLink link;
548 m_pQuery->Link(&link);
549 return (m_startObj != link.source);
551 return TRUE;
554 void cCorpseIter::Next(void)
556 Assert_(m_pQuery != NULL);
557 m_pQuery->Next();
560 ////////////////////////////////////////
561 void InitSimpleDamageModel(void)
563 AutoAppIPtr(Unknown);
564 new cSimpleDamageModel(pUnknown);
567 ////////////////////////////////////////////////////////////
568 // culpable.h api
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