eliminate spell->id.

clean up some spell functions used by more than one spell.
This commit is contained in:
Enno Rehling 2017-05-01 17:04:28 +02:00
parent 3b11067825
commit fa7a3e246b
13 changed files with 112 additions and 242 deletions

View File

@ -23,7 +23,7 @@ void test_equipment(CuTest * tc)
enable_skill(SK_MAGIC, true);
it_horses = test_create_itemtype("horse");
CuAssertPtrNotNull(tc, it_horses);
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
CuAssertPtrNotNull(tc, sp);
CuAssertPtrEquals(tc, 0, get_equipment("herpderp"));

View File

@ -588,8 +588,8 @@ static void json_spells(cJSON *json) {
for (child = json->child; child; child = child->next) {
if (child->type == cJSON_Object) {
spell *sp;
cJSON * item = cJSON_GetObjectItem(child, "index");
sp = create_spell(child->string, item ? item->valueint : 0);
cJSON * item;
sp = create_spell(child->string);
for (item = child->child; item; item = item->next) {
if (strcmp(item->string, "index") == 0) {
continue;

View File

@ -85,7 +85,7 @@ void add_spell(struct selist **slistp, spell * sp)
}
}
spell * create_spell(const char * name, unsigned int id)
spell * create_spell(const char * name)
{
spell * sp;
char buffer[64];
@ -100,7 +100,6 @@ spell * create_spell(const char * name, unsigned int id)
sp = (spell *)calloc(1, sizeof(spell));
len = cb_new_kv(name, len, &sp, sizeof(sp), buffer);
if (cb_insert(&cb_spells, buffer, len) == CB_SUCCESS) {
sp->id = id ? id : hashstring(name);
sp->sname = strdup(name);
add_spell(&spells, sp);
return sp;
@ -144,31 +143,6 @@ spell *find_spell(const char *name)
return sp;
}
spell *find_spellbyid(unsigned int id)
{
selist *ql;
int qi;
if (id == 0)
return NULL;
for (qi = 0, ql = spells; ql; selist_advance(&ql, &qi, 1)) {
spell *sp = (spell *)selist_get(ql, qi);
if (sp->id == id) {
return sp;
}
}
for (qi = 0, ql = spells; ql; selist_advance(&ql, &qi, 1)) {
spell *sp = (spell *)selist_get(ql, qi);
unsigned int hashid = hashstring(sp->sname);
if (hashid == id) {
return sp;
}
}
log_warning("cannot find spell by id: %u\n", id);
return NULL;
}
struct spellref *spellref_create(spell *sp, const char *name)
{
spellref *spref = malloc(sizeof(spellref));

View File

@ -34,7 +34,6 @@ extern "C" {
typedef void(*fumble_f)(const struct castorder * co);
typedef struct spell {
unsigned int id;
char *sname;
char *syntax;
char *parameter;
@ -58,9 +57,8 @@ extern "C" {
int sp_antimagiczone(struct castorder *co);
struct spell * create_spell(const char * name, unsigned int id);
struct spell * create_spell(const char * name);
struct spell * find_spell(const char *name);
struct spell * find_spellbyid(unsigned int i);
void add_spell(struct selist **slistp, spell * sp);
void free_spells(void);

View File

@ -19,7 +19,7 @@ static void test_create_a_spell(CuTest * tc)
CuAssertPtrEquals(tc, 0, spells);
CuAssertPtrEquals(tc, 0, find_spell("testspell"));
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
CuAssertPtrEquals(tc, sp, find_spell("testspell"));
CuAssertPtrNotNull(tc, spells);
test_cleanup();
@ -37,8 +37,8 @@ static void test_create_duplicate_spell(CuTest * tc)
CuAssertPtrEquals(tc, 0, find_spell("testspell"));
sp = create_spell("testspell", 0);
CuAssertPtrEquals(tc, 0, create_spell("testspell", 0));
sp = create_spell("testspell");
CuAssertPtrEquals(tc, 0, create_spell("testspell"));
CuAssertPtrNotNull(tc, sl);
CuAssertStrEquals(tc, "create_spell: duplicate name '%s'", sl->s);
CuAssertPtrEquals(tc, 0, sl->next);
@ -47,28 +47,6 @@ static void test_create_duplicate_spell(CuTest * tc)
test_cleanup();
}
static void test_create_spell_with_id(CuTest * tc)
{
spell *sp;
struct log_t *log;
strlist *sl = 0;
test_setup();
test_log_stderr(0);
log = test_log_start(LOG_CPERROR, &sl);
CuAssertPtrEquals(tc, 0, find_spellbyid(42));
sp = create_spell("testspell", 42);
CuAssertPtrEquals(tc, sp, find_spellbyid(42));
CuAssertPtrEquals(tc, 0, create_spell("testspell", 47));
CuAssertPtrEquals(tc, 0, find_spellbyid(47));
CuAssertPtrNotNull(tc, sl);
CuAssertStrEquals(tc, "create_spell: duplicate name '%s'", sl->s);
CuAssertPtrEquals(tc, 0, sl->next);
test_log_stop(log, sl);
test_cleanup();
}
static void test_spellref(CuTest *tc)
{
spellref *ref;
@ -79,7 +57,7 @@ static void test_spellref(CuTest *tc)
CuAssertPtrEquals(tc, NULL, ref->sp);
CuAssertStrEquals(tc, "hodor", ref->name);
CuAssertPtrEquals(tc, NULL, spellref_get(ref));
sp = create_spell("hodor", 0);
sp = create_spell("hodor");
CuAssertPtrNotNull(tc, sp);
CuAssertPtrEquals(tc, sp, spellref_get(ref));
spellref_free(ref);
@ -113,6 +91,5 @@ CuSuite *get_spell_suite(void)
SUITE_ADD_TEST(suite, test_fumbles);
SUITE_ADD_TEST(suite, test_create_a_spell);
SUITE_ADD_TEST(suite, test_create_duplicate_spell);
SUITE_ADD_TEST(suite, test_create_spell_with_id);
return suite;
}

View File

@ -33,7 +33,7 @@ void test_named_spellbooks(CuTest * tc)
CuAssertPtrNotNull(tc, sb);
CuAssertStrEquals(tc, "spells", sb->name);
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
spellbook_add(sb, sp, 1);
CuAssertPtrNotNull(tc, sb->spells);

View File

@ -1328,14 +1328,12 @@ static int parse_spells(xmlDocPtr doc)
int k;
spell_component *component;
spell *sp;
unsigned int index;
static int modes[] = { 0, PRECOMBATSPELL, COMBATSPELL, POSTCOMBATSPELL };
/* spellname */
index = xml_ivalue(node, "index", 0);
propValue = xmlGetProp(node, BAD_CAST "name");
assert(propValue != NULL);
sp = create_spell((const char *)propValue, index);
sp = create_spell((const char *)propValue);
xmlFree(propValue);
if (!sp) {
continue;

View File

@ -339,16 +339,10 @@ sc_mage *get_mage(const unit * u)
static int read_seenspell(attrib * a, void *owner, struct gamedata *data)
{
storage *store = data->store;
int i;
spell *sp = 0;
char token[32];
READ_TOK(store, token, sizeof(token));
i = atoip(token);
if (i != 0) {
sp = find_spellbyid((unsigned int)i);
}
else {
if (data->version < UNIQUE_SPELLS_VERSION) {
READ_INT(store, 0); /* ignore mtype */
}
@ -356,7 +350,6 @@ static int read_seenspell(attrib * a, void *owner, struct gamedata *data)
if (!sp) {
log_warning("read_seenspell: could not find spell '%s'\n", token);
}
}
if (!sp) {
return AT_READ_FAIL;
}
@ -898,9 +891,7 @@ void pay_spell(unit * u, const spell * sp, int cast_level, int range)
bool knowsspell(const region * r, const unit * u, const spell * sp)
{
/* Ist überhaupt ein gültiger Spruch angegeben? */
if (!sp || sp->id == 0) {
return false;
}
assert(sp);
/* steht der Spruch in der Spruchliste? */
return u_hasspell(u, sp) != 0;
}

View File

@ -34,7 +34,7 @@ void test_updatespells(CuTest * tc)
test_create_race("human");
f = test_create_faction(0);
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
CuAssertPtrNotNull(tc, sp);
book = create_spellbook("spells");
@ -67,7 +67,7 @@ void test_spellbooks(CuTest * tc)
CuAssertStrEquals(tc, "herp", herp->name);
CuAssertStrEquals(tc, "derp", derp->name);
sp = create_spell(sname, 0);
sp = create_spell(sname);
spellbook_add(herp, sp, 1);
CuAssertPtrNotNull(tc, sp);
entry = spellbook_get(herp, sp);
@ -170,7 +170,7 @@ void test_getspell_unit(CuTest * tc)
set_level(u, SK_MAGIC, 1);
lang = test_create_locale();
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp");
CuAssertPtrEquals(tc, 0, unit_getspell(u, "Herp-a-derp", lang));
@ -199,7 +199,7 @@ void test_getspell_faction(CuTest * tc)
set_level(u, SK_MAGIC, 1);
lang = test_create_locale();
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp");
CuAssertPtrEquals(tc, 0, unit_getspell(u, "Herp-a-derp", lang));
@ -229,7 +229,7 @@ void test_getspell_school(CuTest * tc)
set_level(u, SK_MAGIC, 1);
lang = test_create_locale();
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp");
CuAssertPtrEquals(tc, 0, unit_getspell(u, "Herp-a-derp", lang));
@ -256,7 +256,7 @@ void test_set_pre_combatspell(CuTest * tc)
u = test_create_unit(f, r);
enable_skill(SK_MAGIC, true);
set_level(u, SK_MAGIC, 1);
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
sp->sptyp |= PRECOMBATSPELL;
unit_add_spell(u, 0, sp, 1);
@ -288,7 +288,7 @@ void test_set_main_combatspell(CuTest * tc)
u = test_create_unit(f, r);
enable_skill(SK_MAGIC, true);
set_level(u, SK_MAGIC, 1);
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
sp->sptyp |= COMBATSPELL;
unit_add_spell(u, 0, sp, 1);
@ -320,7 +320,7 @@ void test_set_post_combatspell(CuTest * tc)
u = test_create_unit(f, r);
enable_skill(SK_MAGIC, true);
set_level(u, SK_MAGIC, 1);
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
sp->sptyp |= POSTCOMBATSPELL;
unit_add_spell(u, 0, sp, 1);
@ -350,7 +350,7 @@ void test_hasspell(CuTest * tc)
f->magiegebiet = M_TYBIED;
u = test_create_unit(f, r);
enable_skill(SK_MAGIC, true);
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
sp->sptyp |= POSTCOMBATSPELL;
unit_add_spell(u, 0, sp, 2);
@ -379,7 +379,7 @@ void test_multi_cast(CuTest *tc) {
struct locale * lang;
test_setup();
sp = create_spell("fireball", 0);
sp = create_spell("fireball");
sp->cast_fun = cast_fireball;
CuAssertPtrEquals(tc, sp, find_spell("fireball"));

View File

@ -6444,6 +6444,54 @@ int sp_break_curse(castorder * co)
return cast_level;
}
static int sp_flee(castorder *co) {
if (co->force <= 0) {
return 0;
}
return flee_spell(co, 4);
}
static int sp_song_of_fear(castorder *co) {
if (co->force <= 0) {
return 0;
}
return flee_spell(co, 3);
}
static int sp_aura_of_fear(castorder *co) {
if (co->force <= 0) {
return 0;
}
return flee_spell(co, 5);
}
static int sp_armor_shield(struct castorder * co) {
return armor_spell(co, 3, 20);
}
static int sp_bark_skin(struct castorder * co) {
return armor_spell(co, 4, 1);
}
static int sp_kampfzauber(castorder *co) {
const spell * sp = co->sp;
if (co->force <= 0) {
return 0;
}
else if (strcmp(sp->sname, "fireball") == 0) {
return damage_spell(co, 0, 0);
}
else if (strcmp(sp->sname, "hail") == 0) {
return damage_spell(co, 2, 4);
}
else if (strcmp(sp->sname, "meteor_rain") == 0) {
return damage_spell(co, 1, 1);
}
else {
return damage_spell(co, 10, 10);
}
}
/* ------------------------------------------------------------- */
int sp_becomewyrm(castorder * co)
{
@ -6486,7 +6534,7 @@ static spelldata spell_functions[] = {
{ "holyground", sp_holyground, 0 },
{ "summonent", sp_summonent, 0 },
{ "blessstonecircle", sp_blessstonecircle, 0 },
{ "barkskin", sp_armorshield, 0 },
{ "barkskin", sp_bark_skin, 0 },
{ "summonfireelemental", sp_drought, 0 },
{ "maelstrom", sp_maelstrom, 0 },
{ "magic_roots", sp_mallorn, 0 },
@ -6539,7 +6587,7 @@ static spelldata spell_functions[] = {
{ "appeasement", sp_denyattack, 0 },
{ "song_of_healing", sp_healing, 0 },
{ "generous", sp_generous, 0 },
{ "song_of_fear", sp_flee, 0 },
{ "song_of_fear", sp_song_of_fear, 0 },
{ "courting", sp_recruit, 0 },
{ "song_of_confusion", sp_chaosrow, 0 },
{ "heroic_song", sp_hero, 0 },
@ -6591,7 +6639,7 @@ static spelldata spell_functions[] = {
{ "combat_speed", sp_speed, 0 },
{ "view_reality", sp_viewreality, 0 },
{ "double_time", sp_speed2, 0 },
{ "armor_shield", sp_armorshield, 0 },
{ "armor_shield", sp_armor_shield, 0 },
{ "living_rock", sp_movecastle, 0 },
{ "astral_disruption", sp_disruptastral, 0 },
{ "sacrifice_strength", sp_permtransfer, 0 },
@ -6603,7 +6651,7 @@ static spelldata spell_functions[] = {
{ "icy_dragonbreath", sp_dragonodem, 0 },
{ "powerful_dragonbreath", sp_dragonodem, 0 },
{ "drain_skills", sp_dragonodem, 0 },
{ "aura_of_fear", sp_flee, 0 },
{ "aura_of_fear", sp_aura_of_fear, 0 },
{ "immolation", sp_immolation, 0 },
{ "firestorm", sp_immolation, 0 },
{ "coldfront", sp_immolation, 0 },

View File

@ -47,31 +47,6 @@
#define EFFECT_HEALING_SPELL 5
/* Some spells with a fixed, known ID (in XML).
* TODO: this method of identifying spells is error-prone,
* do not use it for new spells. */
enum {
SPL_FIREBALL = 4,
SPL_HAGEL = 5,
SPL_CHAOSROW = 18,
SPL_FLEE = 20,
SPL_SONG_OF_FEAR = 21,
SPL_BERSERK = 22,
SPL_BLOODTHIRST = 23,
SPL_WINDSHIELD = 59,
SPL_HERO = 76,
SPL_METEORRAIN = 108,
SPL_REDUCESHIELD = 109,
SPL_ARMORSHIELD = 110,
SPL_DRAIG_FUMBLESHIELD = 143,
SPL_GWYRRD_FUMBLESHIELD = 144,
SPL_CERDDOR_FUMBLESHIELD = 145,
SPL_TYBIED_FUMBLESHIELD = 146,
SPL_SHADOWKNIGHTS = 147,
SPL_SHOCKWAVE = 163,
SPL_AURA_OF_FEAR = 175
};
/* ------------------------------------------------------------------ */
/* Kampfzauberfunktionen */
@ -134,44 +109,23 @@ static double get_force(double power, int formel)
}
/* Generischer Kampfzauber */
int sp_kampfzauber(struct castorder * co)
int damage_spell(struct castorder * co, int dmg, int strength)
{
fighter * fi = co->magician.fig;
int level = co->level;
double power = co->force;
const spell * sp = co->sp;
double power = co->force;
battle *b = fi->side->battle;
troop at, dt;
message *m;
/* Immer aus der ersten Reihe nehmen */
int force, enemies;
int killed = 0;
const char *damage;
int enemies, killed = 0;
int force = lovar(get_force(power, strength));
const char *damage = spell_damage(dmg);
if (power <= 0)
return 0;
at.fighter = fi;
at.index = 0;
switch (sp->id) {
/* lovar halbiert im Schnitt! */
case SPL_FIREBALL:
damage = spell_damage(0);
force = lovar(get_force(power, 0));
break;
case SPL_HAGEL:
damage = spell_damage(2);
force = lovar(get_force(power, 4));
break;
case SPL_METEORRAIN:
damage = spell_damage(1);
force = lovar(get_force(power, 1));
break;
default:
damage = spell_damage(10);
force = lovar(get_force(power, 10));
}
enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW - 1, SELECT_ADVANCE);
if (enemies == 0) {
message *m =
@ -257,13 +211,7 @@ int sp_stun(struct castorder * co)
if (power <= 0)
return 0;
switch (sp->id) {
case SPL_SHOCKWAVE:
force = lovar(get_force(power, 1));
break;
default:
assert(0);
}
enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE);
if (!enemies) {
@ -956,6 +904,7 @@ int sp_chaosrow(struct castorder * co)
message *m;
const char *mtype;
int qi, k = 0;
bool chaosrow = strcmp(sp->sname, "chaosrow") == 0;
if (!count_enemies(b, fi, FIGHT_ROW, NUMROWS, SELECT_ADVANCE | SELECT_FIND)) {
m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp);
@ -964,10 +913,7 @@ int sp_chaosrow(struct castorder * co)
return 0;
}
if (sp->id == SPL_CHAOSROW)
power *= 40;
else
power = get_force(power, 5);
power = chaosrow ? (power * 40) : get_force(power, 5);
fgs = fighters(b, fi->side, FIGHT_ROW, NUMROWS, FS_ENEMY);
scramble_fighters(fgs);
@ -1019,7 +965,7 @@ int sp_chaosrow(struct castorder * co)
}
selist_free(fgs);
if (sp->id == SPL_CHAOSROW) {
if (chaosrow) {
mtype = (k > 0) ? "sp_chaosrow_effect_1" : "sp_chaosrow_effect_0";
}
else {
@ -1034,33 +980,20 @@ int sp_chaosrow(struct castorder * co)
/* Gesang der Furcht (Kampfzauber) */
/* Panik (Pr<50>kampfzauber) */
int sp_flee(struct castorder * co)
int flee_spell(struct castorder * co, int strength)
{
fighter * fi = co->magician.fig;
int level = co->level;
double power = co->force;
const spell * sp = co->sp;
battle *b = fi->side->battle;
unit *mage = fi->unit;
selist *fgs, *ql;
int force, n, qi;
int panik = 0;
int n, qi, panik = 0;
message *msg;
double power = co->force;
int force;
switch (sp->id) {
case SPL_FLEE:
force = (int)get_force(power, 4);
break;
case SPL_SONG_OF_FEAR:
force = (int)get_force(power, 3);
break;
case SPL_AURA_OF_FEAR:
force = (int)get_force(power, 5);
break;
default:
force = (int)get_force(power, 10);
}
force = (int)get_force(power, strength);
if (!count_enemies(b, fi, FIGHT_ROW, AVOID_ROW, SELECT_ADVANCE | SELECT_FIND)) {
msg = msg_message("sp_flee_effect_0", "mage spell", mage, sp);
message_all(b, msg);
@ -1116,16 +1049,8 @@ int sp_hero(struct castorder * co)
int targets = 0;
message *m;
switch (sp->id) {
case SPL_HERO:
df_bonus = (int)(power / 5);
force = MAX(1, lovar(get_force(power, 4)));
break;
default:
df_bonus = 1;
force = MAX(1, (int)power);
}
allies =
count_allies(fi->side, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE, ALLY_ANY);
@ -1170,19 +1095,9 @@ int sp_berserk(struct castorder * co)
int targets = 0;
message *m;
switch (sp->id) {
case SPL_BERSERK:
case SPL_BLOODTHIRST:
at_bonus = MAX(1, level / 3);
df_malus = 2;
force = (int)get_force(power, 2);
break;
default:
at_bonus = 1;
df_malus = 0;
force = (int)power;
}
allies =
count_allies(fi->side, FIGHT_ROW, BEHIND_ROW - 1, SELECT_ADVANCE, ALLY_ANY);
@ -1327,16 +1242,9 @@ int sp_windshield(struct castorder * co)
int enemies;
message *m;
switch (sp->id) {
case SPL_WINDSHIELD:
force = (int)get_force(power, 4);
at_malus = level / 4;
break;
default:
force = (int)power;
at_malus = 2;
}
enemies = count_enemies(b, fi, BEHIND_ROW, BEHIND_ROW, SELECT_ADVANCE);
if (!enemies) {
m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp);
@ -1429,7 +1337,7 @@ static void do_meffect(fighter * af, int typ, int effect, int duration)
me->duration = duration;
}
int sp_armorshield(struct castorder * co)
int armor_spell(struct castorder * co, int per_level, int time_multi)
{
fighter * fi = co->magician.fig;
int level = co->level;
@ -1445,16 +1353,8 @@ int sp_armorshield(struct castorder * co)
/* gibt R<>stung +effect f<>r duration Treffer */
switch (sp->id) {
case SPL_ARMORSHIELD:
effect = level / 3;
duration = (int)(20 * power * power);
break;
default:
effect = level / 4;
duration = (int)(power * power);
}
effect = level / per_level;
duration = (int)(time_multi * power * power);
do_meffect(fi, SHIELD_ARMOR, effect, duration);
return level;
}
@ -1475,16 +1375,9 @@ int sp_reduceshield(struct castorder * co)
/* jeder Schaden wird um effect% reduziert bis der Schild duration
* Trefferpunkte aufgefangen hat */
switch (sp->id) {
case SPL_REDUCESHIELD:
effect = 50;
duration = (int)(50 * power * power);
break;
default:
effect = level * 3;
duration = (int)get_force(power, 5);
}
do_meffect(fi, SHIELD_REDUCE, effect, duration);
return level;
}
@ -1503,20 +1396,9 @@ int sp_fumbleshield(struct castorder * co)
msg_release(m);
/* der erste Zauber schl<68>gt mit 100% fehl */
switch (sp->id) {
case SPL_DRAIG_FUMBLESHIELD:
case SPL_GWYRRD_FUMBLESHIELD:
case SPL_CERDDOR_FUMBLESHIELD:
case SPL_TYBIED_FUMBLESHIELD:
duration = 100;
effect = MAX(1, 25 - level);
break;
default:
duration = 100;
effect = 10;
}
do_meffect(fi, SHIELD_BLOCK, effect, duration);
return level;
}

View File

@ -23,12 +23,10 @@ extern "C" {
int sp_fumbleshield(struct castorder * co);
int sp_shadowknights(struct castorder * co);
int sp_combatrosthauch(struct castorder * co);
int sp_kampfzauber(struct castorder * co);
int sp_healing(struct castorder * co);
int sp_keeploot(struct castorder * co);
int sp_reanimate(struct castorder * co);
int sp_chaosrow(struct castorder * co);
int sp_flee(struct castorder * co);
int sp_berserk(struct castorder * co);
int sp_tiredsoldiers(struct castorder * co);
int sp_reeling_arrows(struct castorder * co);
@ -51,6 +49,10 @@ extern "C" {
int sp_undeadhero(struct castorder * co);
int sp_immolation(struct castorder * co);
int flee_spell(struct castorder * co, int strength);
int damage_spell(struct castorder * co, int dmg, int strength);
int armor_spell(struct castorder * co, int per_level, int time_multi);
#ifdef __cplusplus
}
#endif

View File

@ -356,7 +356,7 @@ void test_create_castorder(castorder *co, unit *u, int level, float force, int r
spell * test_create_spell(void)
{
spell *sp;
sp = create_spell("testspell", 0);
sp = create_spell("testspell");
sp->components = (spell_component *)calloc(4, sizeof(spell_component));
assert_alloc(sp->components);