diff --git a/src/common/kernel/battle.c b/src/common/kernel/battle.c index 4b84d60cc..1f1173203 100644 --- a/src/common/kernel/battle.c +++ b/src/common/kernel/battle.c @@ -946,8 +946,8 @@ remove_troop(troop dt) /* ------------------------------------------------------------- */ #if SKILLPOINTS -static void -drain_exp(unit *u, int n) +void +drain_exp(const struct unit *u, int n) { skill_t sk = (skill_t)(rand() % MAXSKILLS); skill_t ssk; @@ -971,8 +971,8 @@ drain_exp(unit *u, int n) /** reduces the target's exp by an equivalent of n points learning * 30 points = 1 week */ -static void -drain_exp(unit *u, int n) +void +drain_exp(const struct unit *u, int n) { skill_t sk = (skill_t)(rand() % MAXSKILLS); skill_t ssk; @@ -1334,6 +1334,13 @@ select_enemy(fighter * af, int minrow, int maxrow) void ** si; int enemies; + if(af->unit->race->flags & RCF_FLY) { + /* flying races ignore min- and maxrow and can attack anyone fighting + * them */ + minrow = FIGHT_ROW; + maxrow = BEHIND_ROW; + } + enemies = count_enemies(af->side, FS_ENEMY, minrow, maxrow); if (!enemies) diff --git a/src/common/kernel/battle.h b/src/common/kernel/battle.h index 9dcb9192f..ca1f03d4e 100644 --- a/src/common/kernel/battle.h +++ b/src/common/kernel/battle.h @@ -120,7 +120,6 @@ typedef struct weapon { /*** fighter::flags ***/ #define FIG_ATTACKED 1 #define FIG_NOLOOT 2 -#define FIG_COMBATEXP 4 typedef unsigned char armor_t; enum { @@ -237,5 +236,6 @@ extern boolean enemy (const struct side * a, const struct side * b); extern struct troop select_corpse(struct battle * b, struct fighter * af); extern fighter * make_fighter(struct battle * b, struct unit * u, boolean attack); void flee(const troop dt); +void drain_exp(const struct unit *u, int d); #endif diff --git a/src/common/kernel/combatspells.c b/src/common/kernel/combatspells.c index ef1c9ca74..a56859962 100644 --- a/src/common/kernel/combatspells.c +++ b/src/common/kernel/combatspells.c @@ -637,12 +637,116 @@ sp_dragonodem(fighter * fi, int level, int power, spell * sp) scat("."); battlerecord(b, buf); return level; +} +int +sp_drainodem(fighter * fi, int level, int power, spell * sp) +{ + battle *b = fi->side->battle; + troop dt; + troop at; + /* Immer aus der ersten Reihe nehmen */ + int minrow = FIGHT_ROW; + int maxrow = BEHIND_ROW-1; + int force, enemies; + int drained = 0; + int killed = 0; + const char *damage; + + sprintf(buf, "%s zaubert %s", unitname(fi->unit), sp->name); + /* 11-26 HP */ + damage = spell_damage(4); + /* Jungdrache 3->54, Drache 6->216, Wyrm 12->864 Treffer */ + force = lovar(get_force(level,6)); + + enemies = count_enemies(fi->side, FS_ENEMY, minrow, + maxrow); + + if (!enemies) { + scat(", aber niemand war in Reichweite."); + battlerecord(b, buf); + return 0; + } + scat(":"); + battlerecord(b, buf); + + at.fighter = fi; + at.index = 0; + + do { + dt = select_enemy(fi, minrow, maxrow); + assert(dt.fighter); + if (hits(at, dt, NULL)) { + drain_exp(dt.fighter->unit, 90); + drained++; + } + killed += terminate(dt, at, AT_COMBATSPELL, damage, false); + --force; + } while (force && drained < enemies); + + sprintf(buf, "%d Person%s wurde ihre Lebenskraft entzogen", + drained, drained == 1 ? " wurde" : "en wurden"); + + scat("."); + battlerecord(b, buf); + return level; } /* ------------------------------------------------------------- */ /* PRECOMBAT */ +int +sp_shadowcall(fighter * fi, int level, int power, spell * sp) +{ + battle *b = fi->side->battle; + region *r = b->region; + unit *mage = fi->unit; + attrib *a; + int force = get_force(power, 3)/2; + race *rc; + int num; + unit *u; + + unused(sp); + + switch(rand()%3) { + case 0: + rc = new_race[RC_SHADOWBAT]; + num = 5000+dice_rand("3d5000"); + break; + case 1: + rc = new_race[RC_NIGHTMARE]; + num = 500+dice_rand("3d500"); + break; + case 2: + rc = new_race[RC_VAMPUNICORN]; + num = 500+dice_rand("3d500"); + break; + } + + u = createunit(r, mage->faction, force, rc); + u->status = ST_FIGHT; + + set_string(&u->name, racename(mage->faction->locale, u, u->race)); + set_level(u, SK_WEAPONLESS, power/2); + set_level(u, SK_AUSDAUER, power/2); + u->hp = u->number * unit_max_hp(u); + + if (fval(mage, FL_PARTEITARNUNG)) + fset(u, FL_PARTEITARNUNG); + + a = a_new(&at_unitdissolve); + a->data.ca[0] = 0; + a->data.ca[1] = 100; + a_add(&u->attribs, a); + + make_fighter(b, u, true); + sprintf(buf, "%s ruft %d %s zu Hilfe", unitname(mage), force, + racename(default_locale, u, u->race)); + battlerecord(b, buf); + return level; +} + int sp_wolfhowl(fighter * fi, int level, int power, spell * sp) { @@ -656,7 +760,7 @@ sp_wolfhowl(fighter * fi, int level, int power, spell * sp) u->status = ST_FIGHT; - set_string(&u->name, force == 1 ? "Wolf" : "Wölfe"); + set_string(&u->name, racename(mage->faction->locale, u, u->race)); set_level(u, SK_WEAPONLESS, power/3); set_level(u, SK_AUSDAUER, power/3); u->hp = u->number * unit_max_hp(u); @@ -671,7 +775,7 @@ sp_wolfhowl(fighter * fi, int level, int power, spell * sp) make_fighter(b, u, true); sprintf(buf, "%s ruft %d %s zu Hilfe", unitname(mage), force, - force == 1 ? "Wolf" : "Wölfe"); + racename(default_locale, u, u->race)); battlerecord(b, buf); return level; } @@ -841,6 +945,10 @@ sp_flee(fighter * fi, int level, int power, spell * sp) sprintf(buf, "%s stimmt einen düsteren Gesang an", unitname(mage)); force = get_force(power,3); break; + case SPL_AURA_OF_FEAR: + sprintf(buf, "%s ist von dunklen Schatten umgeben", unitname(mage)); + force = get_force(power,5); + break; default: force = get_force(power,10); } diff --git a/src/common/kernel/eressea.h b/src/common/kernel/eressea.h index 40bf0fe0d..7576b24b6 100644 --- a/src/common/kernel/eressea.h +++ b/src/common/kernel/eressea.h @@ -685,7 +685,11 @@ enum { RC_GNOME, /* 60 */ RC_TEMPLATE, /* 61 */ RC_CLONE, /* 62 */ - + + RC_SHADOWDRAGON, + RC_SHADOWBAT, + RC_NIGHTMARE, + RC_VAMPUNICORN, MAXRACES, NORACE = (race_t) - 1 diff --git a/src/common/kernel/magic.c b/src/common/kernel/magic.c index 368e06a56..850bbf8e6 100644 --- a/src/common/kernel/magic.c +++ b/src/common/kernel/magic.c @@ -622,8 +622,9 @@ get_combatspelllevel(const unit *u, int nr) { sc_mage *m; + assert(nr < MAXCOMBATSPELLS); m = get_mage(u); - if (!m || !(nr < MAXCOMBATSPELLS)) { + if (!m) { return -1; } @@ -638,12 +639,15 @@ get_combatspell(const unit *u, int nr) { sc_mage *m; + assert(nr < MAXCOMBATSPELLS); m = get_mage(u); - if (!m || !(nr < MAXCOMBATSPELLS)) { - return (spell *)NULL; + if (m) { + return find_spellbyid(m->combatspell[nr]); + } else if(u->race->precombatspell != NO_SPELL) { + return find_spellbyid(u->race->precombatspell); } - return find_spellbyid(m->combatspell[nr]); + return NULL; } void diff --git a/src/common/kernel/race.c b/src/common/kernel/race.c index 1101239cc..ac3cd1117 100644 --- a/src/common/kernel/race.c +++ b/src/common/kernel/race.c @@ -72,6 +72,7 @@ rc_new(const char * zName) rc->_name[2] = strdup(zBuffer); sprintf(zBuffer, "%s_x", zName); rc->_name[3] = strdup(zBuffer); + rc->precombatspell = NO_SPELL; return rc; } @@ -169,7 +170,7 @@ static const char * oldracenames[MAXRACES] = { "centaur", "skeleton", "skeleton lord", "zombie", "juju-zombie", "ghoul", "ghast", "museumghost", "gnome", "template", - "clone" + "clone", "shadowdragon", "shadowbat", "nightmare", "vampunicorn" }; /* magres, {3 namen}, @@ -659,6 +660,7 @@ tagbegin(struct xml_stack * stack) if (xml_bvalue(tag, "resistbash")) rc->battle_flags |= BF_RES_BASH; if (xml_bvalue(tag, "resistcut")) rc->battle_flags |= BF_RES_CUT; if (xml_bvalue(tag, "resistpierce")) rc->battle_flags |= BF_RES_PIERCE; + state->race = rc; } else if (strcmp(tag->name, "ai")==0) { @@ -698,6 +700,9 @@ tagbegin(struct xml_stack * stack) } a->type = xml_ivalue(tag, "type"); a->flags = xml_ivalue(tag, "flags"); + } else if (strcmp(tag->name, "precombatspell") == 0) { + race * rc = state->race; + rc->precombatspell = xml_ivalue(tag, "spell"); } else if (strcmp(tag->name, "function")==0) { race * rc = state->race; const char * name = xml_value(tag, "name"); diff --git a/src/common/kernel/race.h b/src/common/kernel/race.h index c0ca1595d..ede219ba8 100644 --- a/src/common/kernel/race.h +++ b/src/common/kernel/race.h @@ -66,7 +66,8 @@ typedef struct race { char df_default; /* Verteidigungsskill Unbewaffnet (default: -2)*/ char at_bonus; /* Verändert den Angriffsskill (default: 0)*/ char df_bonus; /* Verändert den Verteidigungskill (default: 0)*/ - struct att attack[6]; + spellid_t precombatspell; + struct att attack[10]; char bonus[MAXSKILLS]; boolean __remove_me_nonplayer; int flags; diff --git a/src/common/kernel/reports.c b/src/common/kernel/reports.c index 131199b15..6506fbbc1 100644 --- a/src/common/kernel/reports.c +++ b/src/common/kernel/reports.c @@ -636,13 +636,16 @@ spskill(const struct locale * lang, const struct unit * u, skill_t sk, int *dh, sbuf += sprintf(sbuf, " [%d]", get_skill(u, sk) / u->number); } #else - if(effsk > 0 && u->faction->options & Pow(O_SHOWSKCHANGE)) { - attrib *a; - for(a = a_find(u->attribs,&at_showskchange); a; a=a->nexttype) { - if(a->data.sa[0] == sk) { - sbuf += sprintf(sbuf, " (%s%hd)", (a->data.sa[1]>0)?"+":"", a->data.sa[1]); - break; - } + if(u->faction->options & Pow(O_SHOWSKCHANGE)) { + skill *skill = get_skill(u, sk); + int oldeff = skill->old + get_modifier(u, sk, skill->old, u->region); + int diff; + + oldeff = max(0, oldeff); + diff = effsk - oldeff; + + if(diff != 0) { + sbuf += sprintf(sbuf, " (%s%hd)", (diff>0)?"+":"", diff); } } #endif diff --git a/src/common/kernel/save.c b/src/common/kernel/save.c index f92204f5a..f30451f90 100644 --- a/src/common/kernel/save.c +++ b/src/common/kernel/save.c @@ -1474,7 +1474,7 @@ readgame(boolean backup) assert(weeks>0 && weeks<=lvl+1); if (lvl) { skill * sv = add_skill(u, sk); - sv->level = (unsigned char)lvl; + sv->level = sv->old = (unsigned char)lvl; sv->weeks = (unsigned char)weeks; } } @@ -1484,7 +1484,7 @@ readgame(boolean backup) int weeks = ri(F); if (level) { skill * sv = add_skill(u, sk); - sv->level = (unsigned char)level; + sv->level = sv->old = (unsigned char)level; sv->weeks = (unsigned char)weeks; } } diff --git a/src/common/kernel/skill.h b/src/common/kernel/skill.h index efad33b49..564005203 100644 --- a/src/common/kernel/skill.h +++ b/src/common/kernel/skill.h @@ -30,6 +30,7 @@ typedef struct skill { unsigned char id; unsigned char level; unsigned char weeks; + unsigned char old; #endif } skill; diff --git a/src/common/kernel/spell.c b/src/common/kernel/spell.c index b745f2dca..197794a5f 100644 --- a/src/common/kernel/spell.c +++ b/src/common/kernel/spell.c @@ -10369,6 +10369,48 @@ spell spelldaten[] = {0, 0, 0}}, (spell_f)sp_dragonodem, patzer }, + + { SPL_DRAINODEM, "Schattenodem", + "Entzieht Talentstufen und macht Schaden wie Großer Odem", + NULL, + NULL, + M_GRAU, (COMBATSPELL), 5, 12, + { + {R_AURA, 4, SPC_FIX}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}}, + (spell_f)sp_dragonodem, patzer + }, + + {SPL_AURA_OF_FEAR, "Gesang der Furcht", + "Panik", + NULL, + NULL, + M_GRAU, (COMBATSPELL), 5, 12, + { + {R_AURA, 1, SPC_LEVEL}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}}, + (spell_f)sp_flee, patzer + }, + + {SPL_SHADOWCALL, "Schattenruf", + "Ruft Schattenwesen.", + NULL, + NULL, + M_GRAU, (PRECOMBATSPELL), 5, 12, + { + {R_AURA, 2, SPC_LEVEL}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}}, + (spell_f)sp_shadowcall, patzer + }, /* SPL_NOSPELL MUSS der letzte Spruch der Liste sein*/ diff --git a/src/common/kernel/spell.h b/src/common/kernel/spell.h index 43df0d86b..2c0dcd2a6 100644 --- a/src/common/kernel/spell.h +++ b/src/common/kernel/spell.h @@ -197,6 +197,9 @@ enum { SPL_BLOODSACRIFICE, SPL_MALLORN, SPL_CLONECOPY, + SPL_DRAINODEM, /* 174? */ + SPL_AURA_OF_FEAR, /* 175? */ + SPL_SHADOWCALL, /* 176? */ MAXALLSPELLS, NO_SPELL = (spellid_t) -1 }; @@ -238,6 +241,7 @@ extern int sp_reduceshield(struct fighter * fi, int level, int power, struct spe extern int sp_armorshield(struct fighter * fi, int level, int power, struct spell * sp); extern int sp_stun(struct fighter * fi, int level, int power, struct spell * sp); extern int sp_undeadhero(struct fighter * fi, int level, int power, struct spell * sp); +extern int sp_shadowcall(struct fighter * fi, int level, int power, struct spell * sp); /* ------------------------------------------------------------- */ diff --git a/src/common/kernel/unit.c b/src/common/kernel/unit.c index c447ec0ff..e6dbe8640 100644 --- a/src/common/kernel/unit.c +++ b/src/common/kernel/unit.c @@ -914,10 +914,6 @@ set_number(unit * u, int count) #if !SKILLPOINTS -attrib_type at_showskchange = { - "showskchange", NULL, NULL, NULL, NULL, NULL, ATF_UNIQUE -}; - boolean learn_skill(unit * u, skill_t sk, double chance) { @@ -969,22 +965,6 @@ learn_skill(unit * u, skill_t sk, double chance) } */ -/* - if(load == false) { - for(a = a_find(u->attribs, &at_showskchange);a;a = a->nexttype) { - if(a->data.sa[0] == id) { - a->data.sa[1] = (short)(a->data.sa[1] + (level-oldlevel)); - break; - } - } - if(a == NULL) { - a = a_add(&u->attribs, a_new(&at_showskchange)); - a->data.sa[0] = id; - a->data.sa[1] = (short)(level-oldlevel); - } - } -*/ - skill * add_skill(unit * u, skill_t id) { diff --git a/src/res/de/strings.xml b/src/res/de/strings.xml index 763fcab45..d93ec8baf 100644 --- a/src/res/de/strings.xml +++ b/src/res/de/strings.xml @@ -2909,6 +2909,74 @@ ent + + Schattendrache + shadow dragon + + + Schattendrachen + shadow dragons + + + Schattendrachen + shadow dragons + + + Schattendrachen + shadow dragon + + + + Todesflatter + darkbat + + + Todesflattern + darkbats + + + Todesflattern + darkbats + + + Todesflatter + darkbat + + + + Alptraum + nightmare + + + Alpträume + nightmares + + + Alpträumen + nightmares + + + Alptraum + nightmare + + + + Nachteinhorn + vampiric unicorn + + + Nachteinhörner + vampiric unicorns + + + Nachteinhörnern + vampiric unicorns + + + Nachteinhorn + vampiric unicorn + + Wyrm wyrm diff --git a/src/res/races.xml b/src/res/races.xml index 85f96c3ad..97fb3b5f3 100644 --- a/src/res/races.xml +++ b/src/res/races.xml @@ -1263,4 +1263,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +