Works fine, and it's much more realistic.
I also have mobs casting spells assigned to them in the enhanced mobiles portion of the mob files during combat too. Without spec_procs, and they must actually "know" the spell.
My perform_violence() with multiple attacks, dual wield, weapon spells, and mob spells and mob skills:
(Yes I realize that many people just do mob spells via DG Scripts... however the way I've done them they actually make the mob use mana, which is set per mob in my MUD, plus they're very easy to set from OLC)
/* control the fights going on. Called every 2 seconds from comm.c. */
void perform_violence(void)
{
struct char_data *ch;
struct obj_data *wielded, *held;
int i;
int spell_num[50], num_spells = 0, to_cast;
for (ch = combat_list; ch; ch = next_combat_list) {
next_combat_list = ch->next_fighting;
if (FIGHTING(ch) == NULL || IN_ROOM(ch) != IN_ROOM(FIGHTING(ch))) {
stop_fighting(ch);
continue;
}
if ((IS_NPC(FIGHTING(ch)) && MOB_FLAGGED(FIGHTING(ch), MOB_NOKILL)) ||
(IS_NPC(ch) && MOB_FLAGGED(ch, MOB_NOKILL))) {
if (!IS_NPC(ch))
send_to_char(ch, "Dunno how you were fighting them... but this has to stop!\r\n");
stop_fighting(ch);
continue;
}
if (IS_NPC(ch)) {
if (GET_MOB_WAIT(ch) > 0) {
GET_MOB_WAIT(ch) -= PULSE_VIOLENCE;
if (TMP_FLAGGED(ch, TMP_BASHED | TMP_SPELL_STUN)) {
send_to_char(ch, "You stagger around stunned!!\r\n");
continue;
}
} else {
GET_MOB_WAIT(ch) = 0;
if (GET_POS(ch) < POS_FIGHTING)
GET_POS(ch) = POS_FIGHTING;
if (TMP_FLAGGED(ch, TMP_BASHED | TMP_SPELL_STUN)) {
REMOVE_BIT(TMP_FLAGS(ch), TMP_BASHED);
REMOVE_BIT(TMP_FLAGS(ch), TMP_SPELL_STUN);
act("$n regains $s senses!", TRUE, ch, 0, 0, TO_ROOM);
}
}
}
if (TMP_FLAGGED(ch, TMP_BASHED) || TMP_FLAGGED(ch, TMP_SPELL_STUN)) {
send_to_char(ch, "You stagger around stunned!!\r\n");
continue;
}
if (GET_POS(ch) < POS_FIGHTING) {
send_to_char(ch, "You can't fight while sitting!!\r\n");
continue;
}
for (k = ch->followers; k; k=k->next) {
if (PRF_FLAGGED(k->follower, PRF_AUTOASSIST) &&
(k->follower->in_room == ch->in_room))
do_assist(k->follower, GET_NAME(ch), 0, 0);
}
hit(ch, FIGHTING(ch), TYPE_WEAPON);
if ((GET_EQ(ch, WEAR_WIELD) !=NULL && GET_OBJ_TYPE(GET_EQ(ch, WEAR_WIELD)) == ITEM_WEAPON) || GET_SKILL(ch, SKILL_UNARMED_COMBAT)) {
if (rand_number(0, 100) <= GET_SKILL(ch, SKILL_SECOND_ATK))
hit(ch, FIGHTING(ch), TYPE_WEAPON);
if (rand_number(0, 100) <= GET_SKILL(ch, SKILL_THIRD_ATK))
hit(ch, FIGHTING(ch), TYPE_WEAPON);
if (rand_number(0, 100) <= GET_SKILL(ch, SKILL_FOURTH_ATK))
hit(ch, FIGHTING(ch), TYPE_WEAPON);
}
if ((GET_EQ(ch, WEAR_HOLD) != NULL && GET_OBJ_TYPE(GET_EQ(ch, WEAR_HOLD)) == ITEM_WEAPON) || GET_SKILL(ch, SKILL_UNARMED_COMBAT)) {
if (rand_number(0, 100) <= GET_SKILL(ch, SKILL_DUAL_WIELD))
hit(ch, FIGHTING(ch), TYPE_DUAL);
if (rand_number(0, 100) <= GET_SKILL(ch, SKILL_DUAL_WIELD) && rand_number(0, 100) <= GET_SKILL(ch, SKILL_SECOND_ATK))
hit(ch, FIGHTING(ch), TYPE_DUAL);
if (rand_number(0, 100) <= GET_SKILL(ch, SKILL_DUAL_WIELD) && rand_number(0, 100) <= GET_SKILL(ch, SKILL_THIRD_ATK))
hit(ch, FIGHTING(ch), TYPE_DUAL);
if (rand_number(0, 100) <= GET_SKILL(ch, SKILL_DUAL_WIELD) && rand_number(0, 100) <= GET_SKILL(ch, SKILL_FOURTH_ATK))
hit(ch, FIGHTING(ch), TYPE_DUAL);
}
if (GET_EQ(ch, WEAR_WIELD) != NULL) {
wielded = GET_EQ(ch, WEAR_WIELD);
if (wielded->obj_spell.spellnum > 0) {
if (dice(0,100) <= (wielded->obj_spell.percent)){
call_magic(ch, FIGHTING(ch), NULL,
wielded->obj_spell.spellnum,
GET_LEVEL(ch), CAST_SPELL);
}
}
}
if (GET_EQ(ch, WEAR_HOLD) != NULL && GET_OBJ_TYPE(GET_EQ(ch, WEAR_HOLD)) == ITEM_WEAPON && GET_SKILL(ch, SKILL_DUAL_WIELD)) {
held = GET_EQ(ch, WEAR_HOLD);
if (held != NULL) {
if (held->obj_spell.spellnum > 0) {
if (dice(0, 100) <= (held->obj_spell.percent)) {
call_magic(ch, FIGHTING(ch), NULL,
held->obj_spell.spellnum,
GET_LEVEL(ch), CAST_SPELL);
}
}
}
}
if(IS_NPC(ch) && ch->mob_specials.has_spells == TRUE && !(GET_MOB_WAIT(ch) > 0)) {
for (i = 1; i <= MAX_SKILLS; i++) {
if (GET_SKILL(ch, i) > 0 && i <= 1900) {
num_spells++;
spell_num[num_spells] = i;
}
}
to_cast = rand_number(1, num_spells);
if (spell_num[to_cast] <= MAX_SPELLS) {
if (rand_number(0, 100) <= GET_SKILL(ch, spell_num[to_cast])) {
char buf[255];
sprintf(buf, " '%s'", spell_info[spell_num[to_cast]].name);
if(spell_info[spell_num[to_cast]].violent)
do_cast(ch, buf, find_command("cast"), 0);
}
} else {
perform_mob_skill(ch, FIGHTING(ch), spell_num[to_cast]);
}
}
if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_AUTODIAG) && FIGHTING(ch) && GET_POS(FIGHTING(ch)) != POS_DEAD)
diag_char_to_char(FIGHTING(ch), ch);
if (MOB_FLAGGED(ch, MOB_SPEC) && GET_MOB_SPEC(ch) && !MOB_FLAGGED(ch, MOB_NOTDEADYET)) {
char actbuf[MAX_INPUT_LENGTH] = "";
(GET_MOB_SPEC(ch)) (ch, ch, 0, actbuf);
}
}
}
If anyone is interested in the full code for any of that, let me know. Some is based on other people's work off the CircleMUD ftp site, but some is my own ideas too... like how I did mob spells and skills.
And here is my hit() function so you can see I don't stop mobs from using weapons once it gets here either:
void hit(struct char_data *ch, struct char_data *victim, int type)
{
struct obj_data *wielded, *held;
int w_type, victim_ac, calc_thaco, dam, diceroll;
wielded = GET_EQ(ch, WEAR_WIELD);
held = GET_EQ(ch, WEAR_HOLD);
/* check if the character has a fight trigger */
fight_mtrigger(ch);
/* Do some sanity checking, in case someone flees, etc. */
if (IN_ROOM(ch) != IN_ROOM(victim)) {
if (FIGHTING(ch) && FIGHTING(ch) == victim)
stop_fighting(ch);
return;
}
/* Find the weapon type (for display purposes only) */
if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
w_type = GET_OBJ_VAL(wielded, 3) + TYPE_HIT;
else {
if (IS_NPC(ch) && ch->mob_specials.attack_type != 0)
w_type = ch->mob_specials.attack_type + TYPE_HIT;
else
w_type = TYPE_HIT;
}
if (type == TYPE_DUAL) {
/* Find the weapon type (for display purposes only) */
if (held && GET_OBJ_TYPE(held) == ITEM_WEAPON)
w_type = GET_OBJ_VAL(held, 3) + TYPE_HIT;
}
/* Calculate chance of hit. Lower THAC0 is better for attacker. */
calc_thaco = compute_thaco(ch, victim);
/* Calculate the raw armor including magic armor. Lower AC is better for defender. */
victim_ac = compute_armor_class(victim) / 10;
/* roll the die and take your chances... */
diceroll = rand_number(1, 100);
/*
* Decide whether this is a hit or a miss.
*
* Victim asleep = hit, otherwise:
* 1 = Automatic miss.
* 2..19 = Checked vs. AC.
* 20 = Automatic hit.
*/
if (diceroll >= 20 || !AWAKE(victim))
dam = TRUE;
else if (diceroll == 1)
dam = FALSE;
else
dam = (calc_thaco - diceroll <= victim_ac);
if (!dam)
/* the attacker missed the victim */
damage(ch, victim, 0, type == SKILL_BACKSTAB ? SKILL_BACKSTAB : w_type);
else {
/* okay, we know the guy has been hit. now calculate damage. */
/* Start with the damage bonuses: the damroll and strength apply */
dam = str_app[STRENGTH_APPLY_INDEX(ch)].todam;
dam += GET_DAMROLL(ch);
/* Maybe holding arrow? */
if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON && type == TYPE_WEAPON) {
/* Add weapon-based damage if a weapon is being wielded */
dam += dice(GET_OBJ_VAL(wielded, 1), GET_OBJ_VAL(wielded, 2));
} else if (held && GET_OBJ_TYPE(held) == ITEM_WEAPON && type == TYPE_DUAL) {
/* Add weapon-based damage if a weapon is being held */
dam += dice(GET_OBJ_VAL(held, 1), GET_OBJ_VAL(held, 2));
} else {
/* If no weapon, add bare hand damage instead */
if (IS_NPC(ch))
dam += dice(ch->mob_specials.damnodice, ch->mob_specials.damsizedice);
else
dam += rand_number(0, 2); /* Max 2 bare hand damage for players */
}
/*
* Include a damage multiplier if victim isn't ready to fight:
*
* Position sitting 1.33 x normal
* Position resting 1.66 x normal
* Position sleeping 2.00 x normal
* Position stunned 2.33 x normal
* Position incap 2.66 x normal
* Position mortally 3.00 x normal
*
* Note, this is a hack because it depends on the particular
* values of the POSITION_XXX constants.
*/
if (GET_POS(victim) < POS_FIGHTING)
dam *= 1 + (POS_FIGHTING - GET_POS(victim)) / 3;
/* at least 1 hp damage min per hit */
dam = MAX(1, dam);
if (type == SKILL_BACKSTAB)
damage(ch, victim, dam * backstab_mult(GET_LEVEL(ch)), SKILL_BACKSTAB);
else
damage(ch, victim, dam, w_type);
}
/* check if the victim has a hitprcnt trigger */
hitprcnt_mtrigger(victim);
}

