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); }
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")); }
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]]" });
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(); }
/** * 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)); }
/** * 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; }
/** * 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; }
/* 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; }