[null, null, null, null], // NONE self::TARGET_SELF => [null, null, null, null], // Self cast self::TARGET_VICTIM => [null, null, null, null], // Our current target (ie: highest aggro) self::TARGET_HOSTILE_SECOND_AGGRO => [null, null, null, null], // Second highest aggro, maxdist, playerOnly, powerType + 1 self::TARGET_HOSTILE_LAST_AGGRO => [null, null, null, null], // Dead last on aggro, maxdist, playerOnly, powerType + 1 self::TARGET_HOSTILE_RANDOM => [null, null, null, null], // Just any random target on our threat list, maxdist, playerOnly, powerType + 1 self::TARGET_HOSTILE_RANDOM_NOT_TOP => [null, null, null, null], // Any random target except top threat, maxdist, playerOnly, powerType + 1 self::TARGET_ACTION_INVOKER => [null, null, null, null], // Unit who caused this Event to occur self::TARGET_POSITION => [null, null, null, null], // use xyz from event params self::TARGET_CREATURE_RANGE => [Type::NPC, ['numRange', 10, false], null, null], // CreatureEntry(0any), minDist, maxDist self::TARGET_CREATURE_GUID => [null, Type::NPC, null, null], // guid, entry self::TARGET_CREATURE_DISTANCE => [Type::NPC, null, null, null], // CreatureEntry(0any), maxDist self::TARGET_STORED => [null, null, null, null], // id, uses pre-stored target(list) self::TARGET_GAMEOBJECT_RANGE => [Type::OBJECT, ['numRange', 10, false], null, null], // entry(0any), min, max self::TARGET_GAMEOBJECT_GUID => [null, Type::OBJECT, null, null], // guid, entry self::TARGET_GAMEOBJECT_DISTANCE => [Type::OBJECT, null, null, null], // entry(0any), maxDist self::TARGET_INVOKER_PARTY => [null, null, null, null], // invoker's party members self::TARGET_PLAYER_RANGE => [['numRange', 10, false], null, null, null], // min, max self::TARGET_PLAYER_DISTANCE => [null, null, null, null], // maxDist self::TARGET_CLOSEST_CREATURE => [Type::NPC, null, null, null], // CreatureEntry(0any), maxDist, dead? self::TARGET_CLOSEST_GAMEOBJECT => [Type::OBJECT, null, null, null], // entry(0any), maxDist self::TARGET_CLOSEST_PLAYER => [null, null, null, null], // maxDist self::TARGET_ACTION_INVOKER_VEHICLE => [null, null, null, null], // Unit's vehicle who caused this Event to occur self::TARGET_OWNER_OR_SUMMONER => [null, null, null, null], // Unit's owner or summoner, Use Owner/Charmer of this unit self::TARGET_THREAT_LIST => [null, null, null, null], // All units on creature's threat list, maxdist self::TARGET_CLOSEST_ENEMY => [null, null, null, null], // maxDist, playerOnly self::TARGET_CLOSEST_FRIENDLY => [null, null, null, null], // maxDist, playerOnly self::TARGET_LOOT_RECIPIENTS => [null, null, null, null], // all players that have tagged this creature (for kill credit) self::TARGET_FARTHEST => [null, null, null, null], // maxDist, playerOnly, isInLos self::TARGET_VEHICLE_PASSENGER => [null, null, null, null], // seatMask (0 - all seats) self::TARGET_CLOSEST_UNSPAWNED_GO => [Type::OBJECT, null, null, null] // entry(0any), maxDist ); private array $jsGlobals = []; public function __construct( private int $id, public readonly int $type, private array $param, private array $worldPos, private SmartAI &$smartAI) { // additional parameters Util::checkNumeric($this->param, NUM_CAST_INT); Util::checkNumeric($this->worldPos, NUM_CAST_FLOAT); $this->param = array_pad($this->param, 15, ''); $this->worldPos = array_pad($this->worldPos, 4, 0.0); } public function process() : string { $target = ''; $targetTT = Lang::smartAI('targetTT', array_merge([$this->type], $this->param, $this->worldPos)); for ($i = 0; $i < 4; $i++) { $tParams = $this->targets[$this->type]; if (is_array($tParams[$i])) { [$fn, $idx, $extraParam] = $tParams[$i]; $this->param[$idx] = $this->{$fn}($this->param[$i], $this->param[$i + 1], $extraParam); } else if (is_int($tParams[$i]) && $this->param[$i]) $this->jsGlobals[$tParams[$i]][$this->param[$i]] = $this->param[$i]; } // non-generic cases switch ($this->type) { case self::TARGET_HOSTILE_SECOND_AGGRO: case self::TARGET_HOSTILE_LAST_AGGRO: case self::TARGET_HOSTILE_RANDOM: case self::TARGET_HOSTILE_RANDOM_NOT_TOP: if ($this->param[2]) $this->param[10] = Lang::spell('powerTypes', $this->param[2] - 1); break; case self::TARGET_VEHICLE_PASSENGER: if ($this->param[0]) $this->param[10] = Lang::concat(Util::mask2bits($this->param[0])); break; case self::TARGET_CREATURE_GUID: if ($_ = $this->resolveGuid(Type::NPC, $this->param[0])) { $this->jsGlobals[Type::NPC][$_] = $_; $this->param[10] = $_; } break; case self::TARGET_GAMEOBJECT_GUID: if ($_ = $this->resolveGuid(Type::OBJECT, $this->param[0])) { $this->jsGlobals[Type::OBJECT][$_] = $_; $this->param[10] = $_; } break; } $this->smartAI->addJsGlobals($this->jsGlobals); $target = Lang::smartAI('targets', $this->type, $this->param) ?? Lang::smartAI('targetUNK', [$this->type]); // resolve conditionals $i = 0; while (strstr($target, ')?') && $i++ < 3) $target = preg_replace_callback('/\(([^\)]*?)\)\?([^:]*):(([^;]*);*);/i', fn($m) => $m[1] ? $m[2] : $m[3], $target); // wrap in tooltip (suspend action-tooltip) return '[/span]'.sprintf(self::TARGET_TPL, $targetTT, $target).'[span tooltip=a-#rowIdx#]'; } public function getWorldPos() : array { return $this->worldPos; } // not really feasable. Too many target types can be players or creatures, depending on context public function getTalkSource(bool &$playerSrc = false) : int { if ($this->type == SmartTarget::TARGET_CLOSEST_PLAYER) $playerSrc = true; return match ($this->type) { SmartTarget::TARGET_CREATURE_GUID => $this->resolveGuid(Type::NPC, $this->param[0]) ?? 0, SmartTarget::TARGET_CREATURE_RANGE, SmartTarget::TARGET_CREATURE_DISTANCE, SmartTarget::TARGET_CLOSEST_CREATURE => $this->param[0], SmartTarget::TARGET_CLOSEST_PLAYER, SmartTarget::TARGET_SELF => $this->smartAI->getEntry(), default => $this->smartAI->getEntry() }; } } ?>