[10739] Cleanup pet remove code and some fixes.

* Merge Player::RemovePet and Pet::Remove function code to Pet::Unsummon
  This let be sure that in all cases all required steps doen. For example this fix
  creature's guardians propertly remove from guardians list.
* Add new pet save mode PET_SAVE_REAGENTS as replacement PET_SAVE_NOT_IN_SLOT+true-arg stable pair in old function args
  This will avoid use reagent save arg with wrong different save modes.
* Fixed recently added code with absent check for re-summon protector pet call.
This commit is contained in:
VladimirMangos 2010-11-19 02:07:14 +03:00
parent de66f32882
commit f9bcfa3a89
13 changed files with 138 additions and 125 deletions

View file

@ -1045,7 +1045,7 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac
bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
// unsummon current and summon old pet if there was one and there isn't a current pet
plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
plr->RemovePet(PET_SAVE_NOT_IN_SLOT);
plr->ResummonPetTemporaryUnSummonedIfAny();
if (isRated() && GetStatus() == STATUS_IN_PROGRESS)

View file

@ -62,7 +62,7 @@ void WorldSession::HandleRepopRequestOpcode( WorldPacket & recv_data )
}
//this is spirit release confirm?
GetPlayer()->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
GetPlayer()->RemovePet(PET_SAVE_REAGENTS);
GetPlayer()->BuildPlayerRepop();
GetPlayer()->RepopAtGraveyard();
}

View file

@ -614,7 +614,7 @@ void WorldSession::HandleStablePet( WorldPacket & recv_data )
if( free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots)
{
_player->RemovePet(pet,PetSaveMode(free_slot));
pet->Unsummon(PetSaveMode(free_slot), _player);
SendStableResult(STABLE_SUCCESS_STABLE);
}
else
@ -678,7 +678,7 @@ void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
// delete dead pet
if(pet)
_player->RemovePet(pet,PET_SAVE_AS_DELETED);
pet->Unsummon(PET_SAVE_AS_DELETED, _player);
Pet *newpet = new Pet(HUNTER_PET);
if(!newpet->LoadPetFromDB(_player,creature_id,petnumber))
@ -790,7 +790,7 @@ void WorldSession::HandleStableSwapPet( WorldPacket & recv_data )
}
// move alive pet to slot or delete dead pet
_player->RemovePet(pet,pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
pet->Unsummon(pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED, _player);
// summon unstabled pet
Pet *newpet = new Pet;

View file

@ -356,8 +356,11 @@ void Pet::SavePetToDB(PetSaveMode mode)
// current/stable/not_in_slot
if (mode >= PET_SAVE_AS_CURRENT)
{
// reagents must be returned before save call
if (mode == PET_SAVE_REAGENTS)
mode = PET_SAVE_NOT_IN_SLOT;
// not save pet as current if another pet temporary unsummoned
if (mode == PET_SAVE_AS_CURRENT && pOwner->GetTemporaryUnsummonedPetNumber() &&
else if (mode == PET_SAVE_AS_CURRENT && pOwner->GetTemporaryUnsummonedPetNumber() &&
pOwner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber())
{
// pet will lost anyway at restore temporary unsummoned
@ -453,7 +456,7 @@ void Pet::SetDeathState(DeathState s) // overwrite virtual
{
//remove summoned pet (no corpse)
if(getPetType()==SUMMON_PET)
Remove(PET_SAVE_NOT_IN_SLOT);
Unsummon(PET_SAVE_NOT_IN_SLOT);
// other will despawn at corpse desppawning (Pet::Update code)
else
{
@ -488,7 +491,7 @@ void Pet::Update(uint32 diff)
if (m_corpseDecayTimer <= diff)
{
MANGOS_ASSERT(getPetType()!=SUMMON_PET && "Must be already removed.");
Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER!
Unsummon(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER!
return;
}
break;
@ -499,7 +502,7 @@ void Pet::Update(uint32 diff)
Unit* owner = GetOwner();
if (!owner || (!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (!owner->GetCharmGuid().IsEmpty() && (owner->GetCharmGuid() != GetObjectGuid()))) || (isControlled() && owner->GetPetGuid().IsEmpty()))
{
Remove(PET_SAVE_NOT_IN_SLOT, true);
Unsummon(PET_SAVE_REAGENTS);
return;
}
@ -507,7 +510,7 @@ void Pet::Update(uint32 diff)
{
if (owner->GetPetGuid() != GetObjectGuid())
{
Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
Unsummon(getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT, owner);
return;
}
}
@ -518,7 +521,7 @@ void Pet::Update(uint32 diff)
m_duration -= (int32)diff;
else
{
Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
Unsummon(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT, owner);
return;
}
}
@ -656,23 +659,81 @@ bool Pet::CanTakeMoreActiveSpells(uint32 spellid)
return true;
}
void Pet::Remove(PetSaveMode mode, bool returnreagent)
void Pet::Unsummon(PetSaveMode mode, Unit* owner /*= NULL*/)
{
Unit* owner = GetOwner();
if (!owner)
owner = GetOwner();
CombatStop();
if(owner)
{
if(owner->GetTypeId()==TYPEID_PLAYER)
{
((Player*)owner)->RemovePet(this,mode,returnreagent);
if (GetOwnerGuid() != owner->GetObjectGuid())
return;
Player* p_owner = owner->GetTypeId()==TYPEID_PLAYER ? (Player*)owner : NULL;
if (p_owner)
{
// not save secondary permanent pet as current
if (mode == PET_SAVE_AS_CURRENT && p_owner->GetTemporaryUnsummonedPetNumber() &&
p_owner->GetTemporaryUnsummonedPetNumber() != GetCharmInfo()->GetPetNumber())
mode = PET_SAVE_NOT_IN_SLOT;
if (mode == PET_SAVE_REAGENTS)
{
//returning of reagents only for players, so best done here
uint32 spellId = GetUInt32Value(UNIT_CREATED_BY_SPELL);
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if (spellInfo)
{
for(uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i)
{
if (spellInfo->Reagent[i] > 0)
{
ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout)
uint8 msg = p_owner->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i]);
if (msg == EQUIP_ERR_OK)
{
Item* item = p_owner->StoreNewItem(dest, spellInfo->Reagent[i], true);
if (p_owner->IsInWorld())
p_owner->SendNewItem(item, spellInfo->ReagentCount[i], true, false);
}
}
}
}
}
if (isControlled())
{
p_owner->RemovePetActionBar();
if (p_owner->GetGroup())
p_owner->SetGroupUpdateFlag(GROUP_UPDATE_PET);
}
}
// only if current pet in slot
switch(getPetType())
{
case MINI_PET:
if (p_owner)
p_owner->_SetMiniPet(NULL);
break;
case PROTECTOR_PET:
case GUARDIAN_PET:
owner->RemoveGuardian(this);
break;
default:
if (owner->GetPetGuid() == GetObjectGuid())
owner->SetPet(0);
owner->SetPet(NULL);
break;
}
}
SavePetToDB(mode);
AddObjectToRemoveList();
m_removed = true;
}

View file

@ -43,7 +43,8 @@ enum PetSaveMode
PET_SAVE_AS_CURRENT = 0, // in current slot (with player)
PET_SAVE_FIRST_STABLE_SLOT = 1,
PET_SAVE_LAST_STABLE_SLOT = MAX_PET_STABLES, // last in DB stable slot index (including), all higher have same meaning as PET_SAVE_NOT_IN_SLOT
PET_SAVE_NOT_IN_SLOT = 100 // for avoid conflict with stable size grow will use 100
PET_SAVE_NOT_IN_SLOT = 100, // for avoid conflict with stable size grow will use 100
PET_SAVE_REAGENTS = 101 // PET_SAVE_NOT_IN_SLOT with reagents return
};
// There might be a lot more
@ -152,7 +153,7 @@ class Pet : public Creature
bool CreateBaseAtCreature(Creature* creature);
bool LoadPetFromDB( Player* owner,uint32 petentry = 0,uint32 petnumber = 0, bool current = false );
void SavePetToDB(PetSaveMode mode);
void Remove(PetSaveMode mode, bool returnreagent = false);
void Unsummon(PetSaveMode mode, Unit* owner = NULL);
static void DeleteFromDB(uint32 guidlow);
void SetDeathState(DeathState s); // overwrite virtual Creature::SetDeathState and Unit::SetDeathState

View file

@ -144,7 +144,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data )
{
Pet* p = (Pet*)pet;
if(p->getPetType() == HUNTER_PET)
_player->RemovePet(p,PET_SAVE_AS_DELETED);
p->Unsummon(PET_SAVE_AS_DELETED, _player);
else
//dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
p->SetDeathState(CORPSE);
@ -552,7 +552,7 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data )
pet->SetPower(POWER_HAPPINESS ,(feelty-50000) > 0 ?(feelty-50000) : 0);
}
_player->RemovePet((Pet*)pet,PET_SAVE_AS_DELETED);
((Pet*)pet)->Unsummon(PET_SAVE_AS_DELETED, _player);
}
else if (pet->GetObjectGuid() == _player->GetCharmGuid())
{

View file

@ -577,7 +577,6 @@ Player::Player (WorldSession *session): Unit(), m_mover(this), m_camera(this), m
m_summon_y = 0.0f;
m_summon_z = 0.0f;
m_miniPet = 0;
m_contestedPvPTimer = 0;
m_declinedname = NULL;
@ -1463,9 +1462,7 @@ void Player::Update( uint32 p_time )
Pet* pet = GetPet();
if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (!GetCharmGuid().IsEmpty() && (pet->GetObjectGuid() != GetCharmGuid())))
{
RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
}
pet->Unsummon(PET_SAVE_REAGENTS, this);
if (IsHasDelayedTeleport())
TeleportTo(m_teleport_dest, m_teleport_options);
@ -1490,7 +1487,7 @@ void Player::SetDeathState(DeathState s)
RemoveAurasDueToSpell(m_ShapeShiftFormSpellId);
//FIXME: is pet dismissed at dying or releasing spirit? if second, add SetDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
RemovePet(PET_SAVE_REAGENTS);
// remove uncontrolled pets
RemoveMiniPet();
@ -3876,12 +3873,12 @@ bool Player::resetTalents(bool no_cost, bool all_specs)
}
//FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras
RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
RemovePet(PET_SAVE_REAGENTS);
/* when prev line will dropped use next line
if(Pet* pet = GetPet())
{
if(pet->getPetType()==HUNTER_PET && !pet->GetCreatureInfo()->isTameable(CanTameExoticPets()))
RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
pet->Unsummon(PET_SAVE_REAGENTS, this);
}
*/
return true;
@ -17773,90 +17770,24 @@ void Player::UpdateDuelFlag(time_t currTime)
duel->opponent->duel->startTime = currTime;
}
void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
void Player::RemovePet(PetSaveMode mode)
{
if (!pet)
pet = GetPet();
if (!pet || pet->GetOwnerGuid() != GetObjectGuid())
return;
// not save secondary permanent pet as current
if (pet && m_temporaryUnsummonedPetNumber && m_temporaryUnsummonedPetNumber != pet->GetCharmInfo()->GetPetNumber() && mode == PET_SAVE_AS_CURRENT)
mode = PET_SAVE_NOT_IN_SLOT;
if (returnreagent && pet && mode != PET_SAVE_AS_CURRENT)
{
//returning of reagents only for players, so best done here
uint32 spellId = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(spellInfo)
{
for(uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i)
{
if(spellInfo->Reagent[i] > 0)
{
ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout)
uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i] );
if( msg == EQUIP_ERR_OK )
{
Item* item = StoreNewItem( dest, spellInfo->Reagent[i], true);
if(IsInWorld())
SendNewItem(item,spellInfo->ReagentCount[i],true,false);
}
}
}
}
}
// only if current pet in slot
switch(pet->getPetType())
{
case MINI_PET:
m_miniPet = 0;
break;
case PROTECTOR_PET:
case GUARDIAN_PET:
RemoveGuardian(pet);
break;
default:
if (GetPetGuid() == pet->GetObjectGuid())
SetPet(NULL);
break;
}
pet->CombatStop();
pet->SavePetToDB(mode);
pet->AddObjectToRemoveList();
pet->m_removed = true;
if (pet->isControlled())
{
RemovePetActionBar();
if(GetGroup())
SetGroupUpdateFlag(GROUP_UPDATE_PET);
}
if (Pet* pet = GetPet())
pet->Unsummon(mode, this);
}
void Player::RemoveMiniPet()
{
if (Pet* pet = GetMiniPet())
{
pet->Remove(PET_SAVE_AS_DELETED);
m_miniPet = 0;
}
pet->Unsummon(PET_SAVE_AS_DELETED);
}
Pet* Player::GetMiniPet() const
{
if (!m_miniPet)
if (m_miniPetGuid.IsEmpty())
return NULL;
return GetMap()->GetPet(m_miniPet);
return GetMap()->GetPet(m_miniPetGuid);
}
void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype, const std::string& text, uint32 language) const
@ -19500,7 +19431,7 @@ template<>
inline void BeforeVisibilityDestroy<Creature>(Creature* t, Player* p)
{
if (p->GetPetGuid() == t->GetObjectGuid() && ((Creature*)t)->IsPet())
((Pet*)t)->Remove(PET_SAVE_NOT_IN_SLOT, true);
((Pet*)t)->Unsummon(PET_SAVE_REAGENTS);
}
void Player::UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* target)
@ -21834,7 +21765,7 @@ void Player::UnsummonPetTemporaryIfAny()
if(!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned() )
m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
RemovePet(pet, PET_SAVE_AS_CURRENT);
pet->Unsummon(PET_SAVE_AS_CURRENT, this);
}
void Player::ResummonPetTemporaryUnSummonedIfAny()

View file

@ -1213,10 +1213,12 @@ class MANGOS_DLL_SPEC Player : public Unit
time_t GetTimeInnEnter() const { return time_inn_enter; }
void UpdateInnerTime (time_t time) { time_inn_enter = time; }
void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
void RemovePet(PetSaveMode mode);
void RemoveMiniPet();
Pet* GetMiniPet() const;
void SetMiniPet(Pet* pet) { m_miniPet = pet->GetGUID(); }
// use only in Pet::Unsummon/Spell::DoSummon
void _SetMiniPet(Pet* pet) { m_miniPetGuid = pet ? pet->GetObjectGuid() : ObjectGuid(); }
template<typename Func>
void CallForAllControlledUnits(Func const& func, bool withTotems, bool withGuardians, bool withCharms, bool withMiniPet);
@ -2624,7 +2626,7 @@ class MANGOS_DLL_SPEC Player : public Unit
uint32 m_groupUpdateMask;
uint64 m_auraUpdateMask;
uint64 m_miniPet;
ObjectGuid m_miniPetGuid;
// Player summoning
time_t m_summon_expire;

View file

@ -2430,7 +2430,7 @@ void Aura::HandleAuraDummy(bool apply, bool Real)
if (apply)
owner->CastSpell(owner, 8985, true);
else
((Player*)owner)->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
((Player*)owner)->RemovePet(PET_SAVE_REAGENTS);
}
return;
}
@ -2446,7 +2446,7 @@ void Aura::HandleAuraDummy(bool apply, bool Real)
if (apply)
owner->CastSpell(owner, 19704, true);
else
((Player*)owner)->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
((Player*)owner)->RemovePet(PET_SAVE_REAGENTS);
}
return;
}
@ -3595,7 +3595,7 @@ void Aura::HandleModPossessPet(bool apply, bool Real)
// out of range pet dismissed
if (!pet->IsWithinDistInMap(p_caster, pet->GetMap()->GetVisibilityDistance()))
{
pet->Remove(PET_SAVE_NOT_IN_SLOT, true);
p_caster->RemovePet(PET_SAVE_REAGENTS);
}
else
{
@ -7595,24 +7595,23 @@ void Aura::HandleAuraControlVehicle(bool apply, bool Real)
return;
Vehicle* vehicle = (Vehicle*)target;
Unit *player = GetCaster();
if(!player || player->GetTypeId() != TYPEID_PLAYER)
Unit *caster = GetCaster();
if (!caster || caster->GetTypeId() != TYPEID_PLAYER)
return;
if (apply)
{
if(Pet *pet = player->GetPet())
pet->Remove(PET_SAVE_AS_CURRENT);
((Player*)player)->EnterVehicle(vehicle);
((Player*)caster)->RemovePet(PET_SAVE_AS_CURRENT);
((Player*)caster)->EnterVehicle(vehicle);
}
else
{
SpellEntry const *spell = GetSpellProto();
// some SPELL_AURA_CONTROL_VEHICLE auras have a dummy effect on the player - remove them
player->RemoveAurasDueToSpell(spell->Id);
caster->RemoveAurasDueToSpell(spell->Id);
((Player*)player)->ExitVehicle(vehicle);
((Player*)caster)->ExitVehicle(vehicle);
}
}

View file

@ -4590,6 +4590,25 @@ void Spell::DoSummonGuardian(SpellEffectIndex eff_idx, uint32 forceFaction)
if (!propEntry)
return;
PetType petType = propEntry->Title == UNITNAME_SUMMON_TITLE_COMPANION ? PROTECTOR_PET : GUARDIAN_PET;
// protectors allowed only in single amount
if (petType == PROTECTOR_PET)
{
Pet* old_protector = m_caster->GetProtectorPet();
// for same pet just despawn
if (old_protector && old_protector->GetEntry() == pet_entry)
{
old_protector->Unsummon(PET_SAVE_AS_DELETED, m_caster);
return;
}
// despawn old pet before summon new
if (old_protector)
old_protector->Unsummon(PET_SAVE_AS_DELETED, m_caster);
}
// in another case summon new
uint32 level = m_caster->getLevel();
@ -4621,7 +4640,7 @@ void Spell::DoSummonGuardian(SpellEffectIndex eff_idx, uint32 forceFaction)
for(int32 count = 0; count < amount; ++count)
{
Pet* spawnCreature = new Pet(propEntry->Title == UNITNAME_SUMMON_TITLE_COMPANION ? PROTECTOR_PET : GUARDIAN_PET);
Pet* spawnCreature = new Pet(petType);
Map *map = m_caster->GetMap();
uint32 pet_number = sObjectMgr.GeneratePetNumber();
@ -5039,7 +5058,7 @@ void Spell::EffectSummonPet(SpellEffectIndex eff_idx)
}
if(m_caster->GetTypeId() == TYPEID_PLAYER)
((Player*)m_caster)->RemovePet(OldSummon,(OldSummon->getPetType()==HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT),false);
OldSummon->Unsummon(OldSummon->getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT, m_caster);
else
return;
}
@ -5051,7 +5070,7 @@ void Spell::EffectSummonPet(SpellEffectIndex eff_idx)
return;
// not error in case fail hunter call pet
if(!petentry)
if (!petentry)
{
delete NewSummon;
return;
@ -6194,7 +6213,7 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx)
{
// Is this all to be done at completion?
if (Pet* pPet = m_caster->FindGuardianWithEntry(pSpell->EffectMiscValue[EFFECT_INDEX_0]))
((Player*)m_caster)->RemovePet(pPet, PET_SAVE_NOT_IN_SLOT);
pPet->Unsummon(PET_SAVE_NOT_IN_SLOT, m_caster);
}
return;
}
@ -7224,7 +7243,7 @@ void Spell::EffectDismissPet(SpellEffectIndex /*eff_idx*/)
if(!pet||!pet->isAlive())
return;
((Player*)m_caster)->RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
pet->Unsummon(PET_SAVE_NOT_IN_SLOT, m_caster);
}
void Spell::EffectSummonObject(SpellEffectIndex eff_idx)
@ -7602,7 +7621,7 @@ void Spell::DoSummonCritter(SpellEffectIndex eff_idx, uint32 forceFaction)
if(duration > 0)
critter->SetDuration(duration);
player->SetMiniPet(critter);
player->_SetMiniPet(critter);
map->Add((Creature*)critter);
}

View file

@ -5901,7 +5901,7 @@ void Unit::RemoveGuardians()
{
uint64 guid = *m_guardianPets.begin();
if(Pet* pet = GetMap()->GetPet(guid))
pet->Remove(PET_SAVE_AS_DELETED);
pet->Unsummon(PET_SAVE_AS_DELETED, this);
m_guardianPets.erase(guid);
}

View file

@ -391,7 +391,7 @@ void WorldSession::LogoutPlayer(bool Save)
}
///- Remove pet
_player->RemovePet(NULL, PET_SAVE_AS_CURRENT, true);
_player->RemovePet(PET_SAVE_AS_CURRENT);
///- empty buyback items and save the player in the database
// some save parts only correctly work in case player present in map/player_lists (pets, etc)

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "10738"
#define REVISION_NR "10739"
#endif // __REVISION_NR_H__