convert line ends
[canaan.git] / prj / cam / src / shock / shkpldmg.cpp
blob4d8d53d787b5378b576195c553214f285066cb9b
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/shock/shkpldmg.cpp,v 1.11 1999/08/05 17:58:39 Justin Exp $
8 #include <shkpldmg.h>
10 #include <math.h>
12 #include <cfgdbg.h>
13 #include <str.h>
15 #include <appagg.h>
16 #include <autolink.h>
17 #include <dmgbase.h>
18 #include <iobjsys.h>
19 #include <linkbase.h>
20 #include <relation.h>
21 #include <physapi.h>
22 #include <playrobj.h>
23 #include <propbase.h>
24 #include <traitman.h>
25 #include <bintrait.h>
26 #include <objhp.h>
27 #include <questapi.h>
29 #include <shkarmpr.h>
30 #include <shkimcst.h>
31 #include <shkmelee.h>
32 #include <shkparam.h>
33 #include <shkplayr.h>
34 #include <shkplcst.h>
35 #include <shkpsapi.h>
36 #include <shkstcst.h>
37 #include <shktrcst.h>
38 #include <shkrsrch.h>
40 // For damage networking:
41 #include <netman.h>
42 #include <iobjnet.h>
43 #include <netmsg.h>
44 #include <netprops.h>
46 // must be last header
47 #include <dbmem.h>
49 // this is stupid
50 #define min(x, y) ((x<y)?(x):(y))
52 ////////////////////////////////////////
54 // NETWORKING CODE
57 static IObjectNetworking *g_pObjNet = NULL;
58 static INetManager *g_pNetMan = NULL;
59 static cNetMsg *g_pDamageMsg = NULL;
61 // TRUE iff we're handling some damage that was filtered elsewhere:
62 static BOOL g_bRemoteDamage = FALSE;
64 static void handleDamage(ObjID victim, ObjID culprit, sDamage *damage)
66 // Okay, deal out the damage. We don't actually send a cause; hopefully
67 // everyone below can cope?
68 AutoAppIPtr(DamageModel);
69 g_bRemoteDamage = TRUE;
70 pDamageModel->DamageObject(victim, culprit, damage, NULL);
71 g_bRemoteDamage = FALSE;
74 static sNetMsgDesc sDamageDesc =
76 kNMF_SendToObjOwner,
77 "Damage",
78 "Damage By Local Object",
79 NULL,
80 handleDamage,
81 {{kNMPT_ReceiverObjID, kNMPF_None, "Victim"},
82 // The culprit may be local-only, so we can't count upon it
83 // actually being hosted here
84 // @TBD: Is this going to cause us problems down the line? If the
85 // victim's owner doesn't know about the culprit, will it get
86 // confused?
87 {kNMPT_SenderObjID, kNMPF_NoAssertHostedHere, "Culprit"},
88 {kNMPT_Block, kNMPF_None, "Damage", sizeof(sDamage)},
89 {kNMPT_End}}
93 // The network filter. This deals with the fairly complex ways we have to
94 // deal with damage, because the circumstances decide who "calls" the
95 // damage. In general, players always call their own damage -- you can't
96 // be hurt unless you saw it coming. Other than that, damage is generally
97 // decided by the culprit. So if a player shoots an AI that is hosted on
98 // a different machine, and the player thinks that he hit, but the AI
99 // thought that it had missed, it *did* hit.
101 // Basically, this code is responsible for deciding what we should do with
102 // this damage. If it's our problem to deal with it, we let it through
103 // untouched. If it's not our problem at all, then we simply cut it off.
104 // And if we need to deal with it, but the object is owned elsewhere, we
105 // need to tell the owner to deal the damage, and then cut it off here.
107 // It's all a tad kludgy, but should do what we need.
109 #define CUTOFF_DAMAGE {damage->amount = 0; return kDamageStatusQuo;}
111 eDamageResult LGAPI ShockNetDamageFilter(ObjID victim,
112 ObjID culprit,
113 sDamage* damage,
114 tDamageCallbackData /* data */)
116 if (g_bRemoteDamage) {
117 // Someone already handed this damage to us, because we owned the
118 // victim. Do we, in fact, *still* own the victim?
119 if (g_pObjNet->ObjIsProxy(victim)) {
120 // Nope; pass it on...
121 // @NOTE: we're not bothering to check hops here. If we can
122 // ever get into a state where two machines each think the
123 // other owns the victim, we're in deep kimchee here. But we'll
124 // assume the best, on the theory that we have deeper problems
125 // if things get that corrupted...
126 g_pDamageMsg->Send(OBJ_NULL, victim, culprit, damage);
127 CUTOFF_DAMAGE;
128 } else {
129 // All is cool
130 return kDamageNoOpinion;
134 if (!g_pNetMan->Networking())
135 return kDamageNoOpinion;
137 // First, make sure that players call their own damage
138 if (IsAPlayer(victim)) {
139 if (IsPlayerObj(victim)) {
140 // Okay, it's this player, so let it through untouched
141 } else {
142 // It's another player, so let them make their own decision
143 CUTOFF_DAMAGE;
145 } else if (g_pObjNet->ObjLocalOnly(victim)) {
146 // Since no one else knows about the victim, we'd better deal
147 // the damage ourselves
148 } else if (culprit == OBJ_NULL) {
149 // Terrain damage, or something like that; only let it happen
150 // to our own victims:
151 if (g_pObjNet->ObjIsProxy(victim)) {
152 CUTOFF_DAMAGE;
154 } else if (g_pObjNet->ObjHostedHere(culprit)) {
155 // We dealt the damage, so it's our responsibility to make sure
156 // it happens
157 if (g_pObjNet->ObjIsProxy(victim)) {
158 // Tell the victim's owner he's been hurt
159 g_pDamageMsg->Send(OBJ_NULL, victim, culprit, damage);
160 CUTOFF_DAMAGE;
161 } else {
162 // Culprit and victim both live here, so just do it
164 } else if (g_pObjNet->ObjIsProxy(culprit)) {
165 // Since we didn't cause this mess, it's not our problem to deal with it
166 CUTOFF_DAMAGE;
167 } else {
168 // Sound like it's a local-only culprit, such as a bullet
169 AssertMsg1(g_pObjNet->ObjLocalOnly(culprit),
170 "Damage culprit %d in weird network state!", culprit);
171 BOOL copy;
172 if (!gLocalCopyProp->Get(culprit, &copy) || !copy) {
173 // We have the original of the culprit
174 if (g_pObjNet->ObjIsProxy(victim)) {
175 // Tell the victim's owner he's been hurt
176 // We can't transmit the culprit, since it's a local-only
177 // Hopefully, this won't cause horrible problems
178 g_pDamageMsg->Send(OBJ_NULL, victim, OBJ_NULL, damage);
179 CUTOFF_DAMAGE;
180 } else {
181 // Just deal with it ourselves
183 } else {
184 // We have a copy of the culprit, so it's not out problem
185 CUTOFF_DAMAGE;
189 // So if we've gotten to this point, we're letting the damage pass
190 // through:
191 return kDamageNoOpinion;
194 ////////////////////////////////////////
196 // Armor Effect Relation
199 IRelation *g_pArmorEffectRel;
201 static IRelation *g_pOrganRel = NULL;
202 static ITrait *g_pOrganTrait = NULL;
204 static sRelationDesc ArmorEffectRDesc = {"Armor Effect", 0, 0, 0};
205 static sRelationDataDesc noDataDesc = { "None", 0 };
207 ////////////////////////////////////////
209 // Armor Filter
210 // Actually filters player armor & damage inflicted by player
213 eDamageResult LGAPI ShockPlayerDamageFilter(ObjID victim, ObjID culprit, sDamage* damage, tDamageCallbackData data)
215 if (g_bRemoteDamage)
216 // We got sent this damage from someone else, who should have
217 // already done this filtering for himself
218 return kDamageNoOpinion;
220 // don't let things heal past max hp
221 if (damage->amount < 0)
223 int hp, maxhp;
224 if (ObjGetHitPoints(victim,&hp) && ObjGetMaxHitPoints(victim,&maxhp))
225 if (hp - damage->amount > maxhp)
227 damage->amount = hp - maxhp;
228 //dmgRes=kDamageStatusQuo;
232 // Player traits & implants that reduce damage
233 if (victim == PlayerObject())
235 AutoAppIPtr(ShockPlayer);
237 if (pShockPlayer->HasTrait(PlayerObject(), kTraitNimble))
239 if (PhysIsProjectile(culprit))
241 ConfigSpew("ArmorSpew", ("Damage obj %d, reduced from %d to %d because nimble\n",
242 victim, damage->amount, int(floor(float(damage->amount)*kTraitNimbleDamageFrac+0.5))));
243 // argh, where is the round function?
244 damage->amount = int(floor(float(damage->amount)*kTraitNimbleDamageFrac+0.5));
248 BOOL slim_mode;
249 AutoAppIPtr(QuestData);
250 slim_mode = pQuestData->Get("HideInterface");
251 if (slim_mode)
253 // The player doesn't ever take damage during slim_mode:
254 damage->amount = 0;
256 else if (pShockPlayer->HasImplant(PlayerObject(), kImplantWormMind))
258 int psiDamage = int(floor(float(damage->amount)*kImplantWormMindDamageFrac+0.5));
259 AutoAppIPtr(PlayerPsi);
260 int psi = pPlayerPsi->GetPoints();
261 psiDamage = min(psiDamage, psi);
263 pPlayerPsi->SetPoints(psi-psiDamage);
264 ConfigSpew("ArmorSpew", ("Damage obj %d, reduced from %d to %d (%d to psi)\n",
265 victim, damage->amount, damage->amount-psiDamage, psiDamage));
266 damage->amount -= psiDamage;
269 // Player traits that affect melee weapon damage
270 // This shouldn't really be here, but easier than defining a new filter....
271 else
273 if (IsMelee(culprit)) // && (victim != PlayerObject())
275 AutoAppIPtr(ShockPlayer);
276 ConfigSpew("ArmorSpew", ("Damage obj %d, changed from %d to %d because strength %d\n",
277 victim, damage->amount, damage->amount+GetMeleeStrengthParams()->m_meleeMods[pShockPlayer->GetStat(kStatStrength)-1],
278 pShockPlayer->GetStat(kStatStrength)-1));
279 damage->amount += GetMeleeStrengthParams()->m_meleeMods[pShockPlayer->GetStat(kStatStrength)-1];
280 if (damage->amount<0)
281 damage->amount = 0;
284 // do we have any "organ" links which represent research vulnerabilities?
285 if (g_pOrganRel == NULL)
287 AutoAppIPtr(LinkManager);
288 g_pOrganRel = pLinkManager->GetRelationNamed("Organ");
290 if (g_pOrganTrait == NULL)
291 g_pOrganTrait = MakeTraitFromRelation(g_pOrganRel);
293 ILinkQuery *query;
294 sLink slink;
295 ObjID organarch;
296 int research;
298 query = QueryInheritedLinksSingle(g_pOrganTrait,g_pOrganRel, victim, LINKOBJ_WILDCARD);
299 if (!query->Done())
301 query->Link(&slink);
302 organarch = slink.dest;
303 // is our organ researched?
304 research = GetResearchQBArch(organarch,"ResearchState");
305 if (research == 1)
307 sSkillParams *params;
308 params = GetSkillParams();
309 damage->amount = damage->amount * params->m_researchdmg;
312 SafeRelease(query);
315 return kDamageNoOpinion;
318 void ShockUnequipArmor(ObjID equipperID, ObjID armorID)
320 if (armorID != OBJ_NULL)
322 AutoAppIPtr(TraitManager);
323 cAutoLinkQuery linkQuery(g_pArmorEffectRel, pTraitManager->GetArchetype(armorID), LINKOBJ_WILDCARD);
324 while (!linkQuery->Done())
326 pTraitManager->RemoveObjMetaProperty(equipperID, linkQuery.GetDest());
327 linkQuery->Next();
332 void ShockEquipArmor(ObjID equipperID, ObjID armorID)
334 //ObjID currentArmorID;
336 AutoAppIPtr(ShockPlayer);
338 if ((currentArmorID = pShockPlayer->GetEquip(equipperID, kEquipArmor)) != OBJ_NULL)
339 ShockUnequipArmor(equipperID, currentArmorID);
341 if (armorID != OBJ_NULL)
343 AutoAppIPtr(TraitManager);
344 cAutoLinkQuery linkQuery(g_pArmorEffectRel, pTraitManager->GetArchetype(armorID), LINKOBJ_WILDCARD);
345 while (!linkQuery->Done())
347 pTraitManager->AddObjMetaProperty(equipperID, linkQuery.GetDest());
348 linkQuery->Next();
353 void ShockPlayerDamageInit(void)
355 // Armor prop & rel
356 ArmorPropertyInit();
357 g_pArmorEffectRel = CreateStandardRelation(&ArmorEffectRDesc, &noDataDesc, kQCaseSetSourceKnown);
359 // Set up networking:
360 g_pObjNet = AppGetObj(IObjectNetworking);
361 g_pNetMan = AppGetObj(INetManager);
362 g_pDamageMsg = new cNetMsg(&sDamageDesc, NULL);
364 // Install damage listener/filters
365 IDamageModel* pDamageModel = AppGetObj(IDamageModel);
366 // Note that the order here is *essential* -- networking should come
367 // after the ordinary filter! This way, we modify the damage for the
368 // player's stats before we try to network it:
369 pDamageModel->Filter(ShockPlayerDamageFilter,NULL);
370 pDamageModel->Filter(ShockNetDamageFilter,NULL);
371 SafeRelease(pDamageModel);
374 void ShockPlayerDamageTerm(void)
376 ArmorPropertyTerm();
377 SafeRelease(g_pArmorEffectRel);
378 SafeRelease(g_pObjNet);
379 SafeRelease(g_pNetMan);
380 delete g_pDamageMsg;