2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
66 // must be last header
69 #pragma off(unreferenced)
71 ////////////////////////////////////////
73 // Miss-Spang Relation
76 IRelation
*g_pMissSpangs
;
78 static sRelationDesc missSpangRDesc
=
83 static sRelationDataDesc missSpangDDesc
= {"Normal to terrain?", sizeof(BOOL
),};
85 static sFieldDesc missSpangRawFields
=
86 { "Normal to terrain?", kFieldTypeBool
, sizeof(BOOL
), 0, 0 };
87 static sStructDesc missSpangSDesc
=
88 { "Normal to terrain?", sizeof(BOOL
), kStructFlagNone
, 1, &missSpangRawFields
};
90 void CreateMissSpangRelation(void)
92 g_pMissSpangs
= CreateStandardRelation(&missSpangRDesc
, &missSpangDDesc
, kQCaseSetSourceKnown
);
93 StructDescRegister(&missSpangSDesc
);
96 ObjID
ChooseMissSpang(ObjID obj
, BOOL
* pOrientToNormal
)
99 ILinkQuery
* query
= QueryInheritedLinksSingleUncached(g_pMissSpangs
,obj
,LINKOBJ_WILDCARD
);
100 ObjID result
= OBJ_NULL
;
107 // deal with old version data that doesn't have the field
108 if (query
->Data() != NULL
)
109 *pOrientToNormal
= *((BOOL
*)(query
->Data()));
111 *pOrientToNormal
= FALSE
;
117 ////////////////////////////////////////
119 // Hit-Spang Relation
122 // Place one of these links from an archetypal projectile object to the
123 // archetypal object that the hit spang is to occur on. The data on this
124 // link is the name of the hit spang object to be created.
126 IRelation
*g_pHitSpangs
;
128 static sRelationDesc hitSpangRDesc
=
133 static sRelationDataDesc hitSpangDDesc
= {"Hit Spang Obj", sizeof(int),};
135 static sFieldDesc hitSpangRawFields
=
136 { "Hit Spang Obj", kFieldTypeInt
, sizeof(int), 0, 0 };
137 static sStructDesc hitSpangSDesc
=
138 { "Hit Spang Obj", sizeof(int), kStructFlagNone
, 1, &hitSpangRawFields
};
140 void CreateHitSpangRelation(void)
142 g_pHitSpangs
= CreateStandardRelation(&hitSpangRDesc
, &hitSpangDDesc
, kQCaseSetSourceKnown
);
143 StructDescRegister(&hitSpangSDesc
);
146 static ITrait
* g_pHitSpangTrait
;
148 ObjID
ChooseHitSpang(ObjID bulletID
, ObjID victimID
)
150 ObjID result
= OBJ_NULL
;
152 ILinkQuery
* query
= QueryInheritedLinks(g_pHitSpangTrait
,g_pHitSpangs
,bulletID
,victimID
);
154 result
= *((ObjID
*)(query
->Data()));
159 ////////////////////////////////////////
163 // This little message just indicates that a creature has taken damage,
164 // so everyone should show the HUD for it.
166 static cNetMsg
*g_pShowDmgMsg
= NULL
;
168 static void handleShowDmg(ObjID victim
)
170 gPropHUDTime
->Set(victim
, GetSimTime() + 5000);
173 static sNetMsgDesc sShowDmgDesc
=
177 "Show Creature Damage",
180 {{kNMPT_SenderObjID
, kNMPF_IfHostedHere
, "Victim"},
184 ////////////////////////////////////////
189 const float kDPCLandForce
= 3600;
190 const float kDPCFallDmgRatio
= 1800;
192 #define MAX_TEX_CORPSES 16
194 // how far we backup from collision pt to place miss spang
195 // a pathetic attempt to stay in the world...
196 const float kMissSpangBackupAmt
= 0.01;
199 static void DoOrientToNormal(const sDamageMsg
*pMsg
, ObjID spang
, float backup
= kMissSpangBackupAmt
)
201 sPhysClsnEvent
* pClsnEvent
= (sPhysClsnEvent
*)pMsg
->Find(kEventKindCollision
);
202 cPhysClsn
*pClsn
= pClsnEvent
->collision
;
208 mx_mk_move_x_mat(&mat
, &pClsn
->GetNormal());
209 mx_mat2ang(&fac
, &mat
);
210 loc
= pClsn
->GetClsnPt();
211 // backup to try to stay in the world
212 mx_scale_addeq_vec(&loc
, &pClsn
->GetNormal(), backup
);
213 ObjPosUpdate(spang
, &loc
, &fac
);
216 eDamageResult LGAPI
DPCDamageListener(const sDamageMsg
* pMsg
, tDamageCallbackData data
)
220 case kDamageMsgDamage
:
222 // Play an environmental sound
223 cTagSet
Event("Event Damage");
225 int damage
= pMsg
->data
.damage
->amount
;
227 int hp
= 100, maxhp
= 100;
228 ObjGetHitPoints(pMsg
->victim
,&hp
);
229 ObjGetMaxHitPoints(pMsg
->victim
,&maxhp
);
238 Event
.Append(cTag("Damage",damage
*100/maxhp
));
239 Event
.Append(cTag("Health",hp
*100/maxhp
));
241 // Add class tags for damage type
242 ObjID dmgtype
= (ObjID
)pMsg
->data
.damage
->kind
;
243 sESndTagList
* pDamageTags
= NULL
;
244 if (ObjGetESndClass(dmgtype
,&pDamageTags
))
245 Event
.Append(*pDamageTags
->m_pTagSet
);
247 ESndPlayObj(&Event
,pMsg
->victim
,pMsg
->culprit
); // ,&ObjPosGet(pMsg->victim)->loc.vec);
249 // need something to filter this down to only monsters?
251 gPropShowHP
->Get(pMsg
->victim
,&drawhp
);
254 gPropHUDTime
->Set(pMsg
->victim
, GetSimTime() + 5000);
255 // Tell the other players to show the damage, too
256 g_pShowDmgMsg
->Send(OBJ_NULL
, pMsg
->victim
);
258 DPCReleaseBlood(pMsg
);
260 if (pMsg
->victim
==PlayerObject())
261 GhostNotify(PlayerObject(),kGhostStWounded
);
264 case kDamageMsgImpact
:
266 int textureID
= GetObjTextureIdx(pMsg
->culprit
);
270 // terrain collision - generate Miss-spang
272 ObjID spang
= ChooseMissSpang(pMsg
->victim
, &orientToNormal
);
273 AutoAppIPtr(ObjectNetworking
);
274 // Don't generate miss spangs for things we don't own:
275 if ((spang
!= OBJ_NULL
) &&
276 !pObjectNetworking
->ObjIsProxy(pMsg
->victim
))
279 AutoAppIPtr(ObjectSystem
);
281 spang
= pObjectSystem
->BeginCreate(spang
, kObjectConcrete
);
282 Assert_(spang
!= OBJ_NULL
);
285 // Place the spang normal to the terrain
286 DoOrientToNormal(pMsg
, spang
);
290 // Place the spang at same location & orientation as colliding object
292 PhysGetModRotation(pMsg
->victim
, &fac
);
293 PhysGetModLocation(pMsg
->victim
, &loc
);
294 ObjPosUpdate(spang
, &loc
, &fac
);
296 pObjectSystem
->EndCreate(spang
);
299 if (pMsg
->victim
!= PlayerObject())
301 // switch the texture to a damaged form, potentially
302 // does it have any corpse links?
303 AutoAppIPtr(LinkManager
);
304 AutoAppIPtr(TraitManager
);
305 AutoAppIPtr(DynTexture
);
307 ObjID texcand
[MAX_TEX_CORPSES
];
308 ObjID texobj
= GetTextureObj(textureID
);
309 ObjID texarch
= pTraitManager
->GetArchetype(texobj
);
310 ObjID newarch
, newobj
;
311 char respath
[255],resname
[255], temp
[255];
312 const char *archname
;
316 static ITrait
*pTrait
= NULL
;
318 IRelation
*pRel
= pLinkManager
->GetRelationNamed("Corpse");
321 pTrait
= MakeTraitFromRelation(pRel
);
323 ILinkQuery
*query
= QueryInheritedLinksSingle(pTrait
,pRel
,texarch
, LINKOBJ_WILDCARD
);
324 while (!query
->Done())
327 texcand
[count
] = slink
.dest
;
336 newarch
= texcand
[r
];
337 archname
= ObjEditName(newarch
);
338 sscanf(archname
,"t_fam/%s",&temp
);
339 strcpy(resname
,strchr(temp
,'/') + 1);
340 n
= strlen(temp
) - strlen(resname
) - 1;
342 sprintf(respath
,"fam\\%s",temp
);
344 newobj
= GetTextureObjNamed(respath
,resname
);
345 if (!IsTextureObj(newobj
))
347 Warning(("Obj %d is not a texture!\n",newobj
));
351 sPhysClsnEvent
* pClsnEvent
= (sPhysClsnEvent
*)pMsg
->Find(kEventKindCollision
);
352 cPhysClsn
*pClsn
= pClsnEvent
->collision
;
357 vec
= pClsn
->GetClsnPt();
358 memcpy(&loc
.vec
,&vec
,sizeof(mxs_vector
));
360 id2
= GetObjTextureIdx(newobj
);
361 //pDynTexture->ChangeTexture(&loc, 0.1, textureID, id2);
362 pDynTexture
->ChangeTexture(pMsg
->victim
, textureID
, id2
);
364 // throw out some sparky crap
365 AutoAppIPtr(ObjectSystem
);
367 // yeah, if I were cool I'd use a link
368 ObjID arch
= pObjectSystem
->GetObjectNamed("Anim Texture Break");
369 if (arch
!= OBJ_NULL
)
371 ObjID spark
= pObjectSystem
->BeginCreate(arch
, kObjectConcrete
);
372 DoOrientToNormal(pMsg
, spark
, 0.6);
373 pObjectSystem
->EndCreate(spark
);
376 // play a sound effect too
377 SchemaPlayLoc((Label
*)"texbreak", &vec
);
384 // object collission - generate hit-spang
385 ObjID spang
= ChooseHitSpang(pMsg
->victim
, pMsg
->culprit
);
386 if (spang
!= OBJ_NULL
)
390 AutoAppIPtr(ObjectSystem
);
392 PhysGetModLocation(pMsg
->victim
, &loc
);
393 PhysGetModRotation(pMsg
->victim
, &fac
);
394 spang
= pObjectSystem
->BeginCreate(spang
, kObjectConcrete
);
395 ObjPosUpdate(spang
, &loc
, &fac
); // position it correctly
396 pObjectSystem
->EndCreate(spang
);
400 // projectiles hitting objects is handled inside simpdmg for the moment
401 // we can move it into a DPCProj... fn if we need to
403 if (PhysIsProjectile(pMsg
->victim
))
405 if (pMsg
->culprit
== 0)
407 return GunProjTerrImpactHandler(pMsg
->victim
);
416 // experience points from AI kills
417 // @TODO: detect whether the player was "responsible" for death
420 int exp = ObjGetExp(pMsg->victim);
423 // hmm, need better algorithm for determining who gets the exp?
424 AutoAppIPtr(DPCPlayer);
425 pDPCPlayer->AddExperience(PlayerObject(), exp);
429 // Play an environmental sound
430 cTagSet
Event("Event Death");
431 ESndPlayLoc(&Event
,pMsg
->victim
,(ObjID
) pMsg
->data
.slay
, &ObjPosGet(pMsg
->victim
)->loc
.vec
);
433 // destroy particles?
434 if (ObjIsParticle(pMsg
->victim
))
436 ParticlesDeleteFromObjID(pMsg
->victim
);
437 return kDamageDestroy
;
441 return kDamageNoOpinion
;
444 void DPCDamageInit(void)
446 CreateMissSpangRelation();
447 CreateHitSpangRelation();
448 g_pHitSpangTrait
= MakeTraitFromRelation(g_pHitSpangs
);
449 g_pShowDmgMsg
= new cNetMsg(&sShowDmgDesc
);
452 void DPCDamageShutDown(void)
454 SafeRelease(g_pMissSpangs
);
455 delete g_pShowDmgMsg
;
458 #pragma on(unreferenced)