diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 0630f734b..11f584ab0 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -218,9 +218,8 @@ spell_scripts * datalong3 = creature search radius * data_flags = flag_original_source_as_target = 0x02 flag_buddy_as_target = 0x04 (When this flag is not set, buddy will be the attacker when buddy is defined) - - - - - - +27 SCRIPT_COMMAND_GO_LOCK_STATE source or target must be WorldObject + * datalong = flag_go_lock = 0x01, flag_go_unlock = 0x02, + flag_go_nonInteract = 0x04, flag_go_interact = 0x08 + * datalong2 = go entry (searching closest to source (if worldobject) or target + * datalong3 = go search radius diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index c2c16142a..bd86e01d6 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -398,8 +398,11 @@ void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0) { SendObjectDeSpawnAnim(GetObjectGuid()); - //reset flags - SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); + // reset flags: In Instances do not restore GO_FLAG_LOCKED or GO_FLAG_NO_INTERACT + if (GetMap()->Instanceable()) + SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags & ~(GO_FLAG_LOCKED | GO_FLAG_NO_INTERACT)); + else + SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); } loot.clear(); diff --git a/src/game/Map.cpp b/src/game/Map.cpp index 55b15d14b..4d47cb7fd 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -2869,6 +2869,42 @@ void Map::ScriptsProcess() sLog.outError("SCRIPT_COMMAND_ATTACK_START (script id %u) unexpected error, attacker or victim could not be found, no action.", step.script->id); break; } + case SCRIPT_COMMAND_GO_LOCK_STATE: + { + if ((!source || !source->isType(TYPEMASK_WORLDOBJECT)) && (!target || !target->isType(TYPEMASK_WORLDOBJECT))) + { + sLog.outError("SCRIPT_COMMAND_GO_LOCK_STATE (script id %u) call for non-worldobject (TypeIdSource: %u)(TypeIdTarget: %u), skipping.", step.script->id, source ? source->GetTypeId() : 0, target ? target->GetTypeId() : 0); + break; + } + + WorldObject* pSearcher = source && source->isType(TYPEMASK_WORLDOBJECT) ? (WorldObject*)source : (WorldObject*)target; + GameObject* pGo = NULL; + + MaNGOS::NearestGameObjectEntryInObjectRangeCheck u_check(*pSearcher, step.script->goLockState.goEntry, step.script->goLockState.searchRadius); + MaNGOS::GameObjectLastSearcher searcher(pGo, u_check); + + Cell::VisitGridObjects(pSearcher, searcher, step.script->goLockState.searchRadius); + + /* flag lockState + * go_lock 0x01 + * go_unlock 0x02 + * go_nonInteract 0x04 + * go_Interact 0x08 + */ + if (pGo) + { + // Lock or Unlock + if (step.script->goLockState.lockState & 0x01) + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + else if (step.script->goLockState.lockState & 0x02) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + // Set Non Interactable or Set Interactable + if (step.script->goLockState.lockState & 0x04) + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + else if (step.script->goLockState.lockState & 0x08) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + } + } default: sLog.outError("Unknown SCRIPT_COMMAND_ %u called for script id %u.",step.script->command, step.script->id); break; diff --git a/src/game/ScriptMgr.cpp b/src/game/ScriptMgr.cpp index c0ed52bb8..05ac6c4ff 100644 --- a/src/game/ScriptMgr.cpp +++ b/src/game/ScriptMgr.cpp @@ -533,6 +533,32 @@ void ScriptMgr::LoadScripts(ScriptMapMap& scripts, const char* tablename) } break; } + case SCRIPT_COMMAND_GO_LOCK_STATE: + { + if (!ObjectMgr::GetGameObjectInfo(tmp.goLockState.goEntry)) + { + sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_GO_LOCK_STATE for script id %u, but this gameobject_template does not exist.", tablename, tmp.goLockState.goEntry, tmp.id); + continue; + } + if (!tmp.goLockState.searchRadius) + { + sLog.outErrorDb("Table `%s` has invalid search radius (datalong3 = %u) in SCRIPT_COMMAND_GO_LOCK_STATE for script id %u.", tablename, tmp.goLockState.searchRadius, tmp.id); + continue; + } + if (// lock(0x01) and unlock(0x02) together + ((tmp.goLockState.lockState & 0x01) && (tmp.goLockState.lockState & 0x02)) || + // non-interact (0x4) and interact (0x08) together + ((tmp.goLockState.lockState & 0x04) && (tmp.goLockState.lockState & 0x08)) || + // no setting + !tmp.goLockState.lockState || + // invalid number + tmp.goLockState.lockState >= 0x10) + { + sLog.outErrorDb("Table `%s` has invalid lock state (datalong = %u) in SCRIPT_COMMAND_GO_LOCK_STATE for script id %u.", tablename, tmp.goLockState.lockState, tmp.id); + continue; + } + break; + } } if (scripts.find(tmp.id) == scripts.end()) diff --git a/src/game/ScriptMgr.h b/src/game/ScriptMgr.h index 419413e3a..9fa6b6662 100644 --- a/src/game/ScriptMgr.h +++ b/src/game/ScriptMgr.h @@ -92,6 +92,9 @@ enum eScriptCommand // datalong2=creature entry, datalong3=search radius SCRIPT_COMMAND_ATTACK_START = 26, // source = Creature (or WorldObject when creature entry are defined), target = Player // datalong2 = creature entry (searching for a buddy, closest to source), datalong3 = creature search radius + SCRIPT_COMMAND_GO_LOCK_STATE = 27, // source or target must be WorldObject + // datalong= 1=lock, 2=unlock, 4=set not-interactable, 8=set interactable + // datalong2= go entry, datalong3= go search radius }; #define MAX_TEXT_ID 4 // used for SCRIPT_COMMAND_TALK @@ -284,6 +287,13 @@ struct ScriptInfo uint32 flags; // data_flags } attack; + struct // SCRIPT_COMMAND_GO_LOCK_STATE (27) + { + uint32 lockState; // datalong + uint32 goEntry; // datalong2 + uint32 searchRadius; // datalong3 + } goLockState; + struct { uint32 data[9]; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 5db7aa673..905190e7f 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -1258,7 +1258,7 @@ enum GameObjectFlags GO_FLAG_LOCKED = 0x00000002, //require key, spell, event, etc to be opened. Makes "Locked" appear in tooltip GO_FLAG_INTERACT_COND = 0x00000004, //cannot interact (condition to interact) GO_FLAG_TRANSPORT = 0x00000008, //any kind of transport? Object can transport (elevator, boat, car) - GO_FLAG_UNK1 = 0x00000010, // + GO_FLAG_NO_INTERACT = 0x00000010, //players cannot interact with this go (often need to remove flag in event) GO_FLAG_NODESPAWN = 0x00000020, //never despawn, typically for doors, they just change state GO_FLAG_TRIGGERED = 0x00000040, //typically, summoned objects. Triggered by spell or other events GO_FLAG_UNK_8 = 0x00000080, diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 3b9a919f5..dbaa32d72 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -289,6 +289,13 @@ void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data ) if(!obj) return; + // Additional check preventing exploits (ie loot despawned chests) + if (!obj->isSpawned()) + { + sLog.outError("HandleGameObjectUseOpcode: CMSG_GAMEOBJ_USE for despawned GameObject (Entry %u), didn't expect this to happen.", obj->GetEntry()); + return; + } + // Never expect this opcode for some type GO's if (obj->GetGoType() == GAMEOBJECT_TYPE_GENERIC) { @@ -296,6 +303,13 @@ void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data ) return; } + // Never expect this opcode for non intractable GO's + if (obj->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT)) + { + sLog.outError("HandleGameObjectUseOpcode: CMSG_GAMEOBJ_USE for GameObject (Entry %u) with non intractable flag (Flags %u), didn't expect this to happen.", obj->GetEntry(), obj->GetUInt32Value(GAMEOBJECT_FLAGS)); + return; + } + obj->Use(_player); } diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 7fa75d44f..896751c5c 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11557" + #define REVISION_NR "11558" #endif // __REVISION_NR_H__