From b764200c2a7824a84b0af7188422eccfdfaec594 Mon Sep 17 00:00:00 2001 From: Sarjuuk Date: Thu, 20 Nov 2025 23:22:17 +0100 Subject: [PATCH] SmartAI/Conditions * embed Conditions into SmartAI table so we can evaluate CONDITION_SOURCE_TYPE_SMART_EVENT (22) * make SmartAI table display flexible --- endpoints/areatrigger/areatrigger.php | 2 +- endpoints/item/item.php | 2 +- endpoints/npc/npc.php | 8 +- endpoints/quest/quest.php | 2 +- endpoints/spell/spell.php | 2 +- .../Conditions/Conditions.class.php | 45 +++---- includes/components/SmartAI/SmartAI.class.php | 110 ++++++++++-------- .../components/SmartAI/SmartAction.class.php | 6 + .../components/SmartAI/SmartEvent.class.php | 2 +- includes/game/loot/lootbycontainer.class.php | 2 +- includes/game/loot/lootbyitem.class.php | 2 +- setup/sql/updates/1763677664_01.sql | 1 + .../filegen/templates/global.js/markup.js | 12 ++ 13 files changed, 119 insertions(+), 77 deletions(-) create mode 100644 setup/sql/updates/1763677664_01.sql diff --git a/endpoints/areatrigger/areatrigger.php b/endpoints/areatrigger/areatrigger.php index edd991cf..c8a02e54 100644 --- a/endpoints/areatrigger/areatrigger.php +++ b/endpoints/areatrigger/areatrigger.php @@ -105,7 +105,7 @@ class AreatriggerBaseResponse extends TemplateResponse implements ICache // tab: conditions $cnd = new Conditions(); - $cnd->getBySourceEntry($this->typeId, Conditions::SRC_AREATRIGGER_CLIENT)->prepare(); + $cnd->getBySource(Conditions::SRC_AREATRIGGER_CLIENT, entry: $this->typeId)->prepare(); if ($tab = $cnd->toListviewTab()) { $this->extendGlobalData($cnd->getJsGlobals()); diff --git a/endpoints/item/item.php b/endpoints/item/item.php index 7a734022..ba606e5a 100644 --- a/endpoints/item/item.php +++ b/endpoints/item/item.php @@ -734,7 +734,7 @@ class ItemBaseResponse extends TemplateResponse implements ICache $extraCols = ['$Listview.extraCols.stock', "\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost']; $cnd = new Conditions(); - $cnd->getBySourceEntry($this->typeId, Conditions::SRC_NPC_VENDOR)->prepare(); + $cnd->getBySource(Conditions::SRC_NPC_VENDOR, entry: $this->typeId)->prepare(); foreach ($sbData as $k => &$row) { $currency = []; diff --git a/endpoints/npc/npc.php b/endpoints/npc/npc.php index 8d126feb..ef314118 100644 --- a/endpoints/npc/npc.php +++ b/endpoints/npc/npc.php @@ -385,7 +385,7 @@ class NpcBaseResponse extends TemplateResponse implements ICache } $cnd = new Conditions(); - $cnd->getBySourceGroup($this->typeId, Conditions::SRC_VEHICLE_SPELL)->prepare(); + $cnd->getBySource(Conditions::SRC_VEHICLE_SPELL, group: $this->typeId)->prepare(); if ($cnd->toListviewColumn($controled, $extraCols, $this->typeId, 'id')) $this->extendGlobalData($cnd->getJsGlobals()); @@ -547,7 +547,7 @@ class NpcBaseResponse extends TemplateResponse implements ICache } $cnd = new Conditions(); - if ($cnd->getBySourceGroup($this->typeId, Conditions::SRC_NPC_VENDOR)->prepare()) + if ($cnd->getBySource(Conditions::SRC_NPC_VENDOR, group: $this->typeId)->prepare()) { $this->extendGlobalData($cnd->getJsGlobals()); $cnd->toListviewColumn($lvData, $extraCols, $this->typeId, 'id'); @@ -805,8 +805,8 @@ class NpcBaseResponse extends TemplateResponse implements ICache // tab: conditions $cnd = new Conditions(); - $cnd->getBySourceEntry($this->typeId, Conditions::SRC_CREATURE_TEMPLATE_VEHICLE) - ->getBySourceGroup($this->typeId, Conditions::SRC_SPELL_CLICK_EVENT) + $cnd->getBySource(Conditions::SRC_CREATURE_TEMPLATE_VEHICLE, entry: $this->typeId) + ->getBySource(Conditions::SRC_SPELL_CLICK_EVENT, group: $this->typeId) ->getByCondition(Type::NPC, $this->typeId) ->prepare(); if ($tab = $cnd->toListviewTab()) diff --git a/endpoints/quest/quest.php b/endpoints/quest/quest.php index 190770b5..e13e75b7 100644 --- a/endpoints/quest/quest.php +++ b/endpoints/quest/quest.php @@ -979,7 +979,7 @@ class QuestBaseResponse extends TemplateResponse implements ICache // tab: conditions $cnd = new Conditions(); - $cnd->getBySourceEntry($this->typeId, Conditions::SRC_QUEST_AVAILABLE, Conditions::SRC_QUEST_SHOW_MARK) + $cnd->getBySource([Conditions::SRC_QUEST_AVAILABLE, Conditions::SRC_QUEST_SHOW_MARK], entry: $this->typeId) ->getByCondition(Type::QUEST, $this->typeId) ->prepare(); diff --git a/endpoints/spell/spell.php b/endpoints/spell/spell.php index dae8f96b..321e508b 100644 --- a/endpoints/spell/spell.php +++ b/endpoints/spell/spell.php @@ -1210,7 +1210,7 @@ class SpellBaseResponse extends TemplateResponse implements ICache // tab: conditions $cnd = new Conditions(); - $cnd->getBySourceEntry($this->typeId, Conditions::SRC_SPELL_IMPLICIT_TARGET, Conditions::SRC_SPELL, Conditions::SRC_SPELL_CLICK_EVENT, Conditions::SRC_VEHICLE_SPELL, Conditions::SRC_SPELL_PROC) + $cnd->getBySource([Conditions::SRC_SPELL_IMPLICIT_TARGET, Conditions::SRC_SPELL, Conditions::SRC_SPELL_CLICK_EVENT, Conditions::SRC_VEHICLE_SPELL, Conditions::SRC_SPELL_PROC], entry: $this->typeId) ->getByCondition(Type::SPELL, $this->typeId) ->prepare(); if ($tab = $cnd->toListviewTab()) diff --git a/includes/components/Conditions/Conditions.class.php b/includes/components/Conditions/Conditions.class.php index 96b3ea34..175f6cf7 100644 --- a/includes/components/Conditions/Conditions.class.php +++ b/includes/components/Conditions/Conditions.class.php @@ -221,29 +221,26 @@ class Conditions /* IN */ /******/ - public function getBySourceEntry(int $entry, int ...$srcType) : self + public function getBySource(int|array $type, int|array $group = 0, int|array $entry = 0, int|array $id = 0) : self { + if ($group) + $group = is_int($group) ? [$group] : array_map('intVal', $group); + if ($entry) + $entry = is_int($entry) ? [$entry] : array_map('intVal', $entry); + if ($id) + $id = is_int($id) ? [$id] : array_map('intVal', $id); + if ($type) + $type = is_int($type) ? [$type] : array_map('intVal', $type); + else + return $this; + $this->rows = array_merge($this->rows, DB::World()->select( 'SELECT `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition` FROM conditions - WHERE `SourceTypeOrReferenceId` IN (?a) AND `SourceEntry` = ?d + WHERE `SourceTypeOrReferenceId` IN (?a){ AND `SourceGroup` IN (?a)}{ AND `SourceEntry` IN (?a)}{ AND `SourceId` IN (?a)} ORDER BY `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `ElseGroup` ASC', - $srcType, $entry - )); - - return $this; - } - - public function getBySourceGroup(int $group, int ...$srcType) : self - { - $this->rows = array_merge($this->rows, DB::World()->select( - 'SELECT `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `SourceId`, `ElseGroup`, - `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition` - FROM conditions - WHERE `SourceTypeOrReferenceId` IN (?a) AND `SourceGroup` = ?d - ORDER BY `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `ElseGroup` ASC', - $srcType, $group + $type, $group ?: DBSIMPLE_SKIP, $entry ?: DBSIMPLE_SKIP, $id ?: DBSIMPLE_SKIP )); return $this; @@ -386,6 +383,14 @@ class Conditions return $success; } + public function toMarkupTag() : string + { + if (!$this->result) + return ''; + + return '[condition]' . json_encode($this->result, JSON_NUMERIC_CHECK) . '[/condition]'; + } + public function getJsGlobals() : array { return $this->jsGlobals; @@ -486,9 +491,9 @@ class Conditions $this->jsGlobals[$grp][$sGroup] = $sGroup; if (is_int($entry)) $this->jsGlobals[$entry][$sEntry] = $sEntry; - // Note: sourceId currently has no typed content - // if (is_int($id)) - // $this->jsGlobals[$id][$sId] = $sId; + // Note: sourceId currently has no typed content + // if (is_int($id)) + // $this->jsGlobals[$id][$sId] = $sId; // more checks? not all sources can retarget $cTarget = min(1, max(0, $cTarget)); diff --git a/includes/components/SmartAI/SmartAI.class.php b/includes/components/SmartAI/SmartAI.class.php index f5e0fcfc..626d3f27 100644 --- a/includes/components/SmartAI/SmartAI.class.php +++ b/includes/components/SmartAI/SmartAI.class.php @@ -257,8 +257,16 @@ class SmartAI private array $result = []; private array $tabs = []; private array $itr = []; + private array $quotes = []; - private array $quotes = []; + public string $css = <<title = $miscData['title'] ?? ''; $this->teleportTargetArea = $miscData['teleportTargetArea'] ?? 0; + if ($this->baseEntry) // my parent handles base css + $this->css = ''; + $raw = DB::World()->select( - 'SELECT `id`, `link`, - `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, - `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, - `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o` - FROM smart_scripts - WHERE `entryorguid` = ?d AND `source_type` = ?d - ORDER BY `id` ASC', + 'SELECT `id`, `link`, + `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, + `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, + `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o` + FROM smart_scripts + WHERE `entryorguid` = ?d AND `source_type` = ?d + ORDER BY `id` ASC', $this->entry, $this->srcType); foreach ($raw as $r) { $this->rawData[$r['id']] = array( - 'id' => $r['id'], - 'link' => $r['link'], - 'event' => new SmartEvent($r['id'], $r['event_type'], $r['event_phase_mask'], $r['event_chance'], $r['event_flags'], [$r['event_param1'], $r['event_param2'], $r['event_param3'], $r['event_param4'], $r['event_param5']], $this), - 'action' => new SmartAction($r['id'], $r['action_type'], [$r['action_param1'], $r['action_param2'], $r['action_param3'], $r['action_param4'], $r['action_param5'], $r['action_param6']], $this), - 'target' => new SmartTarget($r['id'], $r['target_type'], [$r['target_param1'], $r['target_param2'], $r['target_param3'], $r['target_param4']], [$r['target_x'], $r['target_y'], $r['target_z'], $r['target_o']], $this) + 'id' => $r['id'], + 'link' => $r['link'], + 'event' => new SmartEvent($r['id'], $r['event_type'], $r['event_phase_mask'], $r['event_chance'], $r['event_flags'], [$r['event_param1'], $r['event_param2'], $r['event_param3'], $r['event_param4'], $r['event_param5']], $this), + 'action' => new SmartAction($r['id'], $r['action_type'], [$r['action_param1'], $r['action_param2'], $r['action_param3'], $r['action_param4'], $r['action_param5'], $r['action_param6']], $this), + 'target' => new SmartTarget($r['id'], $r['target_type'], [$r['target_param1'], $r['target_param2'], $r['target_param3'], $r['target_param4']], [$r['target_x'], $r['target_y'], $r['target_z'], $r['target_o']], $this), + 'condition' => (new Conditions())->getBySource(Conditions::SRC_SMART_EVENT, $r['id'] + 1, $entry, $srcType) ); } } @@ -619,10 +631,9 @@ class SmartAI if ($this->result) return true; - $hidePhase = - $hideChance = true; + $visibleCols = (1 << 0) | (1 << 2) | (1 << 4); - foreach ($this->iterate() as $id => $__) + foreach ($this->iterate() as $__) { $rowIdx = Util::createHash(8); @@ -636,53 +647,60 @@ class SmartAI $evtBody = str_replace(['#target#', '#rowIdx#'], [$this->itr['target']->process(), $rowIdx], $evtBody); $actBody = str_replace(['#target#', '#rowIdx#'], [$this->itr['target']->process(), $rowIdx], $actBody); - if (!$this->itr['event']->hasPhases()) - $hidePhase = false; + if ($this->itr['event']->hasPhases()) + $visibleCols |= (1 << 1); if ($this->itr['event']->chance != 100) - $hideChance = false; + $visibleCols |= (1 << 3); + + if ($this->itr['condition']->prepare()) + { + $visibleCols |= (1 << 5); + Util::mergeJsGlobals($this->jsGlobals, $this->itr['condition']->getJsGlobals()); + } $this->result[] = array( $this->itr['id'], implode(', ', Util::mask2bits($this->itr['event']->phaseMask, 1)), - $evtBody.($evtFooter ? '[div float=right margin=0px clear=both][i][small class=q0]'.$evtFooter.'[/small][/i][/div]' : null), + $evtBody.($evtFooter ? '[div float=right margin=0px clear=both width=100% align=right][i][small class=q0]'.$evtFooter.'[/small][/i][/div]' : ''), $this->itr['event']->chance.'%', - $actBody.($actFooter ? '[div float=right margin=0px clear=both][i][small class=q0]'.$actFooter.'[/small][/i][/div]' : null) + $actBody.($actFooter ? '[div float=right margin=0px clear=both width=100% align=right][i][small class=q0]'.$actFooter.'[/small][/i][/div]' : ''), + $this->itr['condition']->toMarkupTag() ); } $th = array( - '#' => 16, - 'Phase' => 32, - 'Event' => 350, - 'Chance' => 24, - 'Action' => 0 + ['#' , '24px'], + ['Phase', '48px'], + ['Event', '30%%'], + ['Chance', '60px'], + ['Action', 'auto'], + ['Condition', 'auto'] ); - if ($hidePhase) + for ($i = 0, $j = count($th); $i < $j; $i++) { - unset($th['Phase']); - foreach ($this->result as &$r) - unset($r[1]); - } - unset($r); + if ($visibleCols & (1 << $i)) + continue; - if ($hideChance) - { - unset($th['Chance']); + unset($th[$i]); foreach ($this->result as &$r) - unset($r[3]); - } - unset($r); + unset($r[$i]); - $tbl = '[tr]'; - foreach ($th as $n => $w) - $tbl .= '[td header '.($w ? 'width='.$w.'px' : null).']'.$n.'[/td]'; - $tbl .= '[/tr]'; + unset($r); + } + + $tblId = Util::createHash(12); + + $this->css .= "\n#tbl-".$tblId." { grid-template-columns: ".implode(' ', array_column($th, 1))."; }"; + + $tbl = '[tr]' . array_reduce(array_column($th, 0), fn($out, $n) => $out .= '[td header]'.$n.'[/td]', '') . '[/tr]'; foreach ($this->result as $r) $tbl .= '[tr][td]'.implode('[/td][td]', $r).'[/td][/tr]'; + $tbl = '[table id=tbl-'.$tblId.' class=grid]'.$tbl.'[/table]'; + if ($this->srcType == self::SRC_TYPE_ACTIONLIST) $this->tabs[$this->entry] = $tbl; else @@ -698,16 +716,16 @@ class SmartAI if (!$this->rawData) return null; - $wrapper = '[table class=grid width=940px]%s[/table]'; - $return = '[style]#smartai-generic .grid { clear:left; } #smartai-generic .tabbed-contents { padding:0px; clear:left; }[/style][pad][h3][toggler id=sai]SmartAI'.$this->title.'[/toggler][/h3][div id=sai clear=left]%s[/div]'; + $wrapper = '%s'; + $return = '[style]'.strtr($this->css, "\n", ' ').'[/style][pad][h3][toggler id=sai]SmartAI'.$this->title.'[/toggler][/h3][div id=sai clear=left]%s[/div]'; $tabs = ''; if (count($this->tabs) > 1) { - $wrapper = '[tabs name=sai width=942px]%s[/tabs]'; + $wrapper = '[tabs name=sai]%s[/tabs]'; $return = "[script]function TalTabClick(id) { $('#dsf67g4d-sai').find('[href=\'#sai-actionlist-' + id + '\']').click(); }[/script]" . $return; foreach ($this->tabs as $guid => $data) { - $buff = '[tab name="'.($guid ? 'ActionList #'.$guid : 'Main').'"][table class=grid width=940px]'.$data.'[/table][/tab]'; + $buff = '[tab name="'.($guid ? 'ActionList #'.$guid : 'Main').'"]'.$data.'[/tab]'; if ($guid) $tabs .= $buff; else diff --git a/includes/components/SmartAI/SmartAction.class.php b/includes/components/SmartAI/SmartAction.class.php index 924c8358..0ae6b681 100644 --- a/includes/components/SmartAI/SmartAction.class.php +++ b/includes/components/SmartAI/SmartAction.class.php @@ -562,6 +562,8 @@ class SmartAction $tal = new SmartAI(SmartAI::SRC_TYPE_ACTIONLIST, $this->param[0], ['baseEntry' => $this->smartAI->getEntry()]); $tal->prepare(); + $this->smartAI->css .= $tal->css; + Util::mergeJsGlobals($this->jsGlobals, $tal->getJSGlobals()); foreach ($tal->getTabs() as $guid => $tt) @@ -587,6 +589,8 @@ class SmartAction $tal = new SmartAI(SmartAI::SRC_TYPE_ACTIONLIST, $this->param[$i], ['baseEntry' => $this->smartAI->getEntry()]); $tal->prepare(); + $this->smartAI->css .= $tal->css; + Util::mergeJsGlobals($this->jsGlobals, $tal->getJSGlobals()); foreach ($tal->getTabs() as $guid => $tt) @@ -603,6 +607,8 @@ class SmartAction $tal = new SmartAI(SmartAI::SRC_TYPE_ACTIONLIST, $i, ['baseEntry' => $this->smartAI->getEntry()]); $tal->prepare(); + $this->smartAI->css .= $tal->css; + Util::mergeJsGlobals($this->jsGlobals, $tal->getJSGlobals()); foreach ($tal->getTabs() as $guid => $tt) diff --git a/includes/components/SmartAI/SmartEvent.class.php b/includes/components/SmartAI/SmartEvent.class.php index 726ac3a6..1afabb81 100644 --- a/includes/components/SmartAI/SmartEvent.class.php +++ b/includes/components/SmartAI/SmartEvent.class.php @@ -368,7 +368,7 @@ class SmartEvent public function hasPhases() : bool { - return $this->phaseMask == 0; + return $this->phaseMask && ($this->phaseMask & 0xFFF) != 0xFFF; } private function formatFlags() : string diff --git a/includes/game/loot/lootbycontainer.class.php b/includes/game/loot/lootbycontainer.class.php index 3ede055d..a8f345fd 100644 --- a/includes/game/loot/lootbycontainer.class.php +++ b/includes/game/loot/lootbycontainer.class.php @@ -138,7 +138,7 @@ class LootByContainer extends Loot $groupChances[$k] = (100 - $sum) / ($nGroupEquals[$k] ?: 1); } - if ($cnd->getBySourceGroup($lootId, Conditions::lootTableToConditionSource($tableName))->prepare()) + if ($cnd->getBySource(Conditions::lootTableToConditionSource($tableName), group: $lootId)->prepare()) { $this->storeJSGlobals($cnd->getJsGlobals()); $cnd->toListviewColumn($loot, $this->extraCols, $lootId, 'content'); diff --git a/includes/game/loot/lootbyitem.class.php b/includes/game/loot/lootbyitem.class.php index ea75e947..5ed6093c 100644 --- a/includes/game/loot/lootbyitem.class.php +++ b/includes/game/loot/lootbyitem.class.php @@ -171,7 +171,7 @@ class LootByItem extends Loot if ($newRefs) { $cnd = new Conditions(); - if ($cnd->getBySourceEntry($this->entry, Conditions::SRC_REFERENCE_LOOT_TEMPLATE)) + if ($cnd->getBySource(Conditions::SRC_REFERENCE_LOOT_TEMPLATE, entry: $this->entry)) if ($cnd->toListviewColumn($newRefs, $x, $this->entry)) $this->storejsGlobals($cnd->getJsGlobals()); } diff --git a/setup/sql/updates/1763677664_01.sql b/setup/sql/updates/1763677664_01.sql new file mode 100644 index 00000000..03adf418 --- /dev/null +++ b/setup/sql/updates/1763677664_01.sql @@ -0,0 +1 @@ +UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs'); diff --git a/setup/tools/filegen/templates/global.js/markup.js b/setup/tools/filegen/templates/global.js/markup.js index 285fbc9a..5cb8f459 100644 --- a/setup/tools/filegen/templates/global.js/markup.js +++ b/setup/tools/filegen/templates/global.js/markup.js @@ -465,6 +465,18 @@ var Markup = { return [str, '']; } }, + condition: + { + ltrim: true, + rtrim: true, + empty: false, + allowedClass: MARKUP_CLASS_STAFF, + allowedChildren: { '': 1 }, + toHtml: function(attr) + { + return ['' + Markup.toHtml(ConditionList.createCell(JSON.parse(attr._nodes[0].attr._rawText)), { skipReset: true }) + '']; + } + }, copy: { empty: false,