I apologize in advance if the image is huge. I store the state of someone's data online once you save, but everything else is handled locally. I have a dedicated slot in my array for each quest. I also have a private variable for each npc if I want them to rotate through a few sayings. In this example, I'm showing how the status of the quest that pertains to this NPC changes what they say or what actions happen.
My array, called Inv is what we're looking at. At 0,7 is the quest for this person. For all of my regular quests, 0 means it is available, 1 means it is currently in-progress, 2 means it is complete.
I also have a check if the NPC is within range (it sets them to "Active = 1" if they are). I have an invisible rectangle with an id variable that references each npc, then I set the visuals for them to that position. I also check the "Speech" layer opacity so it won't let you open up another conversation if one is currently open. In the first event, if 0,7 is 2, then it shows a message if you've completed the quest. I have subevents that test if a certain story element is running or not, too. The second event runs if the quest is open and starts the dialogue for if you want to accept the quest.
Just so you can follow it all, if Speechon = 1, it fades in my speech layer. There is a triangle "Play" button that progresses. There is a variable on there called "more". If it is 0 and you hit the play button is sets Speechon to 0 and fades it back out. If more = 1, it doesn't.
I know there's a lot there, but you only have to write that once and then copy-paste and switch numbers/text for the rest of the NPCs. I'm sure someone may have some more elegant solution, likely using functions, but I really like being able to follow as much as possible right there.
But I hope that example helps!