• This is a read only backup of the old Emudevs forum. If you want to have anything removed, please message me on Discord: KittyKaev

The babbling NPC script

slp13at420

Mad Scientist
<SHARE>
<MOD>

Credits to [MENTION=6]Rochet2[/MENTION] for original script.
Credits to [MENTION=717]slp13at420[/MENTION] for the modifications.

--Built-Tested and Approved for TC2 3.3.5a Eluna ONLY--

this is a fun little script with some bells and whistles.
you can apply this script to 1 or many npc's.(I Don't Recommend you use this for Mobs)
this will cause an npc to randomly yell or say statements from the table(ANN) in the script(based on an adjustable timed event)
you can also have the npc `Emote` and/or cast a spell whenever they fire a statement.
you can make it use multiple statements by linking them.
simple instructions are listed within the script to help explain how to set things.
this will only get triggered when a player is within range to trigger event 27, after that the npc will chatter non-stop.
you can set it so the npc will chatter non-stop once its triggered or only chatter when it gets triggered.

>> Babbling NPC <<

He's a rude, drunken, foul mouthed robot.
don't you just .. luv him.?


timers sped up for video.

Update - slimmed it down a bit
-- remove the sub-announce function
-- condensed the simple if statements
-- fixed the issue with the npc in an idle map
rather than using a value to tell the trigger its
clear to fire again , it now just checks the last
time it fired to the current time.
 
Last edited:

Grandelf

Esteemed Member
Let me start by saying that I really like that you used the 'CREATURE_EVENT_ON_MOVE_IN_LOS', I don't see
why you would still want an option for a continuous loop because this is so much cleaner =P.

The script itself, I didn't like it very much. You are accessing the 'ANN' table a lot, which could easily be
prevented by storing it in some local variables.
You're calling:
Code:
local ostime = tonumber(GetGameTime())
local seed = (ostime*ostime)
math.randomseed(seed)
inside a function, that can be called up to a hundred times a second (depends on how many creatures use this script),
when a simple math.randomseed(os.time()) at the bottom of the script would suffice.
Then (this was actually shocking) you are creating and deleting tables continuously:
Code:
creature:RemoveEvents()
ANN[creature:GetGUIDLow()] = nil;
		
if(allways == 1)then -- checks switch for continuous 1
	creature:RegisterEvent(TimedSay, delay, cycles)
	ANN[creature:GetGUIDLow()] = {reset = 1,};
end
Why not check if 'always' = 0 and remove the table then if it is, or even better, use 'allways' as the last argument
of RegisterEvent and use 1 to imply that the event is only registered once, instead of 0.
One more minor thing, you might have done this by accident though,
Code:
ANN[npcid] = {
	...
}
You're using a table as the key of ANN, imagine 'npcid' having a 100 value's in it, not exactly what I'd use for a key.
Might want to use a string or an integer there.

I'm not quite sure what happened to you when you were scripting this, usually your scripts are better =P, maybe a headache eh?
Anyway that's quite a list, so I decided to modify your script and post it here to illustrate what I meant.

http://pastebin.com/CBk4Y4J4

That's about it I guess, just an idea on the side of all this though. Right now when a player moves in LoS, it takes a 'delay' (30)
amount of seconds for the message to kick in. If it's a low populated area / server there is a big chance the player won't even see it.
Why not show the message when the players moves in LoS and then add a cooldown of 'delay' seconds? But that is just a thought.

Grandelf.
 

slp13at420

Mad Scientist
Let me start by saying that I really like that you used the 'CREATURE_EVENT_ON_MOVE_IN_LOS', I don't see
why you would still want an option for a continuous loop because this is so much cleaner =P.

ye I found that is the only way I could re-trigger this after a `reload Eluna` command happens, and it is part of the way I want it to work by the time i'm finished with it. my goal is to have it triggered by player motion then continue to cycle when it GetPlayersInRange() returns true or go idle if it returns false..
yea he was a little hard to understand so I threw in the ability to choose how it will fire.

The script itself, I didn't like it very much. You are accessing the 'ANN' table a lot, which could easily be
prevented by storing it in some local variables.

yep I dropped the ball on that lol actually I think I can do this also for the same goal:

Code:
local msg, say, linked, emote, spellid = table.unpack(ANN["says"][id])

You're calling:
Code:
local ostime = tonumber(GetGameTime())
local seed = (ostime*ostime)
math.randomseed(seed)
inside a function, that can be called up to a hundred times a second (depends on how many creatures use this script),
when a simple math.randomseed(os.time()) at the bottom of the script would suffice.

ooops thank you fixed.

Then (this was actually shocking) you are creating and deleting tables continuously:
Code:
creature:RemoveEvents()
ANN[creature:GetGUIDLow()] = nil;

huh what ermm crap yea your right I dunno what went thru my mind at that moment :rofl:

if(allways == 1)then -- checks switch for continuous 1
creature:RegisterEvent(TimedSay, delay, cycles)
ANN[creature:GetGUIDLow()] = {reset = 1,};
end[/CODE]
Why not check if 'always' = 0 and remove the table then if it is, or even better, use 'allways' as the last argument
of RegisterEvent and use 1 to imply that the event is only registered once, instead of 0.

yea that would work perfect , just reverse the values impact then I could use that variable for both the check and the RegisterEvent()

One more minor thing, you might have done this by accident though,
Code:
ANN[npcid] = {
	...
}
You're using a table as the key of ANN, imagine 'npcid' having a 100 value's in it, not exactly what I'd use for a key.
Might want to use a string or an integer there.

yea oooops total accident I didn't even think about the table key. good catch thank you .fixed.

I'm not quite sure what happened to you when you were scripting this, usually your scripts are better =P, maybe a headache eh?
Anyway that's quite a list, so I decided to modify your script and post it here to illustrate what I meant.

http://pastebin.com/CBk4Y4J4

That's about it I guess, just an idea on the side of all this though. Right now when a player moves in LoS, it takes a 'delay' (30)
amount of seconds for the message to kick in. If it's a low populated area / server there is a big chance the player won't even see it.
Why not show the message when the players moves in LoS and then add a cooldown of 'delay' seconds? But that is just a thought.

Grandelf.

thank you
:RpS_thumbsup:
 
Last edited:

slp13at420

Mad Scientist
I will be updating it over the next few days and I think I can get it to work the way I want it to for efficiency :D
I want it to trigger by event 27 then continue to fire when GetPlayersInRange() returns true. I think this would be the most efficient way to deploy this.
 

Grandelf

Esteemed Member
I will be updating it over the next few days and I think I can get it to work the way I want it to for efficiency :D
I want it to trigger by event 27 then continue to fire when GetPlayersInRange() returns true. I think this would be the most efficient way to deploy this.

Actually, I think event 27 will do the job. No need to check for players in range because, players are always moving around. Idk, it's just what they do =P,
and for as long as players are moving, event 27 will trigger 'TimedSay'.
 

slp13at420

Mad Scientist
Actually, I think event 27 will do the job. No need to check for players in range because, players are always moving around. Idk, it's just what they do =P,
and for as long as players are moving, event 27 will trigger 'TimedSay'.

that's actually a fun idea . place the npc out in the field some where , a player happens to run by and it starts to yell at them. so they stop to investigate and the npc stops talking. so they head back out and it starts yelling at them again lol.
me personally use 1 npc that is spawned at 3 malls so I was accommodating for players standing at vendors not moving.
 

slp13at420

Mad Scientist
ok now this will trigger when a player is moving then continue if there are players within a certain distance (40 game yards)or players are continuing to move about.
or I should say sofar it is appearing to work that way while testing and observing lol.
beta:
Code:
[COLOR="#808080"]
-- request by Vitrex
-- Script by Rochet2 of EmuDevs
-- Updated by slp13at420 of EmuDevs
-- drunken slurred outbursts by Bender

-- The npc will start once a player is moving 
-- close enough to trigger event 27 then it will babble non-stop while players are idle near it.
-- then the npc will automaticaly go idle when no players around.

-- I DONT Recommend you use this on Mobs. that could cause lag and freezing of your core.

local npcid = {60000}; -- you can apply this to one or multiple npc's here.
local delay = (1*10*1000) -- 30 seconds
local range = 15 -- the distance an idle player can be from the npc to trigger a continuous outburst. 

local  ANN = {}; 

-- {Statement, stated, linked, emote, spellid} 
-- statement // in quotes "blah blah" 
-- stated // say = 0 // yell = 1 :: how its announced
-- linked // table key id if your using multiple statements  for one announcement i.e.(yell THEN say)
-- emote // talk = 1 // yell = 5// Question = 6 // Dance = 10 // Rude = 14 // shout = 22 // 
-- spellid // spell id
-- http://collab.kpsn.org/display/tc/Emote

ANN["Bender"] = {-- {"Statement", stated, linked, emote, spellid} 
	[1] = {"Well,, that was dumb.", 0, 0, 1, 0}, -- say, Emote 1, spell 58837
	[2] = {"!Bite my shiney metal ass!", 1, 0, 14, 0}, -- yell, Emote 14
	[3] = {"Well,, were boned.", 0, 0, 1, 0}, -- say, Emote 1
	[4] = {"Hey sexy momma .. Wanna kill all humans..??.", 0, 0, 1, 0}, -- say, Emote 1, spell 58837
	[5] = {"Goodbye losers whom I allways hated", 1, 0, 22, 0}, -- yell, emote 22
	[6] = {"!Shut the hell up!", 1, 0, 5, 0}, -- yell, emote 5
	[7] = {"Would you kindly shut your noise hole?", 0, 0, 1, 0}, -- say, emote 1
	[8] = {"I'm gonna go build my own theme park.. with blackjack and hookerz.", 0, 101, 1, 0}, -- say, emote 1, links to 101
	[9] = {"Who are you and why should i care?", 0, 0, 25, 0}, -- say, emote 25
	[10] = {"!Shut up and Pay attention To Me !!.. !!BENDER!!", 1, 0, 5, 0}, -- yell , emote 5
	[11] = {"Hasta La Vista , Meat bag.", 0, 0, 1, 0},
	[12] = {"Awww, heres a little song i wrote to cheer you up. Its called ", 0, 100, 1, 0}, -- links to 100
	[13] = {"Do the Bender ,, Do the Bender ,, its your birthday ,, do the bender", 0, 0, 10, 0},
	[14] = {"Shut up baby , you love it", 0, 0, 1, 0}, -- uses spell
	[100] = {"!!Let's go allready!!", 1, 0, 5, 0}, -- linked from 8
	[101] = {"In fact ,, forget the park.", 0, 0, 1, 0}, -- linked from 12
		};
		
local function Drop_Event_On_Death(eventid, creature, killer) -- removes ALL events upon death of npc.
	ANN[creature:GetGUIDLow()] = {reset = 0,};
	creature:RemoveEvents()
end

local function Announce(id, creature)
	
local statement, stated, linked, emote, spellid = table.unpack(ANN["Bender"][id])

	if(emote ~= (nil or 0))then creature:Emote(emote); end -- check emote column for emote.

	if(stated == 0)then creature:SendUnitSay(statement, 0); end-- check stated column if say.

	if(stated == 1)then creature:SendUnitYell(statement, 0); end -- check stated column if yell.

	if(linked ~= (nil or 0))then Announce(linked, creature); end -- check the linked column for key id.

	if(spellid ~= (nil or 0))then creature:CastSpell(creature, spellid); end-- check the spellid column for spell id.
end

local function TimedSay(eventId, duration, repeats, creature)

Announce(math.random(#ANN["Bender"]), creature)

	if(#creature:GetPlayersInRange(range) >= 1)then -- check for continue if idle players are still in range.
		creature:RegisterEvent(TimedSay, delay, 1)
		ANN[creature:GetGUIDLow()] = {reset = 1,};
	else
		ANN[creature:GetGUIDLow()] = {reset = 2,};
	end
end

local function OnMotion(event, creature, unit)

	if(unit:GetObjectType()=="Player")then

		if((ANN[creature:GetGUIDLow()] == nil)or(ANN[creature:GetGUIDLow()].reset == (nil or 0)))then
			ANN[creature:GetGUIDLow()] = {reset = 1,};
			TimedSay(1, delay, 1, creature)
		end
		
		if(ANN[creature:GetGUIDLow()].reset == 2)then
 			ANN[creature:GetGUIDLow()] = {reset = 1,};
			creature:RemoveEvents()
			creature:RegisterEvent(TimedSay, delay, 1)
 		end

	end
end

for npc=1, #npcid do

	RegisterCreatureEvent(npcid[npc], 27, OnMotion) 
	RegisterCreatureEvent(npcid[npc], 4, Drop_Event_On_Death)

end

math.randomseed(os.time());
[/COLOR]

He's a rude, drunken, foul mouthed robot.
don't you just .. luv him.?

imagine a boss spouting out these random comments lol xD

10-25-2014 update
expanded the linked portion.. added a timer and a function to allow multi-linked statements. with a pre-setup example using key 10 that links to 102 and that links to 103. with a settable timer variable.
 
Last edited:

Xaver

Respected Member
hi slp13at420

you final script ist very good but i have found one last problem,

the script ist work and have problem when script work 2-3 hours the script no reload

example:

i have change Player to Creature and work but im join 3h later on my server then stopped script, this have too test on Player when change on Player target and reload script its work ok.. then 2-3 hours later i´m join my server no work. what is this ??

thx
 

slp13at420

Mad Scientist
Cataclysm or WotlK ?
WotlK it runs smooth as glass.
Cataclysm the announcements work fine but the methods Despawn()/RemoveFromWorld() I am having issues with..
but I think I need to compile a new cata solution .
I can recreate that in wotlk so give me some time to figure out `wtf?` lol

thank you for catching that :D
okay re-clone the repo https://github.com/BlackWolfsDen/misc/blob/master/Yelling_npc_Script_Auto.lua

I think I got it working when relogs happen.
its that switch that tells the OnMotion function if it should do something or nothing
just gotta get it to switch at the right time. lol

give it a try n let me know :D
 
Last edited:

Xaver

Respected Member
Report:

sry my freand the script stoped too 1 h.

when reload all scripts is new start and work.

Info: i have change Player to Creature.
 

slp13at420

Mad Scientist
if you set line 138 to check if "Creature" then it won't react to ANY player movement .. only creature movement will trigger it with that check..

so even if a player is standing next to the npc and jumping they wont trigger it .. Only another creature moving within range to trigger event 27 will cause it to work...

the method GetObjectType() is used to check WHAT has triggered it , and if its a "Player" then continue other wise if it's not a "Player" then do nothing and end.

line 138:
Code:
if(unit:GetObjectType()=="Player")then
-- unit is the `unit` triggering event 27
-- this is to check if its a player.
 

Xaver

Respected Member
but even if I have active player still does that happen

and with me also move creatures (waypoints)
 

slp13at420

Mad Scientist
what might be happening is the close npc's movement(way points) are triggering event 27 while a player is within range for line 128 to return true then it will continue to work since there is a player in range(15).
 

Xaver

Respected Member
WoWScrnShot_103014_133319.jpgWoWScrnShot_103014_133332.jpg

1. pic show when player run and jump no work

2. pic show when have reload script

script is on player:

Code:
if(unit:GetObjectType()=="Player")then
 

Grandelf

Esteemed Member
There's an explanation for this, the first time a player moves in LoS, ANN = nil and this code is triggered:
Code:
local function OnMotion(event, creature, unit)

	if(unit:GetObjectType()=="Player")then
	
		local cGuid = creature:GetGUIDLow();
		
		if((ANN[cGuid] == nil)or(ANN[cGuid].reset == (nil or 0)))then -- j/k flip flip fresh trigger 
			ANN[cGuid] = {reset = 1,}; -- set j/k to 1
			TimedSay(1, delay, 1, creature)
		end
		
		if(ANN[cGuid].reset == 2)then -- (NO players are within preset range.) but motion was triggered.
 			ANN[cGuid] = {reset = 1,}; -- set j/k back to position 1
			local ctimer = creature:RegisterEvent(TimedSay, delay, 1)
 		end
	end
end
When the player stays in range this code will keep re-registering 'TimedSay':
Code:
	if(#creature:GetPlayersInRange(range) >= 1)then -- (ANN[cGuid].reset == 1)and
		Announce(math.random(#ANN["Bender"]), creature) -- sends the data to Announce function
		ANN[cGuid] = {reset = 1,}; -- set to 1 (Yes players are within preset range.)
		local ctimer = creature:RegisterEvent(TimedSay, delay, 1) -- time to annoy those idle players
	else
		Announce(math.random(#ANN["Bender"]), creature) -- sends the data to Announce function
	end
However, if there are no players in range anymore, this function will not re-register 'TimedSay', nor will it change ANN to nil or 0
so the 'OnMotion' function can trigger 'TimedSay' again and therefor no code will be executed anymore after a player got in range
and out of range again. So you should add a ANN[cGuid].reset = 1 to the 'TimedSay' function in the case of no players in range, so
the OnMotion even can trigger 'TimedSay' again.
 

slp13at420

Mad Scientist
(nil or 0) : is for a first time trigger either from a reload/restart or from a creature respawning from death or new spawn.
1 : is for a timed event active.
2 : is for clear to start another timed event.

what it appears to be happening is when the map goes neutral(no players in the map then the timer expires but the function doesn't get called(TimedSay) so reset continues to have a value of 1 and there is know way to reset it back to (nil or 0) or 2. butttttt I have an idea to compare game time ... so testing lol gotta add a kinda `catch-22` for this .. make a check for value 1 then cross check time lapse and if its more than the delay value then restart the event.
 
Last edited:

Xaver

Respected Member
emm the mall is neutral i have change this area on dbc.

can say line what change on script pls ? when found this problem ^^.
 

Grandelf

Esteemed Member
Right .. I missed the else in this line xd :
Code:
if(linked ~= (nil or 0))then local ctimer = creature:RegisterEvent(sub_announce, sub_timer, 1) ANN["BENDER"] = {link = linked,}; else ANN[cGuid] = {reset = 2,}; end -- check the linked column for key id. -- time to annoy those idle players Announce(linked, creature)
I still don't see why you would want a continious loop after onMotion triggered, seeing as onMotion will handle everything (range, active players),
and if they aren't moving they are prob afk, buying gear or whatever and they will not pay attention to the message anyway. But those are just
my thoughts =P.

Anyway, I had an idea myself on how to fix this but when I went through your code I noticed some repetitive code, loads of useless assignments
and the readability of this script wasn't the best (no offense). So I decided to rewrite the code. What I did was basically removing a lot of stuff
you don't really need. Also have I fixed that problem, when there are no players in the zone anymore the npc will stop announcing but it will
start back up again when there are players in range.

I didn't add the npc / gobject spawns, simply because I don't like spawns, I was lazy and idk .. xd that's up to you I guess.

http://pastebin.com/7779uMHc

Edit: I figured there are still flaws in this script, it is probably better to pass the guid around and check if the unit is active to prevent errors,
because now I force the function to be called after a few seconds whether the unit is active or not. I don't have time edit it today but I will
most likely post something tomorrow ;).
 
Last edited:
Top