2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/shock/shkpldmg.cpp,v 1.11 1999/08/05 17:58:39 Justin Exp $
40 // For damage networking:
46 // must be last header
50 #define min(x, y) ((x<y)?(x):(y))
52 ////////////////////////////////////////
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
=
78 "Damage By Local Object",
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
87 {kNMPT_SenderObjID
, kNMPF_NoAssertHostedHere
, "Culprit"},
88 {kNMPT_Block
, kNMPF_None
, "Damage", sizeof(sDamage
)},
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
,
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
);
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
142 // It's another player, so let them make their own decision
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
)) {
154 } else if (g_pObjNet
->ObjHostedHere(culprit
)) {
155 // We dealt the damage, so it's our responsibility to make sure
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
);
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
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
);
172 if (!gLocalCopyProp
->Get(culprit
, ©
) || !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
);
181 // Just deal with it ourselves
184 // We have a copy of the culprit, so it's not out problem
189 // So if we've gotten to this point, we're letting the damage pass
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 ////////////////////////////////////////
210 // Actually filters player armor & damage inflicted by player
213 eDamageResult LGAPI
ShockPlayerDamageFilter(ObjID victim
, ObjID culprit
, sDamage
* damage
, tDamageCallbackData data
)
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)
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));
249 AutoAppIPtr(QuestData
);
250 slim_mode
= pQuestData
->Get("HideInterface");
253 // The player doesn't ever take damage during slim_mode:
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....
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)
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
);
298 query
= QueryInheritedLinksSingle(g_pOrganTrait
,g_pOrganRel
, victim
, LINKOBJ_WILDCARD
);
302 organarch
= slink
.dest
;
303 // is our organ researched?
304 research
= GetResearchQBArch(organarch
,"ResearchState");
307 sSkillParams
*params
;
308 params
= GetSkillParams();
309 damage
->amount
= damage
->amount
* params
->m_researchdmg
;
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());
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());
353 void ShockPlayerDamageInit(void)
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)
377 SafeRelease(g_pArmorEffectRel
);
378 SafeRelease(g_pObjNet
);
379 SafeRelease(g_pNetMan
);