convert line ends
[canaan.git] / prj / cam / src / deepc / object / dpcdmg.cpp
blob07037f3dd75057cb3d4a1a794de40f4d5754e884
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 #include <dpcdmg.h>
8 #include <mprintf.h>
9 #include <appagg.h>
11 #include <linkbase.h>
12 #include <linkman.h>
13 #include <relation.h>
14 #include <lnkquery.h>
15 #include <osetlnkq.h>
17 #include <objedit.h>
18 #include <rand.h>
19 #include <schema.h>
21 #include <trait.h>
22 #include <traitman.h>
23 #include <traitbas.h>
25 #include <iobjsys.h>
26 #include <objdef.h>
28 #include <bintrait.h>
30 #include <sdesc.h>
31 #include <sdesbase.h>
33 #include <dmgbase.h>
34 #include <playrobj.h>
35 #include <objpos.h>
36 #include <partprop.h>
37 #include <prjctile.h>
38 #include <particle.h>
39 #include <objhp.h>
40 #include <dmgprop.h>
41 #include <textarch.h>
42 #include <simtime.h>
44 #include <phclsn.h>
45 #include <phcollev.h>
46 #include <physapi.h>
48 #include <esnd.h>
50 #include <dyntex.h>
52 #include <dpcblood.h>
53 #include <gunprop.h>
54 #include <dpcprop.h>
55 #include <dpcplayr.h>
56 #include <gunproj.h>
58 #include <netmsg.h>
59 #include <iobjnet.h>
60 #include <ghostapi.h>
62 // charm
63 #include <culpable.h>
64 #include <scrptapi.h>
66 // must be last header
67 #include <dbmem.h>
69 #pragma off(unreferenced)
71 ////////////////////////////////////////
73 // Miss-Spang Relation
76 IRelation *g_pMissSpangs;
78 static sRelationDesc missSpangRDesc =
80 "Miss Spang",
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;
102 if (!query->Done())
104 sLink link;
105 query->Link(&link);
106 result = link.dest;
107 // deal with old version data that doesn't have the field
108 if (query->Data() != NULL)
109 *pOrientToNormal = *((BOOL*)(query->Data()));
110 else
111 *pOrientToNormal = FALSE;
113 SafeRelease(query);
114 return result;
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 =
130 "Hit Spang",
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);
153 if (!query->Done())
154 result = *((ObjID*)(query->Data()));
155 SafeRelease(query);
156 return result;
159 ////////////////////////////////////////
161 // NETWORKING CODE
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 =
175 kNMF_Broadcast,
176 "ShowDmg",
177 "Show Creature Damage",
178 NULL,
179 handleShowDmg,
180 {{kNMPT_SenderObjID, kNMPF_IfHostedHere, "Victim"},
181 {kNMPT_End}}
184 ////////////////////////////////////////
186 // Damage Listener
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;
203 mxs_angvec fac;
204 mxs_matrix mat;
205 mxs_vector loc;
207 Assert_(pClsnEvent);
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)
218 switch(pMsg->kind)
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);
231 if (maxhp <= 0)
232 maxhp = 1;
233 if (damage > maxhp)
234 damage = maxhp;
235 if (hp < 0)
236 hp = 0;
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?
250 BOOL drawhp = FALSE;
251 gPropShowHP->Get(pMsg->victim,&drawhp);
252 if (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);
263 break;
264 case kDamageMsgImpact:
266 int textureID = GetObjTextureIdx(pMsg->culprit);
268 if (textureID>=0)
270 // terrain collision - generate Miss-spang
271 BOOL orientToNormal;
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))
278 mxs_angvec fac;
279 AutoAppIPtr(ObjectSystem);
281 spang = pObjectSystem->BeginCreate(spang, kObjectConcrete);
282 Assert_(spang != OBJ_NULL);
283 if (orientToNormal)
285 // Place the spang normal to the terrain
286 DoOrientToNormal(pMsg, spang);
288 else
290 // Place the spang at same location & orientation as colliding object
291 mxs_vector loc;
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;
313 sLink slink;
314 int count = 0;
315 int id2, n, r;
316 static ITrait *pTrait = NULL;
318 IRelation *pRel = pLinkManager->GetRelationNamed("Corpse");
319 if (pTrait == NULL)
321 pTrait = MakeTraitFromRelation(pRel);
323 ILinkQuery *query = QueryInheritedLinksSingle(pTrait,pRel,texarch, LINKOBJ_WILDCARD);
324 while (!query->Done())
326 query->Link(&slink);
327 texcand[count] = slink.dest;
328 count++;
329 query->Next();
331 SafeRelease(query);
333 if (count > 0)
335 r = Rand() % count;
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;
341 temp[n] = '\0';
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));
349 else
351 sPhysClsnEvent* pClsnEvent = (sPhysClsnEvent*)pMsg->Find(kEventKindCollision);
352 cPhysClsn *pClsn = pClsnEvent->collision;
353 mxs_vector vec;
354 Location loc;
356 Assert_(pClsnEvent);
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);
382 else
384 // object collission - generate hit-spang
385 ObjID spang = ChooseHitSpang(pMsg->victim, pMsg->culprit);
386 if (spang != OBJ_NULL)
388 mxs_vector loc;
389 mxs_angvec fac;
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
402 // This just
403 if (PhysIsProjectile(pMsg->victim))
405 if (pMsg->culprit == 0)
407 return GunProjTerrImpactHandler(pMsg->victim);
412 break;
413 case kDamageMsgSlay:
416 // experience points from AI kills
417 // @TODO: detect whether the player was "responsible" for death
420 int exp = ObjGetExp(pMsg->victim);
421 if (exp != 0)
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)