Welcome to the Builder Academy

Question Customized Mob Movement

More
Yesterday 15:36 - Yesterday 16:48 #10643 by zi
Customized Mob Movement was created by zi
I wanted to make mob movements a little more unique - after all, we can select their attack text so why not their movements in and out of a room? Join me on a clumsy journey through C and make your mud a little cooler with customized mob movement.

All of this is AFTER I added my "minimum level to kill" snippet (good luck finding it in the forums, I added it in a trigger request) so some of the integers/variables (especially loading/saving for mobs) might have more functions/variables than stock TBA.

Let's begin.

In structs.h add
Code:
/* Mob Movement */ #define MOB_WALKS      0 #define MOB_SKIPS      1 #define MOB_STAGGERS    2 #define MOB_SWIMS      3 #define MOB_SLINKS      4 #define MOB_SAUNTERS    5 #define MOB_STRUTS      6 #define MOB_LIMPS      7 #define MOB_MEANDERS    8 #define MOB_SHUFFLES    9 #define MOB_RUNS        10 #define MOB_ROLLS      11 #define MOB_MARCHES    12 #define NUM_MOB_MOVEMENT 13
This is where you can add more/change the type of movement....

AND added in /** Special data used by NPCs, not PCs */
Code:
byte mobmove;



In utils.h add:
Code:
#define GET_MOBMOVE(ch)      ((ch)->mob_specials.mobmove)



In oasis.h add:
Code:
#define MEDIT_MOVEMENT              41
this number might be different for you, just do the next avail number.


In  act.movement.c add (I added this above the functions):
Code:
/* movement text for mobs */ const char *mob_movement_text[] = {   "walks",   "skips",   "staggers",   "swims",   "slinks",   "saunters",   "struts",   "limps",   "meanders",   "shuffles",   "skips",   "runs",   "rolls",   "marches" };


Change    /* Generate the leave message and display to others in the was_in room. */ to give a movement message w a direction if it's a character OR our modified message if it's a NPC:
Code:
  if (!AFF_FLAGGED(ch, AFF_SNEAK) && !IS_NPC(ch))   {     snprintf(leave_message, sizeof(leave_message), "$n leaves %s.", dirs[dir]);     act(leave_message, TRUE, ch, 0, 0, TO_ROOM);   }   else   {       snprintf(leave_message, sizeof(leave_message), "$n %s %s.", mob_movement_text[(int)GET_MOBMOVE(ch)], dirs[dir]);     act(leave_message, TRUE, ch, 0, 0, TO_ROOM);   }


Since we're here I decided to change the arrival messages as well:

under Local variable definitions add:
Code:
char arrive_message{SMALL_BUFSIZE];

then change the if in  /* Display arrival information to anyone in the destination room... */ to:
Code:
  /* Display arrival information to anyone in the destination room... */   if (!AFF_FLAGGED(ch, AFF_SNEAK) && !IS_NPC(ch))   {       snprintf(arrive_message, sizeof(arrive_message), "$n arrives from the %s.", dirs[rev_dir[dir]]);     act(arrive_message, TRUE, ch, 0, 0, TO_ROOM);   }   else   {       snprintf(arrive_message, sizeof(arrive_message), "$n %s in from the %s.", mob_movement_text[(int)GET_MOBMOVE(ch)], dirs[rev_dir[dir]]);     act(arrive_message, TRUE, ch, 0, 0, TO_ROOM);   }



In act.h under /* Global variables from act.movement.c */ add:
Code:
extern const char *mob_movement_text[];



Over to medit.c so we can select the movement for each mob:

add in to the includes:
Code:
#include "act.h"

in local functions add:
Code:
static void medit_disp_movement_types(struct descriptor_data *d);

then add the movement text menu option:
Code:
sprintbitarray(AFF_FLAGS(mob), affected_bits, AF_ARRAY_MAX, flag2);   write_to_output(d,           "%s6%s) Position  : %s%s\r\n"           "%s7%s) Default  : %s%s\r\n"           "%s8%s) Attack    : %s%s\r\n"           "%sV%s) Move Text : %s%s\r\n"           "%s9%s) Stats Menu...\r\n"           "%sA%s) NPC Flags : %s%s\r\n"           "%sB%s) AFF Flags : %s%s\r\n"           "%sS%s) Script    : %s%s\r\n"           "%sW%s) Copy mob\r\n"           "%sX%s) Delete mob\r\n"           "%sQ%s) Quit\r\n"           "Enter choice : ",           grn, nrm, yel, position_types[(int)GET_POS(mob)],           grn, nrm, yel, position_types[(int)GET_DEFAULT_POS(mob)],           grn, nrm, yel, attack_hit_text[(int)GET_ATTACK(mob)].singular,           grn, nrm, yel, mob_movement_text[(int)GET_MOBMOVE(mob)],           grn, nrm,           grn, nrm, cyn, flags,           grn, nrm, cyn, flag2,           grn, nrm, cyn, OLC_SCRIPT(d) ?"Set.":"Not Set.",           grn, nrm,           grn, nrm,           grn, nrm           );


Then in case MEDIT_MAIN_MENU add:
Code:
    case 'V': // right? V for movement?     case 'v': // Zi rulez       OLC_MODE(d) = MEDIT_MOVEMENT;       medit_disp_movement_types(d);       return;

And in the cases under /* Numerical responses. */ add:
Code:
  case MEDIT_MOVEMENT:     GET_MOBMOVE(OLC_MOB(d)) = LIMIT(i, 0, NUM_MOB_MOVEMENT - 1);     break;


under "Display attack types menu" add:
Code:
/* Display movement types menu. */ static void medit_disp_movement_types(struct descriptor_data *d) {   int i;   get_char_colors(d->character);   clear_screen(d);   for (i = 0; i < NUM_MOB_MOVEMENT; i++) {     write_to_output(d, "%s%2d%s) %s\r\n", grn, i, nrm, mob_movement_text[i]);   }   write_to_output(d, "Enter move type : "); }



Now we turn to saving and loading for the mobs

In genmob.c replaced this in write_mobile_record - please note I'm using my minimum attack level snippet as well which is why I'm writing a little extra in the second line of integers.
Code:
  if(n < MAX_STRING_LENGTH) {     fprintf(fd, "%s", convert_from_tabs(buf));     fprintf(fd, "%d %d %d %d %d %d %d %d %d E\n"         "%d %d %d %dd%d+%d %dd%d+%d %d %d\n",         MOB_FLAGS(mob)[0], MOB_FLAGS(mob)[1],         MOB_FLAGS(mob)[2], MOB_FLAGS(mob)[3],         AFF_FLAGS(mob)[0], AFF_FLAGS(mob)[1],         AFF_FLAGS(mob)[2], AFF_FLAGS(mob)[3],         GET_ALIGNMENT(mob),         GET_LEVEL(mob), 20 - GET_HITROLL(mob), GET_AC(mob) / 10, GET_HIT(mob),         GET_MANA(mob), GET_MOVE(mob), GET_NDD(mob), GET_SDD(mob),         GET_DAMROLL(mob),GET_ATKLEVEL(mob), GET_MOBMOVE(mob));


in db.c (loading mob) in parse_simple_mob increase the array to the appropriate size (I'm up to 11) at the start of the function. It looks like:
Code:
  int j, t[11];


then change this line read to:
Code:
  if (sscanf(line, " %d %d %d %dd%d+%d %dd%d+%d %d %d",           t, t + 1, t + 2, t + 3, t + 4, t + 5, t + 6, t + 7, t + 8, t + 9, t + 10) != 11) {     log("SYSERR: Format error in mob #%d, first line after S flag\n"         "...expecting line of form '# # # #d#+# #d#+# # #'", nr);     exit(1);   }   GET_LEVEL(mob_proto + i) = t[0];   GET_HITROLL(mob_proto + i) = 20 - t[1];   GET_AC(mob_proto + i) = 10 * t[2];   /* max hit = 0 is a flag that H, M, V is xdy+z */   GET_MAX_HIT(mob_proto + i) = 0;   GET_HIT(mob_proto + i) = t[3];   GET_MANA(mob_proto + i) = t[4];   GET_MOVE(mob_proto + i) = t[5];   GET_MAX_MANA(mob_proto + i) = 10;   GET_MAX_MOVE(mob_proto + i) = 50;   mob_proto[i].mob_specials.damnodice = t[6];   mob_proto[i].mob_specials.damsizedice = t[7];   GET_DAMROLL(mob_proto + i) = t[8];   GET_ATKLEVEL(mob_proto + i) = t[9]; //this is added from a previous snippet   GET_MOBMOVE(mob_proto + i) = t[10]; //we're adding this

That SHOULD be it. Now when my cyborg mob enters the room the character sees "A Level 2 Training Cyborg rolls in from the west." and when it leaves they see "A Level 2 Training Cyborg rolls east." My mud is far more descriptive than that... this is for a training area so relax. 

BIG NOTE: You will have to go back and add a number (just do 1) to each mob, so if you have a fully developed mud or are using TBA stock you're in for wrist-breaking trouble. For me it was adding a number to each mob in the x.mob file:  # # # #d#+# #d#+# # # <
this is what I added. The # before our added number is for my minimum attack level (another snippet).

PLEASE let me know if you run into any problems implementing, or if there's a more efficient way to do this. With the effort added to make our mob attacks personalized, this felt like a natural progression. I'd love to see it added to a release, but it would mean changing mob files and who wants to do that?
Last edit: Yesterday 16:48 by zi.
The following user(s) said Thank You: ironfist

Please Log in or Create an account to join the conversation.

More
Yesterday 18:01 - Yesterday 18:25 #10645 by ironfist
Replied by ironfist on topic Customized Mob Movement
Great idea :)   You could have an override if a mob had class = zombie, for example, and just have it set shuffle (if they don't have the default value), in addition to what you have - if you don't feel like going through every mob to edit it.

If someone was implementing it in their mud that didn't have the variable, they could just set it to a default by looking at how many fields were in their files.
Code:
  if (sscanf(line, " %d %d %d %dd%d+%d %dd%d+%d %d %d",           t, t + 1, t + 2, t + 3, t + 4, t + 5, t + 6, t + 7, t + 8, t + 9, t + 10) != 11)

At that line if 11 arguments 
  then set GET_MOBMOVE() = <default number>
if 12 arguments then 
  just read in the variable and set MOBMOVE
else throw error

There are some examples of this in db.c I think, with some of the other sscanf parse/load functions, but I don't remember off the top of my head.

Then where you save, just save whatever is in memory (which you are already doing) and write out 12 fields, so you don't really have to hand edit :)

Edit: 

You also need to do an action that invokes save_mobiles in genmob.c either by editing and saving in medit with one change per zone or making a loop after the zones are loaded in db.c
Code:
/* somewhere around here in db.c */   log("Boot db -- DONE.");   for (zrn = 0; zrn <= top_of_zone_table; zrn++)   {     save_mobiles(zrn); /* from genmob.c)   }
I think that'll work, not sure without testing, but that is essentially where I put a function to save all the files to mysql.  The old files already loaded in memory, then you can just write out to whatever thing you want - in this case back to the old files (just backup everything)
Last edit: Yesterday 18:25 by ironfist.
The following user(s) said Thank You: zi

Please Log in or Create an account to join the conversation.

Time to create page: 0.323 seconds