forked from Mirrors/openclonk
210 lines
5.6 KiB
C
210 lines
5.6 KiB
C
/**
|
|
HitCheck.c
|
|
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.
|
|
|
|
@author Newton, Boni
|
|
*/
|
|
|
|
global func FxHitCheckStart(object target, proplist effect, int temp, object by_obj, bool never_shooter)
|
|
{
|
|
if (temp)
|
|
return;
|
|
effect.x = target->GetX();
|
|
effect.y = target->GetY();
|
|
if (!by_obj || GetType(by_obj) != C4V_C4Object)
|
|
by_obj = target;
|
|
if (by_obj->Contained())
|
|
by_obj = by_obj->Contained();
|
|
effect.shooter = by_obj;
|
|
effect.live = false;
|
|
effect.never_shooter = 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, proplist effect, int reason, bool temp)
|
|
{
|
|
if (temp)
|
|
return;
|
|
|
|
target->SetCategory(target->GetID()->GetCategory());
|
|
return;
|
|
}
|
|
|
|
global func FxHitCheckDoCheck(object target, proplist 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.shooter;
|
|
var live = effect.live;
|
|
|
|
if (live)
|
|
shooter = target;
|
|
|
|
if (dist <= 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) continue; // hit callback of one object might have removed other objects
|
|
if(obj == target) continue;
|
|
if(obj == shooter) continue;
|
|
|
|
// Unlike in hazard, there is no NOFF rule (yet)
|
|
// CheckEnemy
|
|
//if(!CheckEnemy(obj,target)) continue;
|
|
|
|
// IsProjectileTarget will be hit (defaults to true for OCF_Alive).
|
|
if (obj->~IsProjectileTarget(target, shooter))
|
|
{
|
|
target->~HitObject(obj);
|
|
if (!target)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
global func FxHitCheckEffect(string newname)
|
|
{
|
|
if (newname == "HitCheck")
|
|
return -2;
|
|
return;
|
|
}
|
|
|
|
global func FxHitCheckAdd(object target, proplist effect, string neweffectname, int newtimer, by_obj, never_shooter)
|
|
{
|
|
effect.x = target->GetX();
|
|
effect.y = target->GetY();
|
|
if (!by_obj)
|
|
by_obj = target;
|
|
if (by_obj->Contained())
|
|
by_obj = by_obj->Contained();
|
|
effect.shooter = by_obj;
|
|
effect.live = false;
|
|
effect.never_shooter = never_shooter;
|
|
return;
|
|
}
|
|
|
|
global func FxHitCheckTimer(object target, proplist 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.x = target->GetX();
|
|
effect.y = target->GetY();
|
|
var live = effect.live;
|
|
var never_shooter = effect.never_shooter;
|
|
var shooter = effect.shooter;
|
|
|
|
// 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.
|
|
if (shooter)
|
|
{
|
|
if (FindObject(Find_AtPoint(target->GetX(), target->GetY()), Find_InArray([shooter])))
|
|
{
|
|
// we may not switch to "live" yet.
|
|
ready = false;
|
|
}
|
|
}
|
|
// Otherwise, the shot will be live.
|
|
if (ready)
|
|
effect.live = true;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
global func IsProjectileTarget(object projectile, object shooter)
|
|
{
|
|
return GetOCF() & OCF_Alive;
|
|
}
|
|
|
|
/*
|
|
Checks whether an object is ready to take damage from this object, calling QueryCatchBlow.
|
|
*/
|
|
global func WeaponCanHit(object target)
|
|
{
|
|
if (target->~QueryCatchBlow(this)) return false;
|
|
if (!target || !this) return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Deals damage to an object, draining either energy for living things or dealing damage otherwise.
|
|
CatchBlow is called on the target if it's alive.
|
|
*/
|
|
global func WeaponDamage(object target, int damage, int damage_type, bool exact_damage)
|
|
{
|
|
if (!this || !target) return;
|
|
|
|
damage_type = damage_type ?? FX_Call_EngObjHit;
|
|
var true_damage = damage;
|
|
if (exact_damage) true_damage = damage / 1000;
|
|
|
|
if (target->GetAlive())
|
|
{
|
|
target->DoEnergy(-damage, exact_damage, damage_type, GetController());
|
|
if (!target) return;
|
|
|
|
target->~CatchBlow(-true_damage, this);
|
|
}
|
|
else
|
|
{
|
|
target->DoDamage(true_damage, damage_type, GetController());
|
|
}
|
|
}
|
|
|
|
/*
|
|
Tumbles an object based on this object's speed and mass.
|
|
strength = 100 means using 100% of the own mass for tumbling the other object.
|
|
*/
|
|
global func WeaponTumble(object target, int strength)
|
|
{
|
|
if (!this || !target) return;
|
|
|
|
strength = strength ?? 100;
|
|
if (strength <= 0) return;
|
|
|
|
if (target->GetAlive())
|
|
{
|
|
target->SetAction("Tumble");
|
|
// Constrained by conservation of linear momentum, unrealism != 1 for unrealistic behaviour.
|
|
var unrealism = 3;
|
|
var mass = strength * GetMass() / 100;
|
|
var obj_mass = target->GetMass();
|
|
target->SetXDir((target->GetXDir() * obj_mass + GetXDir() * mass * unrealism) / (mass + obj_mass));
|
|
target->SetYDir((target->GetYDir() * obj_mass + GetYDir() * mass * unrealism) / (mass + obj_mass));
|
|
}
|
|
}
|