From 4d6fb4975ef0c975a8c197236ee4fbc66ea50b63 Mon Sep 17 00:00:00 2001 From: Sarjuuk Date: Sun, 13 Aug 2023 05:10:30 +0200 Subject: [PATCH] Emotes * have creature emotes in the same system (on a negeative index) as SAI links were uselessly pointing to player emotes before * player emotes are now gendered * display two more cases of who points at whom when using a player emote * use and link event sound from emote * display more misc info --- includes/basetype.class.php | 2 +- includes/defines.php | 18 ++++ includes/game.php | 5 +- includes/smartAI.class.php | 6 +- includes/types/emote.class.php | 2 +- includes/utilities.php | 4 +- localization/locale_dede.php | 33 ++++++- localization/locale_enus.php | 33 ++++++- localization/locale_eses.php | 33 ++++++- localization/locale_frfr.php | 33 ++++++- localization/locale_ruru.php | 33 ++++++- localization/locale_zhcn.php | 33 ++++++- pages/emote.php | 95 +++++++++++++++---- pages/emotes.php | 6 +- pages/search.php | 2 +- pages/sound.php | 2 +- setup/db_structure.sql | 63 ++++++++----- setup/tools/dbc.class.php | 8 +- setup/tools/sqlgen/emotes.func.php | 141 ++++++++++++++++++++++++----- setup/updates/1691940877_01.sql | 49 ++++++++++ 20 files changed, 495 insertions(+), 106 deletions(-) create mode 100644 setup/updates/1691940877_01.sql diff --git a/includes/basetype.class.php b/includes/basetype.class.php index b6faa392..ccb545de 100644 --- a/includes/basetype.class.php +++ b/includes/basetype.class.php @@ -652,7 +652,7 @@ trait spawnHelper } // guid < 0 are vehicle accessories. those are moved by moving the vehicle - if (User::isInGroup(U_GROUP_MODERATOR) && $worldPos && $s['guid'] > 0) + if (User::isInGroup(U_GROUP_MODERATOR) && $worldPos && $s['guid'] > 0 && isset($worldPos[$s['guid']])) { if ($points = Game::worldPosToZonePos($worldPos[$s['guid']]['mapId'], $worldPos[$s['guid']]['posX'], $worldPos[$s['guid']]['posY'])) { diff --git a/includes/defines.php b/includes/defines.php index a603e510..3a565119 100644 --- a/includes/defines.php +++ b/includes/defines.php @@ -303,6 +303,8 @@ define('NPC_CU_DIFFICULTY_DUMMY', 0x02); define('ITEM_CU_OT_ITEMLOOT', 0x01); // there are no sourceTypes for these two cases define('ITEM_CU_OT_OBJECTLOOT', 0x02); +define('EMOTE_CU_MISSING_CMD', 0x01); // no alias in Globalstrings.lua and thus unusable + // as seen in wFlags define('QUEST_CU_REPEATABLE', 0x01); define('QUEST_CU_DAILY', 0x02); @@ -1734,4 +1736,20 @@ define('MAP_TYPE_ARENA', 6); define('MAP_TYPE_MMODE_RAID', 7); define('MAP_TYPE_MMODE_RAID_HC', 8); +define('EMOTE_FLAG_ONLY_STANDING', 0x0001); // Only while standig +define('EMOTE_FLAG_USE_MOUNT', 0x0002); // Emote applies to mount +define('EMOTE_FLAG_NOT_CHANNELING', 0x0004); // Not while channeling +define('EMOTE_FLAG_ANIM_TALK', 0x0008); // Talk anim - talk +define('EMOTE_FLAG_ANIM_QUESTION', 0x0010); // Talk anim - question +define('EMOTE_FLAG_ANIM_EXCLAIM', 0x0020); // Talk anim - exclamation +define('EMOTE_FLAG_ANIM_SHOUT', 0x0040); // Talk anim - shout +define('EMOTE_FLAG_NOT_SWIMMING', 0x0080); // Not while swimming +define('EMOTE_FLAG_ANIM_LAUGH', 0x0100); // Talk anim - laugh +define('EMOTE_FLAG_CAN_LIE_ON_GROUND', 0x0200); // Ok while sleeping or dead +define('EMOTE_FLAG_NOT_FROM_CLIENT', 0x0400); // Disallow from client +define('EMOTE_FLAG_NOT_CASTING', 0x0800); // Not while casting +define('EMOTE_FLAG_END_MOVEMENT', 0x1000); // Movement ends +define('EMOTE_FLAG_INTERRUPT_ON_ATTACK', 0x2000); // Interrupt on attack +define('EMOTE_FLAG_ONLY_STILL', 0x4000); // Only while still +define('EMOTE_FLAG_NOT_FLYING', 0x8000); // Not while flying ?> diff --git a/includes/game.php b/includes/game.php index cc9cc2d8..85fa9c9b 100644 --- a/includes/game.php +++ b/includes/game.php @@ -339,9 +339,12 @@ class Game $result = DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `id`, `mapId`, `posX`, `posY` FROM dbc_areatrigger WHERE `id` IN (?a)', $guids); break; default: - trigger_error('Game::getWorldPosForGUID - instanced with unsupported TYPE '.$type, E_USER_WARNING); + trigger_error('Game::getWorldPosForGUID - instanced with unsupported TYPE #'.$type, E_USER_WARNING); } + if ($diff = array_diff($guids, array_keys($result))) + trigger_error('Game::getWorldPosForGUID - no spawn points for TYPE #'.$type.' GUIDS: '.implode(', ', $diff), E_USER_WARNING); + return $result; } diff --git a/includes/smartAI.class.php b/includes/smartAI.class.php index 0df29d0e..536b8e92 100644 --- a/includes/smartAI.class.php +++ b/includes/smartAI.class.php @@ -905,7 +905,7 @@ class SmartAI if ($time = $this->numRange('event', 1, true)) $footer = $time; break; - case SAI_EVENT_RECEIVE_EMOTE: // 22 - On Receive Emote. + case SAI_EVENT_RECEIVE_EMOTE: // 22 - On Receive Player Emote. $this->jsGlobals[Type::EMOTE][] = $e['param'][0]; if ($time = $this->numRange('event', 1, true)) @@ -1044,7 +1044,10 @@ class SmartAI case SAI_ACTION_PLAY_EMOTE: // 5 -> any target case SAI_ACTION_SET_EMOTE_STATE: // 17 -> any target if ($a['param'][0]) + { + $a['param'][0] *= -1; // handle creature emote $this->jsGlobals[Type::EMOTE][] = $a['param'][0]; + } break; case SAI_ACTION_FAIL_QUEST: // 6 -> any target case SAI_ACTION_OFFER_QUEST: // 7 -> invoker @@ -1152,6 +1155,7 @@ class SmartAI if (empty($a['param'][$i])) continue; + $a['param'][$i] *= -1; // handle creature emote $buff[] = '[emote='.$a['param'][$i].']'; $this->jsGlobals[Type::EMOTE][] = $a['param'][$i]; } diff --git a/includes/types/emote.class.php b/includes/types/emote.class.php index 4e1b4a57..ce0969f3 100644 --- a/includes/types/emote.class.php +++ b/includes/types/emote.class.php @@ -33,7 +33,7 @@ class EmoteList extends BaseType $data[$this->id] = array( 'id' => $this->curTpl['id'], 'name' => $this->curTpl['cmd'], - 'preview' => $this->getField('self', true) ?: ($this->getField('noTarget', true) ?: $this->getField('target', true)) + 'preview' => Util::parseHtmlText($this->getField('meToExt', true) ?: $this->getField('meToNone', true) ?: $this->getField('extToMe', true) ?: $this->getField('extToExt', true) ?: $this->getField('extToNone', true), true) ); // [nyi] sounds diff --git a/includes/utilities.php b/includes/utilities.php index b72e1c38..656cd74f 100644 --- a/includes/utilities.php +++ b/includes/utilities.php @@ -750,9 +750,9 @@ abstract class Util $from = array( '/\|T([\w]+\\\)*([^\.]+)\.blp:\d+\|t/ui', // images (force size to tiny) |T:|t '/\|c(\w{6})\w{2}([^\|]+)\|r/ui', // color |c|r - '/\$g\s*([^:;]+)\s*:\s*([^:;]+)\s*(:?[^:;]*);/ui',// directed gender-reference $g::: + '/\$g\s*([^:;]*)\s*:\s*([^:;]*)\s*(:?[^:;]*);/ui',// directed gender-reference $g:: '/\$t([^;]+);/ui', // nonsense, that the client apparently ignores - '/\|\d\-?\d?\((\$\w)\)/ui', // and another modifier for something russian |3-6($r) + '/\|\d\-?\d?\(([\$\%]\w)\)/ui', // and another modifier for something russian |3-6($r) '/<([^\"=\/>]+\s[^\"=\/>]+)>/ui', // emotes (workaround: at least one whitespace and never " or = between brackets) '/\$(\d+)w/ui', // worldState(?)-ref found on some pageTexts $1234w '/\$c/i', // class-ref diff --git a/localization/locale_dede.php b/localization/locale_dede.php index 910de818..1dbf18c6 100644 --- a/localization/locale_dede.php +++ b/localization/locale_dede.php @@ -627,7 +627,7 @@ $lang = array( SAI_ACTION_SET_FACTION => ['Setze Fraktion von #target# (%1$d)?auf [faction=%7$d]:zurück;.', null], SAI_ACTION_MORPH_TO_ENTRY_OR_MODEL => ['(%7$d)?Setze Aussehen zurück.:Nimm folgendes Erscheinungsbild an:;(%1$d)? [npc=%1$d]:;(%2$d)?[model npc=%2$d border=1 float=right][/model]:;', null], SAI_ACTION_SOUND => ['Spiele Audio(%2$d)? für auslösenden Spieler:;:[div float=right width=270px][sound=%1$d][/div]', 'Abgespielt durch Welt.'], - SAI_ACTION_PLAY_EMOTE => ['Emote [emote=%1$d] zu #target#.', null], + SAI_ACTION_PLAY_EMOTE => ['(%1$d)?Emote [emote=%1$d] zu #target#.:Beende Emote;', null], SAI_ACTION_FAIL_QUEST => ['[quest=%1$d] von #target# schlägt fehl.', null], SAI_ACTION_OFFER_QUEST => ['(%2$d)?Füge [quest=%1$d] dem Log von #target# hinzu:Biete [quest=%1$d] #target# an;.', null], SAI_ACTION_SET_REACT_STATE => ['#target# wird %7$s.', null], @@ -638,7 +638,7 @@ $lang = array( SAI_ACTION_THREAT_SINGLE_PCT => ['Ändere Bedrohung von #target# um %7$d%%.', null], SAI_ACTION_THREAT_ALL_PCT => ['Ändere Bedrohung aller Ziele um %7$d%%.', null], SAI_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS => ['Erfülle Entdeckungsereignis von [quest=%1$d] für #target#.', null], - SAI_ACTION_SET_EMOTE_STATE => ['Emote [emote=%1$d] kontinuierlich zu #target#.', null], + SAI_ACTION_SET_EMOTE_STATE => ['(%1$d)?Emote [emote=%1$d] kontinuierlich zu #target#:Beende Emote.;', null], SAI_ACTION_SET_UNIT_FLAG => ['Setze (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], SAI_ACTION_REMOVE_UNIT_FLAG => ['Setze (%2$d)?UnitFlags2:UnitFlags; %7$s zurück.', null], /* 20*/ SAI_ACTION_AUTO_ATTACK => ['(%1$d)?Beginne:Beende; automatische Angriffe gegen #target#.', null], @@ -919,12 +919,35 @@ $lang = array( ), 'emote' => array( 'notFound' => "Dieses Emote existiert nicht.", - 'self' => "An Euch selbst", - 'target' => "An Andere mit Ziel", - 'noTarget' => "An Andere ohne Ziel", +// 'self' => "An Euch selbst", +// 'target' => "An Andere mit Ziel", +// 'noTarget' => "An Andere ohne Ziel", + 'targeted' => "Benutzt mit Ziel", + 'untargeted' => "Benutzt ohne Ziel", 'isAnimated' => "Besitzt eine Animation", + 'eventSound' => "Ereignis-Klang", 'aliases' => "Aliasse", 'noText' => "Dieses Emote besitzt keinen Text.", + 'noCommand' => "Dieses Emote besitzt keinen /-Befehl. Es kann nicht benutzt werden.", + 'flags' => array( + EMOTE_FLAG_ONLY_STANDING => "Nur im stehen", + EMOTE_FLAG_USE_MOUNT => "Emote benutzt das Reittier", + EMOTE_FLAG_NOT_CHANNELING => "Nicht beim kanalisieren", + EMOTE_FLAG_ANIM_TALK => "Talk anim - Reden", + EMOTE_FLAG_ANIM_QUESTION => "Talk anim - Fragen", + EMOTE_FLAG_ANIM_EXCLAIM => "Talk anim - Ausrufen", + EMOTE_FLAG_ANIM_SHOUT => "Talk anim - Schreien", + EMOTE_FLAG_NOT_SWIMMING => "Nicht beim schwimmen", + EMOTE_FLAG_ANIM_LAUGH => "Talk anim - Lachen", + EMOTE_FLAG_CAN_LIE_ON_GROUND => "Nutzbar im Schlaf/Tot", + EMOTE_FLAG_NOT_FROM_CLIENT => "Nur für Kreaturen", + EMOTE_FLAG_NOT_CASTING => "Nicht beim Zaubern", + EMOTE_FLAG_END_MOVEMENT => "Emote endet Bewegung", + EMOTE_FLAG_INTERRUPT_ON_ATTACK => "Unerbrochen durch Kampf", + EMOTE_FLAG_ONLY_STILL => "Nur in Ruhe", + EMOTE_FLAG_NOT_FLYING => "Nicht im Flug" + ), + 'state' => ['Einmalig', 'Stetiger Zustand', 'Stetiges Emote'] ), 'enchantment' => array( 'details' => "Details", diff --git a/localization/locale_enus.php b/localization/locale_enus.php index 43d7654f..90024b72 100644 --- a/localization/locale_enus.php +++ b/localization/locale_enus.php @@ -627,7 +627,7 @@ $lang = array( SAI_ACTION_SET_FACTION => ['(%1$d)?Set faction of #target# to [faction=%7$d]:Reset faction of #target#;.', null], SAI_ACTION_MORPH_TO_ENTRY_OR_MODEL => ['(%7$d)?Reset apperance.:Take the appearance of;(%1$d)? [npc=%1$d].:;(%2$d)?[model npc=%2$d border=1 float=right][/model]:;', null], SAI_ACTION_SOUND => ['Play sound(%2$d)? to invoking player:;:[div float=right width=270px][sound=%1$d][/div]', 'Played by environment.'], - SAI_ACTION_PLAY_EMOTE => ['Emote [emote=%1$d] to #target#.', null], + SAI_ACTION_PLAY_EMOTE => ['(%1$d)?Emote [emote=%1$d] to #target#.: End Emote.;', null], SAI_ACTION_FAIL_QUEST => ['Fail [quest=%1$d] for #target#.', null], SAI_ACTION_OFFER_QUEST => ['(%2$d)?Add [quest=%1$d] to #target#\'s log:Offer [quest=%1$d] to #target#;.', null], SAI_ACTION_SET_REACT_STATE => ['#target# becomes %7$s.', null], @@ -638,7 +638,7 @@ $lang = array( SAI_ACTION_THREAT_SINGLE_PCT => ['Modify #target#\'s threat by %7$d%%.', null], SAI_ACTION_THREAT_ALL_PCT => ['Modify the threat of all targets by %7$d%%.', null], SAI_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS => ['Exploration event of [quest=%1$d] is completed for #target#.', null], - SAI_ACTION_SET_EMOTE_STATE => ['Continuously emote [emote=%1$d] to #target#.', null], + SAI_ACTION_SET_EMOTE_STATE => ['(%1$d)?Continuously emote [emote=%1$d] to #target#.:End emote state;', null], SAI_ACTION_SET_UNIT_FLAG => ['Set (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], SAI_ACTION_REMOVE_UNIT_FLAG => ['Unset (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], /* 20*/ SAI_ACTION_AUTO_ATTACK => ['(%1$d)?Start:Stop; auto attacking #target#.', null], @@ -919,12 +919,35 @@ $lang = array( ), 'emote' => array( 'notFound' => "This Emote doesn't exist.", - 'self' => "To Yourself", - 'target' => "To others with a target", - 'noTarget' => "To others without a target", +// 'self' => "To Yourself", +// 'target' => "To others with a target", +// 'noTarget' => "To others without a target", + 'targeted' => "Used with target", + 'untargeted' => "Used without target", 'isAnimated' => "Uses an animation", + 'eventSound' => "Event Sound", 'aliases' => "Aliases", 'noText' => "This Emote has no text.", + 'noCommand' => "This Emote has no /-command. It can not be triggered.", + 'flags' => array( // gm stuff - translation nice but not essential + EMOTE_FLAG_ONLY_STANDING => "Only while standig", + EMOTE_FLAG_USE_MOUNT => "Emote applies to mount", + EMOTE_FLAG_NOT_CHANNELING => "Not while channeling", + EMOTE_FLAG_ANIM_TALK => "Talk anim - talk", + EMOTE_FLAG_ANIM_QUESTION => "Talk anim - question", + EMOTE_FLAG_ANIM_EXCLAIM => "Talk anim - exclamation", + EMOTE_FLAG_ANIM_SHOUT => "Talk anim - shout", + EMOTE_FLAG_NOT_SWIMMING => "Not while swimming", + EMOTE_FLAG_ANIM_LAUGH => "Talk anim - laugh", + EMOTE_FLAG_CAN_LIE_ON_GROUND => "Usable while sleeping or dead", + EMOTE_FLAG_NOT_FROM_CLIENT => "Creature only", + EMOTE_FLAG_NOT_CASTING => "Not while casting", + EMOTE_FLAG_END_MOVEMENT => "Emote ends movement", + EMOTE_FLAG_INTERRUPT_ON_ATTACK => "Interrupt on attacking", + EMOTE_FLAG_ONLY_STILL => "Only while still", + EMOTE_FLAG_NOT_FLYING => "Not while flying" + ), + 'state' => ['Oneshot', 'Continuous State', 'Continuous Emote'] ), 'enchantment' => array( 'details' => "Details", diff --git a/localization/locale_eses.php b/localization/locale_eses.php index f60e1ac9..9676970d 100644 --- a/localization/locale_eses.php +++ b/localization/locale_eses.php @@ -627,7 +627,7 @@ $lang = array( SAI_ACTION_SET_FACTION => ['(%1$d)?Set faction of #target# to [faction=%7$d]:Reset faction of #target#;.', null], SAI_ACTION_MORPH_TO_ENTRY_OR_MODEL => ['(%7$d)?Reset apperance.:Take the appearance of;(%1$d)? [npc=%1$d].:;(%2$d)?[model npc=%2$d border=1 float=right][/model]:;', null], SAI_ACTION_SOUND => ['Play sound(%2$d)? to invoking player:;:[div float=right width=270px][sound=%1$d][/div]', 'Played by environment.'], - SAI_ACTION_PLAY_EMOTE => ['Emote [emote=%1$d] to #target#.', null], + SAI_ACTION_PLAY_EMOTE => ['(%1$d)?Emote [emote=%1$d] to #target#.: End Emote.;', null], SAI_ACTION_FAIL_QUEST => ['Fail [quest=%1$d] for #target#.', null], SAI_ACTION_OFFER_QUEST => ['(%2$d)?Add [quest=%1$d] to #target#\'s log:Offer [quest=%1$d] to #target#;.', null], SAI_ACTION_SET_REACT_STATE => ['#target# becomes %7$s.', null], @@ -638,7 +638,7 @@ $lang = array( SAI_ACTION_THREAT_SINGLE_PCT => ['Modify #target#\'s threat by %7$d%%.', null], SAI_ACTION_THREAT_ALL_PCT => ['Modify the threat of all targets by %7$d%%.', null], SAI_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS => ['Exploration event of [quest=%1$d] is completed for #target#.', null], - SAI_ACTION_SET_EMOTE_STATE => ['Continuously emote [emote=%1$d] to #target#.', null], + SAI_ACTION_SET_EMOTE_STATE => ['(%1$d)?Continuously emote [emote=%1$d] to #target#.:End emote state;', null], SAI_ACTION_SET_UNIT_FLAG => ['Set (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], SAI_ACTION_REMOVE_UNIT_FLAG => ['Unset (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], /* 20*/ SAI_ACTION_AUTO_ATTACK => ['(%1$d)?Start:Stop; auto attacking #target#.', null], @@ -919,12 +919,35 @@ $lang = array( ), 'emote' => array( 'notFound' => "Este emoticón no existe", - 'self' => "Para Usted", - 'target' => "Para otros con un objetivo", - 'noTarget' => "Para otros sin un objetivo", +// 'self' => "Para Usted", +// 'target' => "Para otros con un objetivo", +// 'noTarget' => "Para otros sin un objetivo", + 'targeted' => "[Used with target]", + 'untargeted' => "[Used without target]", 'isAnimated' => "Usa una animación", + 'eventSound' => "[Event Sound]", 'aliases' => "Aliases", 'noText' => "Este emoticón no tiene texto", + 'noCommand' => "[This Emote has no /-command. It can not be triggered.]", + 'flags' => array( + EMOTE_FLAG_ONLY_STANDING => "Only while standig", + EMOTE_FLAG_USE_MOUNT => "Emote applies to mount", + EMOTE_FLAG_NOT_CHANNELING => "Not while channeling", + EMOTE_FLAG_ANIM_TALK => "Talk anim - talk", + EMOTE_FLAG_ANIM_QUESTION => "Talk anim - question", + EMOTE_FLAG_ANIM_EXCLAIM => "Talk anim - exclamation", + EMOTE_FLAG_ANIM_SHOUT => "Talk anim - shout", + EMOTE_FLAG_NOT_SWIMMING => "Not while swimming", + EMOTE_FLAG_ANIM_LAUGH => "Talk anim - laugh", + EMOTE_FLAG_CAN_LIE_ON_GROUND => "Usable while sleeping or dead", + EMOTE_FLAG_NOT_FROM_CLIENT => "Creature only", + EMOTE_FLAG_NOT_CASTING => "Not while casting", + EMOTE_FLAG_END_MOVEMENT => "Emote ends movement", + EMOTE_FLAG_INTERRUPT_ON_ATTACK => "Interrupt on attacking", + EMOTE_FLAG_ONLY_STILL => "Only while still", + EMOTE_FLAG_NOT_FLYING => "Not while flying" + ), + 'state' => ['[Oneshot]', '[Continuous State]', '[Continuous Emote]'] ), 'enchantment' => array( 'details' => "Detalles", diff --git a/localization/locale_frfr.php b/localization/locale_frfr.php index bb038851..b35ab648 100644 --- a/localization/locale_frfr.php +++ b/localization/locale_frfr.php @@ -627,7 +627,7 @@ $lang = array( SAI_ACTION_SET_FACTION => ['(%1$d)?Set faction of #target# to [faction=%7$d]:Reset faction of #target#;.', null], SAI_ACTION_MORPH_TO_ENTRY_OR_MODEL => ['(%7$d)?Reset apperance.:Take the appearance of;(%1$d)? [npc=%1$d].:;(%2$d)?[model npc=%2$d border=1 float=right][/model]:;', null], SAI_ACTION_SOUND => ['Play sound(%2$d)? to invoking player:;:[div float=right width=270px][sound=%1$d][/div]', 'Played by environment.'], - SAI_ACTION_PLAY_EMOTE => ['Emote [emote=%1$d] to #target#.', null], + SAI_ACTION_PLAY_EMOTE => ['(%1$d)?Emote [emote=%1$d] to #target#.: End Emote.;', null], SAI_ACTION_FAIL_QUEST => ['Fail [quest=%1$d] for #target#.', null], SAI_ACTION_OFFER_QUEST => ['(%2$d)?Add [quest=%1$d] to #target#\'s log:Offer [quest=%1$d] to #target#;.', null], SAI_ACTION_SET_REACT_STATE => ['#target# becomes %7$s.', null], @@ -638,7 +638,7 @@ $lang = array( SAI_ACTION_THREAT_SINGLE_PCT => ['Modify #target#\'s threat by %7$d%%.', null], SAI_ACTION_THREAT_ALL_PCT => ['Modify the threat of all targets by %7$d%%.', null], SAI_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS => ['Exploration event of [quest=%1$d] is completed for #target#.', null], - SAI_ACTION_SET_EMOTE_STATE => ['Continuously emote [emote=%1$d] to #target#.', null], + SAI_ACTION_SET_EMOTE_STATE => ['(%1$d)?Continuously emote [emote=%1$d] to #target#.:End emote state;', null], SAI_ACTION_SET_UNIT_FLAG => ['Set (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], SAI_ACTION_REMOVE_UNIT_FLAG => ['Unset (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], /* 20*/ SAI_ACTION_AUTO_ATTACK => ['(%1$d)?Start:Stop; auto attacking #target#.', null], @@ -919,12 +919,35 @@ $lang = array( ), 'emote' => array( 'notFound' => "[This Emote doesn't exist.]", - 'self' => "Vers vous-même", - 'target' => "Vers les autres avec une cible", - 'noTarget' => "Vers les autres sans cible", +// 'self' => "Vers vous-même", +// 'target' => "Vers les autres avec une cible", +// 'noTarget' => "Vers les autres sans cible", + 'targeted' => "[Used with target]", + 'untargeted' => "[Used without target]", 'isAnimated' => "Utilise une animation", + 'eventSound' => "[Event Sound]", 'aliases' => "Alias", 'noText' => "Cette émote n'a pas de texte.", + 'noCommand' => "[This Emote has no /-command. It can not be triggered.]", + 'flags' => array( + EMOTE_FLAG_ONLY_STANDING => "Only while standig", + EMOTE_FLAG_USE_MOUNT => "Emote applies to mount", + EMOTE_FLAG_NOT_CHANNELING => "Not while channeling", + EMOTE_FLAG_ANIM_TALK => "Talk anim - talk", + EMOTE_FLAG_ANIM_QUESTION => "Talk anim - question", + EMOTE_FLAG_ANIM_EXCLAIM => "Talk anim - exclamation", + EMOTE_FLAG_ANIM_SHOUT => "Talk anim - shout", + EMOTE_FLAG_NOT_SWIMMING => "Not while swimming", + EMOTE_FLAG_ANIM_LAUGH => "Talk anim - laugh", + EMOTE_FLAG_CAN_LIE_ON_GROUND => "Usable while sleeping or dead", + EMOTE_FLAG_NOT_FROM_CLIENT => "Creature only", + EMOTE_FLAG_NOT_CASTING => "Not while casting", + EMOTE_FLAG_END_MOVEMENT => "Emote ends movement", + EMOTE_FLAG_INTERRUPT_ON_ATTACK => "Interrupt on attacking", + EMOTE_FLAG_ONLY_STILL => "Only while still", + EMOTE_FLAG_NOT_FLYING => "Not while flying" + ), + 'state' => ['[Oneshot]', '[Continuous State]', '[Continuous Emote]'] ), 'enchantment' => array( 'details' => "En détail", diff --git a/localization/locale_ruru.php b/localization/locale_ruru.php index d8c13941..43acc4b5 100644 --- a/localization/locale_ruru.php +++ b/localization/locale_ruru.php @@ -627,7 +627,7 @@ $lang = array( SAI_ACTION_SET_FACTION => ['(%1$d)?Set faction of #target# to [faction=%7$d]:Reset faction of #target#;.', null], SAI_ACTION_MORPH_TO_ENTRY_OR_MODEL => ['(%7$d)?Reset apperance.:Take the appearance of;(%1$d)? [npc=%1$d].:;(%2$d)?[model npc=%2$d border=1 float=right][/model]:;', null], SAI_ACTION_SOUND => ['Play sound(%2$d)? to invoking player:;:[div float=right width=270px][sound=%1$d][/div]', 'Played by environment.'], - SAI_ACTION_PLAY_EMOTE => ['Emote [emote=%1$d] to #target#.', null], + SAI_ACTION_PLAY_EMOTE => ['(%1$d)?Emote [emote=%1$d] to #target#.: End Emote.;', null], SAI_ACTION_FAIL_QUEST => ['Fail [quest=%1$d] for #target#.', null], SAI_ACTION_OFFER_QUEST => ['(%2$d)?Add [quest=%1$d] to #target#\'s log:Offer [quest=%1$d] to #target#;.', null], SAI_ACTION_SET_REACT_STATE => ['#target# becomes %7$s.', null], @@ -638,7 +638,7 @@ $lang = array( SAI_ACTION_THREAT_SINGLE_PCT => ['Modify #target#\'s threat by %7$d%%.', null], SAI_ACTION_THREAT_ALL_PCT => ['Modify the threat of all targets by %7$d%%.', null], SAI_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS => ['Exploration event of [quest=%1$d] is completed for #target#.', null], - SAI_ACTION_SET_EMOTE_STATE => ['Continuously emote [emote=%1$d] to #target#.', null], + SAI_ACTION_SET_EMOTE_STATE => ['(%1$d)?Continuously emote [emote=%1$d] to #target#.:End emote state;', null], SAI_ACTION_SET_UNIT_FLAG => ['Set (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], SAI_ACTION_REMOVE_UNIT_FLAG => ['Unset (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], /* 20*/ SAI_ACTION_AUTO_ATTACK => ['(%1$d)?Start:Stop; auto attacking #target#.', null], @@ -919,12 +919,35 @@ $lang = array( ), 'emote' => array( 'notFound' => "[This Emote doesn't exist.]", - 'self' => "[To Yourself]", - 'target' => "[To others with a target]", - 'noTarget' => "[To others without a target]", +// 'self' => "[To Yourself]", +// 'target' => "[To others with a target]", +// 'noTarget' => "[To others without a target]", + 'targeted' => "[Used with target]", + 'untargeted' => "[Used without target]", 'isAnimated' => "[Uses an animation]", + 'eventSound' => "[Event Sound]", 'aliases' => "[Aliases]", 'noText' => "[This Emote has no text.]", + 'noCommand' => "[This Emote has no /-command. It can not be triggered.]", + 'flags' => array( + EMOTE_FLAG_ONLY_STANDING => "Only while standig", + EMOTE_FLAG_USE_MOUNT => "Emote applies to mount", + EMOTE_FLAG_NOT_CHANNELING => "Not while channeling", + EMOTE_FLAG_ANIM_TALK => "Talk anim - talk", + EMOTE_FLAG_ANIM_QUESTION => "Talk anim - question", + EMOTE_FLAG_ANIM_EXCLAIM => "Talk anim - exclamation", + EMOTE_FLAG_ANIM_SHOUT => "Talk anim - shout", + EMOTE_FLAG_NOT_SWIMMING => "Not while swimming", + EMOTE_FLAG_ANIM_LAUGH => "Talk anim - laugh", + EMOTE_FLAG_CAN_LIE_ON_GROUND => "Usable while sleeping or dead", + EMOTE_FLAG_NOT_FROM_CLIENT => "Creature only", + EMOTE_FLAG_NOT_CASTING => "Not while casting", + EMOTE_FLAG_END_MOVEMENT => "Emote ends movement", + EMOTE_FLAG_INTERRUPT_ON_ATTACK => "Interrupt on attacking", + EMOTE_FLAG_ONLY_STILL => "Only while still", + EMOTE_FLAG_NOT_FLYING => "Not while flying" + ), + 'state' => ['[Oneshot]', '[Continuous State]', '[Continuous Emote]'] ), 'enchantment' => array( 'details' => "Подробности", diff --git a/localization/locale_zhcn.php b/localization/locale_zhcn.php index 9cd19f77..d1a459d7 100644 --- a/localization/locale_zhcn.php +++ b/localization/locale_zhcn.php @@ -626,7 +626,7 @@ $lang = array( SAI_ACTION_SET_FACTION => ['(%1$d)?Set faction of #target# to [faction=%7$d]:Reset faction of #target#;.', null], SAI_ACTION_MORPH_TO_ENTRY_OR_MODEL => ['(%7$d)?Reset apperance.:Take the appearance of;(%1$d)? [npc=%1$d].:;(%2$d)?[model npc=%2$d border=1 float=right][/model]:;', null], SAI_ACTION_SOUND => ['Play sound(%2$d)? to invoking player:;:[div float=right width=270px][sound=%1$d][/div]', 'Played by environment.'], - SAI_ACTION_PLAY_EMOTE => ['Emote [emote=%1$d] to #target#.', null], + SAI_ACTION_PLAY_EMOTE => ['(%1$d)?Emote [emote=%1$d] to #target#.: End Emote.;', null], SAI_ACTION_FAIL_QUEST => ['Fail [quest=%1$d] for #target#.', null], SAI_ACTION_OFFER_QUEST => ['(%2$d)?Add [quest=%1$d] to #target#\'s log:Offer [quest=%1$d] to #target#;.', null], SAI_ACTION_SET_REACT_STATE => ['#target# becomes %7$s.', null], @@ -637,7 +637,7 @@ $lang = array( SAI_ACTION_THREAT_SINGLE_PCT => ['Modify #target#\'s threat by %7$d%%.', null], SAI_ACTION_THREAT_ALL_PCT => ['Modify the threat of all targets by %7$d%%.', null], SAI_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS => ['Exploration event of [quest=%1$d] is completed for #target#.', null], - SAI_ACTION_SET_EMOTE_STATE => ['Continuously emote [emote=%1$d] to #target#.', null], + SAI_ACTION_SET_EMOTE_STATE => ['(%1$d)?Continuously emote [emote=%1$d] to #target#.:End emote state;', null], SAI_ACTION_SET_UNIT_FLAG => ['Set (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], SAI_ACTION_REMOVE_UNIT_FLAG => ['Unset (%2$d)?UnitFlags2:UnitFlags; %7$s.', null], /* 20*/ SAI_ACTION_AUTO_ATTACK => ['(%1$d)?Start:Stop; auto attacking #target#.', null], @@ -918,12 +918,35 @@ $lang = array( ), 'emote' => array( 'notFound' => "这个表情不存在。", - 'self' => "对你自己", - 'target' => "对别人并且选择了目标", - 'noTarget' => "对别人并且不选择目标", +// 'self' => "对你自己", +// 'target' => "对别人并且选择了目标", +// 'noTarget' => "对别人并且不选择目标", + 'targeted' => "[Used with target]", + 'untargeted' => "[Used without target]", 'isAnimated' => "使用动画", + 'eventSound' => "[Event Sound]", 'aliases' => "别名", 'noText' => "这个表情没有文字。", + 'noCommand' => "[This Emote has no /-command. It can not be triggered.]", + 'flags' => array( + EMOTE_FLAG_ONLY_STANDING => "Only while standig", + EMOTE_FLAG_USE_MOUNT => "Emote applies to mount", + EMOTE_FLAG_NOT_CHANNELING => "Not while channeling", + EMOTE_FLAG_ANIM_TALK => "Talk anim - talk", + EMOTE_FLAG_ANIM_QUESTION => "Talk anim - question", + EMOTE_FLAG_ANIM_EXCLAIM => "Talk anim - exclamation", + EMOTE_FLAG_ANIM_SHOUT => "Talk anim - shout", + EMOTE_FLAG_NOT_SWIMMING => "Not while swimming", + EMOTE_FLAG_ANIM_LAUGH => "Talk anim - laugh", + EMOTE_FLAG_CAN_LIE_ON_GROUND => "Usable while sleeping or dead", + EMOTE_FLAG_NOT_FROM_CLIENT => "Creature only", + EMOTE_FLAG_NOT_CASTING => "Not while casting", + EMOTE_FLAG_END_MOVEMENT => "Emote ends movement", + EMOTE_FLAG_INTERRUPT_ON_ATTACK => "Interrupt on attacking", + EMOTE_FLAG_ONLY_STILL => "Only while still", + EMOTE_FLAG_NOT_FLYING => "Not while flying" + ), + 'state' => ['[Oneshot]', '[Continuous State]', '[Continuous Emote]'] ), 'enchantment' => array( 'details' => "细节", diff --git a/pages/emote.php b/pages/emote.php index 44a8cb29..3405447a 100644 --- a/pages/emote.php +++ b/pages/emote.php @@ -19,6 +19,11 @@ class EmotePage extends GenericPage public function __construct($pageCall, $id) { + /* + * id > 0: player text emote + * id < 0: creature emote + */ + parent::__construct($pageCall, $id); $this->typeId = intVal($id); @@ -46,15 +51,45 @@ class EmotePage extends GenericPage $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); // has Animation - if ($this->subject->getField('isAnimated')) + if ($this->subject->getField('isAnimated') && !$this->subject->getField('stateParam')) + { $infobox[] = Lang::emote('isAnimated'); + // anim state + $state = Lang::emote('state', $this->subject->getField('state')); + if ($this->subject->getField('state') == 1) + $state .= Lang::main('colon').Lang::unit('bytes1', 0, $this->subject->getField('stateParam')); + $infobox[] = $state; + } + + if (User::isInGroup(U_GROUP_STAFF | U_GROUP_TESTER)) + { + // player emote: point to internal data + if ($_ = $this->subject->getField('parentEmote')) + { + $this->extendGlobalIds(Type::EMOTE, $_); + $infobox[] = '[emote='.$_.']'; + } + + if ($flags = $this->subject->getField('flags')) + { + $box = Lang::game('flags').Lang::main('colon').'[ul]'; + foreach (Lang::emote('flags') as $bit => $str) + if ($bit & $flags) + $box .= '[li][tooltip name=hint-'.$bit.']'.Util::asHex($bit).'[/tooltip][span class=tip tooltip=hint-'.$bit.']'.$str.'[/span][/li]'; + $infobox[] = $box.'[/ul]'; + } + } + /****************/ /* Main Content */ /****************/ $text = ''; - if ($aliasses = DB::Aowow()->selectCol('SELECT command FROM ?_emotes_aliasses WHERE id = ?d AND locales & ?d', $this->typeId, 1 << User::$localeId)) + + if ($this->subject->getField('cuFlags') & EMOTE_CU_MISSING_CMD) + $text .= Lang::emote('noCommand').'[br][br]'; + else if ($aliasses = DB::Aowow()->selectCol('SELECT command FROM ?_emotes_aliasses WHERE id = ?d AND locales & ?d', $this->typeId, 1 << User::$localeId)) { $text .= '[h3]'.Lang::emote('aliases').'[/h3][ul]'; foreach ($aliasses as $a) @@ -63,21 +98,43 @@ class EmotePage extends GenericPage $text .= '[/ul][br][br]'; } - $texts = []; - if ($_ = $this->subject->getField('self', true)) - $texts[Lang::emote('self')] = $_; + $target = $noTarget = []; + if ($_ = $this->subject->getField('extToExt', true)) + $target[] = $this->prepare($_); + if ($_ = $this->subject->getField('extToMe', true)) + $target[] = $this->prepare($_); + if ($_ = $this->subject->getField('meToExt', true)) + $target[] = $this->prepare($_); + if ($_ = $this->subject->getField('extToNone', true)) + $noTarget[] = $this->prepare($_); + if ($_ = $this->subject->getField('meToNone', true)) + $noTarget[] =$this->prepare($_); - if ($_ = $this->subject->getField('target', true)) - $texts[Lang::emote('target')] = $_; - - if ($_ = $this->subject->getField('noTarget', true)) - $texts[Lang::emote('noTarget')] = $_; - - if (!$texts) + if (!$target && !$noTarget) $text .= '[div][i class=q0]'.Lang::emote('noText').'[/i][/div]'; - else - foreach ($texts as $h => $t) - $text .= '[pad][b]'.$h.'[/b][ul][li][span class=s4]'.preg_replace('/%\d?\$?s/', '<'.Util::ucFirst(Lang::main('name')).'>', $t).'[/span][/li][/ul]'; + + if ($target) + { + $text .= '[pad][b]'.Lang::emote('targeted').'[/b][ul]'; + foreach ($target as $t) + $text .= '[li][span class=s4]'.$t.'[/span][/li]'; + $text .= '[/ul]'; + } + + if ($noTarget) + { + $text .= '[pad][b]'.Lang::emote('untargeted').'[/b][ul]'; + foreach ($noTarget as $t) + $text .= '[li][span class=s4]'.$t.'[/span][/li]'; + $text .= '[/ul]'; + } + + // event sound + if ($_ = $this->subject->getField('soundId')) + { + $this->extendGlobalIds(Type::SOUND, $_); + $text .= '[h3]'.Lang::emote('eventSound').'[/h3][sound='.$_.']'; + } $this->extraText = $text; $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null; @@ -102,7 +159,7 @@ class EmotePage extends GenericPage $this->extendGlobalData($acv->getJsGlobals()); // tab: sound - if ($em = DB::Aowow()->select('SELECT soundId AS ARRAY_KEY, BIT_OR(1 << (raceId - 1)) AS raceMask, BIT_OR(1 << (gender - 1)) AS gender FROM aowow_emotes_sounds WHERE emoteId = ?d GROUP BY soundId', $this->typeId)) + if ($em = DB::Aowow()->select('SELECT soundId AS ARRAY_KEY, BIT_OR(1 << (raceId - 1)) AS raceMask, BIT_OR(1 << (gender - 1)) AS gender FROM ?_emotes_sounds WHERE -emoteId = ?d GROUP BY soundId', $this->typeId > 0 ? $this->subject->getField('parentEmote') : $this->typeId)) { $sounds = new SoundList(array(['id', array_keys($em)])); if (!$sounds->error) @@ -123,6 +180,12 @@ class EmotePage extends GenericPage } } } + + private function prepare(string $emote) : string + { + $emote = Util::parseHtmlText($emote, true); + return preg_replace('/%\d?\$?s/', '<'.Util::ucFirst(Lang::main('name')).'>', $emote); + } } ?> diff --git a/pages/emotes.php b/pages/emotes.php index a2f12b8d..bc930436 100644 --- a/pages/emotes.php +++ b/pages/emotes.php @@ -25,8 +25,12 @@ class EmotesPage extends GenericPage protected function generateContent() { + $cnd = [CFG_SQL_LIMIT_NONE]; + if (!User::isInGroup(U_GROUP_STAFF)) + $cnd[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; + $tabData = array( - 'data' => array_values((new EmoteList())->getListviewData()), + 'data' => array_values((new EmoteList($cnd))->getListviewData()), 'name' => Util::ucFirst(Lang::game('emotes')) ); diff --git a/pages/search.php b/pages/search.php index 70615034..203ff3f0 100644 --- a/pages/search.php +++ b/pages/search.php @@ -1315,7 +1315,7 @@ class SearchPage extends GenericPage private function _searchEmote($cndBase) // 25 Emotes $searchMask & 0x2000000 { - $cnd = array_merge($cndBase, [$this->createLookup(['cmd', 'self_loc'.User::$localeId, 'target_loc'.User::$localeId, 'noTarget_loc'.User::$localeId])]); + $cnd = array_merge($cndBase, [$this->createLookup(['cmd', 'meToExt_loc'.User::$localeId, 'meToNone_loc'.User::$localeId, 'extToMe_loc'.User::$localeId, 'extToExt_loc'.User::$localeId, 'extToNone_loc'.User::$localeId])]); $emote = new EmoteList($cnd); if ($data = $emote->getListviewData()) diff --git a/pages/sound.php b/pages/sound.php index dfc2ba8e..873e2e02 100644 --- a/pages/sound.php +++ b/pages/sound.php @@ -258,7 +258,7 @@ class SoundPage extends GenericPage // tab: Emotes (EmotesTextSound (containing emote audio)) - if ($em = DB::Aowow()->selectCol('SELECT emoteId FROM ?_emotes_sounds WHERE soundId = ?d GROUP BY emoteId', $this->typeId)) + if ($em = DB::Aowow()->selectCol('SELECT emoteId FROM ?_emotes_sounds WHERE soundId = ?d GROUP BY emoteId UNION SELECT id FROM ?_emotes WHERE soundId = ?d', $this->typeId, $this->typeId)) { $races = new EmoteList(array(['id', $em])); if (!$races->error) diff --git a/setup/db_structure.sql b/setup/db_structure.sql index 6b7cc1fd..7c3e5bd6 100644 --- a/setup/db_structure.sql +++ b/setup/db_structure.sql @@ -675,28 +675,45 @@ DROP TABLE IF EXISTS `aowow_emotes`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `aowow_emotes` ( - `id` smallint(5) unsigned NOT NULL, - `cmd` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL, - `isAnimated` tinyint(1) unsigned NOT NULL, - `cuFlags` int(10) unsigned NOT NULL, - `target_loc0` varchar(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `target_loc2` varchar(70) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `target_loc3` varchar(95) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `target_loc4` varchar(95) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `target_loc6` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `target_loc8` varchar(70) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `noTarget_loc0` varchar(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `noTarget_loc2` varchar(110) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `noTarget_loc3` varchar(85) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `noTarget_loc4` varchar(85) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `noTarget_loc6` varchar(75) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `noTarget_loc8` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `self_loc0` varchar(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `self_loc2` varchar(115) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `self_loc3` varchar(85) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `self_loc4` varchar(85) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `self_loc6` varchar(75) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `self_loc8` varchar(70) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `id` SMALLINT(5) SIGNED NOT NULL, + `cmd` VARCHAR(35) COLLATE utf8mb4_unicode_ci NOT NULL, + `isAnimated` TINYINT(1) UNSIGNED NOT NULL, + `flags` SMALLINT(5) UNSIGNED NOT NULL, + `parentEmote` SMALLINT(5) UNSIGNED NOT NULL, + `soundId` SMALLINT(5) SIGNED NOT NULL, + `state` TINYINT(1) UNSIGNED NOT NULL, + `stateParam` TINYINT(1) UNSIGNED NOT NULL, + `cuFlags` INT(10) UNSIGNED NOT NULL, + `extToExt_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; @@ -3186,7 +3203,7 @@ UNLOCK TABLES; LOCK TABLES `aowow_dbversion` WRITE; /*!40000 ALTER TABLE `aowow_dbversion` DISABLE KEYS */; -INSERT INTO `aowow_dbversion` VALUES (1684849476,0,NULL,NULL); +INSERT INTO `aowow_dbversion` VALUES (1691940878,0,NULL,NULL); /*!40000 ALTER TABLE `aowow_dbversion` ENABLE KEYS */; UNLOCK TABLES; diff --git a/setup/tools/dbc.class.php b/setup/tools/dbc.class.php index 740e212a..34318789 100644 --- a/setup/tools/dbc.class.php +++ b/setup/tools/dbc.class.php @@ -62,8 +62,8 @@ class DBC 'durabilitycosts' => 'niiiiiiiiixiiiiiiiiiiixiiiixix', 'durabilityquality' => 'nf', 'dungeonencounter' => 'niiiisxsssxsxsxxxxxxxxx', - 'emotes' => 'nxixxxx', - 'emotestext' => 'nsiixxxixixxxxxxxxx', + 'emotes' => 'nsiiiii', + 'emotestext' => 'nsiiiixixixiixxixxx', 'emotestextdata' => 'nsxsssxsxsxxxxxxxx', 'emotestextsound' => 'niiii', 'faction' => 'niiiiiiiiiiiiiixxxiffixsxsssxsxsxxxxxxxxxxxxxxxxxxxxxxxxx', @@ -167,8 +167,8 @@ class DBC 'durabilitycosts' => 'id,w0,w1,w2,w3,w4,w5,w6,w7,w8,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,a1,a2,a3,a4,a6', 'durabilityquality' => 'id,mod', 'dungeonencounter' => 'id,map,mode,order,bit,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8', - 'emotes' => 'id,animationId', - 'emotestext' => 'id,command,emoteId,targetId,noTargetId,selfId', + 'emotes' => 'id,name,animationId,flags,state,stateParam,soundId', + 'emotestext' => 'id,command,emoteId,etd0,etd1,etd2,etd4,etd6,etd8,etd9,etd12', 'emotestextsound' => 'id,emotesTextId,raceId,gender,soundId', 'emotestextdata' => 'id,text_loc0,text_loc2,text_loc3,text_loc4,text_loc6,text_loc8', 'faction' => 'id,repIdx,baseRepRaceMask1,baseRepRaceMask2,baseRepRaceMask3,baseRepRaceMask4,baseRepClassMask1,baseRepClassMask2,baseRepClassMask3,baseRepClassMask4,baseRepValue1,baseRepValue2,baseRepValue3,baseRepValue4,repFlags1,parentFaction,spilloverRateIn,spilloverRateOut,spilloverMaxRank,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8', diff --git a/setup/tools/sqlgen/emotes.func.php b/setup/tools/sqlgen/emotes.func.php index 6c6ca436..b9aecc8c 100644 --- a/setup/tools/sqlgen/emotes.func.php +++ b/setup/tools/sqlgen/emotes.func.php @@ -13,6 +13,71 @@ SqlGen::register(new class extends SetupScript protected $dbcSourceFiles = ['emotes', 'emotestext', 'emotestextdata']; + private $textData = []; + + private function mergeGenderedStrings(int $maleTextId, int $femaleTextId, int $locale) : string + { + $maleText = $this->textData[$maleTextId][$locale] ?? ''; + $femaleText = $this->textData[$femaleTextId][$locale] ?? ''; + + if (!$maleText && !$femaleText) + return ''; + else if (!$maleText) + return $femaleText; + else if (!$femaleText) + return $maleText; + else if ($maleText == $femaleText) + return $maleText; + + $front = $back = []; + + $m = explode(' ', $maleText); + $f = explode(' ', $femaleText); + $n = max(count($m), count($f)); + + // advance from left until diff -> store common string + // advance from right until diff -> store common string + // merge leftovers as gendered switch and recombine string + + $i = 0; + for (; $i < $n; $i++) + { + if (!isset($m[$i]) || !isset($f[$i])) + break; + else if ($m[$i] != $f[$i]) + break; + else + $front[] = $m[$i]; + } + + if ($i + 1 == $n) // loop completed all elements + return implode(' ', $m); + + $m = array_reverse($m); + $f = array_reverse($f); + + $j = 0; + for (; $j < $n - $i; $j++) + { + if (!isset($m[$j]) || !isset($f[$j])) + break; + else if ($m[$j] != $f[$j]) + break; + else + $back[] = $m[$j]; + } + + $m = array_reverse($m); + $f = array_reverse($f); + + $midM = array_slice($m, $i, count($m) - $i - $j); + $midF = array_slice($f, $i, count($f) - $i - $j); + + $mid = '$g' . implode(' ', $midM ?? []) . ':' . implode(' ', $midF ?? []) . ';'; + + return implode(' ', array_merge($front, [$mid], array_reverse($back))); + } + public function generate(array $ids = []) : bool { /**********/ @@ -23,8 +88,13 @@ SqlGen::register(new class extends SetupScript $allOK = true; $locPath = []; + DB::Aowow()->query('TRUNCATE ?_emotes'); DB::Aowow()->query('TRUNCATE ?_emotes_aliasses'); + /*********************/ + /* Player controlled */ + /*********************/ + foreach (CLISetup::$localeIds as $lId) { foreach (CLISetup::$expectedPaths as $xp => $locId) @@ -47,28 +117,43 @@ SqlGen::register(new class extends SetupScript $allOK = false; } - $_= DB::Aowow()->query('REPLACE INTO ?_emotes SELECT - et.id, - LOWER(et.command), - IF(e.animationId, 1, 0), - 0, -- cuFlags - etdT.text_loc0, etdT.text_loc2, etdT.text_loc3, etdT.text_loc4, etdT.text_loc6, etdT.text_loc8, - etdNT.text_loc0, etdNT.text_loc2, etdNT.text_loc3, etdNT.text_loc4, etdNT.text_loc6, etdNT.text_loc8, - etdS.text_loc0, etdS.text_loc2, etdS.text_loc3, etdS.text_loc4, etdS.text_loc6, etdS.text_loc8 - FROM - dbc_emotestext et - LEFT JOIN - dbc_emotes e ON e.id = et.emoteId - LEFT JOIN - dbc_emotestextdata etdT ON etdT.id = et.targetId - LEFT JOIN - dbc_emotestextdata etdNT ON etdNT.id = et.noTargetId - LEFT JOIN - dbc_emotestextdata etdS ON etdS.id = et.selfId' - ); + /* EmotesText Data offsets + gender seenBy hasTarget mergedWith example + 0 male others yes 8 %s raises fist in anger at %s. + 1 male self yes 9 %s raises fist in anger at you. + 2 self self yes You raise your fist in anger at %s. + 4 male others no 12 %s raises fist in anger. + 6 self self no You raise your fist in anger. + 8 female others yes 0 - + 9 female self yes 1 - + 12 female others no 4 - + */ - if (!$_) - $allOK = false; + $this->textData = DB::Aowow()->select('SELECT id AS ARRAY_KEY, text_loc0 AS "0", text_loc2 AS "2", text_loc3 AS "3", text_loc4 AS "4", text_loc6 AS "6", text_loc8 AS "8" FROM dbc_emotestextdata'); + + $texts = DB::Aowow()->select('SELECT et.id AS ARRAY_KEY, LOWER(command) AS `cmd`, IF(e.animationId, 1, 0) AS `anim`, -emoteId AS "parent", e.soundId, etd0, etd1, etd2, etd4, etd6, etd8, etd9, etd12 FROM dbc_emotestext et LEFT JOIN dbc_emotes e ON e.id = et.emoteId'); + foreach ($texts AS $id => $t) + { + DB::Aowow()->query( + 'INSERT INTO ?_emotes ( + `id`, `cmd`, `isAnimated`, `parentEmote`, `soundId`, + `extToExt_loc0`, `extToMe_loc0`, `meToExt_loc0`, `extToNone_loc0`, `meToNone_loc0`, + `extToExt_loc2`, `extToMe_loc2`, `meToExt_loc2`, `extToNone_loc2`, `meToNone_loc2`, + `extToExt_loc3`, `extToMe_loc3`, `meToExt_loc3`, `extToNone_loc3`, `meToNone_loc3`, + `extToExt_loc4`, `extToMe_loc4`, `meToExt_loc4`, `extToNone_loc4`, `meToNone_loc4`, + `extToExt_loc6`, `extToMe_loc6`, `meToExt_loc6`, `extToNone_loc6`, `meToNone_loc6`, + `extToExt_loc8`, `extToMe_loc8`, `meToExt_loc8`, `extToNone_loc8`, `meToNone_loc8`) + VALUES + (?d, ?, ?d, ?d, ?d, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + $id, $t['cmd'], $t['anim'], $t['parent'], $t['soundId'], + $this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_EN), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_EN), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_EN), $this->textData[$t['etd2']][LOCALE_EN] ?? '', $this->textData[$t['etd6']][LOCALE_EN] ?? '', + $this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_FR), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_FR), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_FR), $this->textData[$t['etd2']][LOCALE_FR] ?? '', $this->textData[$t['etd6']][LOCALE_FR] ?? '', + $this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_DE), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_DE), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_DE), $this->textData[$t['etd2']][LOCALE_DE] ?? '', $this->textData[$t['etd6']][LOCALE_DE] ?? '', + $this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_CN), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_CN), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_CN), $this->textData[$t['etd2']][LOCALE_CN] ?? '', $this->textData[$t['etd6']][LOCALE_CN] ?? '', + $this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_ES), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_ES), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_ES), $this->textData[$t['etd2']][LOCALE_ES] ?? '', $this->textData[$t['etd6']][LOCALE_ES] ?? '', + $this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_RU), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_RU), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_RU), $this->textData[$t['etd2']][LOCALE_RU] ?? '', $this->textData[$t['etd6']][LOCALE_RU] ?? '' + ); + } // i have no idea, how the indexing in this file works. // sometimes the \d+ after EMOTE is the emoteTextId, but not nearly often enough @@ -78,23 +163,31 @@ SqlGen::register(new class extends SetupScript if (preg_match('/^EMOTE(\d+)_CMD\d+\s=\s\"\/([^"]+)\";$/', $line, $m)) $aliasses[$m[1]][] = [$lId, $m[2]]; - $emotes = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, cmd FROM ?_emotes'); foreach($emotes as $eId => $cmd) { - foreach ($aliasses as $gsId => $data) + foreach ($aliasses as $data) { if (in_array($cmd, array_column($data, 1))) { foreach ($data as $d) DB::Aowow()->query('INSERT IGNORE INTO ?_emotes_aliasses VALUES (?d, ?d, ?) ON DUPLICATE KEY UPDATE locales = locales | ?d', $eId, (1 << $d[0]), strtolower($d[1]), (1 << $d[0])); - break; + continue 2; } } + + DB::Aowow()->query('UPDATE ?_emotes SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', CUSTOM_EXCLUDE_FOR_LISTVIEW | EMOTE_CU_MISSING_CMD, $eId); } + /*********************/ + /* Server controlled */ + /*********************/ + + DB::Aowow()->query('INSERT INTO ?_emotes (`id`, `cmd`, `flags`, `isAnimated`, `parentEmote`, `soundId`, `state`, `stateParam`, `cuFlags`) SELECT -`id`, `name`, `flags`, IF(`animationId`, 1, 0), 0, `soundId`, `state`, `stateParam`, ?d FROM dbc_emotes WHERE 1', CUSTOM_EXCLUDE_FOR_LISTVIEW); + + $this->reapplyCCFlags('emotes', Type::EMOTE); return $allOK; diff --git a/setup/updates/1691940877_01.sql b/setup/updates/1691940877_01.sql new file mode 100644 index 00000000..1d298b91 --- /dev/null +++ b/setup/updates/1691940877_01.sql @@ -0,0 +1,49 @@ +DROP TABLE IF EXISTS `dbc_emotes`; +DROP TABLE IF EXISTS `dbc_emotestext`; +DROP TABLE IF EXISTS `dbc_emotestextdata`; + +DROP TABLE IF EXISTS `aowow_emotes`; +CREATE TABLE `aowow_emotes` ( + `id` SMALLINT(5) SIGNED NOT NULL, + `cmd` VARCHAR(35) COLLATE utf8mb4_unicode_ci NOT NULL, + `isAnimated` TINYINT(1) UNSIGNED NOT NULL, + `flags` SMALLINT(5) UNSIGNED NOT NULL, + `parentEmote` SMALLINT(5) UNSIGNED NOT NULL, + `soundId` SMALLINT(5) SIGNED NOT NULL, + `state` TINYINT(1) UNSIGNED NOT NULL, + `stateParam` TINYINT(1) UNSIGNED NOT NULL, + `cuFlags` INT(10) UNSIGNED NOT NULL, + `extToExt_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToExt_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToMe_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToExt_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `extToNone_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meToNone_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' emotes');