- Fixed: The rail sound used the shooter's position for calculating the sound origin

but should use the camera position instead to get the correct position for
  the closest point along the trail.
- Fixed: Explosions no longer caused splashes.
- Fixed: Copying translations to lower decals had the shade color check wrong.
- Fixed: Waggling floors did not move attached geometry.
- Cleaned up p_floor.cpp so that related parts of the code are grouped together.

SVN r1926 (trunk)
This commit is contained in:
Christoph Oelckers 2009-10-17 11:30:44 +00:00
parent bbcd6ed5f9
commit 0c39b5c66a
12 changed files with 341 additions and 219 deletions

View File

@ -1,4 +1,11 @@
October 17, 2009 (Changes by Graf Zahl)
- Fixed: The rail sound used the shooter's position for calculating the sound origin
but should use the camera position instead to get the correct position for
the closest point along the trail.
- Fixed: Explosions no longer caused splashes.
- Fixed: Copying translations to lower decals had the shade color check wrong.
- Fixed: Waggling floors did not moved attached geometry.
- Cleaned up p_floor.cpp so that related parts of the code are grouped together.
- fixed: The translation addition broke parsing of palette index based translations.
October 16, 2009 (Changes by Graf Zahl)

View File

@ -135,10 +135,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Mushroom)
if (spawntype == NULL) spawntype = PClass::FindClass("FatShot");
P_RadiusAttack (self, self->target, 128, 128, self->DamageType, true);
if (self->z <= self->floorz + (128<<FRACBITS))
{
P_HitFloor (self);
}
P_CheckSplash(self, 128<<FRACBITS);
// Now launch mushroom cloud
AActor *target = Spawn("Mapspot", 0, 0, 0, NO_REPLACE); // We need something to aim at.

View File

@ -51,10 +51,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_TimeBomb)
self->RenderStyle = STYLE_Add;
self->alpha = FRACUNIT;
P_RadiusAttack (self, self->target, 128, 128, self->DamageType, true);
if (self->z <= self->floorz + (128<<FRACBITS))
{
P_HitFloor (self);
}
P_CheckSplash(self, 128<<FRACBITS);
}
class AArtiTimeBomb : public AInventory

View File

@ -641,12 +641,13 @@ DImpactDecal *DImpactDecal::StaticCreate (const FDecalTemplate *tpl, fixed_t x,
{
if (tpl->LowerDecal)
{
int lowercolor = color;
int lowercolor;
const FDecalTemplate * tpl_low = tpl->LowerDecal->GetDecal();
// If the default color of the lower decal is the same as the main decal's
// apply the custom color as well.
if (tpl->ShadeColor == tpl_low->ShadeColor) lowercolor=0;
if (tpl->ShadeColor != tpl_low->ShadeColor) lowercolor=0;
else lowercolor = color;
StaticCreate (tpl_low, x, y, z, wall, ffloor, lowercolor);
}
DImpactDecal::CheckMax();

View File

@ -74,10 +74,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Explode512)
{
self->target->player->extralight = 5;
}
if (self->z <= self->floorz + (512<<FRACBITS))
{
P_HitFloor (self);
}
P_CheckSplash(self, 512<<FRACBITS);
// Strife didn't do this next part, but it looks good
self->RenderStyle = STYLE_Add;

View File

@ -472,17 +472,33 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end
}
else
{
// Only consider sound in 2D (for now, anyway)
// [BB] You have to devide by lengthsquared here, not multiply with it.
r = ((start.Y - FIXED2FLOAT(mo->y)) * (-dir.Y) -
(start.X - FIXED2FLOAT(mo->x)) * (dir.X)) / lengthsquared;
r = ((start.Y - FIXED2FLOAT(viewy)) * (-dir.Y) - (start.X - FIXED2FLOAT(viewx)) * (dir.X)) / lengthsquared;
r = clamp<double>(r, 0., 1.);
dirz = dir.Z;
dir.Z = 0;
point = start + r * dir;
dir.Z = dirz;
S_Sound (FLOAT2FIXED(point.X), FLOAT2FIXED(point.Y), mo->z,
#if 0
Printf("Start = (%1.4f,%1.4f), End = (%1.4f,%1.4f), Dir =(%1.4f,%1.4f), View = (%1.4f,%1.4f), Point = (%1.4f,%1.4f), r=%1.4f\n",
start.X, start.Y, end.X, end.Y, dir.X, dir.Y, FIXED2FLOAT(viewx), FIXED2FLOAT(viewy),
point.X, point.Y, r);
FVector3 _start = start;
FVector3 _end = end;
FVector3 view(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy), 0);
point.Z = _start.Z = _end.Z = 0;
Printf("S: %1.4f, E: %1.4f, H: %1.4f P: %1.4f\n",
(_start-view).Length(), (_end-view).Length(), (((_start+_end)/2)-view).Length(), (point-view).Length());
#endif
S_Sound (FLOAT2FIXED(point.X), FLOAT2FIXED(point.Y), viewz,
CHAN_WEAPON, sound, 1, ATTN_NORM);
}
}

View File

@ -2986,10 +2986,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Detonate)
{
int damage = self->GetMissileDamage (0, 1);
P_RadiusAttack (self, self->target, damage, damage, self->DamageType, true);
if (self->z <= self->floorz + (damage << FRACBITS))
{
P_HitFloor (self);
}
P_CheckSplash(self, damage<<FRACBITS);
}
bool CheckBossDeath (AActor *actor)

View File

@ -29,11 +29,33 @@
#include "doomstat.h"
#include "r_state.h"
#include "tables.h"
#include "p_3dmidtex.h"
#include "r_interpolate.h"
//==========================================================================
//
//
//
//==========================================================================
static void StartFloorSound (sector_t *sec)
{
if (sec->seqType >= 0)
{
SN_StartSequence (sec, CHAN_FLOOR, sec->seqType, SEQ_PLATFORM, 0);
}
else
{
SN_StartSequence (sec, CHAN_FLOOR, "Floor", 0);
}
}
//==========================================================================
//
// FLOORS
//
//==========================================================================
IMPLEMENT_CLASS (DFloor)
@ -60,71 +82,12 @@ void DFloor::Serialize (FArchive &arc)
<< m_Hexencrush;
}
IMPLEMENT_POINTY_CLASS (DElevator)
DECLARE_POINTER(m_Interp_Floor)
DECLARE_POINTER(m_Interp_Ceiling)
END_POINTERS
DElevator::DElevator ()
{
}
void DElevator::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << m_Type
<< m_Direction
<< m_FloorDestDist
<< m_CeilingDestDist
<< m_Speed
<< m_Interp_Floor
<< m_Interp_Ceiling;
}
void DElevator::Destroy()
{
if (m_Interp_Ceiling != NULL)
{
m_Interp_Ceiling->DelRef();
m_Interp_Ceiling = NULL;
}
if (m_Interp_Floor != NULL)
{
m_Interp_Floor->DelRef();
m_Interp_Floor = NULL;
}
Super::Destroy();
}
IMPLEMENT_POINTY_CLASS (DWaggleBase)
DECLARE_POINTER(m_Interpolation)
END_POINTERS
IMPLEMENT_CLASS (DFloorWaggle)
IMPLEMENT_CLASS (DCeilingWaggle)
DWaggleBase::DWaggleBase ()
{
}
void DWaggleBase::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << m_OriginalDist
<< m_Accumulator
<< m_AccDelta
<< m_TargetScale
<< m_Scale
<< m_ScaleDelta
<< m_Ticker
<< m_State
<< m_Interpolation;
}
//==========================================================================
//
// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)
// MOVE A FLOOR TO ITS DESTINATION (UP OR DOWN)
//
//==========================================================================
void DFloor::Tick ()
{
EResult res;
@ -238,62 +201,11 @@ void DFloor::Tick ()
}
}
//==========================================================================
//
// T_MoveElevator()
//
// Move an elevator to it's destination (up or down)
// Called once per tick for each moving floor.
//
// Passed an elevator_t structure that contains all pertinent info about the
// move. See P_SPEC.H for fields.
// No return.
//
// jff 02/22/98 added to support parallel floor/ceiling motion
//
void DElevator::Tick ()
{
EResult res;
fixed_t oldfloor, oldceiling;
oldfloor = m_Sector->floorplane.d;
oldceiling = m_Sector->ceilingplane.d;
if (m_Direction < 0) // moving down
{
res = MoveFloor (m_Speed, m_FloorDestDist, m_Direction);
if (res == ok || res == pastdest)
{
res = MoveCeiling (m_Speed, m_CeilingDestDist, m_Direction);
if (res == crushed)
{
MoveFloor (m_Speed, oldfloor, -m_Direction);
}
}
}
else // up
{
res = MoveCeiling (m_Speed, m_CeilingDestDist, m_Direction);
if (res == ok || res == pastdest)
{
res = MoveFloor (m_Speed, m_FloorDestDist, m_Direction);
if (res == crushed)
{
MoveCeiling (m_Speed, oldceiling, -m_Direction);
}
}
}
if (res == pastdest) // if destination height acheived
{
// make floor stop sound
SN_StopSequence (m_Sector);
m_Sector->floordata = NULL; //jff 2/22/98
m_Sector->ceilingdata = NULL; //jff 2/22/98
Destroy (); // remove elevator from actives
}
}
//==========================================================================
void DFloor::SetFloorChangeType (sector_t *sec, int change)
{
@ -315,37 +227,35 @@ void DFloor::SetFloorChangeType (sector_t *sec, int change)
}
}
static void StartFloorSound (sector_t *sec)
{
if (sec->seqType >= 0)
{
SN_StartSequence (sec, CHAN_FLOOR, sec->seqType, SEQ_PLATFORM, 0);
}
else
{
SN_StartSequence (sec, CHAN_FLOOR, "Floor", 0);
}
}
//==========================================================================
//
//
//
//==========================================================================
void DFloor::StartFloorSound ()
{
::StartFloorSound (m_Sector);
}
void DElevator::StartFloorSound ()
{
::StartFloorSound (m_Sector);
}
//==========================================================================
//
//
//
//==========================================================================
DFloor::DFloor (sector_t *sec)
: DMovingFloor (sec)
{
}
//==========================================================================
//
// HANDLE FLOOR TYPES
// [RH] Added tag, speed, height, crush, change params.
//
//==========================================================================
bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag,
fixed_t speed, fixed_t height, int crush, int change, bool hexencrush)
{
@ -601,10 +511,14 @@ manual_floor:
return rtn;
}
//==========================================================================
//
// [RH]
// EV_FloorCrushStop
// Stop a floor from crushing!
//
//==========================================================================
bool EV_FloorCrushStop (int tag)
{
int secnum = -1;
@ -624,61 +538,11 @@ bool EV_FloorCrushStop (int tag)
return true;
}
//==========================================================================
//
// EV_DoChange()
// Linear tag search to emulate stair building from Doom.exe
//
// Handle pure change types. These change floor texture and sector type
// by trigger or numeric model without moving the floor.
//
// The linedef causing the change and the type of change is passed
// Returns true if any sector changes
//
// jff 3/15/98 added to better support generalized sector types
// [RH] Added tag parameter.
//
bool EV_DoChange (line_t *line, EChange changetype, int tag)
{
int secnum;
bool rtn;
sector_t *sec;
sector_t *secm;
secnum = -1;
rtn = false;
// change all sectors with the same tag as the linedef
while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0)
{
sec = &sectors[secnum];
rtn = true;
// handle trigger or numeric change type
FTextureID oldpic = sec->GetTexture(sector_t::floor);
switch(changetype)
{
case trigChangeOnly:
if (line)
{ // [RH] if no line, no change
sec->SetTexture(sector_t::floor, line->frontsector->GetTexture(sector_t::floor));
sec->special = (sec->special & SECRET_MASK) | (line->frontsector->special & ~SECRET_MASK);
}
break;
case numChangeOnly:
secm = sec->FindModelFloorSector (sec->CenterFloor());
if (secm)
{ // if no model, no change
sec->SetTexture(sector_t::floor, secm->GetTexture(sector_t::floor));
sec->special = secm->special;
}
break;
default:
break;
}
}
return rtn;
}
//==========================================================================
static int P_FindSectorFromTagLinear (int tag, int start)
{
@ -689,6 +553,7 @@ static int P_FindSectorFromTagLinear (int tag, int start)
return -1;
}
//==========================================================================
//
// BUILD A STAIRCASE!
// [RH] Added stairsize, srcspeed, delay, reset, igntxt, usespecials parameters
@ -696,6 +561,8 @@ static int P_FindSectorFromTagLinear (int tag, int start)
// by its special. If usespecials is 2, each sector stays in "sync" with
// the others.
//
//==========================================================================
bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line,
fixed_t stairsize, fixed_t speed, int delay, int reset, int igntxt,
int usespecials)
@ -897,7 +764,12 @@ manual_stair:
return rtn;
}
//==========================================================================
//
// [RH] Added pillarspeed and slimespeed parameters
//
//==========================================================================
bool EV_DoDonut (int tag, fixed_t pillarspeed, fixed_t slimespeed)
{
sector_t* s1;
@ -966,6 +838,21 @@ bool EV_DoDonut (int tag, fixed_t pillarspeed, fixed_t slimespeed)
return rtn;
}
//==========================================================================
//
// Elevators
//
//==========================================================================
IMPLEMENT_POINTY_CLASS (DElevator)
DECLARE_POINTER(m_Interp_Floor)
DECLARE_POINTER(m_Interp_Ceiling)
END_POINTERS
DElevator::DElevator ()
{
}
DElevator::DElevator (sector_t *sec)
: Super (sec)
{
@ -975,6 +862,111 @@ DElevator::DElevator (sector_t *sec)
m_Interp_Ceiling = sec->SetInterpolation(sector_t::CeilingMove, true);
}
void DElevator::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << m_Type
<< m_Direction
<< m_FloorDestDist
<< m_CeilingDestDist
<< m_Speed
<< m_Interp_Floor
<< m_Interp_Ceiling;
}
//==========================================================================
//
//
//
//==========================================================================
void DElevator::Destroy()
{
if (m_Interp_Ceiling != NULL)
{
m_Interp_Ceiling->DelRef();
m_Interp_Ceiling = NULL;
}
if (m_Interp_Floor != NULL)
{
m_Interp_Floor->DelRef();
m_Interp_Floor = NULL;
}
Super::Destroy();
}
//==========================================================================
//
// T_MoveElevator()
//
// Move an elevator to it's destination (up or down)
// Called once per tick for each moving floor.
//
// Passed an elevator_t structure that contains all pertinent info about the
// move. See P_SPEC.H for fields.
// No return.
//
// jff 02/22/98 added to support parallel floor/ceiling motion
//
//==========================================================================
void DElevator::Tick ()
{
EResult res;
fixed_t oldfloor, oldceiling;
oldfloor = m_Sector->floorplane.d;
oldceiling = m_Sector->ceilingplane.d;
if (m_Direction < 0) // moving down
{
res = MoveFloor (m_Speed, m_FloorDestDist, m_Direction);
if (res == ok || res == pastdest)
{
res = MoveCeiling (m_Speed, m_CeilingDestDist, m_Direction);
if (res == crushed)
{
MoveFloor (m_Speed, oldfloor, -m_Direction);
}
}
}
else // up
{
res = MoveCeiling (m_Speed, m_CeilingDestDist, m_Direction);
if (res == ok || res == pastdest)
{
res = MoveFloor (m_Speed, m_FloorDestDist, m_Direction);
if (res == crushed)
{
MoveCeiling (m_Speed, oldceiling, -m_Direction);
}
}
}
if (res == pastdest) // if destination height acheived
{
// make floor stop sound
SN_StopSequence (m_Sector);
m_Sector->floordata = NULL; //jff 2/22/98
m_Sector->ceilingdata = NULL; //jff 2/22/98
Destroy (); // remove elevator from actives
}
}
//==========================================================================
//
//
//
//==========================================================================
void DElevator::StartFloorSound ()
{
::StartFloorSound (m_Sector);
}
//==========================================================================
//
// EV_DoElevator
//
@ -985,6 +977,8 @@ DElevator::DElevator (sector_t *sec)
// jff 2/22/98 new type to move floor and ceiling in parallel
// [RH] Added speed, tag, and height parameters and new types.
//
//==========================================================================
bool EV_DoElevator (line_t *line, DElevator::EElevator elevtype,
fixed_t speed, fixed_t height, int tag)
{
@ -1071,6 +1065,96 @@ bool EV_DoElevator (line_t *line, DElevator::EElevator elevtype,
}
//==========================================================================
//
// EV_DoChange()
//
// Handle pure change types. These change floor texture and sector type
// by trigger or numeric model without moving the floor.
//
// The linedef causing the change and the type of change is passed
// Returns true if any sector changes
//
// jff 3/15/98 added to better support generalized sector types
// [RH] Added tag parameter.
//
//==========================================================================
bool EV_DoChange (line_t *line, EChange changetype, int tag)
{
int secnum;
bool rtn;
sector_t *sec;
sector_t *secm;
secnum = -1;
rtn = false;
// change all sectors with the same tag as the linedef
while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0)
{
sec = &sectors[secnum];
rtn = true;
// handle trigger or numeric change type
FTextureID oldpic = sec->GetTexture(sector_t::floor);
switch(changetype)
{
case trigChangeOnly:
if (line)
{ // [RH] if no line, no change
sec->SetTexture(sector_t::floor, line->frontsector->GetTexture(sector_t::floor));
sec->special = (sec->special & SECRET_MASK) | (line->frontsector->special & ~SECRET_MASK);
}
break;
case numChangeOnly:
secm = sec->FindModelFloorSector (sec->CenterFloor());
if (secm)
{ // if no model, no change
sec->SetTexture(sector_t::floor, secm->GetTexture(sector_t::floor));
sec->special = secm->special;
}
break;
default:
break;
}
}
return rtn;
}
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_POINTY_CLASS (DWaggleBase)
DECLARE_POINTER(m_Interpolation)
END_POINTERS
IMPLEMENT_CLASS (DFloorWaggle)
IMPLEMENT_CLASS (DCeilingWaggle)
DWaggleBase::DWaggleBase ()
{
}
void DWaggleBase::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << m_OriginalDist
<< m_Accumulator
<< m_AccDelta
<< m_TargetScale
<< m_Scale
<< m_ScaleDelta
<< m_Ticker
<< m_State
<< m_Interpolation;
}
//==========================================================================
//
// WaggleBase
@ -1096,6 +1180,12 @@ void DWaggleBase::Destroy()
Super::Destroy();
}
//==========================================================================
//
//
//
//==========================================================================
void DWaggleBase::DoWaggle (bool ceiling)
{
secplane_t *plane;
@ -1159,7 +1249,12 @@ void DWaggleBase::DoWaggle (bool ceiling)
FixedMul (FloatBobOffsets[(m_Accumulator>>FRACBITS)&63], m_Scale));
m_Sector->ChangePlaneTexZ(pos, plane->HeightDiff (dist));
dist = plane->HeightDiff (dist);
P_ChangeSector (m_Sector, true, dist, ceiling, false);
// Interesting: Hexen passes 'true' for the crunch parameter which really is crushing damage here...
// Also, this does not reset the move if it blocks.
P_Scroll3dMidtex(m_Sector, 1, dist, ceiling);
P_MoveLinkedSectors(m_Sector, 1, dist, ceiling);
P_ChangeSector (m_Sector, 1, dist, ceiling, false);
}
//==========================================================================
@ -1176,7 +1271,7 @@ DFloorWaggle::DFloorWaggle (sector_t *sec)
: Super (sec)
{
sec->floordata = this;
m_Interpolation = sec->SetInterpolation(sector_t::FloorMove, false);
m_Interpolation = sec->SetInterpolation(sector_t::FloorMove, true);
}
void DFloorWaggle::Tick ()
@ -1198,7 +1293,7 @@ DCeilingWaggle::DCeilingWaggle (sector_t *sec)
: Super (sec)
{
sec->ceilingdata = this;
m_Interpolation = sec->SetInterpolation(sector_t::CeilingMove, false);
m_Interpolation = sec->SetInterpolation(sector_t::CeilingMove, true);
}
void DCeilingWaggle::Tick ()

View File

@ -403,7 +403,8 @@ void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile ver
void P_TraceBleed (int damage, AActor *target); // random direction version
void P_RailAttack (AActor *source, int damage, int offset, int color1 = 0, int color2 = 0, float maxdiff = 0, bool silent = false, const PClass *puff = NULL, bool pierce = true); // [RH] Shoot a railgun
bool P_HitFloor (AActor *thing);
bool P_HitWater (AActor *thing, sector_t *sec, fixed_t splashx = FIXED_MIN, fixed_t splashy = FIXED_MIN, fixed_t splashz=FIXED_MIN, bool checkabove = false);
bool P_HitWater (AActor *thing, sector_t *sec, fixed_t splashx = FIXED_MIN, fixed_t splashy = FIXED_MIN, fixed_t splashz=FIXED_MIN, bool checkabove = false, bool alert = true);
void P_CheckSplash(AActor *self, fixed_t distance);
bool P_CheckMissileSpawn (AActor *missile);
void P_PlaySpawnSound(AActor *missile, AActor *spawner);

View File

@ -4618,7 +4618,7 @@ int P_GetThingFloorType (AActor *thing)
// Returns true if hit liquid and splashed, false if not.
//---------------------------------------------------------------------------
bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool checkabove)
bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool checkabove, bool alert)
{
if (thing->flags2 & MF2_FLOATBOB || thing->flags3 & MF3_DONTSPLASH)
return false;
@ -4716,7 +4716,7 @@ foundone:
{
mo = Spawn (splash->SplashBase, x, y, z, ALLOW_REPLACE);
}
if (thing->player && !splash->NoAlert)
if (thing->player && !splash->NoAlert && alert)
{
P_NoiseAlert (thing, thing, true);
}
@ -4793,6 +4793,25 @@ bool P_HitFloor (AActor *thing)
return P_HitWater (thing, m->m_sector);
}
//---------------------------------------------------------------------------
//
// P_CheckSplash
//
// Checks for splashes caused by explosions
//
//---------------------------------------------------------------------------
void P_CheckSplash(AActor *self, fixed_t distance)
{
if (self->z <= self->floorz + (distance<<FRACBITS) && self->floorsector == self->Sector)
{
// Explosion splashes never alert monsters. This is because A_Explode has
// a separate parameter for that so this would get in the way of proper
// behavior.
P_HitWater (self, self->Sector, self->x, self->y, self->floorz, false, false);
}
}
//---------------------------------------------------------------------------
//
// FUNC P_CheckMissileSpawn

View File

@ -609,10 +609,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode)
}
P_RadiusAttack (self, self->target, damage, distance, self->DamageType, hurtSource, true, fulldmgdistance);
if (self->z <= self->floorz + (distance<<FRACBITS))
{
P_HitFloor (self);
}
P_CheckSplash(self, distance<<FRACBITS);
if (alert && self->target != NULL && self->target->player != NULL)
{
validcount++;
@ -637,10 +634,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust)
if (distance <= 0) distance = force;
P_RadiusAttack (self, self->target, force, distance, self->DamageType, affectSource, false);
if (self->z <= self->floorz + (distance<<FRACBITS))
{
P_HitFloor (self);
}
P_CheckSplash(self, distance<<FRACBITS);
}
//==========================================================================

View File

@ -708,6 +708,7 @@ ACTOR PhosphorousFire native
+FLOORCLIP
+NOTELEPORT
+NODAMAGETHRUST
+DONTSPLASH
RenderStyle Add
action native A_Burnarea ();