diff --git a/endpoints/npc/npc.php b/endpoints/npc/npc.php index 3133eb27..58d70edc 100644 --- a/endpoints/npc/npc.php +++ b/endpoints/npc/npc.php @@ -202,11 +202,51 @@ class NpcBaseResponse extends TemplateResponse implements ICache if (User::isInGroup(U_GROUP_EMPLOYEE)) { + $spawnData = DB::Aowow()->select('SELECT `guid` AS "0", `ScriptName` AS "1", `StringId` AS "2" FROM ?_spawns WHERE `type` = ?d AND `typeId` = ?d AND `ScriptName` IS NOT NULL ORDER BY `guid` ASC', Type::NPC, $this->typeId); + // AI - if ($_ = $this->subject->getField('scriptName')) - $infobox[] = 'Script'.Lang::main('colon').$_; - else if ($_ = $this->subject->getField('aiName')) - $infobox[] = 'AI'.Lang::main('colon').$_; + $scripts = null; + if ($_ = $this->subject->getField('ScriptOrAI')) + $scripts = match($_) + { + 'NullAI', 'AggressorAI', + 'ReactorAI', 'GuardAI', + 'PetAI', 'TotemAI', + 'SmartAI' => 'AI'.Lang::main('colon').$_, + default => 'Script'.Lang::main('colon').$_ + }; + + + if ($moreAI = array_filter(array_column($spawnData, 1, 0))) + { + $scripts ??= 'Script'.Lang::main('colon').'…'; + $scripts = '[toggler=hidden id=scriptName]'.$scripts.'[/toggler][div=hidden id=scriptName][ul]'; + foreach ($moreAI as $guid => $script) + $scripts .= sprintf('[li]GUID: %d - %s[/li]', $guid, $script); + + $scripts .= '[/ul][/div]'; + } + + if ($scripts) + $infobox[] = $scripts; + + // StringId + $stringIDs = null; + if ($_ = $this->subject->getField('StringId')) + $stringIDs = 'StringID'.Lang::main('colon').$_; + + if ($moreStrings = array_filter(array_column($spawnData, 2, 0))) + { + $stringIDs ??= 'StringID'.Lang::main('colon').'…'; + $stringIDs = '[toggler=hidden id=stringId]'.$stringIDs.'[/toggler][div=hidden id=stringId][ul]'; + foreach ($moreStrings as $guid => $stringId) + $stringIDs .= sprintf('[li]GUID: %d - %s[/li]', $guid, $stringId); + + $stringIDs .= '[/ul][/div]'; + } + + if ($stringIDs) + $infobox[] = $stringIDs; // Mechanic immune if ($immuneMask = $this->subject->getField('mechanicImmuneMask')) @@ -272,7 +312,7 @@ class NpcBaseResponse extends TemplateResponse implements ICache // smart AI $sai = null; - if ($this->subject->getField('aiName') == 'SmartAI') + if ($this->subject->getField('ScriptOrAI') == 'SmartAI') { $sai = new SmartAI(SmartAI::SRC_TYPE_CREATURE, $this->typeId); if (!$sai->prepare()) // no smartAI found .. check per guid @@ -293,7 +333,7 @@ class NpcBaseResponse extends TemplateResponse implements ICache $this->smartAI = $sai->getMarkup(); } else - trigger_error('Creature has SmartAI set in template but no SmartAI defined.'); + trigger_error('Creature has `AIName`: SmartAI set in template but no SmartAI defined.'); } // consider pooled spawns diff --git a/endpoints/object/object.php b/endpoints/object/object.php index 8fc3f2dd..e1b88803 100644 --- a/endpoints/object/object.php +++ b/endpoints/object/object.php @@ -237,10 +237,43 @@ class ObjectBaseResponse extends TemplateResponse implements ICache if ($id == $this->typeId) $infobox[] = Lang::game('mode').Lang::game('modes', $this->mapType, $n); - // AI if (User::isInGroup(U_GROUP_EMPLOYEE)) + { + $spawnData = DB::Aowow()->select('SELECT `guid` AS "0", `ScriptName` AS "1", `StringId` AS "2" FROM ?_spawns WHERE `type` = ?d AND `typeId` = ?d AND `ScriptName` IS NOT NULL ORDER BY `guid` ASC', Type::OBJECT, $this->typeId); + + // AI + $scripts = null; if ($_ = $this->subject->getField('ScriptOrAI')) - $infobox[] = ($_ == 'SmartGameObjectAI' ? 'AI' : 'Script').Lang::main('colon').$_; + $scripts = ($_ == 'SmartGameObjectAI' ? 'AI' : 'Script').Lang::main('colon').$_; + + if ($moreAI = array_filter(array_column($spawnData, 1, 0))) + { + $scripts ??= 'Script'.Lang::main('colon').'…'; + $scripts = '[toggler=hidden id=scriptName]'.$scripts.'[/toggler][div=hidden id=scriptName][ul]'; + foreach ($moreAI as $guid => $script) + $scripts .= sprintf('[li]GUID: %d - %s[/li]', $guid, $script); + + $scripts .= '[/ul][/div]'; + } + + if ($scripts) + $infobox[] = $scripts; + + // StringId + $stringIDs = null; + if ($_ = $this->subject->getField('StringId')) + $stringIDs = 'StringID'.Lang::main('colon').$_; + + if ($moreStrings = array_filter(array_column($spawnData, 2, 0))) + { + $stringIDs ??= 'StringID'.Lang::main('colon').'…'; + $stringIDs = '[toggler=hidden id=stringId]'.$stringIDs.'[/toggler][div=hidden id=stringId][ul]'; + foreach ($moreStrings as $guid => $stringId) + $stringIDs .= sprintf('[li]GUID: %d - %s[/li]', $guid, $stringId); + + $stringIDs .= '[/ul][/div]'; + } + } if ($infobox) $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); @@ -311,7 +344,7 @@ class ObjectBaseResponse extends TemplateResponse implements ICache $this->smartAI = $sai->getMarkup(); } else - trigger_error('Gameobject has AIName set in template but no SmartAI defined.'); + trigger_error('Gameobject has `AIName`: SmartGameObjectAI set in template but no SmartAI defined.'); } $this->redButtons = array( diff --git a/includes/components/dbtypelist.class.php b/includes/components/dbtypelist.class.php index bac9bd9c..d4a8439a 100644 --- a/includes/components/dbtypelist.class.php +++ b/includes/components/dbtypelist.class.php @@ -723,18 +723,23 @@ trait spawnHelper $info[4] = Lang::game('mode').implode(', ', $_); } + if ($s['ScriptName']) + $info[5] = 'ScriptName'.Lang::main('colon').$s['ScriptName']; + if ($s['StringId']) + $info[6] = 'StringId'.Lang::main('colon').$s['StringId']; + if ($s['type'] == Type::AREATRIGGER) { // teleporter endpoint if ($s['guid'] < 0) { $opts['type'] = 4; - $info[5] = 'Teleport Destination'; + $info[7] = 'Teleport Destination'; } else { $o = Util::O2Deg($this->getField('orientation')); - $info[5] = 'Orientation'.Lang::main('colon').$o[0].'° ('.$o[1].')'; + $info[7] = 'Orientation'.Lang::main('colon').$o[0].'° ('.$o[1].')'; } } diff --git a/setup/sql/01-db_structure.sql b/setup/sql/01-db_structure.sql index ee918cdd..a2d8947d 100644 --- a/setup/sql/01-db_structure.sql +++ b/setup/sql/01-db_structure.sql @@ -551,7 +551,6 @@ CREATE TABLE `aowow_creature` ( `vehicleId` mediumint(8) unsigned NOT NULL DEFAULT 0, `minGold` mediumint(8) unsigned NOT NULL DEFAULT 0, `maxGold` mediumint(8) unsigned NOT NULL DEFAULT 0, - `aiName` varchar(50) NOT NULL DEFAULT '', `healthMin` int(10) unsigned NOT NULL DEFAULT 1, `healthMax` int(10) unsigned NOT NULL DEFAULT 1, `manaMin` int(10) unsigned NOT NULL DEFAULT 1, @@ -568,7 +567,8 @@ CREATE TABLE `aowow_creature` ( `mechanicImmuneMask` int(10) unsigned NOT NULL DEFAULT 0, `schoolImmuneMask` int(10) unsigned NOT NULL DEFAULT 0, `flagsExtra` int(10) unsigned NOT NULL DEFAULT 0, - `scriptName` varchar(50) NOT NULL DEFAULT '', + `ScriptOrAI` varchar(64) DEFAULT NULL, + `StringId` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name_loc0`), KEY `difficultyEntry1` (`difficultyEntry1`), @@ -1628,7 +1628,8 @@ CREATE TABLE `aowow_objects` ( `auraSpell` mediumint(8) unsigned NOT NULL DEFAULT 0, `triggeredSpell` mediumint(8) unsigned NOT NULL DEFAULT 0, `miscInfo` varchar(128) NOT NULL, - `ScriptOrAI` varchar(64) NOT NULL, + `ScriptOrAI` varchar(64) DEFAULT NULL, + `StringId` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name_loc0`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; @@ -2555,6 +2556,8 @@ CREATE TABLE `aowow_spawns` ( `posX` float unsigned NOT NULL, `posY` float unsigned NOT NULL, `pathId` int(10) unsigned NOT NULL DEFAULT 0, + `ScriptName` varchar(64) DEFAULT NULL, + `StringId` varchar(64) DEFAULT NULL, PRIMARY KEY (`guid`,`type`,`floor`), KEY `type_idx` (`typeId`,`type`), KEY `zone_idx` (`areaId`), diff --git a/setup/sql/02-db_initial_data.sql b/setup/sql/02-db_initial_data.sql index c9c64720..ba45539a 100644 --- a/setup/sql/02-db_initial_data.sql +++ b/setup/sql/02-db_initial_data.sql @@ -71,7 +71,7 @@ UNLOCK TABLES; LOCK TABLES `aowow_dbversion` WRITE; /*!40000 ALTER TABLE `aowow_dbversion` DISABLE KEYS */; -INSERT INTO `aowow_dbversion` VALUES (1764798162,0,NULL,NULL); +INSERT INTO `aowow_dbversion` VALUES (1767026730,0,NULL,NULL); /*!40000 ALTER TABLE `aowow_dbversion` ENABLE KEYS */; UNLOCK TABLES; diff --git a/setup/sql/updates/1767026729_01.sql b/setup/sql/updates/1767026729_01.sql new file mode 100644 index 00000000..276abad4 --- /dev/null +++ b/setup/sql/updates/1767026729_01.sql @@ -0,0 +1,18 @@ +ALTER TABLE `aowow_spawns` + ADD COLUMN `ScriptName` varchar(64) DEFAULT NULL AFTER `pathId`, + ADD COLUMN `StringId` varchar(64) DEFAULT NULL AFTER `ScriptName` +; + +ALTER TABLE `aowow_objects` + MODIFY COLUMN `ScriptOrAI` varchar(64) DEFAULT NULL, + ADD COLUMN `StringId` varchar(64) DEFAULT NULL AFTER `ScriptOrAI` +; + +ALTER TABLE `aowow_creature` + DROP COLUMN `aiName`, + DROP COLUMN `scriptName`, + ADD COLUMN `ScriptOrAI` varchar(64) DEFAULT NULL AFTER `flagsExtra`, + ADD COLUMN `StringId` varchar(64) DEFAULT NULL AFTER `ScriptOrAI` +; + +UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' creature objects spawns'); diff --git a/setup/tools/sqlgen/creature.ss.php b/setup/tools/sqlgen/creature.ss.php index 954b4fc6..5448ea10 100644 --- a/setup/tools/sqlgen/creature.ss.php +++ b/setup/tools/sqlgen/creature.ss.php @@ -61,7 +61,6 @@ CLISetup::registerSetup("sql", new class extends SetupScript PetSpellDataId, VehicleId, mingold, maxgold, - AIName, (CASE ct.exp WHEN 0 THEN min.basehp0 WHEN 1 THEN min.basehp1 ELSE min.basehp2 END) * ct.HealthModifier AS healthMin, (CASE ct.exp WHEN 0 THEN max.basehp0 WHEN 1 THEN max.basehp1 ELSE max.basehp2 END) * ct.HealthModifier AS healthMax, min.basemana * ct.ManaModifier AS manaMin, @@ -73,7 +72,8 @@ CLISetup::registerSetup("sql", new class extends SetupScript mechanic_immune_mask, spell_school_immune_mask, flags_extra, - ScriptName + NULLIF(IF(ScriptName <> "", ScriptName, AIName), ""), + StringId FROM creature_template ct JOIN creature_classlevelstats min ON ct.unit_class = min.class AND ct.minlevel = min.level JOIN creature_classlevelstats max ON ct.unit_class = max.class AND ct.maxlevel = max.level diff --git a/setup/tools/sqlgen/objects.ss.php b/setup/tools/sqlgen/objects.ss.php index b785dd0a..3fae3d18 100644 --- a/setup/tools/sqlgen/objects.ss.php +++ b/setup/tools/sqlgen/objects.ss.php @@ -53,7 +53,8 @@ CLISetup::registerSetup("sql", new class extends SetupScript IF(`type` = 3, CONCAT_WS(" ", data4, data5, data2), -- miscInfo: loot v IF(`type` = 25, CONCAT_WS(" ", data2, data3, 0), IF(`type` = 23, CONCAT_WS(" ", data0, data1, data2), "")))), -- miscInfo: meetingStone - IF(ScriptName <> "", ScriptName, AIName) + NULLIF(IF(ScriptName <> "", ScriptName, AIName), ""), + StringId FROM gameobject_template go LEFT JOIN gameobject_template_addon goa ON go.entry = goa.entry LEFT JOIN gameobject_template_locale gtl2 ON go.entry = gtl2.entry AND gtl2.`locale` = "frFR" diff --git a/setup/tools/sqlgen/spawns.ss.php b/setup/tools/sqlgen/spawns.ss.php index 4bb79ade..5e8182f1 100644 --- a/setup/tools/sqlgen/spawns.ss.php +++ b/setup/tools/sqlgen/spawns.ss.php @@ -212,9 +212,9 @@ CLISetup::registerSetup("sql", new class extends SetupScript private function creature() : array { - // [guid, type, typeId, map, posX, posY [, respawn, spawnMask, phaseMask, areaId, floor, pathId]] + // [guid, type, typeId, map, posX, posY [, respawn, spawnMask, phaseMask, areaId, floor, pathId, ScriptName, StringId]] return DB::World()->select( - 'SELECT c.`guid`, ?d AS `type`, c.`id` AS `typeId`, c.`map`, c.`position_x` AS `posX`, c.`position_y` AS `posY`, c.`spawntimesecs` AS `respawn`, c.`spawnMask`, c.`phaseMask`, c.`zoneId` AS `areaId`, IFNULL(ca.`path_id`, IFNULL(cta.`path_id`, 0)) AS `pathId` + 'SELECT c.`guid`, ?d AS `type`, c.`id` AS `typeId`, c.`map`, c.`position_x` AS `posX`, c.`position_y` AS `posY`, c.`spawntimesecs` AS `respawn`, c.`spawnMask`, c.`phaseMask`, c.`zoneId` AS `areaId`, IFNULL(ca.`path_id`, IFNULL(cta.`path_id`, 0)) AS `pathId`, NULLIF(`ScriptName`, "") AS "ScriptName", NULLIF(`StringId`, "") AS "StringId" FROM creature c LEFT JOIN creature_addon ca ON ca.guid = c.guid LEFT JOIN creature_template_addon cta ON cta.entry = c.id', @@ -224,9 +224,9 @@ CLISetup::registerSetup("sql", new class extends SetupScript private function gameobject() : array { - // [guid, type, typeId, map, posX, posY [, respawn, spawnMask, phaseMask, areaId, floor, pathId]] + // [guid, type, typeId, map, posX, posY [, respawn, spawnMask, phaseMask, areaId, floor, pathId, ScriptName, StringId]] return DB::World()->select( - 'SELECT `guid`, ?d AS `type`, `id` AS `typeId`, `map`, `position_x` AS `posX`, `position_y` AS `posY`, `spawntimesecs` AS `respawn`, `spawnMask`, `phaseMask`, `zoneId` AS `areaId` + 'SELECT `guid`, ?d AS `type`, `id` AS `typeId`, `map`, `position_x` AS `posX`, `position_y` AS `posY`, `spawntimesecs` AS `respawn`, `spawnMask`, `phaseMask`, `zoneId` AS `areaId`, NULLIF(`ScriptName`, "") AS "ScriptName", NULLIF(`StringId`, "") AS "StringId" FROM gameobject', Type::OBJECT ); @@ -234,7 +234,7 @@ CLISetup::registerSetup("sql", new class extends SetupScript private function soundemitter() : array { - // [guid, type, typeId, map, posX, posY [, respawn, spawnMask, phaseMask, areaId, floor, pathId]] + // [guid, type, typeId, map, posX, posY [, respawn, spawnMask, phaseMask, areaId, floor, pathId, ScriptName, StringId]] return DB::Aowow()->select( 'SELECT `id` AS `guid`, ?d AS `type`, `soundId` AS `typeId`, `mapId` AS `map`, `posX`, `posY` FROM dbc_soundemitters', @@ -244,7 +244,7 @@ CLISetup::registerSetup("sql", new class extends SetupScript private function areatrigger() : array { - // [guid, type, typeId, map, posX, posY [, respawn, spawnMask, phaseMask, areaId, floor, pathId]] + // [guid, type, typeId, map, posX, posY [, respawn, spawnMask, phaseMask, areaId, floor, pathId, ScriptName, StringId]] $base = DB::Aowow()->select( 'SELECT `id` AS `guid`, ?d AS `type`, `id` AS `typeId`, `mapId` AS `map`, `posX`, `posY` FROM dbc_areatrigger',