Thread Tools Display Modes
02-15-12, 08:37 AM   #1
Savaena
Warcrafter
 
Savaena's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 4
Exclamation Need Lua Coding Help!

Hello!

I'm very new to lua and have been researching the language to learn how to make my first addon, called Blush Response. Please bear with me, as I don't know a lot of the vocabulary programmers use so I might mess up. I'm running into some trouble and was hoping an expert might help me a bit. (I realize there was another addon similar to this called Emote Reply, but it is severely out of date, and I am writing this addon from scratch.)

The goal of my addon is to:

1. Trigger an emote response when a player targets you and emotes at you.
2. Make the triggered response fire after a 2-3 second delay, like a timer.
3. Trigger a /shrug or /ponder emote when a member of the opposite faction targets you and uses custom emotes. ie "%N makes strange gestures."
4. Not respond at all when a custom emote is used by another player while targeting you.
5. Allow you to add custom trigger/reply emotes through an in-game UI Frame.
6. Eventually allow random responses either through emote or /say.
7. Eventually allow profiling, especially for custom emotes.

My current issue is that I'm stuck at the event handler section of the lua file, specifically where I want to tell my addon to reply with an emote, where the ????? are. Here is what I have so far:


function BlushResponseVersion(self)
print("They're just questions, Leon. In answer to your query, they're written down for me. It's a test, designed to provoke an emotional response... Shall we continue? --- Addon: Blush Response v. 1.0 successfully loaded!");
end

function BlushResponse:OnEvent(event)
if event == "CHAT_MSG_TEXT_EMOTE" then
DoEmote(?????);
elseif event == "CHAT_MSG_EMOTE" then
DoEmote(?????);
end
end


So my question here is, what string should go there, and where/how do I add the emote lists for replies and triggers? Any help would be super appreciated. Thanks!

Last edited by Savaena : 02-16-12 at 08:59 AM. Reason: added desires to the goal of the addon
  Reply With Quote
02-15-12, 10:20 AM   #2
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,326
DoEmote() takes in an emote token string that represents the emote you want to send. Here is a list of tokens taken from ChatFrame.lua line 357.

Code:
-- These are text emote tokens - add new ones at the bottom of the list!
EMOTE1_TOKEN = "AGREE";
EMOTE2_TOKEN = "AMAZE";
EMOTE3_TOKEN = "ANGRY";
EMOTE4_TOKEN = "APOLOGIZE";
EMOTE5_TOKEN = "APPLAUD";
EMOTE6_TOKEN = "BASHFUL";
EMOTE7_TOKEN = "BECKON";
EMOTE8_TOKEN = "BEG";
EMOTE9_TOKEN = "BITE";
EMOTE10_TOKEN = "BLEED";
EMOTE11_TOKEN = "BLINK";
EMOTE12_TOKEN = "BLUSH";
EMOTE13_TOKEN = "BONK";
EMOTE14_TOKEN = "BORED";
EMOTE15_TOKEN = "BOUNCE";
EMOTE16_TOKEN = "BRB";
EMOTE17_TOKEN = "BOW";
EMOTE18_TOKEN = "BURP";
EMOTE19_TOKEN = "BYE";
EMOTE20_TOKEN = "CACKLE";
EMOTE21_TOKEN = "CHEER";
EMOTE22_TOKEN = "CHICKEN";
EMOTE23_TOKEN = "CHUCKLE";
EMOTE24_TOKEN = "CLAP";
EMOTE25_TOKEN = "CONFUSED";
EMOTE26_TOKEN = "CONGRATULATE";
EMOTE27_TOKEN = "UNUSED";
EMOTE28_TOKEN = "COUGH";
EMOTE29_TOKEN = "COWER";
EMOTE30_TOKEN = "CRACK";
EMOTE31_TOKEN = "CRINGE";
EMOTE32_TOKEN = "CRY";
EMOTE33_TOKEN = "CURIOUS";
EMOTE34_TOKEN = "CURTSEY";
EMOTE35_TOKEN = "DANCE";
EMOTE36_TOKEN = "DRINK";
EMOTE37_TOKEN = "DROOL";
EMOTE38_TOKEN = "EAT";
EMOTE39_TOKEN = "EYE";
EMOTE40_TOKEN = "FART";
EMOTE41_TOKEN = "FIDGET";
EMOTE42_TOKEN = "FLEX";
EMOTE43_TOKEN = "FROWN";
EMOTE44_TOKEN = "GASP";
EMOTE45_TOKEN = "GAZE";
EMOTE46_TOKEN = "GIGGLE";
EMOTE47_TOKEN = "GLARE";
EMOTE48_TOKEN = "GLOAT";
EMOTE49_TOKEN = "GREET";
EMOTE50_TOKEN = "GRIN";
EMOTE51_TOKEN = "GROAN";
EMOTE52_TOKEN = "GROVEL";
EMOTE53_TOKEN = "GUFFAW";
EMOTE54_TOKEN = "HAIL";
EMOTE55_TOKEN = "HAPPY";
EMOTE56_TOKEN = "HELLO";
EMOTE57_TOKEN = "HUG";
EMOTE58_TOKEN = "HUNGRY";
EMOTE59_TOKEN = "KISS";
EMOTE60_TOKEN = "KNEEL";
EMOTE61_TOKEN = "LAUGH";
EMOTE62_TOKEN = "LAYDOWN";
EMOTE63_TOKEN = "MASSAGE";
EMOTE64_TOKEN = "MOAN";
EMOTE65_TOKEN = "MOON";
EMOTE66_TOKEN = "MOURN";
EMOTE67_TOKEN = "NO";
EMOTE68_TOKEN = "NOD";
EMOTE69_TOKEN = "NOSEPICK";
EMOTE70_TOKEN = "PANIC";
EMOTE71_TOKEN = "PEER";
EMOTE72_TOKEN = "PLEAD";
EMOTE73_TOKEN = "POINT";
EMOTE74_TOKEN = "POKE";
EMOTE75_TOKEN = "PRAY";
EMOTE76_TOKEN = "ROAR";
EMOTE77_TOKEN = "ROFL";
EMOTE78_TOKEN = "RUDE";
EMOTE79_TOKEN = "SALUTE";
EMOTE80_TOKEN = "SCRATCH";
EMOTE81_TOKEN = "SEXY";
EMOTE82_TOKEN = "SHAKE";
EMOTE83_TOKEN = "SHOUT";
EMOTE84_TOKEN = "SHRUG";
EMOTE85_TOKEN = "SHY";
EMOTE86_TOKEN = "SIGH";
EMOTE87_TOKEN = "SIT";
EMOTE88_TOKEN = "SLEEP";
EMOTE89_TOKEN = "SNARL";
EMOTE90_TOKEN = "SPIT";
EMOTE91_TOKEN = "STARE";
EMOTE92_TOKEN = "SURPRISED";
EMOTE93_TOKEN = "SURRENDER";
EMOTE94_TOKEN = "TALK";
EMOTE95_TOKEN = "TALKEX";
EMOTE96_TOKEN = "TALKQ";
EMOTE97_TOKEN = "TAP";
EMOTE98_TOKEN = "THANK";
EMOTE99_TOKEN = "THREATEN";
EMOTE100_TOKEN = "TIRED";
EMOTE101_TOKEN = "VICTORY";
EMOTE102_TOKEN = "WAVE";
EMOTE103_TOKEN = "WELCOME";
EMOTE104_TOKEN = "WHINE";
EMOTE105_TOKEN = "WHISTLE";
EMOTE106_TOKEN = "WORK";
EMOTE107_TOKEN = "YAWN";
EMOTE108_TOKEN = "BOGGLE";
EMOTE109_TOKEN = "CALM";
EMOTE110_TOKEN = "COLD";
EMOTE111_TOKEN = "COMFORT";
EMOTE112_TOKEN = "CUDDLE";
EMOTE113_TOKEN = "DUCK";
EMOTE114_TOKEN = "INSULT";
EMOTE115_TOKEN = "INTRODUCE";
EMOTE116_TOKEN = "JK";
EMOTE117_TOKEN = "LICK";
EMOTE118_TOKEN = "LISTEN";
EMOTE119_TOKEN = "LOST";
EMOTE120_TOKEN = "MOCK";
EMOTE121_TOKEN = "PONDER";
EMOTE122_TOKEN = "POUNCE";
EMOTE123_TOKEN = "PRAISE";
EMOTE124_TOKEN = "PURR";
EMOTE125_TOKEN = "PUZZLE";
EMOTE126_TOKEN = "RAISE";
EMOTE127_TOKEN = "READY";
EMOTE128_TOKEN = "SHIMMY";
EMOTE129_TOKEN = "SHIVER";
EMOTE130_TOKEN = "SHOO";
EMOTE131_TOKEN = "SLAP";
EMOTE132_TOKEN = "SMIRK";
EMOTE133_TOKEN = "SNIFF";
EMOTE134_TOKEN = "SNUB";
EMOTE135_TOKEN = "SOOTHE";
EMOTE136_TOKEN = "STINK";
EMOTE137_TOKEN = "TAUNT";
EMOTE138_TOKEN = "TEASE";
EMOTE139_TOKEN = "THIRSTY";
EMOTE140_TOKEN = "VETO";
EMOTE141_TOKEN = "SNICKER";
EMOTE142_TOKEN = "TICKLE";
EMOTE143_TOKEN = "STAND";
EMOTE144_TOKEN = "VIOLIN";
EMOTE145_TOKEN = "SMILE";
EMOTE146_TOKEN = "RASP";
EMOTE147_TOKEN = "GROWL";
EMOTE148_TOKEN = "BARK";
EMOTE149_TOKEN = "PITY";
EMOTE150_TOKEN = "SCARED";
EMOTE151_TOKEN = "FLOP";
EMOTE152_TOKEN = "LOVE";
EMOTE153_TOKEN = "MOO";
EMOTE154_TOKEN = "COMMEND";
EMOTE155_TOKEN = "TRAIN";
EMOTE156_TOKEN = "HELPME";
EMOTE157_TOKEN = "INCOMING";
EMOTE158_TOKEN = "OPENFIRE";
EMOTE159_TOKEN = "CHARGE";
EMOTE160_TOKEN = "FLEE";
EMOTE161_TOKEN = "ATTACKMYTARGET";
EMOTE162_TOKEN = "OOM";
EMOTE163_TOKEN = "FOLLOW";
EMOTE164_TOKEN = "WAIT";
EMOTE165_TOKEN = "FLIRT";
EMOTE166_TOKEN = "HEALME";
EMOTE167_TOKEN = "JOKE";
EMOTE168_TOKEN = "WINK";
EMOTE169_TOKEN = "PAT";
EMOTE170_TOKEN = "GOLFCLAP";
EMOTE171_TOKEN = "MOUNTSPECIAL";
EMOTE304_TOKEN = "INCOMING";
EMOTE306_TOKEN = "FLEE";
EMOTE368_TOKEN = "BLAME"
EMOTE369_TOKEN = "BLANK"
EMOTE370_TOKEN = "BRANDISH"
EMOTE371_TOKEN = "BREATH"
EMOTE372_TOKEN = "DISAGREE"
EMOTE373_TOKEN = "DOUBT"
EMOTE374_TOKEN = "EMBARRASS"
EMOTE375_TOKEN = "ENCOURAGE"
EMOTE376_TOKEN = "ENEMY"
EMOTE377_TOKEN = "EYEBROW"
EMOTE380_TOKEN = "HIGHFIVE"
EMOTE381_TOKEN = "ABSENT"
EMOTE382_TOKEN = "ARM"
EMOTE383_TOKEN = "AWE"
EMOTE384_TOKEN = "BACKPACK"
EMOTE385_TOKEN = "BADFEELING"
EMOTE386_TOKEN = "CHALLENGE"
EMOTE387_TOKEN = "CHUG"
EMOTE389_TOKEN = "DING"
EMOTE390_TOKEN = "FACEPALM"
EMOTE391_TOKEN = "FAINT"
EMOTE392_TOKEN = "GO"
EMOTE393_TOKEN = "GOING"
EMOTE394_TOKEN = "GLOWER"
EMOTE395_TOKEN = "HEADACHE"
EMOTE396_TOKEN = "HICCUP"
EMOTE398_TOKEN = "HISS"
EMOTE399_TOKEN = "HOLDHAND"
EMOTE401_TOKEN = "HURRY"
EMOTE402_TOKEN = "IDEA"
EMOTE403_TOKEN = "JEALOUS"
EMOTE404_TOKEN = "LUCK"
EMOTE405_TOKEN = "MAP"
EMOTE406_TOKEN = "MERCY"
EMOTE407_TOKEN = "MUTTER"
EMOTE408_TOKEN = "NERVOUS"
EMOTE409_TOKEN = "OFFER"
EMOTE410_TOKEN = "PET"
EMOTE411_TOKEN = "PINCH"
EMOTE413_TOKEN = "PROUD"
EMOTE414_TOKEN = "PROMISE"
EMOTE415_TOKEN = "PULSE"
EMOTE416_TOKEN = "PUNCH"
EMOTE417_TOKEN = "POUT"
EMOTE418_TOKEN = "REGRET"
EMOTE420_TOKEN = "REVENGE"
EMOTE421_TOKEN = "ROLLEYES"
EMOTE422_TOKEN = "RUFFLE"
EMOTE423_TOKEN = "SAD"
EMOTE424_TOKEN = "SCOFF"
EMOTE425_TOKEN = "SCOLD"
EMOTE426_TOKEN = "SCOWL"
EMOTE427_TOKEN = "SEARCH"
EMOTE428_TOKEN = "SHAKEFIST"
EMOTE429_TOKEN = "SHIFTY"
EMOTE430_TOKEN = "SHUDDER"
EMOTE431_TOKEN = "SIGNAL"
EMOTE432_TOKEN = "SILENCE"
EMOTE433_TOKEN = "SING"
EMOTE434_TOKEN = "SMACK"
EMOTE435_TOKEN = "SNEAK"
EMOTE436_TOKEN = "SNEEZE"
EMOTE437_TOKEN = "SNORT"
EMOTE438_TOKEN = "SQUEAL"
EMOTE440_TOKEN = "SUSPICIOUS"
EMOTE441_TOKEN = "THINK"
EMOTE442_TOKEN = "TRUCE"
EMOTE443_TOKEN = "TWIDDLE"
EMOTE444_TOKEN = "WARN"
EMOTE445_TOKEN = "SNAP"
EMOTE446_TOKEN = "CHARM"
EMOTE447_TOKEN = "COVEREARS"
EMOTE448_TOKEN = "CROSSARMS"
EMOTE449_TOKEN = "LOOK"
EMOTE450_TOKEN = "OBJECT"
EMOTE451_TOKEN = "SWEAT"
EMOTE452_TOKEN = "YW"
EMOTE453_TOKEN = "READ"


Notice the tokens are assigned to globals in the format EMOTE<TokenID>_TOKEN. If you look up in this list for the same <TokenID>, you can find the corresponding slash commands assigned to globals in the format EMOTE<TokenID>_CMD<Index>. Note <Index> is just a sequential number with no other use than to allow more than one slash command to be registered for a token. The following is taken from GlobalStrings.lua line 2467.
Code:
EMOTE100_CMD1 = "/tired";
EMOTE100_CMD2 = "/tired";
EMOTE101_CMD = "/victory";
EMOTE101_CMD1 = "/victory";
EMOTE101_CMD2 = "/victory";
EMOTE101_CMD3 = "/victory";
EMOTE102_CMD1 = "/wave";
EMOTE102_CMD2 = "/wave";
EMOTE103_CMD1 = "/welcome";
EMOTE103_CMD2 = "/welcome";
EMOTE104_CMD1 = "/whine";
EMOTE104_CMD2 = "/whine";
EMOTE105_CMD1 = "/whistle";
EMOTE105_CMD2 = "/whistle";
EMOTE106_CMD1 = "/work";
EMOTE106_CMD2 = "/work";
EMOTE107_CMD1 = "/yawn";
EMOTE107_CMD2 = "/yawn";
EMOTE107_CMD3 = "/yawn";
EMOTE108_CMD1 = "/boggle";
EMOTE108_CMD2 = "/boggle";
EMOTE109_CMD1 = "/calm";
EMOTE109_CMD2 = "/calm";
EMOTE109_CMD3 = "/calm";
EMOTE10_CMD1 = "/bleed";
EMOTE10_CMD2 = "/blood";
EMOTE10_CMD3 = "/bleed";
EMOTE10_CMD4 = "/blood";
EMOTE110_CMD1 = "/cold";
EMOTE110_CMD2 = "/cold";
EMOTE111_CMD1 = "/comfort";
EMOTE111_CMD2 = "/comfort";
EMOTE112_CMD1 = "/cuddle";
EMOTE112_CMD2 = "/spoon";
EMOTE112_CMD3 = "/cuddle";
EMOTE112_CMD4 = "/spoon";
EMOTE113_CMD1 = "/duck";
EMOTE113_CMD2 = "/duck";
EMOTE114_CMD1 = "/insult";
EMOTE114_CMD2 = "/insult";
EMOTE114_CMD3 = "/insult";
EMOTE115_CMD1 = "/introduce";
EMOTE115_CMD2 = "/introduce";
EMOTE116_CMD1 = "/jk";
EMOTE116_CMD2 = "/jk";
EMOTE117_CMD1 = "/lick";
EMOTE117_CMD2 = "/lick";
EMOTE118_CMD1 = "/listen";
EMOTE118_CMD2 = "/listen";
EMOTE119_CMD1 = "/lost";
EMOTE119_CMD2 = "/lost";
EMOTE11_CMD1 = "/blink";
EMOTE11_CMD2 = "/blink";
EMOTE120_CMD1 = "/mock";
EMOTE120_CMD2 = "/mock";
EMOTE121_CMD1 = "/ponder";
EMOTE121_CMD2 = "/ponder";
EMOTE122_CMD1 = "/pounce";
EMOTE122_CMD2 = "/pounce";
EMOTE123_CMD1 = "/praise";
EMOTE123_CMD2 = "/lavish";
EMOTE123_CMD3 = "/praise";
EMOTE123_CMD4 = "/lavish";
EMOTE124_CMD1 = "/purr";
EMOTE124_CMD2 = "/purr";
EMOTE125_CMD1 = "/puzzled";
EMOTE125_CMD2 = "/puzzled";
EMOTE126_CMD1 = "/raise";
EMOTE126_CMD2 = "/volunteer";
EMOTE126_CMD3 = "/raise";
EMOTE126_CMD4 = "/volunteer";
EMOTE127_CMD1 = "/ready";
EMOTE127_CMD2 = "/rdy";
EMOTE127_CMD3 = "/ready";
EMOTE127_CMD4 = "/rdy";
EMOTE128_CMD1 = "/shimmy";
EMOTE128_CMD2 = "/shimmy";
EMOTE129_CMD1 = "/shiver";
EMOTE129_CMD2 = "/shiver";
EMOTE12_CMD1 = "/blush";
EMOTE12_CMD2 = "/blush";
EMOTE130_CMD1 = "/shoo";
EMOTE130_CMD2 = "/pest";
EMOTE130_CMD3 = "/shoo";
EMOTE130_CMD4 = "/pest";
EMOTE131_CMD1 = "/slap";
EMOTE131_CMD2 = "/slap";
EMOTE132_CMD1 = "/smirk";
EMOTE132_CMD2 = "/smirk";
EMOTE133_CMD1 = "/sniff";
EMOTE133_CMD2 = "/sniff";
EMOTE134_CMD1 = "/snub";
EMOTE134_CMD2 = "/snub";
EMOTE135_CMD1 = "/soothe";
EMOTE135_CMD2 = "/soothe";
EMOTE136_CMD1 = "/stink";
EMOTE136_CMD2 = "/smell";
EMOTE136_CMD3 = "/stink";
EMOTE136_CMD4 = "/smell";
EMOTE137_CMD1 = "/taunt";
EMOTE137_CMD2 = "/taunt";
EMOTE138_CMD1 = "/tease";
EMOTE138_CMD2 = "/tease";
EMOTE139_CMD1 = "/thirsty";
EMOTE139_CMD2 = "/thirsty";
EMOTE13_CMD1 = "/bonk";
EMOTE13_CMD2 = "/doh";
EMOTE13_CMD3 = "/bonk";
EMOTE13_CMD4 = "/doh";
EMOTE140_CMD1 = "/veto";
EMOTE140_CMD2 = "/veto";
EMOTE141_CMD1 = "/snicker";
EMOTE141_CMD2 = "/snicker";
EMOTE142_CMD1 = "/tickle";
EMOTE142_CMD2 = "/tickle";
EMOTE143_CMD1 = "/stand";
EMOTE143_CMD2 = "/stand";
EMOTE144_CMD1 = "/violin";
EMOTE144_CMD2 = "/violin";
EMOTE145_CMD1 = "/smile";
EMOTE145_CMD2 = "/smile";
EMOTE146_CMD1 = "/rasp";
EMOTE146_CMD2 = "/rasp";
EMOTE147_CMD1 = "/growl";
EMOTE147_CMD2 = "/growl";
EMOTE148_CMD1 = "/bark";
EMOTE148_CMD2 = "/bark";
EMOTE149_CMD1 = "/pity";
EMOTE149_CMD2 = "/pity";
EMOTE14_CMD1 = "/bored";
EMOTE14_CMD2 = "/bored";
EMOTE150_CMD1 = "/scared";
EMOTE150_CMD2 = "/scared";
EMOTE151_CMD1 = "/flop";
EMOTE151_CMD2 = "/flop";
EMOTE152_CMD1 = "/love";
EMOTE152_CMD2 = "/love";
EMOTE153_CMD1 = "/moo";
EMOTE153_CMD2 = "/moo";
EMOTE154_CMD1 = "/commend";
EMOTE154_CMD2 = "/commend";
EMOTE155_CMD1 = "/train";
EMOTE155_CMD2 = "/train";
EMOTE156_CMD1 = "/helpme";
EMOTE156_CMD2 = "/helpme";
EMOTE157_CMD1 = "/incoming";
EMOTE157_CMD2 = "/incoming";
EMOTE158_CMD1 = "/openfire";
EMOTE158_CMD2 = "/openfire";
EMOTE159_CMD1 = "/charge";
EMOTE159_CMD2 = "/charge";
EMOTE15_CMD1 = "/bounce";
EMOTE15_CMD2 = "/bounce";
EMOTE160_CMD1 = "/flee";
EMOTE160_CMD2 = "/flee";
EMOTE161_CMD1 = "/attacktarget";
EMOTE161_CMD2 = "/attacktarget";
EMOTE162_CMD1 = "/oom";
EMOTE162_CMD2 = "/oom";
EMOTE163_CMD1 = "/followme";
EMOTE163_CMD2 = "/followme";
EMOTE164_CMD1 = "/wait";
EMOTE164_CMD2 = "/wait";
EMOTE165_CMD1 = "/flirt";
EMOTE165_CMD2 = "/flirt";
EMOTE166_CMD1 = "/healme";
EMOTE166_CMD2 = "/healme";
EMOTE167_CMD1 = "/silly";
EMOTE167_CMD2 = "/silly";
EMOTE168_CMD1 = "/wink";
EMOTE168_CMD2 = "/wink";
EMOTE169_CMD1 = "/pat";
EMOTE169_CMD2 = "/pat";
EMOTE16_CMD1 = "/brb";
EMOTE16_CMD2 = "/brb";
EMOTE170_CMD1 = "/golfclap";
EMOTE170_CMD2 = "/golfclap";
EMOTE171_CMD1 = "/mountspecial";
EMOTE171_CMD2 = "/mountspecial";
EMOTE17_CMD1 = "/bow";
EMOTE17_CMD2 = "/bow";
EMOTE18_CMD1 = "/burp";
EMOTE18_CMD2 = "/belch";
EMOTE18_CMD3 = "/burp";
EMOTE18_CMD4 = "/belch";
EMOTE19_CMD1 = "/bye";
EMOTE19_CMD2 = "/goodbye";
EMOTE19_CMD3 = "/farewell";
EMOTE19_CMD4 = "/bye";
EMOTE19_CMD5 = "/goodbye";
EMOTE19_CMD6 = "/farewell";
EMOTE1_CMD1 = "/agree";
EMOTE1_CMD2 = "/agree";
EMOTE20_CMD1 = "/cackle";
EMOTE20_CMD2 = "/cackle";
EMOTE21_CMD1 = "/cheer";
EMOTE21_CMD2 = "/cheer";
EMOTE21_CMD3 = "/woot";
EMOTE21_CMD4 = "/woot";
EMOTE22_CMD1 = "/chicken";
EMOTE22_CMD2 = "/flap";
EMOTE22_CMD3 = "/strut";
EMOTE22_CMD4 = "/chicken";
EMOTE22_CMD5 = "/flap";
EMOTE22_CMD6 = "/strut";
EMOTE23_CMD1 = "/chuckle";
EMOTE23_CMD2 = "/chuckle";
EMOTE24_CMD1 = "/clap";
EMOTE24_CMD2 = "/clap";
EMOTE25_CMD1 = "/confused";
EMOTE25_CMD2 = "/confused";
EMOTE26_CMD1 = "/congratulate";
EMOTE26_CMD2 = "/congrats";
EMOTE26_CMD3 = "/cong";
EMOTE26_CMD4 = "/congratulate";
EMOTE26_CMD5 = "/grats";
EMOTE26_CMD6 = "/cong";
EMOTE27_CMD1 = "/unused";
EMOTE27_CMD2 = "/unused";
EMOTE28_CMD1 = "/cough";
EMOTE28_CMD2 = "/cough";
EMOTE29_CMD1 = "/cower";
EMOTE29_CMD2 = "/fear";
EMOTE29_CMD3 = "/cower";
EMOTE29_CMD4 = "/fear";
EMOTE2_CMD1 = "/amaze";
EMOTE2_CMD2 = "/amaze";
EMOTE304_CMD1 = "/incoming";
EMOTE304_CMD3 = "/incoming";
EMOTE304_CMD4 = "/inc";
EMOTE306_CMD1 = "/retreat";
EMOTE306_CMD2 = "/retreat";
EMOTE306_CMD3 = "/flee";
EMOTE306_CMD4 = "/flee";
EMOTE30_CMD1 = "/crack";
EMOTE30_CMD2 = "/knuckles";
EMOTE30_CMD3 = "/crack";
EMOTE30_CMD4 = "/knuckles";
EMOTE31_CMD1 = "/cringe";
EMOTE31_CMD2 = "/cringe";
EMOTE32_CMD1 = "/cry";
EMOTE32_CMD2 = "/sob";
EMOTE32_CMD3 = "/weep";
EMOTE32_CMD4 = "/cry";
EMOTE32_CMD5 = "/sob";
EMOTE32_CMD6 = "/weep";
EMOTE33_CMD1 = "/curious";
EMOTE33_CMD2 = "/curious";
EMOTE34_CMD1 = "/curtsey";
EMOTE34_CMD2 = "/curtsey";
EMOTE35_CMD1 = "/dance";
EMOTE35_CMD2 = "/dance";
EMOTE368_CMD1 = "/blame";
EMOTE368_CMD2 = "/blame";
EMOTE369_CMD1 = "/blank";
EMOTE369_CMD2 = "/blank";
EMOTE36_CMD1 = "/drink";
EMOTE36_CMD2 = "/shindig";
EMOTE36_CMD3 = "/drink";
EMOTE36_CMD4 = "/shindig";
EMOTE370_CMD1 = "/brandish";
EMOTE370_CMD2 = "/brandish";
EMOTE371_CMD1 = "/breath";
EMOTE371_CMD2 = "/breath";
EMOTE372_CMD1 = "/disagree";
EMOTE372_CMD2 = "/disagree";
EMOTE373_CMD1 = "/doubt";
EMOTE373_CMD2 = "/doubt";
EMOTE374_CMD1 = "/embarrass";
EMOTE374_CMD2 = "/embarrass";
EMOTE375_CMD1 = "/encourage";
EMOTE375_CMD2 = "/encourage";
EMOTE376_CMD1 = "/enemy";
EMOTE376_CMD2 = "/enemy";
EMOTE377_CMD1 = "/eyebrow";
EMOTE377_CMD2 = "/eyebrow";
EMOTE377_CMD3 = "/brow";
EMOTE377_CMD4 = "/brow";
EMOTE37_CMD1 = "/drool";
EMOTE37_CMD2 = "/drool";
EMOTE380_CMD1 = "/highfive";
EMOTE380_CMD2 = "/highfive";
EMOTE381_CMD1 = "/absent";
EMOTE381_CMD2 = "/absent";
EMOTE382_CMD1 = "/arm";
EMOTE382_CMD2 = "/arm";
EMOTE383_CMD1 = "/awe";
EMOTE383_CMD2 = "/awe";
EMOTE384_CMD1 = "/backpack";
EMOTE384_CMD2 = "/backpack";
EMOTE384_CMD3 = "/pack";
EMOTE384_CMD4 = "/pack";
EMOTE385_CMD1 = "/badfeeling";
EMOTE385_CMD2 = "/badfeeling";
EMOTE385_CMD3 = "/bad";
EMOTE385_CMD4 = "/bad";
EMOTE386_CMD1 = "/challenge";
EMOTE386_CMD2 = "/challenge";
EMOTE387_CMD1 = "/chug";
EMOTE387_CMD2 = "/chug";
EMOTE389_CMD1 = "/ding";
EMOTE389_CMD2 = "/ding";
EMOTE38_CMD1 = "/eat";
EMOTE38_CMD2 = "/chew";
EMOTE38_CMD3 = "/feast";
EMOTE38_CMD4 = "/eat";
EMOTE38_CMD5 = "/chew";
EMOTE38_CMD6 = "/feast";
EMOTE390_CMD1 = "/facepalm";
EMOTE390_CMD2 = "/facepalm";
EMOTE390_CMD3 = "/palm";
EMOTE390_CMD4 = "/palm";
EMOTE391_CMD1 = "/faint";
EMOTE391_CMD2 = "/faint";
EMOTE392_CMD1 = "/go";
EMOTE392_CMD2 = "/go";
EMOTE393_CMD1 = "/going";
EMOTE393_CMD2 = "/going";
EMOTE394_CMD1 = "/glower";
EMOTE394_CMD2 = "/glower";
EMOTE395_CMD1 = "/headache";
EMOTE395_CMD2 = "/headache";
EMOTE396_CMD1 = "/hiccup";
EMOTE396_CMD2 = "/hiccup";
EMOTE398_CMD1 = "/hiss";
EMOTE398_CMD2 = "/hiss";
EMOTE399_CMD1 = "/holdhand";
EMOTE399_CMD2 = "/holdhand";
EMOTE39_CMD1 = "/eye";
EMOTE39_CMD2 = "/eye";
EMOTE3_CMD1 = "/angry";
EMOTE3_CMD2 = "/mad";
EMOTE3_CMD3 = "/angry";
EMOTE3_CMD4 = "/mad";
EMOTE401_CMD1 = "/hurry";
EMOTE401_CMD2 = "/hurry";
EMOTE402_CMD1 = "/idea";
EMOTE402_CMD2 = "/idea";
EMOTE403_CMD1 = "/jealous";
EMOTE403_CMD2 = "/jealous";
EMOTE404_CMD1 = "/luck";
EMOTE404_CMD2 = "/luck";
EMOTE405_CMD1 = "/map";
EMOTE405_CMD2 = "/map";
EMOTE406_CMD1 = "/mercy";
EMOTE406_CMD2 = "/mercy";
EMOTE407_CMD1 = "/mutter";
EMOTE407_CMD2 = "/mutter";
EMOTE408_CMD1 = "/nervous";
EMOTE408_CMD2 = "/nervous";
EMOTE409_CMD1 = "/offer";
EMOTE409_CMD2 = "/offer";
EMOTE40_CMD1 = "/fart";
EMOTE40_CMD2 = "/fart";
EMOTE410_CMD1 = "/pet";
EMOTE410_CMD2 = "/pet";
EMOTE411_CMD1 = "/pinch";
EMOTE411_CMD2 = "/pinch";
EMOTE413_CMD1 = "/proud";
EMOTE413_CMD2 = "/proud";
EMOTE414_CMD1 = "/promise";
EMOTE414_CMD2 = "/promise";
EMOTE415_CMD1 = "/pulse";
EMOTE415_CMD2 = "/pulse";
EMOTE416_CMD1 = "/punch";
EMOTE416_CMD2 = "/punch";
EMOTE417_CMD1 = "/pout";
EMOTE417_CMD2 = "/pout";
EMOTE418_CMD1 = "/regret";
EMOTE418_CMD2 = "/regret";
EMOTE41_CMD1 = "/fidget";
EMOTE41_CMD2 = "/impatient";
EMOTE41_CMD3 = "/fidget";
EMOTE41_CMD4 = "/impatient";
EMOTE420_CMD1 = "/revenge";
EMOTE420_CMD2 = "/revenge";
EMOTE421_CMD1 = "/rolleyes";
EMOTE421_CMD2 = "/rolleyes";
EMOTE421_CMD3 = "/eyeroll";
EMOTE421_CMD4 = "/eyeroll";
EMOTE422_CMD1 = "/ruffle";
EMOTE422_CMD2 = "/ruffle";
EMOTE423_CMD1 = "/sad";
EMOTE423_CMD2 = "/sad";
EMOTE424_CMD1 = "/scoff";
EMOTE424_CMD2 = "/scoff";
EMOTE425_CMD1 = "/scold";
EMOTE425_CMD2 = "/scold";
EMOTE426_CMD1 = "/scowl";
EMOTE426_CMD2 = "/scowl";
EMOTE427_CMD1 = "/search";
EMOTE427_CMD2 = "/search";
EMOTE428_CMD1 = "/shakefist";
EMOTE428_CMD2 = "/shakefist";
EMOTE428_CMD3 = "/fist";
EMOTE428_CMD4 = "/fist";
EMOTE429_CMD1 = "/shifty";
EMOTE429_CMD2 = "/shifty";
EMOTE42_CMD1 = "/flex";
EMOTE42_CMD2 = "/strong";
EMOTE42_CMD3 = "/flex";
EMOTE42_CMD4 = "/strong";
EMOTE430_CMD1 = "/shudder";
EMOTE430_CMD2 = "/shudder";
EMOTE431_CMD1 = "/signal";
EMOTE431_CMD2 = "/signal";
EMOTE432_CMD1 = "/silence";
EMOTE432_CMD2 = "/silence";
EMOTE432_CMD3 = "/shush";
EMOTE432_CMD4 = "/shush";
EMOTE433_CMD1 = "/sing";
EMOTE433_CMD2 = "/sing";
EMOTE434_CMD1 = "/smack";
EMOTE434_CMD2 = "/smack";
EMOTE435_CMD1 = "/sneak";
EMOTE435_CMD2 = "/sneak";
EMOTE436_CMD1 = "/sneeze";
EMOTE436_CMD2 = "/sneeze";
EMOTE437_CMD1 = "/snort";
EMOTE437_CMD2 = "/snort";
EMOTE438_CMD1 = "/squeal";
EMOTE438_CMD2 = "/squeal";
EMOTE43_CMD1 = "/frown";
EMOTE43_CMD2 = "/disappointed";
EMOTE43_CMD3 = "/disappointment";
EMOTE43_CMD4 = "/frown";
EMOTE43_CMD5 = "/disappointed";
EMOTE43_CMD6 = "/disappointment";
EMOTE440_CMD1 = "/suspicious";
EMOTE440_CMD2 = "/suspicious";
EMOTE441_CMD1 = "/think";
EMOTE441_CMD2 = "/think";
EMOTE442_CMD1 = "/truce";
EMOTE442_CMD2 = "/truce";
EMOTE443_CMD1 = "/twiddle";
EMOTE443_CMD2 = "/twiddle";
EMOTE444_CMD1 = "/warn";
EMOTE444_CMD2 = "/warn";
EMOTE445_CMD1 = "/snap";
EMOTE445_CMD2 = "/snap";
EMOTE446_CMD1 = "/charm";
EMOTE446_CMD2 = "/charm";
EMOTE447_CMD1 = "/coverears";
EMOTE447_CMD2 = "/coverears";
EMOTE448_CMD1 = "/crossarms";
EMOTE448_CMD2 = "/crossarms";
EMOTE449_CMD1 = "/look";
EMOTE449_CMD2 = "/look";
EMOTE44_CMD1 = "/gasp";
EMOTE44_CMD2 = "/gasp";
EMOTE44_CMD3 = "/gasp";
EMOTE450_CMD1 = "/object";
EMOTE450_CMD2 = "/object";
EMOTE450_CMD3 = "/objection";
EMOTE450_CMD4 = "/objection";
EMOTE450_CMD5 = "/holdit";
EMOTE450_CMD6 = "/holdit";
EMOTE451_CMD1 = "/sweat";
EMOTE451_CMD2 = "/sweat";
EMOTE452_CMD1 = "/yw";
EMOTE452_CMD2 = "/yw";
EMOTE453_CMD1 = "/read";
EMOTE453_CMD2 = "/read";
EMOTE45_CMD1 = "/gaze";
EMOTE45_CMD2 = "/gaze";
EMOTE46_CMD1 = "/giggle";
EMOTE46_CMD2 = "/giggle";
EMOTE47_CMD1 = "/glare";
EMOTE47_CMD2 = "/glare";
EMOTE48_CMD1 = "/gloat";
EMOTE48_CMD2 = "/gloat";
EMOTE49_CMD1 = "/greet";
EMOTE49_CMD2 = "/greetings";
EMOTE49_CMD3 = "/greet";
EMOTE49_CMD4 = "/greetings";
EMOTE4_CMD1 = "/apologize";
EMOTE4_CMD2 = "/sorry";
EMOTE4_CMD3 = "/apologize";
EMOTE4_CMD4 = "/sorry";
EMOTE50_CMD1 = "/grin";
EMOTE50_CMD2 = "/wicked";
EMOTE50_CMD3 = "/wickedly";
EMOTE50_CMD4 = "/grin";
EMOTE50_CMD5 = "/wicked";
EMOTE50_CMD6 = "/wickedly";
EMOTE51_CMD1 = "/groan";
EMOTE51_CMD2 = "/groan";
EMOTE52_CMD1 = "/grovel";
EMOTE52_CMD2 = "/peon";
EMOTE52_CMD3 = "/grovel";
EMOTE52_CMD4 = "/peon";
EMOTE53_CMD1 = "/guffaw";
EMOTE53_CMD2 = "/guffaw";
EMOTE54_CMD1 = "/hail";
EMOTE54_CMD2 = "/hail";
EMOTE55_CMD1 = "/happy";
EMOTE55_CMD2 = "/glad";
EMOTE55_CMD3 = "/yay";
EMOTE55_CMD4 = "/happy";
EMOTE55_CMD5 = "/glad";
EMOTE55_CMD6 = "/yay";
EMOTE56_CMD1 = "/hello";
EMOTE56_CMD2 = "/hi";
EMOTE56_CMD3 = "/hello";
EMOTE56_CMD4 = "/hi";
EMOTE57_CMD1 = "/hug";
EMOTE57_CMD2 = "/hug";
EMOTE58_CMD1 = "/hungry";
EMOTE58_CMD2 = "/food";
EMOTE58_CMD3 = "/hungry";
EMOTE58_CMD4 = "/food";
EMOTE58_CMD5 = "/pizza";
EMOTE58_CMD6 = "/pizza";
EMOTE59_CMD1 = "/kiss";
EMOTE59_CMD2 = "/blow";
EMOTE59_CMD3 = "/kiss";
EMOTE59_CMD4 = "/blow";
EMOTE5_CMD1 = "/applaud";
EMOTE5_CMD2 = "/bravo";
EMOTE5_CMD3 = "/applause";
EMOTE5_CMD4 = "/applaud";
EMOTE5_CMD5 = "/bravo";
EMOTE5_CMD6 = "/applause";
EMOTE60_CMD1 = "/kneel";
EMOTE60_CMD2 = "/kneel";
EMOTE60_CMD3 = "/kneel";
EMOTE61_CMD1 = "/laugh";
EMOTE61_CMD2 = "/lol";
EMOTE61_CMD3 = "/laugh";
EMOTE61_CMD4 = "/lol";
EMOTE62_CMD1 = "/laydown";
EMOTE62_CMD2 = "/liedown";
EMOTE62_CMD3 = "/lay";
EMOTE62_CMD4 = "/lie";
EMOTE62_CMD5 = "/laydown";
EMOTE62_CMD6 = "/liedown";
EMOTE62_CMD7 = "/lay";
EMOTE62_CMD8 = "/lie";
EMOTE63_CMD1 = "/massage";
EMOTE63_CMD2 = "/massage";
EMOTE64_CMD1 = "/moan";
EMOTE64_CMD2 = "/moan";
EMOTE65_CMD1 = "/moon";
EMOTE65_CMD2 = "/moon";
EMOTE66_CMD1 = "/mourn";
EMOTE66_CMD2 = "/mourn";
EMOTE67_CMD1 = "/no";
EMOTE67_CMD2 = "/no";
EMOTE68_CMD1 = "/nod";
EMOTE68_CMD2 = "/yes";
EMOTE68_CMD3 = "/nod";
EMOTE68_CMD4 = "/yes";
EMOTE69_CMD1 = "/nosepick";
EMOTE69_CMD2 = "/pick";
EMOTE69_CMD3 = "/nosepick";
EMOTE69_CMD4 = "/pick";
EMOTE6_CMD1 = "/bashful";
EMOTE6_CMD2 = "/bashful";
EMOTE70_CMD1 = "/panic";
EMOTE70_CMD2 = "/panic";
EMOTE71_CMD1 = "/peer";
EMOTE71_CMD2 = "/peer";
EMOTE72_CMD1 = "/plead";
EMOTE72_CMD2 = "/plead";
EMOTE73_CMD1 = "/point";
EMOTE73_CMD2 = "/point";
EMOTE74_CMD1 = "/poke";
EMOTE74_CMD2 = "/poke";
EMOTE75_CMD1 = "/pray";
EMOTE75_CMD2 = "/pray";
EMOTE76_CMD1 = "/roar";
EMOTE76_CMD2 = "/roar";
EMOTE76_CMD3 = "/rawr";
EMOTE76_CMD4 = "/rawr";
EMOTE77_CMD1 = "/rofl";
EMOTE77_CMD2 = "/rofl";
EMOTE78_CMD1 = "/rude";
EMOTE78_CMD2 = "/rude";
EMOTE79_CMD1 = "/salute";
EMOTE79_CMD2 = "/salute";
EMOTE7_CMD1 = "/beckon";
EMOTE7_CMD2 = "/beckon";
EMOTE80_CMD1 = "/scratch";
EMOTE80_CMD2 = "/cat";
EMOTE80_CMD3 = "/catty";
EMOTE80_CMD4 = "/scratch";
EMOTE80_CMD5 = "/cat";
EMOTE80_CMD6 = "/catty";
EMOTE81_CMD1 = "/sexy";
EMOTE81_CMD2 = "/sexy";
EMOTE82_CMD1 = "/shake";
EMOTE82_CMD2 = "/rear";
EMOTE82_CMD3 = "/shake";
EMOTE82_CMD4 = "/rear";
EMOTE83_CMD1 = "/shout";
EMOTE83_CMD2 = "/holler";
EMOTE83_CMD3 = "/holler";
EMOTE84_CMD1 = "/shrug";
EMOTE84_CMD2 = "/shrug";
EMOTE85_CMD1 = "/shy";
EMOTE85_CMD2 = "/shy";
EMOTE86_CMD1 = "/sigh";
EMOTE86_CMD2 = "/sigh";
EMOTE87_CMD1 = "/sit";
EMOTE87_CMD2 = "/sit";
EMOTE88_CMD1 = "/sleep";
EMOTE88_CMD2 = "/sleep";
EMOTE89_CMD1 = "/snarl";
EMOTE89_CMD2 = "/snarl";
EMOTE8_CMD1 = "/beg";
EMOTE8_CMD2 = "/beg";
EMOTE90_CMD1 = "/spit";
EMOTE90_CMD2 = "/spit";
EMOTE91_CMD1 = "/stare";
EMOTE91_CMD2 = "/stare";
EMOTE92_CMD1 = "/surprised";
EMOTE92_CMD2 = "/surprised";
EMOTE93_CMD1 = "/surrender";
EMOTE93_CMD2 = "/surrender";
EMOTE94_CMD1 = "/talk";
EMOTE94_CMD2 = "/talk";
EMOTE95_CMD1 = "/talkex";
EMOTE95_CMD2 = "/excited";
EMOTE95_CMD3 = "/talkex";
EMOTE95_CMD4 = "/excited";
EMOTE96_CMD1 = "/talkq";
EMOTE96_CMD2 = "/question";
EMOTE96_CMD3 = "/talkq";
EMOTE96_CMD4 = "/question";
EMOTE97_CMD1 = "/tap";
EMOTE97_CMD2 = "/tap";
EMOTE98_CMD1 = "/thank";
EMOTE98_CMD2 = "/thanks";
EMOTE98_CMD3 = "/ty";
EMOTE98_CMD4 = "/thank";
EMOTE98_CMD5 = "/thanks";
EMOTE98_CMD6 = "/ty";
EMOTE99_CMD1 = "/threaten";
EMOTE99_CMD2 = "/doom";
EMOTE99_CMD3 = "/threat";
EMOTE99_CMD4 = "/wrath";
EMOTE99_CMD5 = "/threaten";
EMOTE99_CMD6 = "/doom";
EMOTE99_CMD7 = "/threat";
EMOTE99_CMD8 = "/wrath";
EMOTE9_CMD1 = "/bite";
EMOTE9_CMD2 = "/bite";


For example, /lol is listed for token ID 61, which matches the ID assigned for LAUGH, so you would run DoEmote("LAUGH") to perform that emote.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
02-15-12, 10:22 AM   #3
Dridzt
A Pyroguard Emberseer
 
Dridzt's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2005
Posts: 1,360
I also replied earlier to your curseforge forums mirror post (which I saw on Wowace actually)
  Reply With Quote
02-15-12, 10:38 PM   #4
Savaena
Warcrafter
 
Savaena's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 4
Thank you so much for the help so far! I have a copy up for download at:

http://www.wowinterface.com/download...20857-1.0.html


But I'm still having trouble getting it to respond correctly. Is there an element I'm forgetting?
  Reply With Quote
02-16-12, 05:03 AM   #5
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,326
Could you further explain what it's doing and how it differs from what you want it to do?
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
02-16-12, 08:23 AM   #6
Savaena
Warcrafter
 
Savaena's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 4
Sure, okay, so I've been going over this script and added/changed a few things, but I still can't seem to get it to function at all. My game doesn't even register the welcome message when I enter world. When a player targets me and does a built in /emote, my character does not react at all.

What I want to have happen is my character replies with an emote after a 3 second delay. I also want my addon to show me a welcome message in the chat box in game when I enter world.

I have a full download of my most updated version addon available at: http://www.wowinterface.com/download...onse.html#info

But here is my current file structure and layout of my addon as well (in case this is easier for you) I just can't figure out why this won't run. >.<

Folder Layout:

Interface>Addons>BlushResponse
Interface>Addons>Ace3

Files Contained in BlushResponse:

AceTimer-3.0.lua
AceTimer-3.0.xml
BlushResponse.xml
BlushResponse.lua
BlushResponse.toc
LibStub.lua

BlushResponse.toc File

Code:
## Interface: 40300
## Title: Blush Response
## Notes: Build Version 1.2 - An addon that replies to emotes for you!  Dependencies: Ace Timer  and LibStub
## Author: Savaena and Poisoned Lizard

BlushResponse.lua
BlushResponse.xml
AceTimer-3.0.lua
AceTimer-3.0.xml
LibStub.lua
BlushResponse.xml File

Code:
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
 </Ui>
BlushResponse.lua File

Code:
-- BLUSH RESPONSE ADDON --

local DELAY = 3 -- Number of seconds before we reply with an emote.
local MAX_REPLIES = 1 -- The maximum number of reply emotes sent for every emote used on you.

local OPPOSITE_FACTION; -- These two get assigned values when PLAYER_LOGIN fires.
local PLAYER_GUID;

local ALLIANCE_RACES = {
	Dwarf = true,
	Draenei = true,
	Gnome = true,
	Human = true,
	NightElf = true,
	Worgen = true,
}

local HORDE_RACES = {
	BloodElf = true,
	Goblin = true,
	Orc = true,
	Tauren = true,
	Troll = true,
	Scourge = true,
}

local BR = CreateFrame("Frame") -- You can use BlushResponse instead of this frame if needed
local BRVersion = CreateFrame("Frame") -- This is my version/greeting frame
LibStub("AceTimer-3.0"):Embed(BR) -- Embed AceTimer, allowing use of its methods.

local triggers = {
-- This table contains the trigger phrases and reply type/messages.
-- The trigger phrase can use any patterns recognised by string.find, but for best results use ^%S at the start to match the name of the emoter.
-- The replyType can be "builtin" for an emote performed with DoEmote (e.g. /dance, /cower) or "custom" for an emote performed with SendChatMessage (e.g. /e is very scared!)
-- The replyMsg can be an EmoteToken for builtin emotes or a message for custom emotes (optionally with %N, which will be replaced with the name of the emoter)
-- You will need to figure out a way for the user to add/remove triggers from here.

	{ trigger = "^%S+ licks you",			replyType = "builtin", 	replyMsg = "COWER" },
	{ trigger = "^%S+ pokes you",			replyType = "builtin", 	replyMsg = "FART" },
	{ trigger = "^%S+ hugs you",			replyType = "builtin", 	replyMsg = "HELPME" },
	{ trigger = "^%S+ slaps you",			replyType = "builtin", 	replyMsg = "BURP" },
	{ trigger = "^%S+ winks slyly at you",		replyType = "builtin", 	replyMsg = "WINK" },
	{ trigger = "^%S+ cries on your shoulder",	replyType = "builtin", 	replyMsg = "VIOLIN" },
	{ trigger = "^%S+ loves you",			replyType = "builtin", 	replyMsg = "BLUSH" },
	{ trigger = "^%S+ thinks you are a sexy devil", 	replyType = "custom", 	replyMsg = "gasps in shock at %N!" },
	{ trigger = "^%S+ dances with you", 		replyType = "custom", 	replyMsg = "admires %N's moves." },
}

BR:SetScript("OnEvent", function(self, event, ...) -- This part only works if BR is a frame
	self[event](self, ...) -- Call the event name as a method of BR with all the arguments supplied.
end)
BRVersion:RegisterEvent("PLAYER_LOGIN")
BR:RegisterEvent("PLAYER_LOGIN")
BR:RegisterEvent("CHAT_MSG_TEXT_EMOTE") -- Fired for builtin emotes like /dance
BR:RegisterEvent("CHAT_MSG_EMOTE") -- Fired for custom emotes like /e is happy.

function BR:ADDON_LOADED(self, event, arg1)
	if (event == "ADDON_LOADED") and (arg1 == "BR") then
		DEFAULT_CHAT_FRAME:AddMessage("Addon: Blush Response v. 1.2 successfully loaded!");
		BR:LoadDefaults()
		self:UnregisterEvent("ADDON_LOADED")
	end
end

function BR:PLAYER_LOGIN()
	OPPOSITE_FACTION = UnitFactionGroup("player") == "Alliance" and HORDE_RACES or ALLIANCE_RACES
	PLAYER_GUID = UnitGUID("player")
end

local function PerformEmotes(args)
 -- AceTimer only allows us to call the function with a single argument, so we put the message and name in a table
	local msg, name = unpack(args)
	local numEmotes = 0
	
	for _, data in ipairs(triggers) do
		if msg:find(data.trigger) then
			numEmotes = numEmotes + 1
			if data.replyType == "builtin" then
				DoEmote(data.replyMsg)
			else
				SendChatMessage(data.replyMsg:gsub("%%N", name), "EMOTE")
			end
			if numEmotes == MAX_EMOTES then break end -- Break the loop if we've performed the maximum number of reply emotes
		end
	end
end

function BR:CHAT_MSG_TEXT_EMOTE(msg, name, _, _, _, _, _, _, _, _, lineId, GUID)
	if GUID == PLAYER_GUID then return end -- Don't respond to emotes the player sent.
	local className, classId, raceName, raceId, gender, name, realm = GetPlayerInfoByGUID(GUID)
	if OPPOSITE_FACTION[raceId] then
		DoEmote("PONDER")
	end
	self:ScheduleTimer(PerformEmotes, DELAY, {msg, name}) -- Call PerformEmotes in DELAY seconds with a table of the message and name as its only argument
end

function BR:CHAT_MSG_EMOTE(msg, name, _, _, _, _, _, _, _, _, lineId, GUID)
	if GUID == PLAYER_GUID then return end
	self:ScheduleTimer(PerformEmotes, DELAY, {msg, name})
end

function BR:LoadDefaults()
	BR = {}
	for i = 1, #Defaults do
		tinsert(EmoteReply.Emotes, Defaults[i])
	end
end

function BR:CHAT_MSG_EMOTE(msg, name, _, _, _, _, _, _, _, _, lineId, GUID)
	if ( event == "CHAT_MSG_TEXT_EMOTE" ) then
	print(arg1)
		local arg1 = string.lower(arg1) --turns all uppercase letters into lowercase ones.
		foreach(trigger, 
			function(k,v)
				if (string.find(arg1, v.Trigger)) then
					if (string.find(arg1)) then
						replyMsg;
					end
					return;
				end
			end
		);
	end
end
LibStub.lua File

Code:
-- LibStub is a simple versioning stub meant for use in Libraries.  http://www.wowace.com/wiki/LibStub for more info
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2  -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
local LibStub = _G[LIBSTUB_MAJOR]

if not LibStub or LibStub.minor < LIBSTUB_MINOR then
	LibStub = LibStub or {libs = {}, minors = {} }
	_G[LIBSTUB_MAJOR] = LibStub
	LibStub.minor = LIBSTUB_MINOR
	
	function LibStub:NewLibrary(major, minor)
		assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
		minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
		
		local oldminor = self.minors[major]
		if oldminor and oldminor >= minor then return nil end
		self.minors[major], self.libs[major] = minor, self.libs[major] or {}
		return self.libs[major], oldminor
	end
	
	function LibStub:GetLibrary(major, silent)
		if not self.libs[major] and not silent then
			error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
		end
		return self.libs[major], self.minors[major]
	end
	
	function LibStub:IterateLibraries() return pairs(self.libs) end
	setmetatable(LibStub, { __call = LibStub.GetLibrary })
end
AceTimer-3.0.lua File

Code:
--- **AceTimer-3.0** provides a central facility for registering timers.
-- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
-- data structure that allows easy dispatching and fast rescheduling. Timers can be registered, rescheduled
-- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
-- AceTimer is currently limited to firing timers at a frequency of 0.1s. This constant may change
-- in the future, but for now it seemed like a good compromise in efficiency and accuracy.
--
-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
-- need to cancel or reschedule the timer you just registered.
--
-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by 
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
-- make into AceTimer.
-- @class file
-- @name AceTimer-3.0
-- @release $Id: AceTimer-3.0.lua 1037 2011-09-02 16:24:08Z mikk $

--[[
	Basic assumptions:
	* In a typical system, we do more re-scheduling per second than there are timer pulses per second
	* Regardless of timer implementation, we cannot guarantee timely delivery due to FPS restriction (may be as low as 10)

	This implementation:
		CON: The smallest timer interval is constrained by HZ (currently 1/10s).
		PRO: It will still correctly fire any timer slower than HZ over a length of time, e.g. 0.11s interval -> 90 times over 10 seconds
		PRO: In lag bursts, the system simly skips missed timer intervals to decrease load
		CON: Algorithms depending on a timer firing "N times per minute" will fail
		PRO: (Re-)scheduling is O(1) with a VERY small constant. It's a simple linked list insertion in a hash bucket.
		CAUTION: The BUCKETS constant constrains how many timers can be efficiently handled. With too many hash collisions, performance will decrease.
		
	Major assumptions upheld:
	- ALLOWS scheduling multiple timers with the same funcref/method
	- ALLOWS scheduling more timers during OnUpdate processing
	- ALLOWS unscheduling ANY timer (including the current running one) at any time, including during OnUpdate processing
]]

local MAJOR, MINOR = "AceTimer-3.0", 6
local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)

if not AceTimer then return end -- No upgrade needed

AceTimer.hash = AceTimer.hash or {}         -- Array of [0..BUCKET-1] = linked list of timers (using .next member)
                                            -- Linked list gets around ACE-88 and ACE-90.
AceTimer.selfs = AceTimer.selfs or {}       -- Array of [self]={[handle]=timerobj, [handle2]=timerobj2, ...}
AceTimer.frame = AceTimer.frame or CreateFrame("Frame", "AceTimer30Frame")

-- Lua APIs
local assert, error, loadstring = assert, error, loadstring
local setmetatable, rawset, rawget = setmetatable, rawset, rawget
local select, pairs, type, next, tostring = select, pairs, type, next, tostring
local floor, max, min = math.floor, math.max, math.min
local tconcat = table.concat

-- WoW APIs
local GetTime = GetTime

-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: DEFAULT_CHAT_FRAME, geterrorhandler

-- Simple ONE-SHOT timer cache. Much more efficient than a full compost for our purposes.
local timerCache = nil

--[[
	Timers will not be fired more often than HZ-1 times per second. 
	Keep at intended speed PLUS ONE or we get bitten by floating point rounding errors (n.5 + 0.1 can be n.599999)
	If this is ever LOWERED, all existing timers need to be enforced to have a delay >= 1/HZ on lib upgrade.
	If this number is ever changed, all entries need to be rehashed on lib upgrade.
	]]
local HZ = 11

--[[
	Prime for good distribution
	If this number is ever changed, all entries need to be rehashed on lib upgrade.
]]
local BUCKETS = 131

local hash = AceTimer.hash
for i=1,BUCKETS do
	hash[i] = hash[i] or false	-- make it an integer-indexed array; it's faster than hashes
end

--[[
	 xpcall safecall implementation
]]
local xpcall = xpcall

local function errorhandler(err)
	return geterrorhandler()(err)
end

local function CreateDispatcher(argCount)
	local code = [[
		local xpcall, eh = ...	-- our arguments are received as unnamed values in "..." since we don't have a proper function declaration
		local method, ARGS
		local function call() return method(ARGS) end
	
		local function dispatch(func, ...)
			 method = func
			 if not method then return end
			 ARGS = ...
			 return xpcall(call, eh)
		end
	
		return dispatch
	]]
	
	local ARGS = {}
	for i = 1, argCount do ARGS[i] = "arg"..i end
	code = code:gsub("ARGS", tconcat(ARGS, ", "))
	return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
end

local Dispatchers = setmetatable({}, {
	__index=function(self, argCount)
		local dispatcher = CreateDispatcher(argCount)
		rawset(self, argCount, dispatcher)
		return dispatcher
	end
})
Dispatchers[0] = function(func)
	return xpcall(func, errorhandler)
end
 
local function safecall(func, ...)
	return Dispatchers[select('#', ...)](func, ...)
end

local lastint = floor(GetTime() * HZ)

-- --------------------------------------------------------------------
-- OnUpdate handler
--
-- traverse buckets, always chasing "now", and fire timers that have expired

local function OnUpdate()
	local now = GetTime()
	local nowint = floor(now * HZ)
	
	-- Have we passed into a new hash bucket?
	if nowint == lastint then return end
	
	local soon = now + 1 -- +1 is safe as long as 1 < HZ < BUCKETS/2
	
	-- Pass through each bucket at most once
	-- Happens on e.g. instance loads, but COULD happen on high local load situations also
	for curint = (max(lastint, nowint - BUCKETS) + 1), nowint do -- loop until we catch up with "now", usually only 1 iteration
		local curbucket = (curint % BUCKETS)+1
		-- Yank the list of timers out of the bucket and empty it. This allows reinsertion in the currently-processed bucket from callbacks.
		local nexttimer = hash[curbucket]
		hash[curbucket] = false -- false rather than nil to prevent the array from becoming a hash

		while nexttimer do
			local timer = nexttimer
			nexttimer = timer.next
			local when = timer.when
			
			if when < soon then
				-- Call the timer func, either as a method on given object, or a straight function ref
				local callback = timer.callback
				if type(callback) == "string" then
					safecall(timer.object[callback], timer.object, timer.arg)
				elseif callback then
					safecall(callback, timer.arg)
				else
					-- probably nilled out by CancelTimer
					timer.delay = nil -- don't reschedule it
				end

				local delay = timer.delay	-- NOW make a local copy, can't do it earlier in case the timer cancelled itself in the callback
				
				if not delay then
					-- single-shot timer (or cancelled)
					AceTimer.selfs[timer.object][tostring(timer)] = nil
					timerCache = timer
				else
					-- repeating timer
					local newtime = when + delay
					if newtime < now then -- Keep lag from making us firing a timer unnecessarily. (Note that this still won't catch too-short-delay timers though.)
						newtime = now + delay
					end
					timer.when = newtime
					
					-- add next timer execution to the correct bucket
					local bucket = (floor(newtime * HZ) % BUCKETS) + 1
					timer.next = hash[bucket]
					hash[bucket] = timer
				end
			else -- if when>=soon 
				-- reinsert (yeah, somewhat expensive, but shouldn't be happening too often either due to hash distribution)
				timer.next = hash[curbucket]
				hash[curbucket] = timer
			end -- if when<soon ... else
		end -- while nexttimer do
	end -- for curint=lastint,nowint
	
	lastint = nowint
end

-- ---------------------------------------------------------------------
-- Reg( callback, delay, arg, repeating )
--
-- callback( function or string ) - direct function ref or method name in our object for the callback
-- delay(int) - delay for the timer
-- arg(variant) - any argument to be passed to the callback function
-- repeating(boolean) - repeating timer, or oneshot
--
-- returns the handle of the timer for later processing (canceling etc)
local function Reg(self, callback, delay, arg, repeating)
	if type(callback) ~= "string" and type(callback) ~= "function" then 
		local error_origin = repeating and "ScheduleRepeatingTimer" or "ScheduleTimer"
		error(MAJOR..": " .. error_origin .. "(callback, delay, arg): 'callback' - function or method name expected.", 3)
	end
	if type(callback) == "string" then
		if type(self)~="table" then
			local error_origin = repeating and "ScheduleRepeatingTimer" or "ScheduleTimer"
			error(MAJOR..": " .. error_origin .. "(\"methodName\", delay, arg): 'self' - must be a table.", 3)
		end
		if type(self[callback]) ~= "function" then 
			local error_origin = repeating and "ScheduleRepeatingTimer" or "ScheduleTimer"
			error(MAJOR..": " .. error_origin .. "(\"methodName\", delay, arg): 'methodName' - method not found on target object.", 3)
		end
	end
	
	if delay < (1 / (HZ - 1)) then
		delay = 1 / (HZ - 1)
	end
	
	-- Create and stuff timer in the correct hash bucket
	local now = GetTime()
	
	local timer = timerCache or {}	-- Get new timer object (from cache if available)
	timerCache = nil
	
	timer.object = self
	timer.callback = callback
	timer.delay = (repeating and delay)
	timer.arg = arg
	timer.when = now + delay

	local bucket = (floor((now+delay)*HZ) % BUCKETS) + 1
	timer.next = hash[bucket]
	hash[bucket] = timer
	
	-- Insert timer in our self->handle->timer registry
	local handle = tostring(timer)
	
	local selftimers = AceTimer.selfs[self]
	if not selftimers then
		selftimers = {}
		AceTimer.selfs[self] = selftimers
	end
	selftimers[handle] = timer
	selftimers.__ops = (selftimers.__ops or 0) + 1
	
	return handle
end

--- Schedule a new one-shot timer.
-- The timer will fire once in `delay` seconds, unless canceled before.
-- @param callback Callback function for the timer pulse (funcref or method name).
-- @param delay Delay for the timer, in seconds.
-- @param arg An optional argument to be passed to the callback function.
-- @usage
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("TimerTest", "AceTimer-3.0")
-- 
-- function MyAddon:OnEnable()
--   self:ScheduleTimer("TimerFeedback", 5)
-- end
--
-- function MyAddon:TimerFeedback()
--   print("5 seconds passed")
-- end
function AceTimer:ScheduleTimer(callback, delay, arg)
	return Reg(self, callback, delay, arg)
end

--- Schedule a repeating timer.
-- The timer will fire every `delay` seconds, until canceled.
-- @param callback Callback function for the timer pulse (funcref or method name).
-- @param delay Delay for the timer, in seconds.
-- @param arg An optional argument to be passed to the callback function.
-- @usage
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("TimerTest", "AceTimer-3.0")
-- 
-- function MyAddon:OnEnable()
--   self.timerCount = 0
--   self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
-- end
--
-- function MyAddon:TimerFeedback()
--   self.timerCount = self.timerCount + 1
--   print(("%d seconds passed"):format(5 * self.timerCount))
--   -- run 30 seconds in total
--   if self.timerCount == 6 then
--     self:CancelTimer(self.testTimer)
--   end
-- end
function AceTimer:ScheduleRepeatingTimer(callback, delay, arg)
	return Reg(self, callback, delay, arg, true)
end

--- Cancels a timer with the given handle, registered by the same addon object as used for `:ScheduleTimer`
-- Both one-shot and repeating timers can be canceled with this function, as long as the `handle` is valid
-- and the timer has not fired yet or was canceled before.
-- @param handle The handle of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
-- @param silent If true, no error is raised if the timer handle is invalid (expired or already canceled)
-- @return True if the timer was successfully cancelled.
function AceTimer:CancelTimer(handle, silent)
	if not handle then return end -- nil handle -> bail out without erroring
	if type(handle) ~= "string" then
		error(MAJOR..": CancelTimer(handle): 'handle' - expected a string", 2)	-- for now, anyway
	end
	local selftimers = AceTimer.selfs[self]
	local timer = selftimers and selftimers[handle]
	if silent then
		if timer then
			timer.callback = nil	-- don't run it again
			timer.delay = nil		-- if this is the currently-executing one: don't even reschedule 
			-- The timer object is removed in the OnUpdate loop
		end
		return not not timer	-- might return "true" even if we double-cancel. we'll live.
	else
		if not timer then
			geterrorhandler()(MAJOR..": CancelTimer(handle[, silent]): '"..tostring(handle).."' - no such timer registered")
			return false
		end
		if not timer.callback then 
			geterrorhandler()(MAJOR..": CancelTimer(handle[, silent]): '"..tostring(handle).."' - timer already cancelled or expired")
			return false
		end
		timer.callback = nil	-- don't run it again
		timer.delay = nil		-- if this is the currently-executing one: don't even reschedule 
		return true
	end
end

--- Cancels all timers registered to the current addon object ('self')
function AceTimer:CancelAllTimers()
	if not(type(self) == "string" or type(self) == "table") then
		error(MAJOR..": CancelAllTimers(): 'self' - must be a string or a table",2)
	end
	if self == AceTimer then
		error(MAJOR..": CancelAllTimers(): supply a meaningful 'self'", 2)
	end
	
	local selftimers = AceTimer.selfs[self]
	if selftimers then
		for handle,v in pairs(selftimers) do
			if type(v) == "table" then  -- avoid __ops, etc
				AceTimer.CancelTimer(self, handle, true)
			end
		end
	end
end

--- Returns the time left for a timer with the given handle, registered by the current addon object ('self').
-- This function will raise a warning when the handle is invalid, but not stop execution.
-- @param handle The handle of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
-- @return The time left on the timer, or false if the handle is invalid.
function AceTimer:TimeLeft(handle)
	if not handle then return end
	if type(handle) ~= "string" then
		error(MAJOR..": TimeLeft(handle): 'handle' - expected a string", 2)    -- for now, anyway
	end
	local selftimers = AceTimer.selfs[self]
	local timer = selftimers and selftimers[handle]
	if not timer then
		geterrorhandler()(MAJOR..": TimeLeft(handle): '"..tostring(handle).."' - no such timer registered")
		return false
	end
	return timer.when - GetTime()
end


-- ---------------------------------------------------------------------
-- PLAYER_REGEN_ENABLED: Run through our .selfs[] array step by step
-- and clean it out - otherwise the table indices can grow indefinitely
-- if an addon starts and stops a lot of timers. AceBucket does this!
--
-- See ACE-94 and tests/AceTimer-3.0-ACE-94.lua

local lastCleaned = nil

local function OnEvent(this, event)
	if event~="PLAYER_REGEN_ENABLED" then
		return
	end
	
	-- Get the next 'self' to process
	local selfs = AceTimer.selfs
	local self = next(selfs, lastCleaned)
	if not self then
		self = next(selfs)
	end
	lastCleaned = self
	if not self then	-- should only happen if .selfs[] is empty
		return
	end
	
	-- Time to clean it out?
	local list = selfs[self]
	if (list.__ops or 0) < 250 then	-- 250 slosh indices = ~10KB wasted (worst case!). For one 'self'.
		return
	end
	
	-- Create a new table and copy all members over
	local newlist = {}
	local n=0
	for k,v in pairs(list) do
		newlist[k] = v
		if type(v)=="table" and v.callback then -- if the timer is actually live: count it
			n=n+1
		end
	end
	newlist.__ops = 0	-- Reset operation count
	
	-- And since we now have a count of the number of live timers, check that it's reasonable. Emit a warning if not.
	if n>BUCKETS then
		DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: The addon/module '"..tostring(self).."' has "..n.." live timers. Surely that's not intended?")
	end
	
	selfs[self] = newlist
end

-- ---------------------------------------------------------------------
-- Embed handling

AceTimer.embeds = AceTimer.embeds or {}

local mixins = {
	"ScheduleTimer", "ScheduleRepeatingTimer", 
	"CancelTimer", "CancelAllTimers",
	"TimeLeft"
}

function AceTimer:Embed(target)
	AceTimer.embeds[target] = true
	for _,v in pairs(mixins) do
		target[v] = AceTimer[v]
	end
	return target
end

-- AceTimer:OnEmbedDisable( target )
-- target (object) - target object that AceTimer is embedded in.
--
-- cancel all timers registered for the object
function AceTimer:OnEmbedDisable( target )
	target:CancelAllTimers()
end


for addon in pairs(AceTimer.embeds) do
	AceTimer:Embed(addon)
end

-- ---------------------------------------------------------------------
-- Debug tools (expose copies of internals to test suites)
AceTimer.debug = AceTimer.debug or {}
AceTimer.debug.HZ = HZ
AceTimer.debug.BUCKETS = BUCKETS

-- ---------------------------------------------------------------------
-- Finishing touchups

AceTimer.frame:SetScript("OnUpdate", OnUpdate)
AceTimer.frame:SetScript("OnEvent", OnEvent)
AceTimer.frame:RegisterEvent("PLAYER_REGEN_ENABLED")

-- In theory, we could hide&show the frame based on there being timers or not.
-- However, this job is fairly expensive, and the chance that there will 
-- actually be zero timers running is diminuitive to say the least.
AceTimer-3.0.xml File

Code:
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
	<Script file="AceTimer-3.0.lua"/>
</Ui>
  Reply With Quote
02-16-12, 10:09 AM   #7
Dridzt
A Pyroguard Emberseer
 
Dridzt's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2005
Posts: 1,360
Before I (or someone else) comments on the code first things first.

It's best if you get a few things out of the way surrounding development before going into the actual lua.

1. Do not package the whole Ace3 framework with your addon.
Libraries (where needed) are meant to be embedded in your addon folder (simplest case)
For example if you need AceTimer-3.0 a typical folder structure would be.
BlushResponse\Libs\AceTimer-3.0\AceTimer-3.0.xml (and other files contained in the library folder)
BlushResponse\Libs\LibStub\LibStub.lua
BlushResponse\Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml (and other files)
Your .toc file references files relative to the addon folder so BlushResponse is the 'root' folder where the .toc is concerned.
.toc
Code:
Libs\LibStub\LibStub.lua
Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
Libs\AceTimer-3.0\AceTimer-3.0\AceTimer-3.0.xml
BlushResponse.lua
would be the corresponding .toc entries with the structure demonstrated above.

2. Get rid of the .xml file, as long as you're creating the frames in lua (there is equivalent API allowing you to create frames, set scripts etc without using xml)

3. If you're just getting started with addon writing and since this addon is relatively simple in scope I'd suggest you stay away from frameworks for your first project.
Libraries simplify common or repetitive tasks but they're only a time saver when the need to use them has arisen 'naturally' as you find yourself writing your Nth command handler, options table etc.
For an author just beginning, the overhead of an additional layer of API over the base wow lua API is probably going to be more confusion than help.

Will get back to you with some code snippets but any author contributing is unlikely to fix your existing code, more likely re-write it

Edit: Forgot
4. Make sure you have an error display addon installed (!BugGrabber + BugSack are a good option)
or at the least the in-game option to "Show Lua Errors" (under game preferences -> Help) enabled.
World of Warcraft\Logs\FrameXML.log might also hold error information about your addon.

Last edited by Dridzt : 02-16-12 at 10:19 AM.
  Reply With Quote
02-16-12, 06:45 PM   #8
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Savaena View Post
BlushResponse.toc File
Code:
BlushResponse.lua
BlushResponse.xml
AceTimer-3.0.lua
AceTimer-3.0.xml
LibStub.lua
Well, this is one problem. Files are loaded in the order they are listed in the TOC file. Here, your addon depends on AceTimer, so you must load AceTimer before you load your addon files. AceTimer depends on LibStub, so you must load LibStub before you load AceTimer.

Also, loading both the Lua and XML files for AceTimer only causes you to load the library twice, as the XML file is nothing but a "loading stub" that points to the Lua file. This is a common convention in libraries, with the idea that addon authors can just list a single XML file, and if more files are added to the library, the library author can just add them to the XML file, and the addon author does not have to add more files to his/her TOC file. However, I feel this is a bad idea, for several reasons:

(1) It promotes laziness on the part of addon authors. If a library change is major enough to add or remove files, an addon author probably needs to update his/her addon anyway, at at the very least should review his/her addon to make sure no such updates are needed.

(2) Most libraries only have one Lua file, so going through an XML stub just doubles the time it takes to load the library (since WoW has to read two files instead of one). Since reading files from your hard disk is by far the biggest contributor to load times, you should avoid reading any more files than are absolutely necessary.

Also, as Dridzt already mentioned, using XML in your addon is a waste of time (both to write it, and to load it every time you log in) unless your addon is tackling one of the very few, very advanced tasks where custom secure frame templates are required. Since your addon isn't doing this, don't use XML.

Your TOC file listing should look more like this:

Code:
Libs\LibStub\LibStub.lua
Libs\AceTimer-3.0\Acetimer-3.0.lua

BlushResponse.xml -- only if you really need XML for some reason
BlushResponse.lua
Each library should be in its own subfolder; later, if you decide to use a version control system, you can easily have the latest versions of the required libraries pulled in automatically by whichever system you're using, instead of having to copy and paste them there by hand, and remember to update them all the time.
  Reply With Quote
02-17-12, 05:55 AM   #9
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,326
I tried to keep from doing this, but I went ahead and rewrote all of the code. This goes at a different approach using an emote queuing system that handles both delays on individual emotes and a global cooldown between sending out more. The trigger table uses the text following the sender as the key for a table of responses. The addon first checks if the response text is a slash command for an emote. Second, it checks if it matches a token ID. If either of these are true, it runs the built-in emote. Otherwise, it sends the text out as a custom emote. Tables supplying multiple responses will have all responses sent sequentially.

This code is standalone and requires no libraries.

lua Code:
  1. --[[    Blush Response
  2.     Based upon original design by Savaena and Poisoned Lizard
  3.  
  4.     Recoded by SDPhantom
  5.     http://www.phantomweb.org   ]]
  6. ------------------------------------------
  7.  
  8. --------------------------
  9. --[[    Configuration   ]]
  10. --------------------------
  11. local MinDelay=3;--         Response delay in seconds.
  12. local Cooldown=1;--         Minimal time between queued emotes in seconds.
  13. local CrossFactionEmote="PONDER";-- Emote sent to cross-faction players.
  14.  
  15. local NamePattern="%%N";--  Pattern for string.gsub() to replace with the sender's name
  16. local Triggers={--      This table contains the trigger phrases and reply type/messages.
  17.     ["licks you."]={"COWER"};
  18.     ["thinks you are a sexy devil."]={"gasps in shock at %N!"};
  19.     ["dances with you."]={"admires %N's moves.","DANCE"};
  20. }
  21.  
  22. --------------------------
  23. --[[    Constants   ]]
  24. --------------------------
  25. local PlayerGUID;-- We'll store this later when it's available
  26. local PlayerFaction=UnitFactionGroup("player");--   This should be available on load
  27.  
  28. local FactionRaces={
  29.     Dwarf       ="Alliance";
  30.     Draenei     ="Alliance";
  31.     Gnome       ="Alliance";
  32.     Human       ="Alliance";
  33.     NightElf    ="Alliance";
  34.     Worgen      ="Alliance";
  35.  
  36.     BloodElf    ="Horde";
  37.     Orc     ="Horde";
  38.     Goblin      ="Horde";
  39.     Scourge     ="Horde";
  40.     Tauren      ="Horde";
  41.     Troll       ="Horde";
  42. }
  43.  
  44. ----------------------------------
  45. --[[    Frame Setup & Events    ]]
  46. ----------------------------------
  47. local BRFrame=CreateFrame("Frame");
  48. BRFrame:RegisterEvent("CHAT_MSG_TEXT_EMOTE");-- Built-in emotes
  49. BRFrame:RegisterEvent("CHAT_MSG_EMOTE");--  Custom emotes
  50.  
  51. BRFrame.ResponseQueue={};
  52. BRFrame.Recycled={};
  53.  
  54. function BRFrame:QueueEmote(emote,target);
  55. --  Get a table from our recycled tables or create a new one
  56.     local tbl=self.Recycled[1] or {};
  57.     if #self.Recycled>1 then table.remove(self.Recycled,1); end
  58.  
  59. --  Setup data and insert
  60.     tbl[1],tbl[2],tbl[3]=emote,target,GetTime();
  61.     table.insert(self.Response,tbl);
  62. end
  63.  
  64. BRFrame:SetScript("OnEvent",function(self,event,...)
  65. --  We'll tag one of these events to update our GUID when it's needed
  66.     if not PlayerGUID then PlayerGUID=UnitGUID("player"); end
  67.  
  68.     local guid,msg,sender=select(12,...),...;
  69.     if guid~=PlayerGUID then--  Don't respond to emotes the player sent.
  70.         if FactionRaces[select(4,GetPlayerInfoByGUID(guid)) or ""]==PLAYER_FACTION then
  71. --          Queue cross-faction response
  72.             self:QueueEmote(CrossFactionEmote,sender);
  73.         else
  74.             local response=Triggers[msg:match("^%S+%s*(.-)$")];
  75.             if type(response)=="table" then--   Handle table syntax
  76.                 for i,j in ipairs(response) do self:QueueEmote(j,sender); end
  77.             elseif type(response=="string" then--   Handle single string
  78.                 self:QueueEmote(response,sender);
  79.             end
  80.         end
  81.     end
  82. end)
  83.  
  84. local lastemote=GetTime();
  85. BRFrame:SetScript("OnUpdate",function(self)
  86.     local now=GetTime();
  87.  
  88. --  Check against our global cooldown and if we have messages waiting
  89.     if now-lastemote>Cooldown and #self.ResponseQueue>0 then
  90.         local ptr=self.ResponseQueue[1];
  91.         if now-ptr[3]>MinDelay then--   Check for individual delay
  92. --          First attempt to check if it's a slash command for an emote
  93.             local token=hash_EmoteTokenList[ptr[1]:lower()];
  94.  
  95. --          If not, check if it's an actual token
  96.             if not token then
  97.                 for i,j in pairs(hash_EmoteTokenList) do
  98.                     if ptr[1]:upper()==j then token=j;break; end
  99.                 end
  100.             end
  101.  
  102. --          Token should be detected now if it's being referenced
  103.             if token then
  104.                 DoEmote(token,"none");--                Perform token emote with no target
  105.             else
  106.                 SendChatMessage(ptr[1]:gsub(NamePattern,ptr[2]),"EMOTE");-- Custom emote
  107.             end
  108.  
  109.             table.remove(self.ResponseQueue,1);--       Shift the queue
  110.             table.insert(self.Recycled,table.wipe(ptr));--  Clean and recycle table
  111.             lastemote=now;--    Reset our timestamp
  112.         end
  113.     end
  114. end);
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 02-19-12 at 05:44 AM.
  Reply With Quote
02-17-12, 03:26 PM   #10
Savaena
Warcrafter
 
Savaena's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 4
Wow- see, the way you write that out makes soooo much more sense to me when I look at it. I used to write batch files as a kid for my computer when I was bored, but never went beyond that, so my understanding of coding lua is limited.

However, your code here seems so much simpler. Overlaying an API on top of another one to start my first addon was probably not a wise idea. XD

I'm about to test this coding with just a toc file that loads the BlushResponse.lua. Were you able to successfully get your rewrite above to work for you?
  Reply With Quote
02-17-12, 05:43 PM   #11
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,326
I haven't had time to test it since I wrote the entire thing in about an hour before going to bed. If you have any problems with it, I'll load it in and try to debug it.

I started with MS-DOS batch files as a kid too.
I later moved onto HTML and Javascript, then just spread out from there over the many years.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 02-17-12 at 05:47 PM.
  Reply With Quote
02-18-12, 09:44 PM   #12
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
I see several problems with your code, SDPhantom...

#1 - You typed semicolons instead of commas inside a table definition:
Code:
local FactionRaces={
	Dwarf		="Alliance";
	Draenei		="Alliance";
#2 - You typed semicolons anywhere. Semicolons are not necessary or useful in Lua. All they do is to make your code harder to read by adding visual clutter.

#3 - The player's GUID is not available when the main chunk executes at login, so this will not work:
Code:
local PlayerGUID=UnitGUID("player");
It will work on a UI reload, but not on a fresh login.

#4 - I don't really know what you were trying to do here:
Code:
BRFrame:SetScript("OnEvent",function(self,event,...)
	local guid,msg,sender=select(12,...),...;
Not only will this not work the way you intended (select returns the specified value and all subsequent values, so this call will set guid=arg12, msg=arg13, and sender=arg14) but it's unnecessarily convoluted. Just name the values in the order they appear, "discarding" the unwanted ones with a junk name:
Code:
BRFrame:SetScript("OnEvent", function(self, event, ...)
	local msg, sender, _, _, _, _, _, _, _, _, _, guid = ...
Also, an addon isn't a space-limited macro. Conventional use of spaces makes your code much easier to read.

#5 - This:
Code:
	if guid~=PLAYER_GUID then--	Don't respond to emotes the player sent.
		-- Do stuff here.
	end
... is slower than this:
Code:
	if guid == PLAYER_GUID then return end -- Don't respond to emotes the player sent.

	-- Do stuff here.
If there's only one condition, and you simply want to "do nothing" if it isn't met, just return. Otherwise, the parser has to run through the whole block of code to see if there's an "else" keyword, or anything after the "if ... end" block, for it to execute.

I don't have time to go through it in more detail than that right now, but there may be other issues as well.

TL;DR: Don't write code while you are falling asleep.
  Reply With Quote
02-19-12, 05:19 AM   #13
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,326
#1: Lua allows use of both commas and semicolons as a field delimiter in table construction. I prefer semicolons when I have a single entry per line.

#2: I'm used to programming languages requiring lines ending with a semicolon. I know Lua doesn't, but it's my programming style to have them in anyway. It doesn't impact processing in any way and I see no need to remove them. As far as making code difficult to read, I don't believe that at all.

#3: I'll have to test this myself, but I was sure I had some player-specific data loading at addon load.
Edit: Tested and was found not to be one of the functions.

#4: It's unconventional and may not be the best way to do it, but the 12th arg is assigned to guid and the first and second are assigned to the other two. Note select() has values listed after it (another vararg), so the extra values from select() are dropped. I've used this before, I know it works and the process that makes it work.

#5: I see no difference in between the performance of these two methods. Again, it's up to programming style. You're running the same number of conditionals, the same checks, even the same relative structure. You move the end keyword up to the top, flip the condition, and add a return keyword for it to run. And the load time changes how?



Update: The GUID detection has been fixed in the code above. Another bug was missing a couple instances of the renamed upvalue.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 02-19-12 at 06:09 AM.
  Reply With Quote
02-19-12, 08:11 PM   #14
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by SDPhantom View Post
#3: I'll have to test this myself, but I was sure I had some player-specific data loading at addon load.
Edit: Tested and was found not to be one of the functions.
Many functions do return data at load, such as UnitClass, UnitName, UnitFactionGroup, etc.

Originally Posted by SDPhantom View Post
#4: It's unconventional and may not be the best way to do it, but the 12th arg is assigned to guid and the first and second are assigned to the other two. Note select() has values listed after it (another vararg), so the extra values from select() are dropped. I've used this before, I know it works and the process that makes it work.
It wastes CPU time by calling an extra function that does not need to be called, and the convoluted syntax is confusing, especially for people who are not experienced programmers (such as the one you wrote the code for).

Originally Posted by SDPhantom View Post
#5: I see no difference in between the performance of these two methods. Again, it's up to programming style. You're running the same number of conditionals, the same checks, even the same relative structure. You move the end keyword up to the top, flip the condition, and add a return keyword for it to run. And the load time changes how?
The load time (how much time it takes to read the file from disk and execute code in the main chunk) does not change, nor did I claim it did.

What does change is the time it takes to execute that code path. Here are two ways to write the same thing. Highlighted in yellow are the parts that actually execute when the function is called but you want it to do nothing:

Code:
function DoStuff(guid)
	if guid == playerGUID then
		return
	end
	-- Function stops here.

	if FactionRaces[select(4,GetPlayerInfoByGUID(guid)) or ""]==PLAYER_FACTION then
		self:QueueEmote(CrossFactionEmote,sender);
	else
		local response=Triggers[msg:match("^%S+%s*(.-)$")];
		if type(response)=="table" then--   Handle table syntax
			for i,j in ipairs(response) do self:QueueEmote(j,sender); end
		elseif type(response=="string" then--   Handle single string
			self:QueueEmote(response,sender);
		end
	end
end
Code:
function DoStuff(guid)
	if guid ~= playerGUID then
		if FactionRaces[select(4,GetPlayerInfoByGUID(guid)) or ""]==PLAYER_FACTION then
			self:QueueEmote(CrossFactionEmote,sender);
		else
			local response=Triggers[msg:match("^%S+%s*(.-)$")];
			if type(response)=="table" then--   Handle table syntax
				for i,j in ipairs(response) do self:QueueEmote(j,sender); end
			elseif type(response=="string" then--   Handle single string
				self:QueueEmote(response,sender);
			end
		end
	end
end
-- Function stops here.
Yes, returning early achieves the same end result. But, it is faster to return early and not look at the rest of the function at all, than to wrap the whole innards of the function in an "if" conditional, in which case the parser has to go through it and find the associated "end" statement and make sure there is no more code after it to run.
  Reply With Quote
02-20-12, 05:32 AM   #15
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,326
Still on the subject of returning form a function at the top instead of having the chunk in an entire conditional. It all depends on how the built-in compiler for Lua handles the situation. I wouldn't call it sufficient enough data to determine the magnitude of efficiency of either method unless run times are actually posted.

I ran the following code to test both methods:
lua Code:
  1. local function func1()
  2.     if true then return; end
  3.     for i=1,100 do
  4. --      Just some random code to skip
  5.     end
  6. end
  7.  
  8. local function func2()
  9.     if false then
  10.         for i=1,100 do
  11. --          Just some random code to skip
  12.         end
  13.     end
  14. end
  15.  
  16. SlashCmdList.RETURNTEST=function()
  17.     local math=math;
  18.     local l1,l2,h1,h2;
  19.     local s1,s2,c1,c2=0,0,0,0;
  20.     for i=1,1000000 do
  21.         local t1,t2;
  22.         debugprofilestart(); func1(); t1=debugprofilestop();
  23.         debugprofilestart(); func2(); t2=debugprofilestop();
  24.  
  25.         s1=s1+t1;
  26.         s2=s2+t2;
  27.  
  28.         if t1<t2 then
  29.             c1=c1+1;
  30.         elseif t1>t2 then
  31.             c2=c2+1;
  32.         end
  33.  
  34.         if not l1 then
  35.             l1,h1,l2,h2=t1,t1,t2,t2;
  36.         else
  37.             l1=math.min(l1,t1);
  38.             l2=math.min(l2,t2);
  39.             h1=math.max(h1,t1);
  40.             h2=math.max(h2,t2);
  41.         end
  42.     end
  43.  
  44.     print("Totals",s1,s2);
  45.     print("Avg",s1/1000000,s2/1000000);
  46.     print("High",h1,h2);
  47.     print("Low",l1,l2);
  48.     print("Best",c1,c2);
  49.     print("Equal",1000000-c1-c2);
  50. end;
  51.  
  52. SLASH_RETURNTEST1="/rettest";

What this does is run each function 1 million times and take times it takes the function to return as well as mark high and low times and compare which runs one function performs better than the other and through which, when both return within the exact same time.

If there was any clear "better" method, you'd see most of the run counts add to one side consistently instead of half being in equal and split results to each side.

One such data set would look like:
Code:
Totals	1348.4072361132		1372.1531647122
Avg	0.0013484072361132	0.0013721531647122
High	6.8232135648525		2.1843558329341
Low	0.000838095344534452004	0.000838095344534452004
Best	247849			173504
Equal	578647
Note the first column is values of func1() while the second is func2().
Also increasing the payload of skipped code does not seem to affect times at all.

Update: For some strange reason, Lua seems to favor which of the functions runs first. Values trend one way when func1() runs before func2() and another when the order is reversed.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 02-20-12 at 05:36 AM.
  Reply With Quote
02-20-12, 11:31 AM   #16
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Regardless of real or imagined parsing time differences, I prefer the early-bailout method to reduce nesting which in turn increases clarity.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
02-20-12, 01:28 PM   #17
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,326
Just proving there isn't much if any difference at all and the moot point of criticizing one's programming style when the situation is such.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
02-21-12, 04:27 AM   #18
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
People are different and have different opinions. There may be general consense but that doesn't mean you have to follow. You don't have to prove yourself for thinking different.
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)
  Reply With Quote

WoWInterface » Developer Discussions » General Authoring Discussion » Need Lua Coding Help!


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off