Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Flush all respawn times to ensure a clean slate for the spawn group system.
-- Compat-mode creatures/GOs will respawn naturally on next grid load;
-- non-compat spawns will be handled by ProcessRespawns().
TRUNCATE TABLE `creature_respawn`;
TRUNCATE TABLE `gameobject_respawn`;
48 changes: 48 additions & 0 deletions data/sql/updates/pending_db_world/rev_spawn_group_tables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
--
DROP TABLE IF EXISTS `spawn_group_template`;
CREATE TABLE `spawn_group_template` (
`groupId` int(10) unsigned NOT NULL,
`groupName` varchar(100) NOT NULL,
`groupFlags` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`groupId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DROP TABLE IF EXISTS `spawn_group`;
CREATE TABLE `spawn_group` (
`groupId` int(10) unsigned NOT NULL,
`spawnType` tinyint(3) unsigned NOT NULL,
`spawnId` int(10) unsigned NOT NULL,
PRIMARY KEY (`groupId`,`spawnType`,`spawnId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Insert default spawn groups
DELETE FROM `spawn_group_template` WHERE `groupId` IN (0, 1);
INSERT INTO `spawn_group_template` (`groupId`, `groupName`, `groupFlags`) VALUES
(0, 'Default Group', 0x01), -- SYSTEM (dynamic respawn by default)
(1, 'Legacy Group', 0x03); -- SYSTEM | COMPATIBILITY_MODE

-- Register spawn group commands
DELETE FROM `command` WHERE `name` IN ('list respawns', 'npc spawngroup', 'npc despawngroup', 'gobject spawngroup', 'gobject despawngroup', 'reload spawn_group');
INSERT INTO `command` (`name`, `security`, `help`) VALUES
('list respawns', 2, 'Syntax: .list respawns\r\nShows all pending creature and gameobject respawns on the current map.'),
('npc spawngroup', 3, 'Syntax: .npc spawngroup #groupId\r\nSpawns all creatures in the given spawn group.'),
('npc despawngroup', 3, 'Syntax: .npc despawngroup #groupId\r\nDespawns all creatures in the given spawn group.'),
('gobject spawngroup', 3, 'Syntax: .gobject spawngroup #groupId\r\nSpawns all gameobjects in the given spawn group.'),
('gobject despawngroup', 3, 'Syntax: .gobject despawngroup #groupId\r\nDespawns all gameobjects in the given spawn group.'),
('reload spawn_group', 3, 'Syntax: .reload spawn_group\r\nReloads the spawn_group_template and spawn_group tables.');

-- Spawn group localized strings
DELETE FROM `acore_string` WHERE `entry` BETWEEN 35411 AND 35424;
INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES
(35411, 'Cannot manually spawn system group {} ({}).', '시스템 그룹 {} ({})을(를) 수동으로 생성할 수 없습니다.', 'Impossible de faire apparaître manuellement le groupe système {} ({}).', 'Systemgruppe {} ({}) kann nicht manuell gespawnt werden.', '无法手动生成系统组 {} ({})。', '無法手動生成系統組 {} ({})。', 'No se puede generar manualmente el grupo del sistema {} ({}).', 'No se puede generar manualmente el grupo del sistema {} ({}).', 'Невозможно вручную создать системную группу {} ({}).'),
(35412, 'Spawn group {} ({}) spawned successfully.', '스폰 그룹 {} ({})이(가) 성공적으로 생성되었습니다.', 'Le groupe d''apparition {} ({}) a été créé avec succès.', 'Spawngruppe {} ({}) erfolgreich erstellt.', '刷新组 {} ({}) 已成功生成。', '重生組 {} ({}) 已成功生成。', 'Grupo de aparición {} ({}) generado correctamente.', 'Grupo de aparición {} ({}) generado correctamente.', 'Группа спавна {} ({}) успешно создана.'),
(35413, 'Failed to spawn group {} ({}).', '스폰 그룹 {} ({}) 생성에 실패했습니다.', 'Échec de l''apparition du groupe {} ({}).', 'Spawngruppe {} ({}) konnte nicht erstellt werden.', '生成组 {} ({}) 失败。', '生成組 {} ({}) 失敗。', 'Error al generar el grupo {} ({}).', 'Error al generar el grupo {} ({}).', 'Не удалось создать группу спавна {} ({}).'),
(35414, 'Cannot manually despawn system group {} ({}).', '시스템 그룹 {} ({})을(를) 수동으로 제거할 수 없습니다.', 'Impossible de retirer manuellement le groupe système {} ({}).', 'Systemgruppe {} ({}) kann nicht manuell entfernt werden.', '无法手动移除系统组 {} ({})。', '無法手動移除系統組 {} ({})。', 'No se puede eliminar manualmente el grupo del sistema {} ({}).', 'No se puede eliminar manualmente el grupo del sistema {} ({}).', 'Невозможно вручную удалить системную группу {} ({}).'),
(35415, 'Spawn group {} ({}) despawned successfully.', '스폰 그룹 {} ({})이(가) 성공적으로 제거되었습니다.', 'Le groupe d''apparition {} ({}) a été retiré avec succès.', 'Spawngruppe {} ({}) erfolgreich entfernt.', '刷新组 {} ({}) 已成功移除。', '重生組 {} ({}) 已成功移除。', 'Grupo de aparición {} ({}) eliminado correctamente.', 'Grupo de aparición {} ({}) eliminado correctamente.', 'Группа спавна {} ({}) успешно удалена.'),
(35416, 'Failed to despawn group {} ({}).', '스폰 그룹 {} ({}) 제거에 실패했습니다.', 'Échec du retrait du groupe {} ({}).', 'Spawngruppe {} ({}) konnte nicht entfernt werden.', '移除组 {} ({}) 失败。', '移除組 {} ({}) 失敗。', 'Error al eliminar el grupo {} ({}).', 'Error al eliminar el grupo {} ({}).', 'Не удалось удалить группу спавна {} ({}).'),
(35419, 'Pending creature respawns on map {} (instance {}):', '맵 {} (인스턴스 {})의 대기 중인 생물 리스폰:', 'Réapparitions de créatures en attente sur la carte {} (instance {}) :', 'Ausstehende Kreatur-Respawns auf Karte {} (Instanz {}):', '地图 {} (副本 {}) 上待处理的生物重生:', '地圖 {} (副本 {}) 上待處理的生物重生:', 'Reapariciones de criaturas pendientes en mapa {} (instancia {}):', 'Reapariciones de criaturas pendientes en mapa {} (instancia {}):', 'Ожидающие респауны существ на карте {} (инстанс {}):'),
(35420, ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}초', ' DB GUID : {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}秒', ' DB GUID: {} - {} ({}) - {}秒', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}с'),
(35421, 'Pending gameobject respawns:', '대기 중인 게임오브젝트 리스폰:', 'Réapparitions de game objects en attente :', 'Ausstehende Spielobjekt-Respawns:', '待处理的游戏对象重生:', '待處理的遊戲物件重生:', 'Reapariciones de objetos pendientes:', 'Reapariciones de objetos pendientes:', 'Ожидающие респауны игровых объектов:'),
(35422, ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}초', ' DB GUID : {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}秒', ' DB GUID: {} - {} ({}) - {}秒', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}s', ' DB GUID: {} - {} ({}) - {}с'),
(35423, ' ... and more (limited to 50)', ' ... 그 외 다수 (50개로 제한)', ' ... et plus (limité à 50)', ' ... und mehr (auf 50 begrenzt)', ' ... 以及更多(限制为50)', ' ... 以及更多(限制為50)', ' ... y más (limitado a 50)', ' ... y más (limitado a 50)', ' ... и ещё (ограничено 50)'),
(35424, 'Spawn group {} not found.', '스폰 그룹 {}을(를) 찾을 수 없습니다.', 'Groupe d''apparition {} introuvable.', 'Spawngruppe {} nicht gefunden.', '刷新组 {} 未找到。', '重生組 {} 未找到。', 'Grupo de aparición {} no encontrado.', 'Grupo de aparición {} no encontrado.', 'Группа спавна {} не найдена.');
21 changes: 21 additions & 0 deletions src/server/apps/worldserver/worldserver.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -4742,6 +4742,27 @@ Respawn.DynamicRateGameObject = 1

Respawn.DynamicMinimumGameObject = 10

#
# Respawn.DynamicEscortNPC
# Description: Enable dynamic respawn behavior for escort quest NPCs.
# When enabled, escort NPCs in spawn groups flagged as ESCORTQUESTNPC
# will use special respawn handling.
# Default: 0 - (Disabled)
#

Respawn.DynamicEscortNPC = 0

#
# Respawn.ForceCompatibilityMode
# Description: Force all spawns to use legacy (compatibility mode) respawn behavior,
# regardless of their spawn group flags. When enabled, creatures and
# gameobjects respawn in-place as they always have in AzerothCore.
# Set to 1 to force legacy behavior for all spawns.
# Default: 0 - (Disabled, spawn groups control respawn mode)
#

Respawn.ForceCompatibilityMode = 0

#
###################################################################################################

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
// 0: uint8
PrepareStatement(WORLD_SEL_REQ_XP, "SELECT Experience FROM player_xp_for_level WHERE Level = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_UPD_VERSION, "UPDATE version SET core_version = ?, core_revision = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_SPAWNGROUP_MEMBER, "DELETE FROM spawn_group WHERE spawnType = ? AND spawnId = ?", CONNECTION_ASYNC);
}

WorldDatabaseConnection::WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ enum WorldDatabaseStatements : uint32
WORLD_SEL_REQ_XP,
WORLD_INS_GAMEOBJECT_ADDON,
WORLD_UPD_VERSION,
WORLD_DEL_SPAWNGROUP_MEMBER,

MAX_WORLDDATABASE_STATEMENTS
};
Expand Down
3 changes: 3 additions & 0 deletions src/server/game/AI/CreatureAI.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ class CreatureAI : public UnitAI
// Called in Creature::Update when deathstate = DEAD. Inherited classes may maniuplate the ability to respawn based on scripted events.
virtual bool CanRespawn() { return true; }

// Whether this creature is an escort NPC (override in escort AI)
virtual bool IsEscortNPC(bool /*onlyIfActive*/ = true) const { return false; }

// Called for reaction at stopping attack at no attackers or targets
virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);

Expand Down
2 changes: 2 additions & 0 deletions src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ struct npc_escortAI : public ScriptedAI

void JustRespawned() override;

bool IsEscortNPC(bool /*onlyIfActive*/ = true) const override { return true; }

void ReturnToLastPoint();

void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
Expand Down
21 changes: 21 additions & 0 deletions src/server/game/AI/SmartScripts/SmartScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2959,6 +2959,27 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
break;
}
case SMART_ACTION_SPAWN_SPAWNGROUP:
{
WorldObject* obj = GetBaseObject();
if (!obj)
break;

obj->GetMap()->SpawnGroupSpawn(e.action.groupSpawn.groupId,
e.action.groupSpawn.ignoreRespawn != 0,
e.action.groupSpawn.force != 0);
break;
}
case SMART_ACTION_DESPAWN_SPAWNGROUP:
{
WorldObject* obj = GetBaseObject();
if (!obj)
break;

obj->GetMap()->SpawnGroupDespawn(e.action.groupSpawn.groupId,
e.action.groupSpawn.ignoreRespawn != 0); // reuse ignoreRespawn as deleteRespawnTimes
break;
}
case SMART_ACTION_SET_GUID:
{
for (WorldObject* target : targets)
Expand Down
17 changes: 13 additions & 4 deletions src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,8 +846,8 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_PLAY_ANIMKIT: return sizeof(SmartAction::raw);
case SMART_ACTION_SCENE_PLAY: return sizeof(SmartAction::raw);
case SMART_ACTION_SCENE_CANCEL: return sizeof(SmartAction::raw);
// case SMART_ACTION_SPAWN_SPAWNGROUP: return sizeof(SmartAction::groupSpawn);
// case SMART_ACTION_DESPAWN_SPAWNGROUP: return sizeof(SmartAction::groupSpawn);
case SMART_ACTION_SPAWN_SPAWNGROUP: return sizeof(SmartAction::groupSpawn);
case SMART_ACTION_DESPAWN_SPAWNGROUP: return sizeof(SmartAction::groupSpawn);
// case SMART_ACTION_RESPAWN_BY_SPAWNID: return sizeof(SmartAction::respawnData);
case SMART_ACTION_PLAY_CINEMATIC: return sizeof(SmartAction::cinematic);
case SMART_ACTION_SET_MOVEMENT_SPEED: return sizeof(SmartAction::movementSpeed);
Expand Down Expand Up @@ -1013,12 +1013,21 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_SET_CAN_FLY:
case SMART_ACTION_REMOVE_AURAS_BY_TYPE:
case SMART_ACTION_REMOVE_MOVEMENT:
case SMART_ACTION_SPAWN_SPAWNGROUP:
case SMART_ACTION_DESPAWN_SPAWNGROUP:
case SMART_ACTION_RESPAWN_BY_SPAWNID:
LOG_ERROR("sql.sql", "SmartAIMgr: EntryOrGuid {} using event({}) has an action type that is not yet supported on AzerothCore ({}), skipped.",
e.entryOrGuid, e.event_id, e.GetActionType());
return false;
case SMART_ACTION_SPAWN_SPAWNGROUP:
case SMART_ACTION_DESPAWN_SPAWNGROUP:
{
if (!sObjectMgr->GetSpawnGroupData(e.action.groupSpawn.groupId))
{
LOG_ERROR("sql.sql", "SmartAIMgr: EntryOrGuid {} using event({}) has action type {} with invalid spawn group id {}.",
e.entryOrGuid, e.event_id, e.GetActionType(), e.action.groupSpawn.groupId);
return false;
}
break;
}
default:
break;
}
Expand Down
11 changes: 9 additions & 2 deletions src/server/game/AI/SmartScripts/SmartScriptMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,8 +670,8 @@ enum SMART_ACTION
SMART_ACTION_PLAY_ANIMKIT = 128, // don't use on 3.3.5a
SMART_ACTION_SCENE_PLAY = 129, // don't use on 3.3.5a
SMART_ACTION_SCENE_CANCEL = 130, // don't use on 3.3.5a
SMART_ACTION_SPAWN_SPAWNGROUP = 131, /// @todo: NOT SUPPORTED YET
SMART_ACTION_DESPAWN_SPAWNGROUP = 132, /// @todo: NOT SUPPORTED YET
SMART_ACTION_SPAWN_SPAWNGROUP = 131, // groupId, ignoreRespawn, force
SMART_ACTION_DESPAWN_SPAWNGROUP = 132, // groupId, deleteRespawnTimes
SMART_ACTION_RESPAWN_BY_SPAWNID = 133, /// @todo: NOT SUPPORTED YET
SMART_ACTION_INVOKER_CAST = 134, // spellID, castFlags, triggerFlags, targetsLimit
SMART_ACTION_PLAY_CINEMATIC = 135, // entry
Expand Down Expand Up @@ -1523,6 +1523,13 @@ struct SmartAction
{
uint32 group;
} gameobjectGroup;

struct
{
uint32 groupId;
uint32 ignoreRespawn;
uint32 force;
} groupSpawn;
};
};

Expand Down
Loading
Loading