Exemple #1
0
static void test_create_a_spell(CuTest * tc)
{
    spell * sp;

    test_setup();
    CuAssertPtrEquals(tc, NULL, spells);
    CuAssertPtrEquals(tc, NULL, find_spell("testspell"));

    sp = create_spell("testspell");
    CuAssertPtrEquals(tc, sp, find_spell("testspell"));
    CuAssertPtrNotNull(tc, spells);
    test_teardown();
}
int get_spell(struct char_data *ch,int spell)
{
  int i;
  i=find_spell(ch,spell);
  if (i>=0) return(MIN(ch->spells[i].learned,max_learn_spell(ch,spell)));
  else return(0);
}
Exemple #3
0
static void test_spells(CuTest * tc)
{
    const char * data = "{\"spells\": { \"fireball\" : { \"syntax\" : \"u+\" } } }";

    cJSON *json = cJSON_Parse(data);
    const spell *sp;

    test_cleanup();
    CuAssertPtrNotNull(tc, json);
    CuAssertPtrEquals(tc, 0, find_spell("fireball"));

    json_config(json);
    sp = find_spell("fireball");
    CuAssertPtrNotNull(tc, sp);
    CuAssertStrEquals(tc, "u+", sp->syntax);

    test_cleanup();
    CuAssertPtrEquals(tc, 0, find_spell("fireball"));
}
Exemple #4
0
string *usage(string str) {
   object target;
   string spellpath, *lines;

   if (str != "") {
      spellpath = find_spell(str);
      if (spellpath) {
         call_other(spellpath, "setup_alsos");
         return call_other(spellpath, "usage");
      }
   }

   lines = ({ "Usage: cast [-h] [spell [target]]" });
Exemple #5
0
static void test_create_duplicate_spell(CuTest * tc)
{
    spell *sp;
    struct log_t *log;
    strlist *sl = 0;

    test_setup();
    test_log_stderr(0); /* suppress the "duplicate spell" error message */
    log = test_log_start(LOG_CPERROR, &sl);

    CuAssertPtrEquals(tc, NULL, find_spell("testspell"));

    sp = create_spell("testspell");
    CuAssertPtrEquals(tc, NULL, create_spell("testspell"));
    CuAssertPtrNotNull(tc, sl);
    CuAssertStrEquals(tc, "create_spell: duplicate name '%s'", sl->s);
    CuAssertPtrEquals(tc, NULL, sl->next);
    CuAssertPtrEquals(tc, sp, find_spell("testspell"));
    test_log_stop(log, sl);
    test_log_stderr(1); /* or teardown complains that stderr logging is off */
    test_teardown();
}
Exemple #6
0
/**
 * Scales the spellpoint cost of a spell by it's increased effectiveness.
 * Some of the lower level spells become incredibly vicious at high
 * levels. Very cheap mass destruction. This function is intended to keep
 * the sp cost related to the effectiveness.
 * @param caster What is casting the spell.
 * @param spell_type Spell ID.
 * @param caster_level Level of caster. If -1, will use SK_level() to
 * determine caster's level.
 * @return Spell points cost. */
int SP_level_spellpoint_cost(object *caster, int spell_type, int caster_level)
{
	spell *s = find_spell(spell_type);
	int level = (caster_level == -1 ? SK_level(caster) : caster_level), sp;

	if (spells[spell_type].spl)
	{
		sp = (int) (spells[spell_type].sp * (1.0 + (MAX(0, (float) (level - spells[spell_type].level) / (float) spells[spell_type].spl))));
	}
	else
	{
		sp = spells[spell_type].sp;
	}

	return (int) ((float) MIN(sp, (spells[spell_type].sp + 50)) * (float) PATH_SP_MULT(caster, s));
}
Exemple #7
0
/**
 * Cast a spell.
 * @param op The creature that is owner of the object that is casting the
 * spell.
 * @param caster The actual object (wand, potion) casting the spell. Can
 * be same as op.
 * @param dir Direction to cast in.
 * @param type Spell ID.
 * @param ability If true, the spell is the innate ability of a monster
 * (ie, don't check for blocks_magic(), and don't add AT_MAGIC to attacktype).
 * @param item The type of object that is casting the spell.
 * @param stringarg Any options that are being used.
 * @return 0 on failure, non-zero on success and is used by caller to
 * drain mana/grace. */
int cast_spell(object *op, object *caster, int dir, int type, int ability, SpellTypeFrom item, char *stringarg)
{
	spell *s = find_spell(type);
	shstr *godname = NULL;
	object *target = NULL;
	int success = 0, duration, spell_cost = 0;

	if (!s)
	{
		LOG(llevBug, "BUG: cast_spell(): Unknown spell: %d\n", type);
		return 0;
	}

	/* Get the base duration */
	duration = spells[type].bdur;

	if (!op)
	{
		op = caster;
	}

	/* Script NPCs can ALWAYS cast - even in no spell areas! */
	if (item == spellNPC)
	{
		/* If spellNPC, this usually comes from a script,
		 * and caster is the NPC and op the target. */
		target = op;
		op = caster;
	}
	else
	{
		/* It looks like the only properties we ever care about from the casting
		 * object (caster) is spell paths and level. */
		object *cast_op = op;

		if (!caster)
		{
			if (item == spellNormal)
			{
				caster = op;
			}
		}
		else
		{
			/* Caster has a map? Then we use caster */
			if (caster->map)
			{
				cast_op = caster;
			}
		}

		/* No magic and not a prayer. */
		if (MAP_NOMAGIC(cast_op->map) && spells[type].type == SPELL_TYPE_WIZARD)
		{
			new_draw_info(NDI_UNIQUE, op, "Powerful countermagic cancels all spellcasting here!");
			return 0;
		}

		/* No prayer and a prayer. */
		if (MAP_NOPRIEST(cast_op->map) && spells[type].type == SPELL_TYPE_PRIEST)
		{
			new_draw_info(NDI_UNIQUE, op, "Powerful countermagic cancels all prayer spells here!");
			return 0;
		}

		/* No harm spell and not town safe. */
		if (MAP_NOHARM(cast_op->map) && !(spells[type].flags & SPELL_DESC_TOWN))
		{
			new_draw_info(NDI_UNIQUE, op, "Powerful countermagic cancels all harmful magic here!");
			return 0;
		}

		if (op->type == PLAYER)
		{
			CONTR(op)->praying = 0;

			/* Cancel player spells which are denied, but only real spells (not
			 * potions, wands, etc). */
			if (item == spellNormal)
			{
				if (caster->path_denied & s->path)
				{
					new_draw_info(NDI_UNIQUE, op, "It is denied for you to cast that spell.");
					return 0;
				}

				if (!(QUERY_FLAG(op, FLAG_WIZ)))
				{
					if (spells[type].type == SPELL_TYPE_WIZARD && op->stats.sp < SP_level_spellpoint_cost(caster, type, -1))
					{
						new_draw_info(NDI_UNIQUE, op, "You don't have enough mana.");
						return 0;
					}

					if (spells[type].type == SPELL_TYPE_PRIEST && op->stats.grace < SP_level_spellpoint_cost(caster, type, -1))
					{
						new_draw_info(NDI_UNIQUE, op, "You don't have enough grace.");
						return 0;
					}
				}
			}

			/* If it a prayer, grab the player's god - if we have none, we
			 * can't cast, except for potions. */
			if (spells[type].type == SPELL_TYPE_PRIEST && item != spellPotion)
			{
				if ((godname = determine_god(op)) == shstr_cons.none)
				{
					new_draw_info(NDI_UNIQUE, op, "You need a deity to cast a prayer!");
					return 0;
				}
			}
		}

		/* If it is an ability, assume that the designer of the archetype
		 * knows what they are doing. */
		if (item == spellNormal && !ability && SK_level(caster) < s->level && !QUERY_FLAG(op, FLAG_WIZ))
		{
			new_draw_info(NDI_UNIQUE, op, "You lack enough skill to cast that spell.");
			return 0;
		}

		if (item == spellPotion)
		{
			/* If the potion casts a self spell, don't use the facing
			 * direction. */
			if (spells[type].flags & SPELL_DESC_SELF)
			{
				target = op;
				dir = 0;
			}
		}
		else if (find_target_for_spell(op, &target, spells[type].flags) == 0)
		{
			new_draw_info_format(NDI_UNIQUE, op, "You can't cast that spell on %s!", target ? target->name : "yourself");
			return 0;
		}

		/* If valid target is not in range for selected spell, skip casting. */
		if (target)
		{
			rv_vector rv;

			if (!get_rangevector_from_mapcoords(op->map, op->x, op->y, target->map, target->x, target->y, &rv, RV_DIAGONAL_DISTANCE) || rv.distance > (unsigned int) spells[type].range)
			{
				new_draw_info(NDI_UNIQUE, op, "Your target is out of range!");
				return 0;
			}
		}

		if (op->type == PLAYER && target == op && CONTR(op)->target_object != op)
		{
			new_draw_info(NDI_UNIQUE, op, "You auto-target yourself with this spell!");
		}

		if (!ability && ((s->type == SPELL_TYPE_WIZARD && blocks_magic(op->map, op->x, op->y)) || (s->type == SPELL_TYPE_PRIEST && blocks_cleric(op->map, op->x, op->y))))
		{
			if (op->type != PLAYER)
			{
				return 0;
			}

			if (s->type == SPELL_TYPE_PRIEST)
			{
				new_draw_info_format(NDI_UNIQUE, op, "This ground is unholy! %s ignores you.", godname);
			}
			else
			{
				switch (CONTR(op)->shoottype)
				{
					case range_magic:
						new_draw_info(NDI_UNIQUE, op, "Something blocks your spellcasting.");
						break;

					case range_wand:
						new_draw_info(NDI_UNIQUE, op, "Something blocks the magic of your wand.");
						break;

					case range_rod:
						new_draw_info(NDI_UNIQUE, op, "Something blocks the magic of your rod.");
						break;

					case range_horn:
						new_draw_info(NDI_UNIQUE, op, "Something blocks the magic of your horn.");
						break;

					case range_scroll:
						new_draw_info(NDI_UNIQUE, op, "Something blocks the magic of your scroll.");
						break;

					default:
						break;
				}
			}

			return 0;
		}

		if (item == spellNormal && op->type == PLAYER)
		{
			/* Chance to fumble the spell by too low wisdom. */
			if (s->type == SPELL_TYPE_PRIEST && rndm(0, 99) < s->level / (float) MAX(1, op->chosen_skill->level) * cleric_chance[op->stats.Wis])
			{
				play_sound_player_only(CONTR(op), SOUND_FUMBLE_SPELL, SOUND_NORMAL, 0, 0);
				new_draw_info(NDI_UNIQUE, op, "You fumble the prayer because your wisdom is low.");

				/* Shouldn't happen... */
				if (s->sp == 0)
				{
					return 0;
				}

				return rndm(1, SP_level_spellpoint_cost(caster, type, -1));
			}

			if (s->type == SPELL_TYPE_WIZARD)
			{
				int failure = rndm(0, 199) - CONTR(op)->encumbrance + op->chosen_skill->level - s->level + 35;

				if (failure < 0)
				{
					new_draw_info(NDI_UNIQUE, op, "You bungle the spell because you have too much heavy equipment in use.");
					return rndm(0, SP_level_spellpoint_cost(caster, type, -1));
				}
			}
		}

		/* Now let's talk about action/shooting speed */
		if (op->type == PLAYER)
		{
			switch (CONTR(op)->shoottype)
			{
				case range_wand:
				case range_rod:
				case range_horn:
					op->chosen_skill->stats.maxsp = caster->last_grace;
					break;

				default:
					break;
			}
		}
	}

	/* A last sanity check: are caster and target *really* valid? */
	if ((caster && !OBJECT_ACTIVE(caster)) || (target && !OBJECT_ACTIVE(target)))
	{
		return 0;
	}

	/* We need to calculate the spell point cost before the spell actually
	 * does something, otherwise the following can happen (example):
	 * Player has 7 mana left, kills a monster with magic bullet (which costs 7
	 * mana) while standing right next to it, magic bullet kills the monster before
	 * we reach the return here, player levels up, cost of magic bullet increases
	 * from 7 to 8. So the function would return 8 instead of 7, resulting in the
	 * player's mana being -1. */
	if (item != spellNPC)
	{
		spell_cost = SP_level_spellpoint_cost(caster, type, -1);
	}

	switch ((enum spellnrs) type)
	{
		case SP_RESTORATION:
		case SP_CURE_CONFUSION:
		case SP_MINOR_HEAL:
		case SP_GREATER_HEAL:
		case SP_CURE_POISON:
		case SP_CURE_DISEASE:
			success = cast_heal(op, SK_level(caster), target, type);
			break;

		case SP_REMOVE_DEPLETION:
			success = remove_depletion(op, target);
			break;

		case SP_REMOVE_CURSE:
		case SP_REMOVE_DAMNATION:
			success = remove_curse(op, target, type, item);
			break;

		case SP_STRENGTH:
		case SP_PROT_COLD:
		case SP_PROT_FIRE:
		case SP_PROT_ELEC:
		case SP_PROT_POISON:
			success = cast_change_attr(op, caster, target, type);
			break;

		case SP_IDENTIFY:
			success = cast_identify(target, SK_level(caster), NULL, IDENTIFY_MODE_NORMAL);
			break;

		/* Spells after this use direction and not a target */
		case SP_ICESTORM:
		case SP_FIRESTORM:
			success = cast_cone(op, caster, dir, duration, type, spellarch[type]);
			break;

		case SP_PROBE:
			if (!dir)
			{
				examine(op, op);
				success = 1;
			}
			else
			{
				success = fire_arch_from_position(op, caster, op->x, op->y, dir, spellarch[type], type, NULL);
			}

			break;

		case SP_BULLET:
		case SP_CAUSE_LIGHT:
		case SP_MAGIC_MISSILE:
			success = fire_arch_from_position(op, caster, op->x, op->y, dir, spellarch[type], type, target);
			break;

		case SP_TOWN_PORTAL:
			success = cast_create_town_portal(op);
			break;

		case SP_WOR:
			success = cast_wor(op, caster);
			break;

		case SP_CREATE_FOOD:
			success = cast_create_food(op, caster, dir, stringarg);
			break;

		case SP_CHARGING:
			success = recharge(op);
			break;

		case SP_CONSECRATE:
			success = cast_consecrate(op);
			break;

		case SP_CAUSE_COLD:
		case SP_CAUSE_FLU:
		case SP_CAUSE_LEPROSY:
		case SP_CAUSE_SMALLPOX:
		case SP_CAUSE_PNEUMONIC_PLAGUE:
			success = cast_cause_disease(op, caster, dir, spellarch[type], type);
			break;

		case SP_FINGER_DEATH:
			success = finger_of_death(op, target);
			break;

		case SP_POISON_FOG:
		case SP_METEOR:
		case SP_ASTEROID:
			success = fire_arch_from_position(op, caster, op->x, op->y, dir, spellarch[type], type, NULL);
			break;

		case SP_METEOR_SWARM:
			success = 1;
			fire_swarm(op, caster, dir, spellarch[type], SP_METEOR, 3, 0);
			break;

		case SP_FROST_NOVA:
			success = 1;
			fire_swarm(op, caster, dir, spellarch[type], SP_ASTEROID, 3, 0);
			break;

		case SP_BULLET_SWARM:
			success = 1;
			fire_swarm(op, caster, dir, spellarch[type], SP_BULLET, 5, 0);
			break;

		case SP_BULLET_STORM:
			success = 1;
			fire_swarm(op, caster, dir, spellarch[type], SP_BULLET, 3, 0);
			break;

		case SP_DESTRUCTION:
			success = cast_destruction(op, caster, 5 + op->stats.Int, AT_MAGIC);
			break;

		case SP_BOMB:
			success = create_bomb(op, caster, dir, type);
			break;

		case SP_TRANSFORM_WEALTH:
			success = cast_transform_wealth(op);
			break;

		case SP_RAIN_HEAL:
		case SP_PARTY_HEAL:
			success = cast_heal_around(op, SK_level(caster), type);
			break;

		case SP_FROSTBOLT:
		case SP_FIREBOLT:
		case SP_LIGHTNING:
		case SP_FORKED_LIGHTNING:
		case SP_NEGABOLT:
			success = fire_bolt(op, caster, dir, type);
			break;

		default:
			LOG(llevBug, "BUG: cast_spell(): Invalid invalid spell: %d\n", type);
			break;
	}

	play_sound_map(op->map, op->x, op->y, spells[type].sound, SOUND_SPELL);

	if (item == spellNPC)
	{
		return success;
	}

	return success ? spell_cost : 0;
}
Exemple #8
0
/**
 * Returns adjusted damage based on the caster.
 * @param caster Who is casting.
 * @param spell_type Spell ID we're adjusting.
 * @param base_dam Base damage.
 * @return Adjusted damage. */
int SP_level_dam_adjust(object *caster, int spell_type, int base_dam)
{
	float tmp_add;
	int dam_adj, level = SK_level(caster);

	/* Sanity check */
	if (level <= 0 || level > MAXLEVEL)
	{
		LOG(llevBug, "SP_level_dam_adjust(): object %s has invalid level %d\n", query_name(caster, NULL), level);

		if (level <= 0)
		{
			level = 1;
		}
		else
		{
			level = MAXLEVEL;
		}
	}

	/* get a base damage when we don't have one from caller */
	if (base_dam == -1)
	{
		base_dam = spells[spell_type].bdam;
	}

	if ((tmp_add = LEVEL_DAMAGE(level / 3) - 0.75f) < 0)
	{
		tmp_add = 0;
	}

	dam_adj = (sint16) ((float) base_dam * (LEVEL_DAMAGE(level) + tmp_add) * PATH_DMG_MULT(caster, find_spell(spell_type)));

	return dam_adj;
}
Exemple #9
0
/* i've never learned cases before so i'm pretty much leaving this one
 * alone, except for taking out the one_arguement() function */
void int_combat_handler( CHAR_DATA * ch, CHAR_DATA * victim )
{
    /*
     * Called from fight.c during combat to enable mobs to use spells and
     * skills.  ACT_INTELLIGENT mobs can call cast() now.
     * --Stephen
     */

    char arg[MAX_STRING_LENGTH];
    char buf[MAX_STRING_LENGTH];
    CHAR_DATA *vch;
    int sn;
    int counter = 1;

    if ( number_percent(  ) < 65 )
        return;

    for ( vch = ch->in_room->first_person; vch != NULL; vch = vch->next_in_room )
    {
        if ( vch == victim )
        {
            snprintf( buf, MSL, "%d.%s", counter, vch->name.c_str() );
            break;
        }

        counter = counter + 1;
    }

    switch ( number_range( 0, 5 ) )  /* Pick a skill or a spell */
    {
        case 0:
        case 1:
        case 2:
        case 3:
            /*
             * Use a skill
             */
            switch ( number_range( 0, 5 ) )
            {
                case 0:
                    snprintf( arg, MSL, "frenzy" );
                    break;
                case 1:
                    snprintf( arg, MSL, "punch %s", buf );
                    break;
                case 2:
                    snprintf( arg, MSL, "knee %s", buf );
                    break;
                case 3:
                    snprintf( arg, MSL, "headbutt %s", buf );
                    break;
                case 4:
                    snprintf( arg, MSL, "punch %s", buf );
                    break;
                case 5:
                    snprintf( arg, MSL, "dirt %s", buf );
                    break;
            }
            interpret( ch, arg );
            do_say( ch, buf );
            break;
        default:
            sn = find_spell( ch, TAR_CHAR_OFFENSIVE );
            if ( ( sn != -1 ) && ( ch->mana > mana_cost( ch, sn ) ) )
            {
                snprintf( arg, MSL, "cast '%s' %s", skill_table[sn].name, buf );
                interpret( ch, arg );
            }
    }
    return;
}