forked from Mirrors/openclonk
219 lines
5.8 KiB
C
219 lines
5.8 KiB
C
![]() |
/*--
|
||
![]() |
HitCheck.c
|
||
![]() |
Authors: Newton, Boni
|
||
![]() |
|
||
![]() |
Effect for hit checking.
|
||
|
Facilitates any hit check of a projectile. The Projectile hits anything
|
||
|
which is either alive or returns for IsProjectileTarget(object projectile,
|
||
|
object shooter) true. If the projectile hits something, it calls
|
||
|
HitObject(object target) in the projectile.
|
||
|
--*/
|
||
|
|
||
![]() |
|
||
|
// EffectVars:
|
||
|
// 0 - old X-Position
|
||
|
// 1 - old Y-Position
|
||
![]() |
// 2 - shooter (Object that shot the projectile)
|
||
![]() |
// 4 - live? If true, the shooter can be hit by the projectile
|
||
|
// 5 - never hit the shooter. True or false
|
||
|
|
||
![]() |
global func FxHitCheckStart(object target, effect, int temp, object by_obj, bool never_shooter)
|
||
![]() |
{
|
||
![]() |
if (temp)
|
||
![]() |
return;
|
||
![]() |
effect.var0 = target->GetX();
|
||
|
effect.var1 = target->GetY();
|
||
![]() |
if (!by_obj)
|
||
|
by_obj = target;
|
||
|
if (by_obj->Contained())
|
||
|
by_obj = by_obj->Contained();
|
||
![]() |
effect.var2 = by_obj;
|
||
|
effect.var4 = false;
|
||
|
effect.var5 = never_shooter;
|
||
![]() |
|
||
![]() |
// C4D_Object has a hitcheck too -> change to vehicle to supress that.
|
||
|
if (target->GetCategory() & C4D_Object)
|
||
![]() |
target->SetCategory((target->GetCategory() - C4D_Object) | C4D_Vehicle);
|
||
![]() |
return;
|
||
![]() |
}
|
||
![]() |
|
||
![]() |
global func FxHitCheckStop(object target, effect, int reason, bool temp)
|
||
![]() |
{
|
||
![]() |
if (temp)
|
||
![]() |
return;
|
||
![]() |
|
||
|
target->SetCategory(target->GetID()->GetCategory());
|
||
![]() |
return;
|
||
![]() |
}
|
||
|
|
||
![]() |
global func FxHitCheckDoCheck(object target, effect)
|
||
![]() |
{
|
||
|
var obj;
|
||
![]() |
// rather search in front of the projectile, since a hit might delete the effect,
|
||
![]() |
// and clonks can effectively hide in front of walls.
|
||
![]() |
var oldx = target->GetX();
|
||
|
var oldy = target->GetY();
|
||
|
var newx = target->GetX() + target->GetXDir() / 10;
|
||
|
var newy = target->GetY() + target->GetYDir() / 10;
|
||
![]() |
var dist = Distance(oldx, oldy, newx, newy);
|
||
|
|
||
![]() |
var shooter = effect.var2;
|
||
|
var live = effect.var4;
|
||
![]() |
|
||
![]() |
if (live)
|
||
|
shooter = target;
|
||
![]() |
|
||
![]() |
if (Distance(oldx, oldy, newx, newy) <= Max(1, Max(Abs(target->GetXDir()), Abs(target->GetYDir()))) * 2)
|
||
![]() |
{
|
||
![]() |
// We search for objects along the line on which we moved since the last check
|
||
|
// and sort by distance (closer first).
|
||
|
for (obj in FindObjects(Find_OnLine(oldx, oldy, newx, newy),
|
||
|
Find_NoContainer(),
|
||
![]() |
Find_Layer(target->GetObjectLayer()),
|
||
![]() |
Find_PathFree(target),
|
||
![]() |
Sort_Distance(oldx, oldy)))
|
||
![]() |
{
|
||
|
// Excludes
|
||
|
if(obj == target) continue;
|
||
|
if(obj == shooter) continue;
|
||
![]() |
|
||
![]() |
// Unlike in hazard, there is no NOFF rule (yet)
|
||
![]() |
// CheckEnemy
|
||
|
//if(!CheckEnemy(obj,target)) continue;
|
||
|
|
||
![]() |
// IsProjectileTarget or Alive will be hit
|
||
![]() |
if (obj->~IsProjectileTarget(target, shooter) || obj->GetOCF() & OCF_Alive)
|
||
![]() |
{
|
||
![]() |
target->~HitObject(obj);
|
||
![]() |
if (!target)
|
||
![]() |
return;
|
||
![]() |
}
|
||
|
}
|
||
|
}
|
||
![]() |
return;
|
||
![]() |
}
|
||
|
|
||
|
global func FxHitCheckEffect(string newname)
|
||
|
{
|
||
![]() |
if (newname == "HitCheck")
|
||
![]() |
return -2;
|
||
|
return;
|
||
![]() |
}
|
||
|
|
||
![]() |
global func FxHitCheckAdd(object target, effect, string neweffectname, int newtimer, by_obj, never_shooter)
|
||
![]() |
{
|
||
![]() |
effect.var0 = target->GetX();
|
||
|
effect.var1 = target->GetY();
|
||
![]() |
if (!by_obj)
|
||
|
by_obj = target;
|
||
|
if (by_obj->Contained())
|
||
|
by_obj = by_obj->Contained();
|
||
![]() |
effect.var2 = by_obj;
|
||
|
effect.var4 = false;
|
||
|
effect.var5 = never_shooter;
|
||
![]() |
return;
|
||
![]() |
}
|
||
![]() |
|
||
![]() |
global func FxHitCheckTimer(object target, effect, int time)
|
||
![]() |
{
|
||
![]() |
EffectCall(target, effect, "DoCheck");
|
||
|
// It could be that it hit something and removed itself. thus check if target is still there.
|
||
|
// The effect will be deleted right after this.
|
||
![]() |
if (!target)
|
||
![]() |
return -1;
|
||
![]() |
|
||
![]() |
effect.var0 = target->GetX();
|
||
|
effect.var1 = target->GetY();
|
||
|
var live = effect.var4;
|
||
|
var never_shooter = effect.var5;
|
||
|
var shooter = effect.var2;
|
||
![]() |
|
||
![]() |
// The projectile will be only switched to "live", meaning that it can hit the
|
||
|
// shooter himself when the shot exited the shape of the shooter one time.
|
||
![]() |
if (!never_shooter)
|
||
![]() |
{
|
||
![]() |
if (!live)
|
||
![]() |
{
|
||
![]() |
var ready = true;
|
||
![]() |
// We search for all objects with the id of our shooter.
|
||
|
for (var foo in FindObjects(Find_AtPoint(target->GetX(), target->GetY()), Find_ID(shooter->GetID())))
|
||
![]() |
{
|
||
![]() |
// If its the shooter...
|
||
![]() |
if(foo == shooter)
|
||
![]() |
// we may not switch to "live" yet.
|
||
![]() |
ready = false;
|
||
|
}
|
||
![]() |
// Otherwise, the shot will be live.
|
||
|
if (ready)
|
||
![]() |
effect.var4 = true;
|
||
![]() |
}
|
||
|
}
|
||
![]() |
return;
|
||
![]() |
}
|
||
![]() |
|
||
![]() |
// flags for the ProjectileHit function
|
||
|
static const ProjectileHit_tumble=1; // the target tumbles
|
||
|
static const ProjectileHit_no_query_catch_blow_callback=2; // if you want to call QueryCatchBlow manually
|
||
|
static const ProjectileHit_exact_damage=4; // exact damage, factor 1000
|
||
|
static const ProjectileHit_no_on_projectile_hit_callback=8;
|
||
|
|
||
|
global func ProjectileHit(object obj, int dmg, int flags, int damage_type)
|
||
![]() |
{
|
||
![]() |
if(flags == nil) flags=0;
|
||
|
if(!damage_type) damage_type=FX_Call_EngObjHit;
|
||
|
var tumble=flags & ProjectileHit_tumble;
|
||
|
|
||
![]() |
if (!this || !obj)
|
||
![]() |
return;
|
||
![]() |
|
||
![]() |
if(!(flags & ProjectileHit_no_query_catch_blow_callback))
|
||
![]() |
{
|
||
|
if (obj->GetAlive())
|
||
|
if (obj->~QueryCatchBlow(this))
|
||
|
return;
|
||
|
|
||
|
if (!this || !obj)
|
||
![]() |
return;
|
||
![]() |
}
|
||
|
|
||
|
if(!(flags & ProjectileHit_no_on_projectile_hit_callback))
|
||
|
{
|
||
|
obj->~OnProjectileHit(this);
|
||
|
if (!this || !obj)
|
||
|
return;
|
||
|
}
|
||
![]() |
|
||
|
this->~OnStrike(obj);
|
||
![]() |
if (!this || !obj)
|
||
|
return;
|
||
![]() |
if (obj->GetAlive())
|
||
![]() |
{
|
||
![]() |
obj->DoEnergy(-dmg, (flags & ProjectileHit_exact_damage), damage_type, GetController());
|
||
![]() |
if(!obj) return;
|
||
![]() |
obj->~CatchBlow(-dmg, this);
|
||
![]() |
}
|
||
|
else
|
||
|
{
|
||
![]() |
var true_dmg=dmg;
|
||
|
if(flags & ProjectileHit_exact_damage) true_dmg/=1000;
|
||
|
|
||
|
obj->DoDamage(true_dmg, damage_type, GetController());
|
||
![]() |
}
|
||
![]() |
// Target could have done something with this projectile.
|
||
![]() |
if (!this || !obj)
|
||
![]() |
return;
|
||
![]() |
|
||
![]() |
// Tumble target.
|
||
![]() |
if (obj->GetAlive() && tumble)
|
||
|
{
|
||
![]() |
obj->SetAction("Tumble");
|
||
![]() |
// Constrained by conservation of linear momentum, unrealism != 1 for unrealistic behaviour.
|
||
|
var unrealism = 3;
|
||
|
var mass = GetMass();
|
||
|
var obj_mass = obj->GetMass();
|
||
|
obj->SetXDir((obj->GetXDir() * obj_mass + GetXDir() * mass * unrealism) / (mass + obj_mass));
|
||
|
obj->SetYDir((obj->GetYDir() * obj_mass + GetYDir() * mass * unrealism) / (mass + obj_mass));
|
||
![]() |
}
|
||
![]() |
return;
|
||
![]() |
}
|