1 | /* |
1 | /* |
2 | * static char *rcsid_monster_c = |
2 | * static char *rcsid_monster_c = |
3 | * "$Id: monster.C,v 1.2 2006/08/26 23:36:34 root Exp $"; |
3 | * "$Id: monster.C,v 1.3 2006/08/29 05:03:55 root Exp $"; |
4 | */ |
4 | */ |
5 | |
5 | |
6 | /* |
6 | /* |
7 | CrossFire, A Multiplayer game for X-windows |
7 | CrossFire, A Multiplayer game for X-windows |
8 | |
8 | |
… | |
… | |
1495 | return; |
1495 | return; |
1496 | } |
1496 | } |
1497 | } |
1497 | } |
1498 | } |
1498 | } |
1499 | |
1499 | |
1500 | /* This replaces all the msglang stuff about which seems to be a lot of |
|
|
1501 | * unneeded complication - since the setup of that data is never re-used |
|
|
1502 | * (say 'hi' to monster, then 'yes', it would re-do the entire parse-message) |
|
|
1503 | * it seems to me to make more sense to just have simple function that returns |
|
|
1504 | * the 'text' portion of the message that it matches - this savees us a bunch |
|
|
1505 | * of malloc's and free's, as well as that setup. |
|
|
1506 | * This function takes the message to be parsed in 'msg', the text to |
|
|
1507 | * match in 'match', and returns the portion of the message. This |
|
|
1508 | * returned portion is in a malloc'd buf that should be freed. |
|
|
1509 | * Returns NULL if no match is found. |
|
|
1510 | * The player is passed too, so that quest-related messages can be checked too. |
|
|
1511 | */ |
|
|
1512 | static char *find_matching_message(object* pl, const char *msg, const char *match) |
|
|
1513 | { |
|
|
1514 | const char *cp=msg, *cp1, *cp2; |
|
|
1515 | char *cp3, regex[MAX_BUF], gotmatch=0; |
|
|
1516 | |
|
|
1517 | while (1) { |
|
|
1518 | if (strncmp(cp, "@match ", 7)) { |
|
|
1519 | LOG(llevDebug,"find_matching_message: Invalid message %s", msg); |
|
|
1520 | return NULL; |
|
|
1521 | } |
|
|
1522 | else { |
|
|
1523 | /* Find the end of the line, and copy the regex portion into it */ |
|
|
1524 | cp2 = strchr(cp+7, '\n'); |
|
|
1525 | strncpy(regex, cp+7, (cp2 - cp -7 )); |
|
|
1526 | regex[cp2 - cp -7] = 0; |
|
|
1527 | |
|
|
1528 | /* Find the next match command */ |
|
|
1529 | cp1 = strstr(cp+6, "\n@match"); |
|
|
1530 | |
|
|
1531 | /* Got a match - handle * as special case - proper regex would be .*, |
|
|
1532 | * but lots of messages don't use that form. |
|
|
1533 | */ |
|
|
1534 | if (regex[0] == '*') gotmatch=1; |
|
|
1535 | else { |
|
|
1536 | char *pipe, *pnext=NULL; |
|
|
1537 | /* need to parse all the | seperators. Our re_cmp isn't |
|
|
1538 | * realy a fully blown regex parser. |
|
|
1539 | */ |
|
|
1540 | for (pipe=regex; pipe != NULL; pipe = pnext) { |
|
|
1541 | pnext = strchr(pipe, '|'); |
|
|
1542 | if (pnext) { |
|
|
1543 | *pnext = 0; |
|
|
1544 | pnext ++; |
|
|
1545 | } |
|
|
1546 | if (re_cmp(match, pipe)) { |
|
|
1547 | gotmatch = 1; |
|
|
1548 | break; |
|
|
1549 | } |
|
|
1550 | } |
|
|
1551 | } |
|
|
1552 | if (gotmatch) { |
|
|
1553 | if (cp1) { |
|
|
1554 | cp3 = (char*) malloc(cp1 - cp2 + 1); |
|
|
1555 | strncpy(cp3, cp2+1, cp1 - cp2); |
|
|
1556 | cp3[cp1 - cp2] = 0; |
|
|
1557 | } |
|
|
1558 | else { /* if no next match, just want the rest of the string */ |
|
|
1559 | cp3 = strdup_local(cp2+1); |
|
|
1560 | } |
|
|
1561 | return cp3; |
|
|
1562 | } |
|
|
1563 | gotmatch = 0; |
|
|
1564 | if (cp1) cp = cp1 + 1; |
|
|
1565 | else return NULL; |
|
|
1566 | } |
|
|
1567 | } |
|
|
1568 | /* Should never get reached */ |
|
|
1569 | } |
|
|
1570 | |
|
|
1571 | /* This function looks for an object or creature that is listening. |
|
|
1572 | * I've disabled the bit that has only the first npc monster listen - |
|
|
1573 | * we'll see how this works out. only the first npc listens, which |
|
|
1574 | * is sort of bogus since it uses the free_arr which has a preference |
|
|
1575 | * to certain directions. |
|
|
1576 | * |
|
|
1577 | * There is a rare even that the orig_map is used for - basically, if |
|
|
1578 | * a player says the magic word that gets him teleported off the map, |
|
|
1579 | * it can result in the new map putting the object count too high, |
|
|
1580 | * which forces the swap out of some other map. In some cases, the |
|
|
1581 | * map the player was just on now gets swapped out - thus, the |
|
|
1582 | * object on that map are no longer in memory. So check to see if the |
|
|
1583 | * players map changes, and if so, don't process any further. |
|
|
1584 | * If it does change, most likely we don't care about the results |
|
|
1585 | * of further conversation. Also, depending on the value of i, |
|
|
1586 | * the conversation would continue on the new map, which probably isn't |
|
|
1587 | * what is really wanted either. |
|
|
1588 | */ |
|
|
1589 | void communicate(object *op, const char *txt) { |
|
|
1590 | object *npc; |
|
|
1591 | int i, mflags; |
|
|
1592 | sint16 x, y; |
|
|
1593 | mapstruct *mp, *orig_map = op->map; |
|
|
1594 | |
|
|
1595 | int flag=1; /*hasn't spoken to a NPC yet*/ |
|
|
1596 | for(i = 0; i <= SIZEOFFREE2; i++) { |
|
|
1597 | |
|
|
1598 | mp = op->map; |
|
|
1599 | x = op->x + freearr_x[i]; |
|
|
1600 | y = op->y + freearr_y[i]; |
|
|
1601 | |
|
|
1602 | mflags = get_map_flags(mp, &mp, x, y, &x, &y); |
|
|
1603 | if (mflags & P_OUT_OF_MAP) continue; |
|
|
1604 | |
|
|
1605 | for(npc = get_map_ob(mp,x,y); npc != NULL; npc = npc->above) { |
|
|
1606 | if (npc->type == MAGIC_EAR) { |
|
|
1607 | (void) talk_to_wall(op, npc, txt); /* Maybe exit after 1. success? */ |
|
|
1608 | if (orig_map != op->map) { |
|
|
1609 | LOG(llevDebug,"Warning: Forced to swap out very recent map - MAX_OBJECTS should probably be increased\n"); |
|
|
1610 | return; |
|
|
1611 | } |
|
|
1612 | } |
|
|
1613 | else if (flag) { |
|
|
1614 | #if 0 |
|
|
1615 | if (talk_to_npc(op, npc,txt)) |
|
|
1616 | flag=0; /* Can be crowded */ |
|
|
1617 | #else |
|
|
1618 | talk_to_npc(op, npc,txt); |
|
|
1619 | #endif |
|
|
1620 | if (orig_map != op->map) { |
|
|
1621 | LOG(llevDebug,"Warning: Forced to swap out very recent map - MAX_OBJECTS should probably be increased\n"); |
|
|
1622 | return; |
|
|
1623 | } |
|
|
1624 | } |
|
|
1625 | } |
|
|
1626 | } |
|
|
1627 | } |
|
|
1628 | |
|
|
1629 | static int do_talk_npc(object* op, object* npc, object* override, const char* txt) |
|
|
1630 | { |
|
|
1631 | char* cp; |
|
|
1632 | char buf[MAX_BUF]; |
|
|
1633 | |
|
|
1634 | if(override->msg == NULL || *override->msg != '@') |
|
|
1635 | return 0; |
|
|
1636 | |
|
|
1637 | cp = find_matching_message(op, override->msg, txt); |
|
|
1638 | if (cp) { |
|
|
1639 | sprintf(buf,"%s says:",query_name(npc)); |
|
|
1640 | new_info_map(NDI_NAVY|NDI_UNIQUE, npc->map,buf); |
|
|
1641 | new_info_map(NDI_NAVY | NDI_UNIQUE, npc->map, cp); |
|
|
1642 | quest_apply_items(override,op->contr); |
|
|
1643 | free(cp); |
|
|
1644 | return 1; |
|
|
1645 | } |
|
|
1646 | return 0; |
|
|
1647 | } |
|
|
1648 | |
|
|
1649 | int talk_to_npc(object *op, object *npc, const char *txt) { |
|
|
1650 | object *cobj; |
|
|
1651 | |
|
|
1652 | /* Move this commone area up here - shouldn't cost much extra cpu |
|
|
1653 | * time, and makes the function more readable */ |
|
|
1654 | /* Lauwenmark: Handle for plugin say event */ |
|
|
1655 | if (op==npc) return 0; |
|
|
1656 | if (execute_event(npc, EVENT_SAY,op,NULL,txt,SCRIPT_FIX_ALL)!=0) |
|
|
1657 | return 0; |
|
|
1658 | /* Lauwenmark - Here we let the objects inside inventories hear and answer, too. */ |
|
|
1659 | /* This allows the existence of "intelligent" weapons you can discuss with */ |
|
|
1660 | for(cobj=npc->inv;cobj!=NULL; cobj = cobj->below) |
|
|
1661 | { |
|
|
1662 | if (execute_event(cobj, EVENT_SAY,op,NULL,txt,SCRIPT_FIX_ALL)!=0) |
|
|
1663 | return 0; |
|
|
1664 | } |
|
|
1665 | for ( cobj = npc->inv; cobj; cobj = cobj->below ) |
|
|
1666 | if ( quest_is_override_compatible( cobj, op ) ) |
|
|
1667 | if ( do_talk_npc( op, npc, cobj, txt ) ) |
|
|
1668 | return 1; |
|
|
1669 | return do_talk_npc( op, npc, npc, txt ); |
|
|
1670 | } |
|
|
1671 | |
|
|
1672 | static int do_talk_wall(object* pl, object* npc, object* override, const char* txt) |
|
|
1673 | { |
|
|
1674 | char* cp; |
|
|
1675 | if(override->msg == NULL || *override->msg != '@') |
|
|
1676 | return 0; |
|
|
1677 | |
|
|
1678 | cp = find_matching_message(pl, override->msg, txt); |
|
|
1679 | if (!cp) |
|
|
1680 | return 0; |
|
|
1681 | |
|
|
1682 | new_info_map(NDI_NAVY | NDI_UNIQUE, npc->map,cp); |
|
|
1683 | use_trigger(npc); |
|
|
1684 | quest_apply_items(npc, pl->contr); |
|
|
1685 | free(cp); |
|
|
1686 | |
|
|
1687 | return 1; |
|
|
1688 | } |
|
|
1689 | |
|
|
1690 | int talk_to_wall(object* pl, object *npc, const char *txt) { |
|
|
1691 | |
|
|
1692 | object* inv; |
|
|
1693 | |
|
|
1694 | for ( inv = npc->inv; inv; inv = inv->below) |
|
|
1695 | if ( quest_is_override_compatible(inv, pl ) ) |
|
|
1696 | if ( do_talk_wall( pl, npc, inv, txt ) ) |
|
|
1697 | return 1; |
|
|
1698 | |
|
|
1699 | return do_talk_wall( pl, npc, npc, txt );; |
|
|
1700 | } |
|
|
1701 | |
|
|
1702 | /* find_mon_throw_ob() - modeled on find_throw_ob |
1500 | /* find_mon_throw_ob() - modeled on find_throw_ob |
1703 | * This is probably overly simplistic as it is now - We want |
1501 | * This is probably overly simplistic as it is now - We want |
1704 | * monsters to throw things like chairs and other pieces of |
1502 | * monsters to throw things like chairs and other pieces of |
1705 | * furniture, even if they are not good throwable objects. |
1503 | * furniture, even if they are not good throwable objects. |
1706 | * Probably better to have the monster throw a throwable object |
1504 | * Probably better to have the monster throw a throwable object |