From cef8ae56325dd3ffddf8c755c0d59d9a652b062c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 27 Dec 2014 19:19:15 +0100 Subject: [PATCH 1/6] - fixed: The A_BossDeath code in P_MorphedDeath was missing a NULL pointer check. --- src/g_shared/a_morph.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 6014d6fee..e3433f12e 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -529,24 +529,26 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor { AMorphedMonster *fakeme = static_cast(actor); AActor *realme = fakeme->UnmorphedMe; - if ((fakeme->UnmorphTime) && - (fakeme->MorphStyle & MORPH_UNDOBYDEATH) && - (realme)) + if (realme != NULL) { - int realstyle = fakeme->MorphStyle; - int realhealth = fakeme->health; - if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) + if ((fakeme->UnmorphTime) && + (fakeme->MorphStyle & MORPH_UNDOBYDEATH)) { - *morphed = realme; - *morphedstyle = realstyle; - *morphedhealth = realhealth; - return true; + int realstyle = fakeme->MorphStyle; + int realhealth = fakeme->health; + if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) + { + *morphed = realme; + *morphedstyle = realstyle; + *morphedhealth = realhealth; + return true; + } + } + if (realme->flags4 & MF4_BOSSDEATH) + { + realme->health = 0; // make sure that A_BossDeath considers it dead. + CALL_ACTION(A_BossDeath, realme); } - } - if (realme->flags4 & MF4_BOSSDEATH) - { - realme->health = 0; // make sure that A_BossDeath considers it dead. - CALL_ACTION(A_BossDeath, realme); } fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die() return false; From fdf2d6c493ea898e13e1b92b145566949f1ac38b Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Dec 2014 12:47:48 -0600 Subject: [PATCH 2/6] - Cleaned up some of the reflective code. - Added null checks to AIMREFLECT. - The missile being reflected now corrects the z velocity to perfectly reflect towards the actor's middle. --- src/p_mobj.cpp | 79 ++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 8c76d793e..a88ec6515 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1961,48 +1961,50 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) // Don't change the angle if there's THRUREFLECT on the monster. if (!(BlockingMobj->flags7 & MF7_THRUREFLECT)) { - int dir; - angle_t delta; - - if (BlockingMobj->flags7 & MF7_MIRRORREFLECT) - angle = mo->angle + ANG180; - else - angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y); - + //int dir; + //angle_t delta; + bool dontReflect = (mo->AdjustReflectionAngle(BlockingMobj, angle)); // Change angle for deflection/reflection - // AIMREFLECT calls precedence so make sure not to bother with adjusting here if declared. - if (!(BlockingMobj->flags7 & MF7_AIMREFLECT) && (mo->AdjustReflectionAngle(BlockingMobj, angle))) - { - goto explode; - } - // Reflect the missile along angle - if (BlockingMobj->flags7 & MF7_AIMREFLECT) + if (!dontReflect) { - dir = P_FaceMobj(mo, mo->target, &delta); - if (dir) - { // Turn clockwise - mo->angle += delta; + bool tg = (mo->target != NULL); + bool blockingtg = (BlockingMobj->target != NULL); + if (BlockingMobj->flags7 & MF7_AIMREFLECT && (tg || blockingtg)) + { + AActor *origin; + if (tg) + origin = mo->target; + else if (blockingtg) + origin = BlockingMobj->target; + + float speed = (float)(mo->Speed); + //dest->x - source->x + FVector3 velocity(origin->x - mo->x, origin->y - mo->y, (origin->z + (origin->height/2)) - mo->z); + velocity.Resize(speed); + angle = mo->angle >> ANGLETOFINESHIFT; + mo->velx = (fixed_t)(velocity.X); + mo->vely = (fixed_t)(velocity.Y); + mo->velz = (fixed_t)(velocity.Z); + /* + mo->velx = FixedMul(mo->Speed, finecosine[angle]); + mo->vely = FixedMul(mo->Speed, finesine[angle]); + mo->velz = -mo->velz; + */ } else - { // Turn counter clockwise - mo->angle -= delta; + { + mo->angle = angle; + angle >>= ANGLETOFINESHIFT; + mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]); + mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]); + mo->velz = -mo->velz / 2; } - angle = mo->angle >> ANGLETOFINESHIFT; - mo->velx = FixedMul(mo->Speed, finecosine[angle]); - mo->vely = FixedMul(mo->Speed, finesine[angle]); - mo->velz = -mo->velz; } else { - mo->angle = angle; - angle >>= ANGLETOFINESHIFT; - mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]); - mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]); - mo->velz = -mo->velz / 2; - } - - + goto explode; + } } if (mo->flags2 & MF2_SEEKERMISSILE) { @@ -2928,8 +2930,10 @@ bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle) if (flags2 & MF2_DONTREFLECT) return true; if (thing->flags7 & MF7_THRUREFLECT) return false; + if (thing->flags7 & MF7_MIRRORREFLECT) + angle += ANGLE_180; // Change angle for reflection - if (thing->flags4&MF4_SHIELDREFLECT) + else if (thing->flags4&MF4_SHIELDREFLECT) { // Shield reflection (from the Centaur if (abs (angle - thing->angle)>>24 > 45) @@ -2952,6 +2956,13 @@ bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle) else angle -= ANG45; } + else if (thing->flags7 & MF7_AIMREFLECT) + { + if (this->target != NULL) + A_Face(this, this->target); + else if (thing->target != NULL) + A_Face(this, thing->target); + } else angle += ANGLE_1 * ((pr_reflect()%16)-8); return false; From 519ff8b7d114b6d38ba0813b8d7f509ef6e6329c Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Dec 2014 14:15:14 -0600 Subject: [PATCH 3/6] - HITTARGET/MASTER/TRACER now set the puff's pointer(s) within P_SpawnPuff. - PUFFGETSOWNER, for the sake of compatibility, maintains override for target. --- src/p_local.h | 2 +- src/p_map.cpp | 18 +++--------------- src/p_mobj.cpp | 10 +++++++++- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index c0a754e25..fe7958807 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -133,7 +133,7 @@ enum EPuffFlags PF_NORANDOMZ = 16 }; -AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0); +AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0, AActor *vict = NULL); void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor *originator); void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator); void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator); diff --git a/src/p_map.cpp b/src/p_map.cpp index 456db2d0f..aecead6ed 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3761,14 +3761,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, puffFlags |= PF_HITTHINGBLEED; // We must pass the unreplaced puff type here - puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING); - } - - if (puffDefaults != NULL && trace.Actor != NULL && puff != NULL) - { - if (puffDefaults->flags7 && MF7_HITTARGET) puff->target = trace.Actor; - if (puffDefaults->flags7 && MF7_HITMASTER) puff->master = trace.Actor; - if (puffDefaults->flags7 && MF7_HITTRACER) puff->tracer = trace.Actor; + puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING, trace.Actor); } // Allow puffs to inflict poison damage, so that hitscans can poison, too. @@ -4211,14 +4204,9 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i } if (spawnpuff) { - P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags); - } - if (hitactor != NULL && puffDefaults != NULL && thepuff != NULL) - { - if (puffDefaults->flags7 & MF7_HITTARGET) thepuff->target = hitactor; - if (puffDefaults->flags7 & MF7_HITMASTER) thepuff->master = hitactor; - if (puffDefaults->flags7 & MF7_HITTRACER) thepuff->tracer = hitactor; + P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags, hitactor); } + if (puffDefaults && puffDefaults->PoisonDamage > 0 && puffDefaults->PoisonDuration != INT_MIN) { P_PoisonMobj(hitactor, thepuff ? thepuff : source, source, puffDefaults->PoisonDamage, puffDefaults->PoisonDuration, puffDefaults->PoisonPeriod, puffDefaults->PoisonDamageType); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a88ec6515..dbce9e2ff 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4930,7 +4930,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) // P_SpawnPuff // -AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags) +AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags, AActor *vict) { AActor *puff; @@ -4940,9 +4940,17 @@ AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t puff = Spawn (pufftype, x, y, z, ALLOW_REPLACE); if (puff == NULL) return NULL; + //Moved puff creation and target/master/tracer setting to here. + if (puff && vict) + { + if (puff->flags7 & MF7_HITTARGET) puff->target = vict; + if (puff->flags7 & MF7_HITMASTER) puff->master = vict; + if (puff->flags7 & MF7_HITTRACER) puff->tracer = vict; + } // [BB] If the puff came from a player, set the target of the puff to this player. if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER)) puff->target = source; + if (source != NULL) puff->angle = R_PointToAngle2(x, y, source->x, source->y); From b5d0c5c357e3d74c1b90b9492cf176f14b1c7f72 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 28 Dec 2014 22:15:12 +0100 Subject: [PATCH 4/6] - fixed: When a player drops his inventory, the dropped weapons must be checked for their class to ensure that they are not DehackedPickups which cannot be modified as intended. --- src/p_user.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index bf656d2b9..e91766cf0 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1365,7 +1365,7 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags) weap->SpawnState != ::GetDefault()->SpawnState) { item = P_DropItem (this, weap->GetClass(), -1, 256); - if (item != NULL) + if (item != NULL && item->IsKindOf(RUNTIME_CLASS(AWeapon))) { if (weap->AmmoGive1 && weap->Ammo1) { From 4ddfd0f46a26df2b349182197b4a16c7a0e4f3f8 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 30 Dec 2014 19:59:31 -0600 Subject: [PATCH 5/6] - Added 3 new properties and 3 functions to control them. - Rippers will rip through anything with an equivalent ripper level, or if their level is between or on the min and max ranges. - If no min or max is defined, it simply checks if the monster's ripper level is lower than the missiles. - Functions: A_SetRipperLevel(int level), A_SetRipMin(int min), A_SetRipMax(int max) - Properties: RipperLevel, RipLevelMin, and RipLevelMax. - RipperLevel: Applicable to monsters and projectiles. - RipLevelMin and RipLevelMax are only useful on monsters. - By default, all are 0. --- src/actor.h | 3 ++ src/p_map.cpp | 12 +++++++- src/p_mobj.cpp | 7 +++++ src/thingdef/thingdef_codeptr.cpp | 42 ++++++++++++++++++++++++++++ src/thingdef/thingdef_properties.cpp | 27 ++++++++++++++++++ src/version.h | 2 +- wadsrc/static/actors/actor.txt | 6 ++++ 7 files changed, 97 insertions(+), 2 deletions(-) diff --git a/src/actor.h b/src/actor.h index 57736f43b..bc6e830f0 100644 --- a/src/actor.h +++ b/src/actor.h @@ -985,6 +985,9 @@ public: FNameNoInit DeathType; const PClass *TeleFogSourceType; const PClass *TeleFogDestType; + int RipperLevel; + int RipLevelMin; + int RipLevelMax; FState *SpawnState; FState *SeeState; diff --git a/src/p_map.cpp b/src/p_map.cpp index aecead6ed..233cc50cd 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1207,7 +1207,17 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) { return true; } - if (tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) + // Rippers will rip through anything with an equivalent ripper level, + // or if the missile's ripper level is within the min/max range, + // or if there's no min/max range and the missile's ripper level is + // >= the monster's, then let 'er rip! + bool ripmin = (thing->RipLevelMin != 0) ? true : false; + bool ripmax = (thing->RipLevelMax != 0) ? true : false; + if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && + ((!(ripmin) && !(ripmax) && (thing->RipperLevel <= tm.thing->RipperLevel)) || + ((thing->RipperLevel == tm.thing->RipperLevel) || + (thing->RipLevelMin <= tm.thing->RipperLevel) && + (thing->RipLevelMax >= tm.thing->RipperLevel)))) { if (!(tm.thing->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS)) { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index dbce9e2ff..6a035933d 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -338,6 +338,13 @@ void AActor::Serialize (FArchive &arc) arc << TeleFogSourceType << TeleFogDestType; } + if (SaveVersion >= 4518) + { + arc << RipperLevel + << RipLevelMin + << RipLevelMax; + } + { FString tagstr; if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 50969ce85..54645d864 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5613,3 +5613,45 @@ DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) self->TeleFogDestType = temp; } } + +//=========================================================================== +// +// A_SetRipperLevel(int level) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipperLevel) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(level, 0); + self->RipperLevel = level; +} + +//=========================================================================== +// +// A_SetRipMin(int min) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMin) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(min, 1); + self->RipLevelMin = min; +} + +//=========================================================================== +// +// A_SetRipMin(int min) +// +// Sets the ripper level/requirement of the calling actor. +// Also sets the minimum and maximum levels to rip through. +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(max, 1); + self->RipLevelMax = max; +} \ No newline at end of file diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index b6a2937ae..a00ac62e1 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1436,6 +1436,33 @@ DEFINE_PROPERTY(telefogdesttype, S, Actor) else defaults->TeleFogDestType = FindClassTentative(str, "TeleportFog"); } +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(ripperlevel, I, Actor) +{ + PROP_INT_PARM(id, 0); + defaults->RipperLevel = id; +} + +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(riplevelmin, I, Actor) +{ + PROP_INT_PARM(id, 0); + defaults->RipLevelMin = id; +} + +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(riplevelmax, I, Actor) +{ + PROP_INT_PARM(id, 0); + defaults->RipLevelMax = id; +} + //========================================================================== // // Special inventory properties diff --git a/src/version.h b/src/version.h index 6863f0573..09b830438 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4517 +#define SAVEVER 4518 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 73a5e0e4d..c32ecb958 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -28,6 +28,9 @@ ACTOR Actor native //: Thinker DeathType Normal TeleFogSourceType "TeleportFog" TeleFogDestType "TeleportFog" + RipperLevel 0 + RipLevelMin 0 + RipLevelMax 0 // Variables for the expression evaluator // NOTE: fixed_t and angle_t are only used here to ensure proper conversion @@ -321,6 +324,9 @@ ACTOR Actor native //: Thinker action native A_TakeFromSiblings(class itemtype, int amount = 0); action native A_SetTeleFog(name oldpos, name newpos); action native A_SwapTeleFog(); + action native A_SetRipperLevel(int level); + action native A_SetRipMin(int min); + action native A_SetRipMax(int max); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From c57cc91d7c062b7629abc8f7c4fdaa95086432bf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 31 Dec 2014 10:13:15 +0100 Subject: [PATCH 6/6] - cleaned up the RipLevel logic a bit to be less confusing. --- src/p_map.cpp | 27 ++++++++++++++++----------- src/thingdef/thingdef_properties.cpp | 12 ++++++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 233cc50cd..68d0b21f0 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -887,6 +887,20 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) return true; } + +//========================================================================== +// +// Isolated to keep the code readable and fix the logic +// +//========================================================================== + +static bool CheckRipLevel(AActor *victim, AActor *projectile) +{ + if (victim->RipLevelMin > 0 && projectile->RipperLevel < victim->RipLevelMin) return false; + if (victim->RipLevelMax > 0 && projectile->RipperLevel > victim->RipLevelMax) return false; + return true; +} + //========================================================================== // // PIT_CheckThing @@ -1207,17 +1221,8 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) { return true; } - // Rippers will rip through anything with an equivalent ripper level, - // or if the missile's ripper level is within the min/max range, - // or if there's no min/max range and the missile's ripper level is - // >= the monster's, then let 'er rip! - bool ripmin = (thing->RipLevelMin != 0) ? true : false; - bool ripmax = (thing->RipLevelMax != 0) ? true : false; - if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && - ((!(ripmin) && !(ripmax) && (thing->RipperLevel <= tm.thing->RipperLevel)) || - ((thing->RipperLevel == tm.thing->RipperLevel) || - (thing->RipLevelMin <= tm.thing->RipperLevel) && - (thing->RipLevelMax >= tm.thing->RipperLevel)))) + + if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && CheckRipLevel(thing, tm.thing)) { if (!(tm.thing->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS)) { diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index a00ac62e1..677979276 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1442,6 +1442,10 @@ DEFINE_PROPERTY(telefogdesttype, S, Actor) DEFINE_PROPERTY(ripperlevel, I, Actor) { PROP_INT_PARM(id, 0); + if (id < 0) + { + I_Error ("RipperLevel must not be negative"); + } defaults->RipperLevel = id; } @@ -1451,6 +1455,10 @@ DEFINE_PROPERTY(ripperlevel, I, Actor) DEFINE_PROPERTY(riplevelmin, I, Actor) { PROP_INT_PARM(id, 0); + if (id < 0) + { + I_Error ("RipLevelMin must not be negative"); + } defaults->RipLevelMin = id; } @@ -1460,6 +1468,10 @@ DEFINE_PROPERTY(riplevelmin, I, Actor) DEFINE_PROPERTY(riplevelmax, I, Actor) { PROP_INT_PARM(id, 0); + if (id < 0) + { + I_Error ("RipLevelMax must not be negative"); + } defaults->RipLevelMax = id; }