Free Web Hosting Provider - Web Hosting - E-commerce - High Speed Internet - Free Web Page
Search the Web

New Triggers for DG Script System

Browse New Triggers for the DG Script System...


New Triggers are actually a lot like new flags in objects, mobs and rooms. To add them, do the following steps:

  1. Add a flag #define for the appropriate type. It is up to you if you use an unused flag, or create a new one. Personally, I prefer to keep similar flags only as the same bitvector. The flag type depends on the trigger type: MTRIG_ for mobs, OTRIG_ for object, WTRIG_ for rooms. These #defines are added to the dg_scripts.h file.
  2. Add the trigger type name to the appropriate char constant. This is just like adding to the constants.c, only is done in the dg_triggers.c file.
  3. Add the code for the trigger itself.
  4. Add the call to the trigger where needed. This all depends on the trigger itself.


Example of adding a new trigger


This is an example trigger. Don't actually add it. It won't work. It is put here to understand how to add new triggers to the system. This way, all that needs to be done is a posting of the trigger code itself and where the calls to it go, and that saves a lot of space and time.

Step 1


Since this will be a mob trigger, we add
#define MTRIG_SAMPLE                 (1 << 13)
to the define list in dg_scripts.h

Step 2

Since this is a mob trigger, add the following line to the char *trig_types definition:
  "Sample",
As you can guess, this line MUST coincide with the define in the dg_scripts.h file. For objects, use otrig_types, and rooms, wtrig_types.

Step 3

Here, you add the code itself to the dg_triggers.c file, as well as a prototype in the dg_scripts.h file. We won't have any sample code, as it is just a waste. Look at the existing ones, and the new ones below, for how to make one of your own.

Step 4

Now, we would add the call to the trigger itself. As was stated above, this all depends on the trigger itself. For example, the enter trigger is called in act.movement.c, just when the character (mob or PC) enters the room in the do_simple_move function.



New Triggers


Casting Trigger


This trigger is designed for mobs, objects and rooms. The mob and object versions are only called if the mob or object is the target of the spell. The room one traps all spells. How you place these for calling will determine their behaviour. You can call them before the spell is called, and have it an (if cast_mtrig....) type deal where the trigger can actually stop the spell, or you can have it called after the spell is released. This is a matter of personal opinion. I personally suggest calling them before the spell is cast. If you want the trigger to occur after the spell has been cast, simply add in a small delay to the code of the trigger itself.
The Code:
int cast_mtrigger(char_data *ch, char_data *actor, char *targ, int spellnum)
{
  trig_data *t;
  char buf[MAX_INPUT_LENGTH];

  if (ch == NULL)
    return 1;

  if (!SCRIPT_CHECK(ch, MTRIG_CAST) || AFF_FLAGGED(ch, AFF_CHARM))
    return 1;

  for (t = TRIGGERS(SCRIPT(ch)); t; t = t->next) {
    if (TRIGGER_CHECK(t, MTRIG_CAST) &&
        (number(1, 100) <= GET_TRIG_NARG(t))) {

      ADD_UID_VAR(buf, t, actor, "actor");
      skip_spaces(&targ);
      add_var(&GET_TRIG_VARS(t), "arg", targ);
      sprintf(buf, "%d", spellnum);
      add_var(&GET_TRIG_VARS(t), "spell", buf);
      add_var(&GET_TRIG_VARS(t), "spellname", spells[spellnum]);
      return script_driver(ch, t, MOB_TRIGGER, TRIG_NEW);
    }
  }

  return 1;
}


int cast_otrigger(obj_data *obj, char_data *actor, char *targ, int spellnum)
{
  trig_data *t;
  char buf[MAX_INPUT_LENGTH];

  if (obj == NULL)
    return 1;

  if (!SCRIPT_CHECK(obj, OTRIG_CAST))
    return 1;

  for (t = TRIGGERS(SCRIPT(obj)); t; t = t->next) {
    if (TRIGGER_CHECK(t, OTRIG_CAST) &&
        (number(1, 100) <= GET_TRIG_NARG(t))) {

      ADD_UID_VAR(buf, t, actor, "actor");
      skip_spaces(&targ);
      add_var(&GET_TRIG_VARS(t), "arg", targ);
      sprintf(buf, "%d", spellnum);
      add_var(&GET_TRIG_VARS(t), "spell", buf);
      add_var(&GET_TRIG_VARS(t), "spellname", spells[spellnum]);
      return script_driver(obj, t, OBJ_TRIGGER, TRIG_NEW);
    }
  }

  return 1;
}


int cast_wtrigger(char_data *actor, char_data *vict, obj_data *obj, char *targ, int spellnum)
{
  room_data *room;
  trig_data *t;
  char buf[MAX_INPUT_LENGTH];

  if (!actor || !SCRIPT_CHECK(&world[IN_ROOM(actor)], WTRIG_CAST))
    return 1;

  room = &world[IN_ROOM(actor)];
  for (t = TRIGGERS(SCRIPT(room)); t; t = t->next) {
    if (TRIGGER_CHECK(t, WTRIG_CAST) &&
        (number(1, 100) <= GET_TRIG_NARG(t))) {

      ADD_UID_VAR(buf, t, actor, "actor");
      if (vict)
        ADD_UID_VAR(buf, t, vict, "vict");
      if (obj)
        ADD_UID_VAR(buf, t, obj, "obj");
      skip_spaces(&targ);
      add_var(&GET_TRIG_VARS(t), "arg", targ);
      sprintf(buf, "%d", spellnum);
      add_var(&GET_TRIG_VARS(t), "spell", buf);
      add_var(&GET_TRIG_VARS(t), "spellname", spells[spellnum]);
      return script_driver(room, t, WLD_TRIGGER, TRIG_NEW);
    }
  }

  return 1;
}

You notice that for the object and mob one, I return if no no mob or object is even referenced. Where I call these scripts, the mob and object targets are put in, even if NULL. That just makes sure that there actually WAS a target even though the functions are called. You can, if you want, have another sanity check to make sure that there is even a character or object target before calling them, but that's up to you.


Leave/Leave-All Trigger


These triggers are similar to the Entry/Greet/Greet-All triggers. The main difference is that instead of trapping as the player enters the room, they trap before the player exits. This can be used for things like a mob that won't let players leave the room till they kill the mob, or even a mob that reports when a player leaves the room to someone else. I personally have a trigger attached to familiar on my MUD that have them report to their master when someone leaves or enters the room they are in. These are mob and room triggers only.
The Code:
int leave_mtrigger(char_data *actor, int dir)
{
  trig_data *t;
  char_data *ch;
  char buf[MAX_INPUT_LENGTH];

  for (ch = world[IN_ROOM(actor)].people; ch; ch = ch->next_in_room) {
    if (!SCRIPT_CHECK(ch, MTRIG_LEAVE | MTRIG_LEAVE_ALL) ||
        !AWAKE(ch) || FIGHTING(ch) || (ch == actor) ||
        AFF_FLAGGED(ch, AFF_CHARM))
      continue;

    for (t = TRIGGERS(SCRIPT(ch)); t; t = t->next) {
      if (((IS_SET(GET_TRIG_TYPE(t), MTRIG_LEAVE) && CAN_SEE(ch, actor)) ||
           IS_SET(GET_TRIG_TYPE(t), MTRIG_LEAVE_ALL)) &&
          !GET_TRIG_DEPTH(t) && (number(1, 100) <= GET_TRIG_NARG(t))) {
        add_var(&GET_TRIG_VARS(t), "direction", dirs[dir]);
        ADD_UID_VAR(buf, t, actor, "actor");
        return script_driver(ch, t, MOB_TRIGGER, TRIG_NEW);
        break;
      }
    }
  }
  return 1;
}


int leave_wtrigger(struct room_data *room, char_data *actor, int dir)
{
  trig_data *t;
  char buf[MAX_INPUT_LENGTH];

  if (!SCRIPT_CHECK(room, WTRIG_LEAVE))
    return 1;

  for (t = TRIGGERS(SCRIPT(room)); t; t = t->next) {
    if (TRIGGER_CHECK(t, WTRIG_LEAVE) &&
        (number(1, 100) <= GET_TRIG_NARG(t))) {
      add_var(&GET_TRIG_VARS(t), "direction", dirs[dir]);
      ADD_UID_VAR(buf, t, actor, "actor");
      return script_driver(room, t, WLD_TRIGGER, TRIG_NEW);
    }
  }

  return 1;
}

Here is the code I used to call these two functions:
  if (!leave_mtrigger(ch, dir))
    return 0;
  if (!leave_wtrigger(&world[ch->in_room], ch, dir))
    return 0;

They were placed immediately after this line in do_simple_move:
  if (need_specials_check && special(ch, dir + 1, ""))
    return 0;



Door Trigger


This trigger is a little more specific than most. It could be done with the command trigger, but then the control possible with this version would not be easily obtained. This trigger tells you what door command is being performed, and on what direction. This is very useful for mobs that will stop someone from picking a door, or for security systems in the rooms for doors. It is only for mobs or rooms.
The Code:
static char *door_act[] = {
  "open",
  "close",
  "unlock",
  "lock",
  "pick",
  "\n"
};

int door_mtrigger(char_data *actor, int subcmd, int dir)
{
  trig_data *t;
  char_data *ch;
  char buf[MAX_INPUT_LENGTH];

  for (ch = world[IN_ROOM(actor)].people; ch; ch = ch->next_in_room) {
    if (!SCRIPT_CHECK(ch, MTRIG_DOOR) ||
        !AWAKE(ch) || FIGHTING(ch) || (ch == actor) ||
        AFF_FLAGGED(ch, AFF_CHARM))
      continue;

    for (t = TRIGGERS(SCRIPT(ch)); t; t = t->next) {
      if (IS_SET(GET_TRIG_TYPE(t), MTRIG_DOOR) && CAN_SEE(ch, actor) &&
          !GET_TRIG_DEPTH(t) && (number(1, 100) <= GET_TRIG_NARG(t))) {
        add_var(&GET_TRIG_VARS(t), "cmd", door_act[subcmd]);
        add_var(&GET_TRIG_VARS(t), "direction", dirs[dir]);
        ADD_UID_VAR(buf, t, actor, "actor");
        return script_driver(ch, t, MOB_TRIGGER, TRIG_NEW);
        break;
      }
    }
  }
  return 1;
}


int door_wtrigger(char_data *actor, int subcmd, int dir)
{
  room_data *room;
  trig_data *t;
  char buf[MAX_INPUT_LENGTH];

  if (!actor || !SCRIPT_CHECK(&world[IN_ROOM(actor)], WTRIG_DOOR))
    return 1;

  room = &world[IN_ROOM(actor)];
  for (t = TRIGGERS(SCRIPT(room)); t; t = t->next) {
    if (TRIGGER_CHECK(t, WTRIG_DOOR) &&
        (number(1, 100) <= GET_TRIG_NARG(t))) {
      add_var(&GET_TRIG_VARS(t), "cmd", door_act[subcmd]);
      add_var(&GET_TRIG_VARS(t), "direction", dirs[dir]);
      ADD_UID_VAR(buf, t, actor, "actor");
      return script_driver(room, t, WLD_TRIGGER, TRIG_NEW);
    }
  }

  return 1;
}

Here is the yrigger calling used. It is in the do_doorcmd, right at the beginning.
  if (!door_mtrigger(ch, scmd, door))
    return;

  if (!door_wtrigger(ch, scmd, door))
    return;



Return to the Death's Gate Script page