модуль npc дипломатов

This commit is contained in:
2026-03-14 17:50:08 +04:00
parent feceaab626
commit c53d6b30d4
7 changed files with 960 additions and 0 deletions
@@ -0,0 +1,20 @@
# Start Zone Race Teleporters
Eluna module for race-specific start-zone teleport NPCs.
## Files
- `start_zone_race_teleporters.lua`
- Generic gossip handler for all teleporter NPC entries.
- `sql/world/start_zone_race_teleporters.sql`
- Base world SQL for `creature_template` and `creature_template_model`.
- Stores ASCII fallback names in base tables and Russian `ruRU` strings in locale tables.
- Imported automatically by `./setup-modules.sh` when `ac-database` is running.
- `sql/world/examples/start_zone_race_teleporters_human_start_example.sql`
- Manual example spawn SQL for the human start zone only.
## Notes
- The module SQL does not create all world spawns automatically.
- Use the example spawn file as a template and add your own spawns for each start zone.
- Draenei and blood elf access are gated in Lua through the adapters in `start_zone_race_teleporters.lua`.
@@ -0,0 +1,42 @@
-- Example spawns for the human start zone (Northshire / Elwynn Forest).
-- This is intentionally a template: replace GUID range and exact coordinates for your layout.
-- The Lua script is generic, so you can repeat the same pattern for every other start zone.
SET @CGUID := 5300600; -- TODO: replace with a free creature GUID range on your world DB.
DELETE FROM `creature`
WHERE `guid` BETWEEN @CGUID AND @CGUID + 2;
INSERT INTO `creature` (
`guid`,
`id1`,
`id2`,
`id3`,
`map`,
`zoneId`,
`areaId`,
`spawnMask`,
`phaseMask`,
`equipment_id`,
`position_x`,
`position_y`,
`position_z`,
`orientation`,
`spawntimesecs`,
`wander_distance`,
`currentwaypoint`,
`curhealth`,
`curmana`,
`MovementType`,
`npcflag`,
`unit_flags`,
`dynamicflags`,
`ScriptName`,
`VerifiedBuild`,
`CreateObject`,
`Comment`
)
VALUES
(@CGUID + 0, 910300, 0, 0, 0, 12, 0, 1, 1, 0, -8946.50, -136.20, 83.60, 0.95, 120, 0, 0, 0, 0, 0, 1, 0, 0, '', 12340, 0, 'Northshire example - dwarf teleporter'),
(@CGUID + 1, 910301, 0, 0, 0, 12, 0, 1, 1, 0, -8949.25, -132.70, 83.60, 1.15, 120, 0, 0, 0, 0, 0, 1, 0, 0, '', 12340, 0, 'Northshire example - night elf teleporter'),
(@CGUID + 2, 910302, 0, 0, 0, 12, 0, 1, 1, 0, -8952.10, -136.20, 83.60, 1.35, 120, 0, 0, 0, 0, 0, 1, 0, 0, '', 12340, 0, 'Northshire example - draenei teleporter');
@@ -0,0 +1,345 @@
-- Start Zone Race Teleporters
-- World DB template for lua_scripts/StartZoneRaceTeleporters/start_zone_race_teleporters.lua
-- Imported automatically by ./setup-modules.sh when ac-database is running.
--
-- Safe default display IDs below reuse existing race placeholder/commoner models
-- from this local world DB. Replace them if you want female variants or custom looks.
-- Source examples:
-- Human male = 19178 (entry 18936, [PH] Gossip NPC, Human Male)
-- Dwarf male = 19174 (entry 19115, [PH] Gossip NPC, Dwarf Male)
-- Night Elf male= 19180 (entry 19116, [PH] Gossip NPC, Night Elf Male)
-- Orc male = 19182 (entry 19117, [PH] Gossip NPC, Orc Male)
-- Undead male = 19188 (entry 19119, [PH] Gossip NPC, Undead Male)
-- Tauren male = 19184 (entry 19118, [PH] Gossip NPC, Tauren Male)
-- Draenei male = 19172 (entry 19114, [PH] Gossip NPC, Draenei Male)
-- Blood Elf male= 19170 (entry 19113, [PH] Gossip NPC, Blood Elf Male)
SET NAMES utf8mb4;
SET @DISPLAY_HUMAN_MALE := 19178;
SET @DISPLAY_DWARF_MALE := 19174;
SET @DISPLAY_NIGHTELF_MALE := 19180;
SET @DISPLAY_ORC_MALE := 19182;
SET @DISPLAY_UNDEAD_MALE := 19188;
SET @DISPLAY_TAUREN_MALE := 19184;
SET @DISPLAY_DRAENEI_MALE := 19172;
SET @DISPLAY_BLOODELF_MALE := 19170;
SET @NPC_TEXT_HUMAN := 9102400;
SET @NPC_TEXT_DWARF := 9102401;
SET @NPC_TEXT_NIGHTELF := 9102402;
SET @NPC_TEXT_DRAENEI := 9102403;
SET @NPC_TEXT_ORC := 9102404;
SET @NPC_TEXT_UNDEAD := 9102405;
SET @NPC_TEXT_TAUREN := 9102406;
SET @NPC_TEXT_BLOODELF := 9102407;
-- Cache-bust migration:
-- move old teleporter spawns (910200-910223) to the new entry range (910300-910323)
-- so existing client-side creature cache does not keep showing stale names.
UPDATE `creature`
SET `id1` = CASE `id1`
WHEN 910200 THEN 910300
WHEN 910201 THEN 910301
WHEN 910202 THEN 910302
WHEN 910203 THEN 910303
WHEN 910204 THEN 910304
WHEN 910205 THEN 910305
WHEN 910206 THEN 910306
WHEN 910207 THEN 910307
WHEN 910208 THEN 910308
WHEN 910209 THEN 910309
WHEN 910210 THEN 910310
WHEN 910211 THEN 910311
WHEN 910212 THEN 910312
WHEN 910213 THEN 910313
WHEN 910214 THEN 910314
WHEN 910215 THEN 910315
WHEN 910216 THEN 910316
WHEN 910217 THEN 910317
WHEN 910218 THEN 910318
WHEN 910219 THEN 910319
WHEN 910220 THEN 910320
WHEN 910221 THEN 910321
WHEN 910222 THEN 910322
WHEN 910223 THEN 910323
ELSE `id1`
END
WHERE `id1` IN (
910200, 910201, 910202,
910203, 910204, 910205,
910206, 910207, 910208,
910209, 910210, 910211,
910212, 910213, 910214,
910215, 910216, 910217,
910218, 910219, 910220,
910221, 910222, 910223
);
DELETE FROM `npc_text`
WHERE `ID` IN (
@NPC_TEXT_HUMAN,
@NPC_TEXT_DWARF,
@NPC_TEXT_NIGHTELF,
@NPC_TEXT_DRAENEI,
@NPC_TEXT_ORC,
@NPC_TEXT_UNDEAD,
@NPC_TEXT_TAUREN,
@NPC_TEXT_BLOODELF
);
INSERT INTO `npc_text` (`ID`, `text0_0`, `text0_1`)
VALUES
(@NPC_TEXT_HUMAN, 'I represent the humans. Are you ready to travel with me?', 'I represent the humans. Are you ready to travel with me?'),
(@NPC_TEXT_DWARF, 'I represent the dwarves. Are you ready to travel with me?', 'I represent the dwarves. Are you ready to travel with me?'),
(@NPC_TEXT_NIGHTELF, 'I represent the night elves. Are you ready to travel with me?', 'I represent the night elves. Are you ready to travel with me?'),
(@NPC_TEXT_DRAENEI, 'I represent the draenei. Are you ready to travel with me?', 'I represent the draenei. Are you ready to travel with me?'),
(@NPC_TEXT_ORC, 'I represent the orcs. Are you ready to travel with me?', 'I represent the orcs. Are you ready to travel with me?'),
(@NPC_TEXT_UNDEAD, 'I represent the undead. Are you ready to travel with me?', 'I represent the undead. Are you ready to travel with me?'),
(@NPC_TEXT_TAUREN, 'I represent the tauren. Are you ready to travel with me?', 'I represent the tauren. Are you ready to travel with me?'),
(@NPC_TEXT_BLOODELF, 'I represent the blood elves. Are you ready to travel with me?', 'I represent the blood elves. Are you ready to travel with me?');
DELETE FROM `npc_text_locale`
WHERE `ID` IN (
@NPC_TEXT_HUMAN,
@NPC_TEXT_DWARF,
@NPC_TEXT_NIGHTELF,
@NPC_TEXT_DRAENEI,
@NPC_TEXT_ORC,
@NPC_TEXT_UNDEAD,
@NPC_TEXT_TAUREN,
@NPC_TEXT_BLOODELF
)
AND `Locale` = 'ruRU';
INSERT INTO `npc_text_locale` (`ID`, `Locale`, `Text0_0`, `Text0_1`)
VALUES
(@NPC_TEXT_HUMAN, 'ruRU', 'Я представитель людей. Ты готов отправиться со мной?', 'Я представитель людей. Ты готов отправиться со мной?'),
(@NPC_TEXT_DWARF, 'ruRU', 'Я представитель дворфов. Ты готов отправиться со мной?', 'Я представитель дворфов. Ты готов отправиться со мной?'),
(@NPC_TEXT_NIGHTELF, 'ruRU', 'Я представитель ночных эльфов. Ты готов отправиться со мной?', 'Я представитель ночных эльфов. Ты готов отправиться со мной?'),
(@NPC_TEXT_DRAENEI, 'ruRU', 'Я представитель дренеев. Ты готов отправиться со мной?', 'Я представитель дренеев. Ты готов отправиться со мной?'),
(@NPC_TEXT_ORC, 'ruRU', 'Я представитель орков. Ты готов отправиться со мной?', 'Я представитель орков. Ты готов отправиться со мной?'),
(@NPC_TEXT_UNDEAD, 'ruRU', 'Я представитель нежити. Ты готов отправиться со мной?', 'Я представитель нежити. Ты готов отправиться со мной?'),
(@NPC_TEXT_TAUREN, 'ruRU', 'Я представитель тауренов. Ты готов отправиться со мной?', 'Я представитель тауренов. Ты готов отправиться со мной?'),
(@NPC_TEXT_BLOODELF, 'ruRU', 'Я представитель эльфов крови. Ты готов отправиться со мной?', 'Я представитель эльфов крови. Ты готов отправиться со мной?');
DELETE FROM `creature_template_model`
WHERE `CreatureID` IN (
910200, 910201, 910202,
910203, 910204, 910205,
910206, 910207, 910208,
910209, 910210, 910211,
910212, 910213, 910214,
910215, 910216, 910217,
910218, 910219, 910220,
910221, 910222, 910223,
910300, 910301, 910302,
910303, 910304, 910305,
910306, 910307, 910308,
910309, 910310, 910311,
910312, 910313, 910314,
910315, 910316, 910317,
910318, 910319, 910320,
910321, 910322, 910323
);
DELETE FROM `creature_template`
WHERE `entry` IN (
910200, 910201, 910202,
910203, 910204, 910205,
910206, 910207, 910208,
910209, 910210, 910211,
910212, 910213, 910214,
910215, 910216, 910217,
910218, 910219, 910220,
910221, 910222, 910223,
910300, 910301, 910302,
910303, 910304, 910305,
910306, 910307, 910308,
910309, 910310, 910311,
910312, 910313, 910314,
910315, 910316, 910317,
910318, 910319, 910320,
910321, 910322, 910323
);
DELETE FROM `creature_template_locale`
WHERE `entry` IN (
910200, 910201, 910202,
910203, 910204, 910205,
910206, 910207, 910208,
910209, 910210, 910211,
910212, 910213, 910214,
910215, 910216, 910217,
910218, 910219, 910220,
910221, 910222, 910223,
910300, 910301, 910302,
910303, 910304, 910305,
910306, 910307, 910308,
910309, 910310, 910311,
910312, 910313, 910314,
910315, 910316, 910317,
910318, 910319, 910320,
910321, 910322, 910323
)
AND `locale` = 'ruRU';
INSERT INTO `creature_template` (
`entry`,
`difficulty_entry_1`,
`difficulty_entry_2`,
`difficulty_entry_3`,
`KillCredit1`,
`KillCredit2`,
`name`,
`subname`,
`IconName`,
`gossip_menu_id`,
`minlevel`,
`maxlevel`,
`exp`,
`faction`,
`npcflag`,
`speed_walk`,
`speed_run`,
`speed_swim`,
`speed_flight`,
`detection_range`,
`scale`,
`rank`,
`dmgschool`,
`DamageModifier`,
`BaseAttackTime`,
`RangeAttackTime`,
`BaseVariance`,
`RangeVariance`,
`unit_class`,
`unit_flags`,
`unit_flags2`,
`dynamicflags`,
`family`,
`type`,
`type_flags`,
`lootid`,
`pickpocketloot`,
`skinloot`,
`PetSpellDataId`,
`VehicleId`,
`mingold`,
`maxgold`,
`AIName`,
`MovementType`,
`HoverHeight`,
`HealthModifier`,
`ManaModifier`,
`ArmorModifier`,
`ExperienceModifier`,
`RacialLeader`,
`movementId`,
`RegenHealth`,
`mechanic_immune_mask`,
`spell_school_immune_mask`,
`flags_extra`,
`ScriptName`,
`VerifiedBuild`
)
VALUES
(910300, 0, 0, 0, 0, 0, 'Bromdur', 'Envoy of the Dwarves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- human host -> dwarf destination
(910301, 0, 0, 0, 0, 0, 'Laelina', 'Envoy of the Night Elves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- human host -> night elf destination
(910302, 0, 0, 0, 0, 0, 'Velaris', 'Envoy of the Draenei', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- human host -> draenei destination
(910303, 0, 0, 0, 0, 0, 'Edric', 'Envoy of the Humans', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- dwarf host -> human destination
(910304, 0, 0, 0, 0, 0, 'Shaeris', 'Envoy of the Night Elves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- dwarf host -> night elf destination
(910305, 0, 0, 0, 0, 0, 'Irenius', 'Envoy of the Draenei', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- dwarf host -> draenei destination
(910306, 0, 0, 0, 0, 0, 'Roland', 'Envoy of the Humans', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- night elf host -> human destination
(910307, 0, 0, 0, 0, 0, 'Targrim', 'Envoy of the Dwarves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- night elf host -> dwarf destination
(910308, 0, 0, 0, 0, 0, 'Adara', 'Envoy of the Draenei', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- night elf host -> draenei destination
(910309, 0, 0, 0, 0, 0, 'Mariella', 'Envoy of the Humans', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- draenei host -> human destination
(910310, 0, 0, 0, 0, 0, 'Kazgrim', 'Envoy of the Dwarves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- draenei host -> dwarf destination
(910311, 0, 0, 0, 0, 0, 'Telarion', 'Envoy of the Night Elves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- draenei host -> night elf destination
(910312, 0, 0, 0, 0, 0, 'Morvena', 'Envoy of the Forsaken', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- orc host -> undead destination
(910313, 0, 0, 0, 0, 0, 'Stonehorn', 'Envoy of the Tauren', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- orc host -> tauren destination
(910314, 0, 0, 0, 0, 0, 'Kalariel', 'Envoy of the Blood Elves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- orc host -> blood elf destination
(910315, 0, 0, 0, 0, 0, 'Gorkan', 'Envoy of the Orcs', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- undead host -> orc destination
(910316, 0, 0, 0, 0, 0, 'Quietwind', 'Envoy of the Tauren', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- undead host -> tauren destination
(910317, 0, 0, 0, 0, 0, 'Selinar', 'Envoy of the Blood Elves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- undead host -> blood elf destination
(910318, 0, 0, 0, 0, 0, 'Durmak', 'Envoy of the Orcs', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- tauren host -> orc destination
(910319, 0, 0, 0, 0, 0, 'Syren', 'Envoy of the Forsaken', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- tauren host -> undead destination
(910320, 0, 0, 0, 0, 0, 'Liadra', 'Envoy of the Blood Elves', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- tauren host -> blood elf destination
(910321, 0, 0, 0, 0, 0, 'Targok', 'Envoy of the Orcs', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- blood elf host -> orc destination
(910322, 0, 0, 0, 0, 0, 'Vargos', 'Envoy of the Forsaken', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340), -- blood elf host -> undead destination
(910323, 0, 0, 0, 0, 0, 'Graybull', 'Envoy of the Tauren', NULL, 0, 1, 1, 0, 35, 1, 1, 1.14286, 1, 1, 20, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, '', 12340); -- blood elf host -> tauren destination
INSERT INTO `creature_template_locale` (`entry`, `locale`, `Name`, `Title`, `VerifiedBuild`)
VALUES
(910300, 'ruRU', 'Бромдур', 'Посланник дворфов', 12340),
(910301, 'ruRU', 'Лаэлина', 'Посланник ночных эльфов', 12340),
(910302, 'ruRU', 'Веларис', 'Посланник дренеев', 12340),
(910303, 'ruRU', 'Эдрик', 'Посланник людей', 12340),
(910304, 'ruRU', 'Шаэрис', 'Посланник ночных эльфов', 12340),
(910305, 'ruRU', 'Ирений', 'Посланник дренеев', 12340),
(910306, 'ruRU', 'Роланд', 'Посланник людей', 12340),
(910307, 'ruRU', 'Таргрим', 'Посланник дворфов', 12340),
(910308, 'ruRU', 'Адара', 'Посланник дренеев', 12340),
(910309, 'ruRU', 'Мариэлла', 'Посланник людей', 12340),
(910310, 'ruRU', 'Казгрим', 'Посланник дворфов', 12340),
(910311, 'ruRU', 'Тэларион', 'Посланник ночных эльфов', 12340),
(910312, 'ruRU', 'Морвена', 'Посланник нежити', 12340),
(910313, 'ruRU', 'Камнерог', 'Посланник тауренов', 12340),
(910314, 'ruRU', 'Калариэль', 'Посланник эльфов крови', 12340),
(910315, 'ruRU', 'Горкан', 'Посланник орков', 12340),
(910316, 'ruRU', 'Тихий Ветер', 'Посланник тауренов', 12340),
(910317, 'ruRU', 'Селинар', 'Посланник эльфов крови', 12340),
(910318, 'ruRU', 'Дурмак', 'Посланник орков', 12340),
(910319, 'ruRU', 'Сайрен', 'Посланник нежити', 12340),
(910320, 'ruRU', 'Лиадра', 'Посланник эльфов крови', 12340),
(910321, 'ruRU', 'Таргок', 'Посланник орков', 12340),
(910322, 'ruRU', 'Варгос', 'Посланник нежити', 12340),
(910323, 'ruRU', 'Седой Бык', 'Посланник тауренов', 12340);
INSERT INTO `creature_template_model` (
`CreatureID`,
`Idx`,
`CreatureDisplayID`,
`DisplayScale`,
`Probability`,
`VerifiedBuild`
)
VALUES
(910300, 0, @DISPLAY_DWARF_MALE, 1, 1, 12340),
(910301, 0, @DISPLAY_NIGHTELF_MALE, 1, 1, 12340),
(910302, 0, @DISPLAY_DRAENEI_MALE, 1, 1, 12340),
(910303, 0, @DISPLAY_HUMAN_MALE, 1, 1, 12340),
(910304, 0, @DISPLAY_NIGHTELF_MALE, 1, 1, 12340),
(910305, 0, @DISPLAY_DRAENEI_MALE, 1, 1, 12340),
(910306, 0, @DISPLAY_HUMAN_MALE, 1, 1, 12340),
(910307, 0, @DISPLAY_DWARF_MALE, 1, 1, 12340),
(910308, 0, @DISPLAY_DRAENEI_MALE, 1, 1, 12340),
(910309, 0, @DISPLAY_HUMAN_MALE, 1, 1, 12340),
(910310, 0, @DISPLAY_DWARF_MALE, 1, 1, 12340),
(910311, 0, @DISPLAY_NIGHTELF_MALE, 1, 1, 12340),
(910312, 0, @DISPLAY_UNDEAD_MALE, 1, 1, 12340),
(910313, 0, @DISPLAY_TAUREN_MALE, 1, 1, 12340),
(910314, 0, @DISPLAY_BLOODELF_MALE, 1, 1, 12340),
(910315, 0, @DISPLAY_ORC_MALE, 1, 1, 12340),
(910316, 0, @DISPLAY_TAUREN_MALE, 1, 1, 12340),
(910317, 0, @DISPLAY_BLOODELF_MALE, 1, 1, 12340),
(910318, 0, @DISPLAY_ORC_MALE, 1, 1, 12340),
(910319, 0, @DISPLAY_UNDEAD_MALE, 1, 1, 12340),
(910320, 0, @DISPLAY_BLOODELF_MALE, 1, 1, 12340),
(910321, 0, @DISPLAY_ORC_MALE, 1, 1, 12340),
(910322, 0, @DISPLAY_UNDEAD_MALE, 1, 1, 12340),
(910323, 0, @DISPLAY_TAUREN_MALE, 1, 1, 12340);
@@ -0,0 +1,487 @@
local SCRIPT_NAME = "start_zone_race_teleporters"
local GOSSIP_EVENT_ON_HELLO = 1
local GOSSIP_EVENT_ON_SELECT = 2
local GOSSIP_ICON_CHAT = 0
local GOSSIP_SENDER_MAIN = 0
local ACTION_TELEPORT = 100
local ACTION_TELEPORT_BLOCKED = 101
local ACTION_CLOSE = 102
local DEFAULT_GOSSIP_TEXT_ID = 1
local PROGRESSION_SETTINGS_SOURCE = "mod-individual-progression"
local PROGRESSION_SETTING_INDEX = 0
local DEFAULT_INDIVIDUAL_PROGRESSION_ENABLED = true -- Matches mod-individual-progression source default.
local DEFAULT_TBC_RACES_UNLOCK_PROGRESSION = 0 -- Matches mod-individual-progression source default.
local QUEST_STATUS_COMPLETE = 1
local QUEST_STATUS_REWARDED = 6
local FALLBACK_TBC_RACES_ALLOW_CONFIG_KEY = "StartZoneRaceTeleporters.AllowTbcRacesTeleport"
local FALLBACK_TBC_RACES_ALLOW_CONFIG_KEY_LEGACY = "StartZoneRaceTeleporters.AllowDraeneiTeleport" -- Legacy alias for older local configs.
local FALLBACK_TBC_RACES_REQUIRED_QUEST_ID = 0 -- TODO: replace with a confirmed unlock quest if you use quest/status fallback.
local FALLBACK_TBC_RACES_DEFAULT_ACCESS = false
local TRAVEL_BUTTON_LABEL = "Отправляемся в путь"
local CLOSE_BUTTON_LABEL = "Я пока останусь"
local UNAVAILABLE_SUFFIX = " [Недоступно]"
local ACCESS_DENIED_MESSAGE_DRAENEI = "Доступ к стартовой зоне дренеев ещё не открыт вашим индивидуальным прогрессом."
local ACCESS_DENIED_MESSAGE_BLOODELF = "Доступ к стартовой зоне эльфов крови ещё не открыт вашим индивидуальным прогрессом."
local INVALID_CONFIGURATION_MESSAGE = "Конфигурация телепорта не завершена. Сообщите администратору."
local LOCATIONS = {
-- These coordinates come from the local playercreateinfo table.
-- TODO: replace them if you want a custom landing point instead of the raw race spawn.
human = { map = 0, x = -8949.95, y = -132.493, z = 83.5312, o = 0.0 },
dwarf = { map = 0, x = -6240.32, y = 331.033, z = 382.758, o = 6.17716 },
nightelf = { map = 1, x = 10311.3, y = 832.463, z = 1326.41, o = 5.69632 },
orc = { map = 1, x = -618.518, y = -4251.67, z = 38.718, o = 0.0 },
undead = { map = 0, x = 1676.71, y = 1678.31, z = 121.67, o = 2.70526 },
tauren = { map = 1, x = -2917.58, y = -257.98, z = 52.9968, o = 0.0 },
draenei = { map = 530, x = -3961.64, y = -13931.2, z = 100.615, o = 2.08364 },
bloodelf = { map = 530, x = 10349.6, y = -6357.29, z = 33.4026, o = 5.31605 },
}
local PRESENTER_PROFILES = {
human = {
gossipTextId = 9102400,
},
dwarf = {
gossipTextId = 9102401,
},
nightelf = {
gossipTextId = 9102402,
},
draenei = {
gossipTextId = 9102403,
},
orc = {
gossipTextId = 9102404,
},
undead = {
gossipTextId = 9102405,
},
tauren = {
gossipTextId = 9102406,
},
bloodelf = {
gossipTextId = 9102407,
},
}
local DESTINATIONS = {
human = {
label = "Стартовая зона людей",
requiresIndividualProgress = false,
requiredProgressKey = nil,
},
dwarf = {
label = "Стартовая зона дворфов и гномов",
requiresIndividualProgress = false,
requiredProgressKey = nil,
},
nightelf = {
label = "Стартовая зона ночных эльфов",
requiresIndividualProgress = false,
requiredProgressKey = nil,
},
orc = {
label = "Стартовая зона орков и троллей",
requiresIndividualProgress = false,
requiredProgressKey = nil,
},
undead = {
label = "Стартовая зона нежити",
requiresIndividualProgress = false,
requiredProgressKey = nil,
},
tauren = {
label = "Стартовая зона тауренов",
requiresIndividualProgress = false,
requiredProgressKey = nil,
},
draenei = {
label = "Стартовая зона дренеев",
requiresIndividualProgress = true,
requiredProgressKey = "draenei_start_zone",
},
bloodelf = {
label = "Стартовая зона эльфов крови",
requiresIndividualProgress = true,
requiredProgressKey = "bloodelf_start_zone",
},
}
local TELEPORTER_LAYOUT = {
-- One generic script serves every start zone.
-- The host zone key only documents where each entry is meant to be spawned.
human = {
{ entry = 910300, destinationKey = "dwarf" },
{ entry = 910301, destinationKey = "nightelf" },
{ entry = 910302, destinationKey = "draenei" },
},
dwarf = {
{ entry = 910303, destinationKey = "human" },
{ entry = 910304, destinationKey = "nightelf" },
{ entry = 910305, destinationKey = "draenei" },
},
nightelf = {
{ entry = 910306, destinationKey = "human" },
{ entry = 910307, destinationKey = "dwarf" },
{ entry = 910308, destinationKey = "draenei" },
},
draenei = {
{ entry = 910309, destinationKey = "human" },
{ entry = 910310, destinationKey = "dwarf" },
{ entry = 910311, destinationKey = "nightelf" },
},
orc = {
{ entry = 910312, destinationKey = "undead" },
{ entry = 910313, destinationKey = "tauren" },
{ entry = 910314, destinationKey = "bloodelf" },
},
undead = {
{ entry = 910315, destinationKey = "orc" },
{ entry = 910316, destinationKey = "tauren" },
{ entry = 910317, destinationKey = "bloodelf" },
},
tauren = {
{ entry = 910318, destinationKey = "orc" },
{ entry = 910319, destinationKey = "undead" },
{ entry = 910320, destinationKey = "bloodelf" },
},
bloodelf = {
{ entry = 910321, destinationKey = "orc" },
{ entry = 910322, destinationKey = "undead" },
{ entry = 910323, destinationKey = "tauren" },
},
}
local function SafeSendNotification(player, message)
if type(player.SendNotification) == "function" then
player:SendNotification(message)
return
end
player:SendBroadcastMessage(message)
end
local function SafeGetConfigValue(key)
if type(GetConfigValue) ~= "function" then
return nil
end
local ok, value = pcall(GetConfigValue, key)
if not ok then
return nil
end
return value
end
local function NormalizeBoolean(value)
if type(value) == "boolean" then
return value
end
if type(value) == "number" then
return value ~= 0
end
if type(value) ~= "string" then
return nil
end
local normalized = value:lower()
if normalized == "1" or normalized == "true" or normalized == "yes" then
return true
end
if normalized == "0" or normalized == "false" or normalized == "no" then
return false
end
return nil
end
local function GetConfigNumber(key, defaultValue)
local value = SafeGetConfigValue(key)
if value == nil or value == "" then
return defaultValue
end
local numericValue = tonumber(value)
if numericValue == nil then
return defaultValue
end
return numericValue
end
local function RequireLocation(locationKey)
assert(LOCATIONS[locationKey] ~= nil, string.format("%s: missing LOCATIONS[%q]", SCRIPT_NAME, locationKey))
end
local function CreateTeleporterConfig(hostZoneKey, destinationKey)
local destinationConfig = DESTINATIONS[destinationKey]
local presenterProfile = PRESENTER_PROFILES[destinationKey]
assert(destinationConfig ~= nil, string.format("%s: missing DESTINATIONS[%q]", SCRIPT_NAME, destinationKey))
assert(presenterProfile ~= nil, string.format("%s: missing PRESENTER_PROFILES[%q]", SCRIPT_NAME, destinationKey))
RequireLocation(hostZoneKey)
RequireLocation(destinationKey)
return {
hostZoneKey = hostZoneKey,
destinationKey = destinationKey,
label = destinationConfig.label,
requiresIndividualProgress = destinationConfig.requiresIndividualProgress,
requiredProgressKey = destinationConfig.requiredProgressKey,
gossipTextId = presenterProfile.gossipTextId,
}
end
local function BuildTeleporterNpcTable()
local teleporterNpcs = {}
for hostZoneKey, presenters in pairs(TELEPORTER_LAYOUT) do
for _, presenter in ipairs(presenters) do
assert(teleporterNpcs[presenter.entry] == nil, string.format("%s: duplicate teleporter entry %d", SCRIPT_NAME, presenter.entry))
teleporterNpcs[presenter.entry] = CreateTeleporterConfig(hostZoneKey, presenter.destinationKey)
end
end
return teleporterNpcs
end
local TELEPORTER_NPCS = BuildTeleporterNpcTable()
local function GetTeleporterConfigByEntry(entry)
return TELEPORTER_NPCS[entry]
end
local function GetDestinationLocation(destinationKey)
return LOCATIONS[destinationKey]
end
local function GetCurrentServerRequiredProgress(requiredProgressKey)
if requiredProgressKey == "draenei_start_zone" or requiredProgressKey == "bloodelf_start_zone" then
return GetConfigNumber("IndividualProgression.TbcRacesUnlockProgression", DEFAULT_TBC_RACES_UNLOCK_PROGRESSION)
end
return nil
end
local function IsCurrentServerProgressIntegrationEnabled()
local enabled = NormalizeBoolean(SafeGetConfigValue("IndividualProgression.Enable"))
if enabled ~= nil then
return enabled
end
return DEFAULT_INDIVIDUAL_PROGRESSION_ENABLED
end
local function TryCurrentServerProgressAccess(player, requiredProgressKey)
if not IsCurrentServerProgressIntegrationEnabled() then
return false, nil
end
local requiredProgressValue = GetCurrentServerRequiredProgress(requiredProgressKey)
if requiredProgressValue == nil then
return false, nil
end
if type(player.GetPlayerSettingValue) ~= "function" then
return false, nil
end
-- Current server integration:
-- mod-eluna exposes Player:GetPlayerSettingValue(...)
-- mod-individual-progression stores the progression state under source/index below.
local ok, currentProgressValue = pcall(
player.GetPlayerSettingValue,
player,
PROGRESSION_SETTINGS_SOURCE,
PROGRESSION_SETTING_INDEX
)
if not ok or type(currentProgressValue) ~= "number" then
return false, nil
end
return true, currentProgressValue >= requiredProgressValue
end
local function CanUseTbcRacesTeleportFallback(player)
local allowByConfigFlag = NormalizeBoolean(SafeGetConfigValue(FALLBACK_TBC_RACES_ALLOW_CONFIG_KEY))
if allowByConfigFlag == nil then
allowByConfigFlag = NormalizeBoolean(SafeGetConfigValue(FALLBACK_TBC_RACES_ALLOW_CONFIG_KEY_LEGACY))
end
if allowByConfigFlag == true then
return true
end
if FALLBACK_TBC_RACES_REQUIRED_QUEST_ID > 0 then
local questStatus = player:GetQuestStatus(FALLBACK_TBC_RACES_REQUIRED_QUEST_ID)
if questStatus == QUEST_STATUS_COMPLETE or questStatus == QUEST_STATUS_REWARDED then
return true
end
end
-- TODO: Replace this fallback with your final project-specific unlock signal
-- if the current server integration is unavailable or intentionally disabled.
-- This fallback intentionally gates both TBC races together.
return FALLBACK_TBC_RACES_DEFAULT_ACCESS
end
local function CanUseDraeneiTeleport(player)
local handled, allowed = TryCurrentServerProgressAccess(player, "draenei_start_zone")
if handled then
return allowed
end
return CanUseTbcRacesTeleportFallback(player)
end
local function CanUseBloodElfTeleport(player)
local handled, allowed = TryCurrentServerProgressAccess(player, "bloodelf_start_zone")
if handled then
return allowed
end
return CanUseTbcRacesTeleportFallback(player)
end
local ACCESS_RULES = {
draenei_start_zone = CanUseDraeneiTeleport,
bloodelf_start_zone = CanUseBloodElfTeleport,
}
local function CanUseTeleporter(player, teleporterConfig)
if not teleporterConfig.requiresIndividualProgress or teleporterConfig.requiredProgressKey == nil then
return true
end
local accessRule = ACCESS_RULES[teleporterConfig.requiredProgressKey]
if type(accessRule) ~= "function" then
return false
end
return accessRule(player) == true
end
local function BuildTravelButtonLabel(player, teleporterConfig)
if CanUseTeleporter(player, teleporterConfig) then
return TRAVEL_BUTTON_LABEL
end
return TRAVEL_BUTTON_LABEL .. UNAVAILABLE_SUFFIX
end
local function GetTravelActionId(player, teleporterConfig)
if CanUseTeleporter(player, teleporterConfig) then
return ACTION_TELEPORT
end
return ACTION_TELEPORT_BLOCKED
end
local function GetGossipTextId(teleporterConfig)
if teleporterConfig ~= nil and type(teleporterConfig.gossipTextId) == "number" then
return teleporterConfig.gossipTextId
end
return DEFAULT_GOSSIP_TEXT_ID
end
local function GetAccessDeniedMessage(teleporterConfig)
if teleporterConfig.requiredProgressKey == "draenei_start_zone" then
return ACCESS_DENIED_MESSAGE_DRAENEI
end
if teleporterConfig.requiredProgressKey == "bloodelf_start_zone" then
return ACCESS_DENIED_MESSAGE_BLOODELF
end
return INVALID_CONFIGURATION_MESSAGE
end
local function ShowTeleporterMenu(player, creature, teleporterConfig)
player:GossipClearMenu()
player:GossipMenuAddItem(
GOSSIP_ICON_CHAT,
BuildTravelButtonLabel(player, teleporterConfig),
GOSSIP_SENDER_MAIN,
GetTravelActionId(player, teleporterConfig)
)
player:GossipMenuAddItem(
GOSSIP_ICON_CHAT,
CLOSE_BUTTON_LABEL,
GOSSIP_SENDER_MAIN,
ACTION_CLOSE
)
player:GossipSendMenu(GetGossipTextId(teleporterConfig), creature)
end
local function TeleportPlayer(player, destinationKey)
local location = GetDestinationLocation(destinationKey)
if location == nil then
SafeSendNotification(player, INVALID_CONFIGURATION_MESSAGE)
return
end
player:Teleport(location.map, location.x, location.y, location.z, location.o)
end
local function RaceTeleporter_OnGossipHello(_, player, creature)
if player == nil or creature == nil then
return true
end
local teleporterConfig = GetTeleporterConfigByEntry(creature:GetEntry())
if teleporterConfig == nil then
return true
end
ShowTeleporterMenu(player, creature, teleporterConfig)
return true
end
local function RaceTeleporter_OnGossipSelect(_, player, creature, _, intid)
if player == nil or creature == nil then
return true
end
local teleporterConfig = GetTeleporterConfigByEntry(creature:GetEntry())
if teleporterConfig == nil then
player:GossipComplete()
return true
end
if intid == ACTION_CLOSE then
player:GossipComplete()
return true
end
if intid == ACTION_TELEPORT_BLOCKED then
SafeSendNotification(player, GetAccessDeniedMessage(teleporterConfig))
ShowTeleporterMenu(player, creature, teleporterConfig)
return true
end
if intid ~= ACTION_TELEPORT then
player:GossipComplete()
return true
end
player:GossipComplete()
TeleportPlayer(player, teleporterConfig.destinationKey)
return true
end
for entry in pairs(TELEPORTER_NPCS) do
RegisterCreatureGossipEvent(entry, GOSSIP_EVENT_ON_HELLO, RaceTeleporter_OnGossipHello)
RegisterCreatureGossipEvent(entry, GOSSIP_EVENT_ON_SELECT, RaceTeleporter_OnGossipSelect)
end