npc_chat.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #ifdef PCRE_SUPPORT
  4. #include <stdio.h>
  5. #include <ctype.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <stdarg.h>
  9. #include <time.h>
  10. #include "../common/timer.h"
  11. #include "../common/malloc.h"
  12. #include "../common/version.h"
  13. #include "../common/nullpo.h"
  14. #include "../common/showmsg.h"
  15. #include "map.h"
  16. #include "status.h"
  17. #include "npc.h"
  18. #include "chat.h"
  19. #include "script.h"
  20. #include "battle.h"
  21. #include "pcre.h"
  22. /**
  23. * Written by MouseJstr in a vision... (2/21/2005)
  24. *
  25. * This allows you to make npc listen for spoken text (global
  26. * messages) and pattern match against that spoken text using perl
  27. * regular expressions.
  28. *
  29. * Please feel free to copy this code into your own personal ragnarok
  30. * servers or distributions but please leave my name. Also, please
  31. * wait until I've put it into the main eA branch which means I
  32. * believe it is ready for distribution.
  33. *
  34. * So, how do people use this?
  35. *
  36. * The first and most important function is defpattern
  37. *
  38. * defpattern 1, "[^:]+: (.*) loves (.*)", "label";
  39. *
  40. * this defines a new pattern in set 1 using perl syntax
  41. * (http://www.troubleshooters.com/codecorn/littperl/perlreg.htm)
  42. * and tells it to jump to the supplied label when the pattern
  43. * is matched.
  44. *
  45. * each of the matched Groups will result in a variable being
  46. * set ($p1$ through $p9$ with $p0$ being the entire string)
  47. * before the script gets executed.
  48. *
  49. * activatepset 1;
  50. *
  51. * This activates a set of patterns.. You can have many pattern
  52. * sets defined and many active all at once. This feature allows
  53. * you to set up "conversations" and ever changing expectations of
  54. * the pattern matcher
  55. *
  56. * deactivatepset 1;
  57. *
  58. * turns off a pattern set;
  59. *
  60. * deactivatepset -1;
  61. *
  62. * turns off ALL pattern sets;
  63. *
  64. * deletepset 1;
  65. *
  66. * deletes a pset
  67. */
  68. /* Structure containing all info associated with a single pattern
  69. block */
  70. struct pcrematch_entry {
  71. struct pcrematch_entry *next_;
  72. char *pattern_;
  73. pcre *pcre_;
  74. pcre_extra *pcre_extra_;
  75. char *label_;
  76. };
  77. /* A set of patterns that can be activated and deactived with a single
  78. command */
  79. struct pcrematch_set {
  80. struct pcrematch_set *next_, *prev_;
  81. struct pcrematch_entry *head_;
  82. int setid_;
  83. };
  84. /*
  85. * Entire data structure hung off a NPC
  86. *
  87. * The reason I have done it this way (a void * in npc_data and then
  88. * this) was to reduce the number of patches that needed to be applied
  89. * to a ragnarok distribution to bring this code online. I
  90. * also wanted people to be able to grab this one file to get updates
  91. * without having to do a large number of changes.
  92. */
  93. struct npc_parse {
  94. struct pcrematch_set *active_;
  95. struct pcrematch_set *inactive_;
  96. };
  97. /**
  98. * delete everythign associated with a entry
  99. *
  100. * This does NOT do the list management
  101. */
  102. void finalize_pcrematch_entry(struct pcrematch_entry *e) {
  103. //TODO: For some odd reason this causes a already-free'd error under Windows, but not *nix! [Skotlex]
  104. #ifndef _WIN32
  105. if (e->pcre_) {
  106. free(e->pcre_);
  107. e->pcre_ = NULL;
  108. }
  109. #endif
  110. if (e->pcre_extra_) {
  111. free(e->pcre_extra_);
  112. e->pcre_ = NULL;
  113. }
  114. aFree(e->pattern_);
  115. aFree(e->label_);
  116. }
  117. /**
  118. * Lookup (and possibly create) a new set of patterns by the set id
  119. */
  120. static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid)
  121. {
  122. struct pcrematch_set *pcreset;
  123. struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
  124. if (npcParse == NULL)
  125. nd->chatdb = npcParse = (struct npc_parse *)
  126. aCalloc(sizeof(struct npc_parse), 1);
  127. pcreset = npcParse->active_;
  128. while (pcreset != NULL) {
  129. if (pcreset->setid_ == setid)
  130. break;
  131. pcreset = pcreset->next_;
  132. }
  133. if (pcreset == NULL)
  134. pcreset = npcParse->inactive_;
  135. while (pcreset != NULL) {
  136. if (pcreset->setid_ == setid)
  137. break;
  138. pcreset = pcreset->next_;
  139. }
  140. if (pcreset == NULL) {
  141. pcreset = (struct pcrematch_set *)
  142. aCalloc(sizeof(struct pcrematch_set), 1);
  143. pcreset->next_ = npcParse->inactive_;
  144. if (pcreset->next_ != NULL)
  145. pcreset->next_->prev_ = pcreset;
  146. pcreset->prev_ = 0;
  147. npcParse->inactive_ = pcreset;
  148. pcreset->setid_ = setid;
  149. }
  150. return pcreset;
  151. }
  152. /**
  153. * activate a set of patterns.
  154. *
  155. * if the setid does not exist, this will silently return
  156. */
  157. static void activate_pcreset(struct npc_data *nd,int setid) {
  158. struct pcrematch_set *pcreset;
  159. struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
  160. if (npcParse == NULL)
  161. return; // Nothing to activate...
  162. pcreset = npcParse->inactive_;
  163. while (pcreset != NULL) {
  164. if (pcreset->setid_ == setid)
  165. break;
  166. pcreset = pcreset->next_;
  167. }
  168. if (pcreset == NULL)
  169. return; // not in inactive list
  170. if (pcreset->next_ != NULL)
  171. pcreset->next_->prev_ = pcreset->prev_;
  172. if (pcreset->prev_ != NULL)
  173. pcreset->prev_->next_ = pcreset->next_;
  174. else
  175. npcParse->inactive_ = pcreset->next_;
  176. pcreset->prev_ = NULL;
  177. pcreset->next_ = npcParse->active_;
  178. if (pcreset->next_ != NULL)
  179. pcreset->next_->prev_ = pcreset;
  180. npcParse->active_ = pcreset;
  181. }
  182. /**
  183. * deactivate a set of patterns.
  184. *
  185. * if the setid does not exist, this will silently return
  186. */
  187. static void deactivate_pcreset(struct npc_data *nd,int setid) {
  188. struct pcrematch_set *pcreset;
  189. struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
  190. if (npcParse == NULL)
  191. return; // Nothing to deactivate...
  192. if (setid == -1) {
  193. while(npcParse->active_ != NULL)
  194. deactivate_pcreset(nd, npcParse->active_->setid_);
  195. return;
  196. }
  197. pcreset = npcParse->active_;
  198. while (pcreset != NULL) {
  199. if (pcreset->setid_ == setid)
  200. break;
  201. pcreset = pcreset->next_;
  202. }
  203. if (pcreset == NULL)
  204. return; // not in active list
  205. if (pcreset->next_ != NULL)
  206. pcreset->next_->prev_ = pcreset->prev_;
  207. if (pcreset->prev_ != NULL)
  208. pcreset->prev_->next_ = pcreset->next_;
  209. else
  210. npcParse->active_ = pcreset->next_;
  211. pcreset->prev_ = NULL;
  212. pcreset->next_ = npcParse->inactive_;
  213. if (pcreset->next_ != NULL)
  214. pcreset->next_->prev_ = pcreset;
  215. npcParse->inactive_ = pcreset;
  216. }
  217. /**
  218. * delete a set of patterns.
  219. */
  220. static void delete_pcreset(struct npc_data *nd,int setid) {
  221. int active = 1;
  222. struct pcrematch_set *pcreset;
  223. struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
  224. if (npcParse == NULL)
  225. return; // Nothing to deactivate...
  226. pcreset = npcParse->active_;
  227. while (pcreset != NULL) {
  228. if (pcreset->setid_ == setid)
  229. break;
  230. pcreset = pcreset->next_;
  231. }
  232. if (pcreset == NULL) {
  233. active = 0;
  234. pcreset = npcParse->inactive_;
  235. while (pcreset != NULL) {
  236. if (pcreset->setid_ == setid)
  237. break;
  238. pcreset = pcreset->next_;
  239. }
  240. }
  241. if (pcreset == NULL)
  242. return;
  243. if (pcreset->next_ != NULL)
  244. pcreset->next_->prev_ = pcreset->prev_;
  245. if (pcreset->prev_ != NULL)
  246. pcreset->prev_->next_ = pcreset->next_;
  247. if(active)
  248. npcParse->active_ = pcreset->next_;
  249. else
  250. npcParse->inactive_ = pcreset->next_;
  251. pcreset->prev_ = NULL;
  252. pcreset->next_ = NULL;
  253. while (pcreset->head_) {
  254. struct pcrematch_entry *n = pcreset->head_->next_;
  255. finalize_pcrematch_entry(pcreset->head_);
  256. aFree(pcreset->head_); // Cleanin' the last ones.. [Lance]
  257. pcreset->head_ = n;
  258. }
  259. aFree(pcreset);
  260. }
  261. /**
  262. * create a new pattern entry
  263. */
  264. static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set) {
  265. struct pcrematch_entry * e = (struct pcrematch_entry *)
  266. aCalloc(sizeof(struct pcrematch_entry), 1);
  267. struct pcrematch_entry * last = set->head_;
  268. // Normally we would have just stuck it at the end of the list but
  269. // this doesn't sink up with peoples usage pattern. They wanted
  270. // the items defined first to have a higher priority then the
  271. // items defined later.. as a result, we have to do some work up
  272. // front..
  273. /* if we are the first pattern, stick us at the end */
  274. if (last == NULL) {
  275. set->head_ = e;
  276. return e;
  277. }
  278. /* Look for the last entry */
  279. while (last->next_ != NULL)
  280. last = last->next_;
  281. last->next_ = e;
  282. e->next_ = NULL;
  283. return e;
  284. }
  285. /**
  286. * define/compile a new pattern
  287. */
  288. void npc_chat_def_pattern(struct npc_data *nd, int setid,
  289. const char *pattern, const char *label)
  290. {
  291. const char *err;
  292. int erroff;
  293. struct pcrematch_set * s = lookup_pcreset(nd, setid);
  294. struct pcrematch_entry *e = create_pcrematch_entry(s);
  295. e->pattern_ = aStrdup(pattern);
  296. e->label_ = aStrdup(label);
  297. e->pcre_ = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL);
  298. e->pcre_extra_ = pcre_study(e->pcre_, 0, &err);
  299. }
  300. /**
  301. * Delete everything associated with a NPC concerning the pattern
  302. * matching code
  303. *
  304. * this could be more efficent but.. how often do you do this?
  305. */
  306. void npc_chat_finalize(struct npc_data *nd)
  307. {
  308. struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
  309. if (npcParse == NULL)
  310. return;
  311. while(npcParse->active_)
  312. delete_pcreset(nd, npcParse->active_->setid_);
  313. while(npcParse->inactive_)
  314. delete_pcreset(nd, npcParse->inactive_->setid_);
  315. // Additional cleaning up [Lance]
  316. aFree(npcParse);
  317. }
  318. /**
  319. * Handler called whenever a global message is spoken in a NPC's area
  320. */
  321. int npc_chat_sub(struct block_list *bl, va_list ap)
  322. {
  323. struct npc_data *nd = (struct npc_data *)bl;
  324. struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
  325. char *msg;
  326. int len, pos, i;
  327. struct map_session_data *sd;
  328. struct npc_label_list *lst;
  329. struct pcrematch_set *pcreset;
  330. // Not interested in anything you might have to say...
  331. if (npcParse == NULL || npcParse->active_ == NULL)
  332. return 0;
  333. msg = va_arg(ap,char*);
  334. len = va_arg(ap,int);
  335. sd = va_arg(ap,struct map_session_data *);
  336. // grab the active list
  337. pcreset = npcParse->active_;
  338. // interate across all active sets
  339. while (pcreset != NULL) {
  340. struct pcrematch_entry *e = pcreset->head_;
  341. // interate across all patterns in that set
  342. while (e != NULL) {
  343. int offsets[20];
  344. char buf[255];
  345. // perform pattern match
  346. int r = pcre_exec(e->pcre_, e->pcre_extra_, msg, len, 0,
  347. 0, offsets, sizeof(offsets) / sizeof(offsets[0]));
  348. if (r >= 0) {
  349. // save out the matched strings
  350. switch (r) {
  351. case 10:
  352. memcpy(buf, &msg[offsets[18]], offsets[19]);
  353. buf[offsets[19]] = '\0';
  354. set_var(sd, "$p9$", buf);
  355. case 9:
  356. memcpy(buf, &msg[offsets[16]], offsets[17]);
  357. buf[offsets[17]] = '\0';
  358. set_var(sd, "$p8$", buf);
  359. case 8:
  360. memcpy(buf, &msg[offsets[14]], offsets[15]);
  361. buf[offsets[15]] = '\0';
  362. set_var(sd, "$p7$", buf);
  363. case 7:
  364. memcpy(buf, &msg[offsets[12]], offsets[13]);
  365. buf[offsets[13]] = '\0';
  366. set_var(sd, "$p6$", buf);
  367. case 6:
  368. memcpy(buf, &msg[offsets[10]], offsets[11]);
  369. buf[offsets[11]] = '\0';
  370. set_var(sd, "$p5$", buf);
  371. case 5:
  372. memcpy(buf, &msg[offsets[8]], offsets[9]);
  373. buf[offsets[9]] = '\0';
  374. set_var(sd, "$p4$", buf);
  375. case 4:
  376. memcpy(buf, &msg[offsets[6]], offsets[7]);
  377. buf[offsets[7]] = '\0';
  378. set_var(sd, "$p3$", buf);
  379. case 3:
  380. memcpy(buf, &msg[offsets[4]], offsets[5]);
  381. buf[offsets[5]] = '\0';
  382. set_var(sd, "$p2$", buf);
  383. case 2:
  384. memcpy(buf, &msg[offsets[2]], offsets[3]);
  385. buf[offsets[3]] = '\0';
  386. set_var(sd, "$p1$", buf);
  387. case 1:
  388. memcpy(buf, &msg[offsets[0]], offsets[1]);
  389. buf[offsets[1]] = '\0';
  390. set_var(sd, "$p0$", buf);
  391. }
  392. // find the target label.. this sucks..
  393. lst=nd->u.scr.label_list;
  394. pos = -1;
  395. for (i = 0; i < nd->u.scr.label_list_num; i++) {
  396. if (strncmp(lst[i].name, e->label_, sizeof(lst[i].name)) == 0) {
  397. pos = lst[i].pos;
  398. break;
  399. }
  400. }
  401. if (pos == -1) {
  402. ShowWarning("Unable to find label: %s", e->label_);
  403. // unable to find label... do something..
  404. return 0;
  405. }
  406. // run the npc script
  407. run_script(nd->u.scr.script,pos,sd->bl.id,nd->bl.id);
  408. return 0;
  409. }
  410. e = e->next_;
  411. }
  412. pcreset = pcreset->next_;
  413. }
  414. return 0;
  415. }
  416. int mob_chat_sub(struct block_list *bl, va_list ap){
  417. struct mob_data *md = (struct mob_data *)bl;
  418. if(md->nd){
  419. npc_chat_sub(&md->nd->bl, ap);
  420. }
  421. return 0;
  422. }
  423. // Various script builtins used to support these functions
  424. int buildin_defpattern(struct script_state *st) {
  425. int setid=conv_num(st, script_getdata(st,2));
  426. const char *pattern=conv_str(st, script_getdata(st,3));
  427. const char *label=conv_str(st, script_getdata(st,4));
  428. struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
  429. npc_chat_def_pattern(nd, setid, pattern, label);
  430. return 0;
  431. }
  432. int buildin_activatepset(struct script_state *st) {
  433. int setid=conv_num(st, script_getdata(st,2));
  434. struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
  435. activate_pcreset(nd, setid);
  436. return 0;
  437. }
  438. int buildin_deactivatepset(struct script_state *st) {
  439. int setid=conv_num(st, script_getdata(st,2));
  440. struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
  441. deactivate_pcreset(nd, setid);
  442. return 0;
  443. }
  444. int buildin_deletepset(struct script_state *st) {
  445. int setid=conv_num(st, script_getdata(st,2));
  446. struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
  447. delete_pcreset(nd, setid);
  448. return 0;
  449. }
  450. #endif