/* vi: set ts=2: +-------------------+ Christian Schlittchen | | Enno Rehling | Eressea PBEM host | Katja Zedel | (c) 1998 - 2003 | Henning Peters | | Ingo Wilken +-------------------+ Stefan Reich This program may not be used, modified or distributed without prior permission by the authors of Eressea. */ #include #include #include "combatspells.h" /* kernel includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* util includes */ #include #include #include #include /* libc includes */ #include #include #define EFFECT_HEALING_SPELL 5 /* ------------------------------------------------------------------ */ /* Kampfzauberfunktionen */ /* COMBAT */ static const char * spell_damage(int sp) { switch (sp) { case 0: /* meist tödlich 20-65 HP */ return "5d10+15"; case 1: /* sehr variabel 4-48 HP */ return "4d12"; case 2: /* leicht verwundet 4-18 HP */ return "2d8+2"; case 3: /* fast immer tödlich 30-50 HP */ return "5d5+25"; case 4: /* verwundet 11-26 HP */ return "3d6+8"; case 5: /* leichter Schaden */ return "2d4"; default: /* schwer verwundet 14-34 HP */ return "4d6+10"; } } static double get_force(double power, int formel) { switch (formel) { case 0: /* (4,8,12,16,20,24,28,32,36,40,44,..)*/ return (power * 4); case 1: /* (15,30,45,60,75,90,105,120,135,150,165,..) */ return (power*15); case 2: /* (40,80,120,160,200,240,280,320,360,400,440,..)*/ return (power*40); case 3: /* (2,8,18,32,50,72,98,128,162,200,242,..)*/ return (power*power*2); case 4: /* (4,16,36,64,100,144,196,256,324,400,484,..)*/ return (power*power*4); case 5: /* (10,40,90,160,250,360,490,640,810,1000,1210,1440,..)*/ return (power*power*10); case 6: /* (6,24,54,96,150,216,294,384,486,600,726,864)*/ return (power*power*6); default: return power; } } /* Generischer Kampfzauber */ int sp_kampfzauber(fighter * fi, int level, double power, spell * sp) { 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; 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 = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } while (force>0 && killed < enemies) { dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE); assert(dt.fighter); --force; killed += terminate(dt, at, AT_COMBATSPELL, damage, false); } m = msg_message("battle::combatspell", "mage spell dead", fi->unit, sp, killed); message_all(b, m); msg_release(m); return level; } /* Versteinern */ int sp_petrify(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; /* Wirkt auf erste und zweite Reihe */ int force, enemies; int stoned = 0; message * m; force = lovar(get_force(power, 0)); enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); if (!enemies) { message * m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } while (force && stoned < enemies) { troop dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); unit * du = dt.fighter->unit; if (is_magic_resistant(mage, du, 0) == false) { /* person ans ende hinter die lebenden schieben */ remove_troop(dt); ++stoned; } --force; } m = msg_message("cast_petrify_effect", "mage spell amount", fi->unit, sp, stoned); message_all(b, m); msg_release(m); return level; } /* Benommenheit: eine Runde kein Angriff */ int sp_stun(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; message * m; troop at; /* Aus beiden Reihen nehmen */ int force=0, enemies; int stunned; if (power <= 0) return 0; at.fighter = fi; at.index = 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) { message * m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } stunned = 0; while (force && stunned < enemies) { troop dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); fighter * df = dt.fighter; unit * du = df->unit; --force; if (is_magic_resistant(mage, du, 0) == false) { df->person[dt.index].flags |= FL_STUNNED; ++stunned; } } m = msg_message("cast_stun_effect", "mage spell amount", fi->unit, sp, stunned); message_all(b, m); msg_release(m); return level; } /* ------------------------------------------------------------- */ /* Für Sprüche 'get_scrambled_list_of_enemys_in_row', so daß man diese * Liste nur noch einmal durchlaufen muss, um Flächenzauberwirkungen * abzuarbeiten */ /* Rosthauch */ int sp_combatrosthauch(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; cvector *fgs; void **fig; int force = lovar(power * 15); int k = 0; /* Immer aus der ersten Reihe nehmen */ unused(sp); if (!count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE|SELECT_FIND)) { message * msg = msg_message("rust_effect_0", "mage", fi->unit); message_all(b, msg); msg_release(msg); return 0; } fgs = fighters(b, fi->side, FIGHT_ROW, BEHIND_ROW-1, FS_ENEMY); v_scramble(fgs->begin, fgs->end); for (fig = fgs->begin; fig != fgs->end; ++fig) { fighter *df = *fig; if (df->alive==0) continue; if (force<=0) break; /* da n MIN(force, x), sollte force maximal auf 0 sinken */ assert(force >= 0); if (df->weapons) { int w; for (w=0;df->weapons[w].type!=NULL;++w) { weapon * wp = df->weapons; int n = MIN(force, wp->used); if (n) { requirement * mat = wp->type->itype->construction->materials; boolean iron = false; while (mat && mat->number>0) { if (mat->rtype==oldresourcetype[R_IRON]) { iron = true; break; } mat++; } if (iron) { int p; force -=n; wp->used -= n; k +=n; i_change(&df->unit->items, wp->type->itype, -n); for (p=0;n && p!=df->unit->number;++p) { if (df->person[p].missile==wp) { df->person[p].missile = NULL; --n; } } for (p=0;n && p!=df->unit->number;++p) { if (df->person[p].melee==wp) { df->person[p].melee = NULL; --n; } } } } } } } cv_kill(fgs); free(fgs); if (k == 0) { /* keine Waffen mehr da, die zerstört werden könnten */ message * msg = msg_message("rust_effect_1", "mage", fi->unit); message_all(b, msg); msg_release(msg); fi->magic = 0; /* kämpft nichtmagisch weiter */ level = 0; } else { message * msg = msg_message("rust_effect_2", "mage", fi->unit); message_all(b, msg); msg_release(msg); } return level; } int sp_sleep(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; unit *du; troop dt; int force, enemies; int k = 0; message * m; /* Immer aus der ersten Reihe nehmen */ force = lovar(power * 25); enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); if (!enemies) { m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } while (force && enemies) { dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); assert(dt.fighter); du = dt.fighter->unit; if (!is_magic_resistant(mage, du, 0)) { dt.fighter->person[dt.index].flags |= FL_SLEEPING; ++k; --enemies; } --force; } m = msg_message("cast_sleep_effect", "mage spell amount", fi->unit, sp, k); message_all(b, m); msg_release(m); return level; } int sp_speed(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; int force; int allies; int targets = 0; message * m; force = lovar(power * power * 5); allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE, ALLY_ANY); /* maximal 2*allies Versuche ein Opfer zu finden, ansonsten bestände * die Gefahr eine Endlosschleife*/ allies *= 2; while (force && allies) { troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW, ALLY_ANY); fighter *df = dt.fighter; --allies; if (df) { if (df->person[dt.index].speed == 1) { df->person[dt.index].speed++; targets++; --force; } } } m = msg_message("cast_speed_effect", "mage spell amount", fi->unit, sp, targets); message_all(b, m); msg_release(m); return 1; } static skill_t random_skill(unit *u, boolean weighted) { int n = 0; skill * sv; for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) { if (sv->level>0) { if (weighted) n+= sv->level; else ++n; } } if (n == 0) return NOSKILL; n = rng_int()%n; for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) { if (sv->level>0) { if (weighted) { if (n<(int)sv->level) return sv->id; n-=sv->level; } else { if (n == 0) return sv->id; --n; } } } assert(0==1); /* Hier sollte er niemals ankommen. */ return NOSKILL; } /** The mind blast spell for regular folks. * This spell temporarily reduces the skill of the victims */ int sp_mindblast_temp(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; int k = 0, reset = 0, maxloss = (level+2)/3; message * m; int force = lovar(power * 25); int enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); if (!enemies) { m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } while (force>0 && enemies>0) { unit *du; troop dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); assert(dt.fighter); du = dt.fighter->unit; if (fval(du, UFL_MARK)) { /* not this one again */ continue; } if (humanoidrace(du->race) && force>=du->number) { if (!is_magic_resistant(mage, du, 0)) { skill_t sk = random_skill(du, true); if (sk != NOSKILL) { int n = 1 + rng_int() % maxloss; attrib * a = make_skillmod(sk, SMF_ALWAYS, NULL, 0.0, n); /* neat: you can add a whole lot of these to a unit, they stack */ a_add(&du->attribs, a); } k += du->number; } force -= du->number; } fset(du, UFL_MARK); reset = 1; enemies -= du->number; } if (reset) { unit * u; for (u=b->region->units;u;u=u->next) { freset(u, UFL_MARK); } } m = msg_message("sp_mindblast_temp_effect", "mage spell amount", mage, sp, k); message_all(b, m); msg_release(m); return level; } /** A mind blast spell for monsters. * This spell PERMANENTLY reduces the skill of the victims or kills them * when they have no skills left. Not currently in use. */ int sp_mindblast(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; int killed = 0, k = 0, reset = 0; message * m; int force = lovar(power * 25); int enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); if (!enemies) { m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } while (enemies>0 && force>0) { unit *du; troop dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); assert(dt.fighter); du = dt.fighter->unit; if (fval(du, UFL_MARK)) { /* not this one again */ continue; } if (humanoidrace(du->race) && force>=du->number) { if (!is_magic_resistant(mage, du, 0)) { skill_t sk = random_skill(du, false); if (sk != NOSKILL) { skill * sv = get_skill(du, sk); int n = 1 + rng_int() % 3; reduce_skill(du, sv, n); k+=du->number; } else { /* unit has no skill. kill it. */ kill_troop(dt); ++killed; } } force -= du->number; } else { /* only works against humanoids, don't try others. but do remove them * from 'force' once or we may never terminate. */ fset(du, UFL_MARK); reset = 1; } enemies -= du->number; } if (reset) { unit * u; for (u=b->region->units;u;u=u->next) { freset(u, UFL_MARK); } } m = msg_message("sp_mindblast_effect", "mage spell amount dead", mage, sp, k, killed); message_all(b, m); msg_release(m); return level; } int sp_dragonodem(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; troop dt; troop at; int force, enemies; int killed = 0; const char *damage; /* 11-26 HP */ damage = spell_damage(4); /* Jungdrache 3->54, Drache 6->216, Wyrm 12->864 Treffer */ force = lovar(get_force(power, 6)); enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE); if (!enemies) { struct message * m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } else { struct message * m; at.fighter = fi; at.index = 0; while (force && killed < enemies) { dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE); assert(dt.fighter); --force; killed += terminate(dt, at, AT_COMBATSPELL, damage, false); } m = msg_message("battle::combatspell", "mage spell dead", fi->unit, sp, killed); message_all(b, m); msg_release(m); } return level; } /* Feuersturm: Betrifft sehr viele Gegner (in der Regel alle), * macht nur vergleichsweise geringen Schaden */ int sp_immolation(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; troop at; int force; int killed = 0; const char *damage; cvector *fgs; void **fig; message * m; /* 2d4 HP */ damage = spell_damage(5); /* Betrifft alle Gegner */ force = 99999; if (!count_enemies(b, fi, FIGHT_ROW, AVOID_ROW, SELECT_ADVANCE|SELECT_FIND)) { message * m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } at.fighter = fi; at.index = 0; fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_ENEMY); for (fig = fgs->begin; fig != fgs->end; ++fig) { fighter *df = *fig; int n = df->alive-df->removed; troop dt; dt.fighter = df; while (n!=0) { dt.index = --n; killed += terminate(dt, at, AT_COMBATSPELL, damage, false); if (--force==0) break; } if (force==0) break; } cv_kill(fgs); free(fgs); m = msg_message("battle::combatspell", "mage spell killed", fi->unit, sp, killed); message_all(b, m); msg_release(m); return level; } int sp_drainodem(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; troop dt; troop at; int force, enemies; int drained = 0; int killed = 0; const char *damage; message * m; /* 11-26 HP */ damage = spell_damage(4); /* Jungdrache 3->54, Drache 6->216, Wyrm 12->864 Treffer */ force = lovar(get_force(power, 6)); enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE); if (!enemies) { m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } at.fighter = fi; at.index = 0; while (force && drained < enemies) { dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE); assert(dt.fighter); if (hits(at, dt, NULL)) { drain_exp(dt.fighter->unit, 90); ++drained; killed += terminate(dt, at, AT_COMBATSPELL, damage, false); } --force; } m = msg_message("cast_drainlife_effect", "mage spell amount", fi->unit, sp, drained); message_all(b, m); msg_release(m); return level; } /* ------------------------------------------------------------- */ /* PRECOMBAT */ int sp_shadowcall(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; region *r = b->region; unit *mage = fi->unit; attrib *a; int force = (int)(get_force(power, 3)/2); unit *u; const char * races[3] = { "shadowbat", "nightmare", "vampunicorn" }; const race *rc = rc_find(races[rng_int()%3]); message * msg; unused(sp); u = create_unit(r, mage->faction, force, rc, 0, NULL, mage); setstatus(u, ST_FIGHT); set_level(u, SK_WEAPONLESS, (int)(power/2)); set_level(u, SK_STAMINA, (int)(power/2)); u->hp = u->number * unit_max_hp(u); a = a_new(&at_unitdissolve); a->data.ca[0] = 0; a->data.ca[1] = 100; a_add(&u->attribs, a); make_fighter(b, u, fi->side, is_attacker(fi)); msg = msg_message("sp_shadowcall_effect", "mage amount race", mage, u->number, u->race); message_all(b, msg); msg_release(msg); return level; } int sp_wolfhowl(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; region *r = b->region; unit *mage = fi->unit; attrib *a; message * msg; int force = (int)(get_force(power, 3)/2); unit * u = create_unit(r, mage->faction, force, new_race[RC_WOLF], 0, NULL, mage); unused(sp); leave(u, true); setstatus(u, ST_FIGHT); set_level(u, SK_WEAPONLESS, (int)(power/3)); set_level(u, SK_STAMINA, (int)(power/3)); u->hp = u->number * unit_max_hp(u); if (fval(mage, UFL_ANON_FACTION)) { fset(u, UFL_ANON_FACTION); } a = a_new(&at_unitdissolve); a->data.ca[0] = 0; a->data.ca[1] = 100; a_add(&u->attribs, a); make_fighter(b, u, fi->side, is_attacker(fi)); msg = msg_message("sp_wolfhowl_effect", "mage amount race", mage, u->number, u->race); message_all(b, msg); msg_release(msg); return level; } int sp_shadowknights(fighter * fi, int level, double power, spell * sp) { unit *u; battle *b = fi->side->battle; region *r = b->region; unit *mage = fi->unit; attrib *a; int force = MAX(1, (int)get_force(power, 3)); message * msg; unused(sp); u = create_unit(r, mage->faction, force, new_race[RC_SHADOWKNIGHT], 0, NULL, mage); setstatus(u, ST_FIGHT); u->hp = u->number * unit_max_hp(u); if (fval(mage, UFL_ANON_FACTION)) { fset(u, UFL_ANON_FACTION); } a = a_new(&at_unitdissolve); a->data.ca[0] = 0; a->data.ca[1] = 100; a_add(&u->attribs, a); make_fighter(b, u, fi->side, is_attacker(fi)); msg = msg_message("sp_shadowknights_effect", "mage", mage); message_all(b, msg); msg_release(msg); return level; } int sp_strong_wall(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; building *burg; double effect; static boolean init = false; message * msg; static const curse_type * strongwall_ct; if (!init) { init = true; strongwall_ct = ct_find("strongwall"); } unused(sp); if (!mage->building) { return 0; } burg = mage->building; effect = power*0.25; create_curse(mage, &burg->attribs, strongwall_ct, power, 1, effect, 0); msg = msg_message("sp_strongwalls_effect", "mage building", mage, mage->building); message_all(b, msg); msg_release(msg); return level; } /** Spells: chaosrow / song of confusion. * German Title: 'Gesang der Verwirrung' */ int sp_chaosrow(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; cvector *fgs; void **fig; message * m; const char * mtype; int k = 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); message_all(b, m); msg_release(m); return 0; } if (sp->id==SPL_CHAOSROW) power *=40; else power = get_force(power, 5); fgs = fighters(b, fi->side, FIGHT_ROW, NUMROWS, FS_ENEMY); v_scramble(fgs->begin, fgs->end); for (fig = fgs->begin; fig != fgs->end; ++fig) { fighter *df = *fig; int n = df->unit->number; if (df->alive==0) continue; if (power<=0.0) break; /* force sollte wegen des MAX(0,x) nicht unter 0 fallen können */ if (is_magic_resistant(mage, df->unit, 0)) continue; if (chance(power/n)) { int row = statusrow(df->status); df->side->size[row] -= df->alive; if (df->unit->race->battle_flags & BF_NOBLOCK) { df->side->nonblockers[row] -= df->alive; } row = FIRST_ROW + (rng_int()%(LAST_ROW-FIRST_ROW)); switch (row) { case FIGHT_ROW: df->status = ST_FIGHT; break; case BEHIND_ROW: df->status = ST_CHICKEN; break; case AVOID_ROW: df->status = ST_AVOID; break; case FLEE_ROW: df->status = ST_FLEE; break; default: assert(!"unknown combatrow"); } assert(statusrow(df->status)==row); df->side->size[row] += df->alive; if (df->unit->race->battle_flags & BF_NOBLOCK) { df->side->nonblockers[row] += df->alive; } k+=df->alive; } power = MAX(0, power-n); } cv_kill(fgs); free(fgs); if (sp->id==SPL_CHAOSROW) { mtype = (k>0) ? "sp_chaosrow_effect_1" : "sp_chaosrow_effect_0"; } else { mtype = (k>0) ? "sp_confusion_effect_1" : "sp_confusion_effect_0"; } m = msg_message(mtype, "mage", mage); message_all(b, m); msg_release(m); return level; } /* Gesang der Furcht (Kampfzauber) */ /* Panik (Präkampfzauber) */ int sp_flee(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; cvector *fgs; void **fig; int force, n; int panik = 0; message * msg; 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); } 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); msg_release(msg); return 0; } fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_ENEMY); v_scramble(fgs->begin, fgs->end); for (fig = fgs->begin; fig != fgs->end; ++fig) { fighter *df = *fig; for (n=0; n!=df->alive; ++n) { if (force < 0) break; if (df->person[n].flags & FL_PANICED) { /* bei SPL_SONG_OF_FEAR möglich */ df->person[n].attack -= 1; --force; ++panik; } else if (!(df->person[n].flags & FL_COURAGE) || !fval(df->unit->race, RCF_UNDEAD)) { if (is_magic_resistant(mage, df->unit, 0) == false) { df->person[n].flags |= FL_PANICED; ++panik; } --force; } } } cv_kill(fgs); free(fgs); msg = msg_message("sp_flee_effect_1", "mage spell amount", mage, sp, panik); message_all(b, msg); msg_release(msg); return level; } /* Heldenmut */ int sp_hero(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; int df_bonus = 0; int force = 0; int allies; 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); /* maximal 2*allies Versuche ein Opfer zu finden, ansonsten bestände * die Gefahr eine Endlosschleife*/ allies *= 2; while (force && allies) { troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW, ALLY_ANY); fighter *df = dt.fighter; --allies; if (df) { if (!(df->person[dt.index].flags & FL_COURAGE)) { df->person[dt.index].defence += df_bonus; df->person[dt.index].flags = df->person[dt.index].flags | FL_COURAGE; targets++; --force; } } } m = msg_message("cast_hero_effect", "mage spell amount", fi->unit, sp, targets); message_all(b, m); msg_release(m); return level; } int sp_berserk(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; int at_bonus = 0; int df_malus = 0; int force = 0; int allies = 0; 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); /* maximal 2*allies Versuche ein Opfer zu finden, ansonsten bestände * die Gefahr eine Endlosschleife*/ allies *= 2; while (force && allies) { troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW-1, ALLY_ANY); fighter *df = dt.fighter; --allies; if (df) { if (!(df->person[dt.index].flags & FL_COURAGE)) { df->person[dt.index].attack += at_bonus; df->person[dt.index].defence -= df_malus; df->person[dt.index].flags = df->person[dt.index].flags | FL_COURAGE; targets++; --force; } } } m = msg_message("cast_berserk_effect", "mage spell amount", fi->unit, sp, targets); message_all(b, m); msg_release(m); return level; } int sp_frighten(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; int at_malus = 0; int df_malus = 0; int force = 0; int enemies = 0; int targets = 0; message * m; at_malus = MAX(1,level - 4); df_malus = 2; force = (int)get_force(power, 2); enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE); if (!enemies) { message * m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } while (force && enemies) { troop dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE); fighter *df = dt.fighter; --enemies; if (!df) break; assert(!helping(fi->side, df->side)); if (df->person[dt.index].flags & FL_COURAGE) { df->person[dt.index].flags &= ~(FL_COURAGE); } if (is_magic_resistant(mage, df->unit, 0) == false) { df->person[dt.index].attack -= at_malus; df->person[dt.index].defence -= df_malus; targets++; } --force; } m = msg_message("cast_frighten_effect", "mage spell amount", fi->unit, sp, targets); message_all(b, m); msg_release(m); return level; } int sp_tiredsoldiers(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; int n = 0; int force = (int)(power * power * 4); message * m; if (!count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE|SELECT_FIND)) { message * m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return 0; } while (force) { troop t = select_enemy(fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); fighter *df = t.fighter; if (!df) break; assert(!helping(fi->side, df->side)); if (!(df->person[t.index].flags & FL_TIRED)) { if (is_magic_resistant(mage, df->unit, 0) == false) { df->person[t.index].flags = df->person[t.index].flags | FL_TIRED; df->person[t.index].defence -= 2; ++n; } } --force; } m = msg_message("cast_tired_effect", "mage spell amount", fi->unit, sp, n); message_all(b, m); msg_release(m); return level; } int sp_windshield(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; int force, at_malus; 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); message_all(b, m); msg_release(m); return 0; } while (force && enemies) { troop dt = select_enemy(fi, BEHIND_ROW, BEHIND_ROW, SELECT_ADVANCE); fighter *df = dt.fighter; --enemies; if (!df) break; assert(!helping(fi->side, df->side)); if (df->person[dt.index].missile) { /* this suxx... affects your melee weapon as well. */ df->person[dt.index].attack -= at_malus; --force; } } m = msg_message("cast_storm_effect", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return level; } int sp_reeling_arrows(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; message * m; unused(power); b->reelarrow = true; m = msg_message("cast_spell_effect", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return level; } int sp_denyattack(fighter * fi, int level, double power, spell * sp) { /* Magier weicht dem Kampf aus. Wenn er sich bewegen kann, zieht er in * eine Nachbarregion, wobei ein NACH berücksichtigt wird. Ansonsten * bleibt er stehen und nimmt nicht weiter am Kampf teil. */ battle *b = fi->side->battle; unit *mage = fi->unit; region *r = b->region; message * m; unused(power); /* Fliehende Einheiten verlassen auf jeden Fall Gebäude und Schiffe. */ if (!fval(r->terrain, SEA_REGION)) { leave(mage, false); } /* und bewachen nicht */ setguard(mage, GUARD_NONE); /* irgendwie den langen befehl sperren */ /* fset(fi, FIG_ATTACKED); */ /* wir tun so, als wäre die Person geflohen */ fset(fi, FIG_NOLOOT); fi->run.hp = mage->hp; fi->run.number = mage->number; /* fighter leeren */ rmfighter(fi, mage->number); m = msg_message("cast_escape_effect", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); return level; } static void do_meffect(fighter * af, int typ, int effect, int duration) { battle *b = af->side->battle; meffect *meffect = calloc(1, sizeof(struct meffect)); cv_pushback(&b->meffects, meffect); meffect->magician = af; meffect->typ = typ; meffect->effect = effect; meffect->duration = duration; } int sp_armorshield(fighter * fi, int level, double power, spell * sp) { int effect; int duration; battle *b = fi->side->battle; message * m = msg_message("cast_spell_effect", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); /* 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); } do_meffect(fi, SHIELD_ARMOR, effect, duration); return level; } int sp_reduceshield(fighter * fi, int level, double power, spell * sp) { int effect; int duration; battle *b = fi->side->battle; message * m = msg_message("cast_spell_effect", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); /* 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; } int sp_fumbleshield(fighter * fi, int level, double power, spell * sp) { int effect; int duration; battle *b = fi->side->battle; message * m = msg_message("cast_spell_effect", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); /* der erste Zauber schlägt mit 100% fehl */ switch(sp->id) { case SPL_DRAIG_FUMBLESHIELD: case SPL_GWYRRD_FUMBLESHIELD: case SPL_CERRDOR_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; } /* ------------------------------------------------------------- */ /* POSTCOMBAT */ static int count_healable(battle *b, fighter *df) { side *s; int healable = 0; for (s=b->sides;s!=b->sides+b->nsides;++s) { if (helping(df->side, s)) { healable += s->casualties; } } return healable; } /* wiederbeleben */ int sp_reanimate(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; int healable, j=0; double c = 0.50 + 0.02 * power; double k = EFFECT_HEALING_SPELL * power; boolean use_item = get_item(mage, I_AMULET_OF_HEALING) > 0; message * msg; if (use_item) { k *= 2; c += 0.10; } healable = count_healable(b, fi); healable = (int)MIN(k, healable); while (healable--) { fighter * tf = select_corpse(b, fi); if (tf!=NULL && tf->side->casualties > 0 && tf->unit->race != new_race[RC_DAEMON] && (chance(c))) { assert(tf->alive < tf->unit->number); /* t.fighter->person[].hp beginnt mit t.index = 0 zu zählen, * t.fighter->alive ist jedoch die Anzahl lebender in der Einheit, * also sind die hp von t.fighter->alive * t.fighter->hitpoints[t.fighter->alive-1] und der erste Tote * oder weggelaufene ist t.fighter->hitpoints[tf->alive] */ tf->person[tf->alive].hp = 2; ++tf->alive; ++tf->side->size[SUM_ROW]; ++tf->side->size[tf->unit->status + 1]; ++tf->side->healed; --tf->side->casualties; assert(tf->side->casualties>=0); --tf->side->dead; assert(tf->side->dead>=0); ++j; } } if (j <= 0) { level = j; } if (use_item) { msg = msg_message("reanimate_effect_1", "mage amount item", mage, j, oldresourcetype[R_AMULET_OF_HEALING]); } else { msg = msg_message("reanimate_effect_0", "mage amount", mage, j); } message_all(b, msg); msg_release(msg); return level; } int sp_keeploot(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; message * m = msg_message("cast_spell_effect", "mage spell", fi->unit, sp); message_all(b, m); msg_release(m); b->keeploot = (int)MAX(25, b->keeploot + 5*power); return level; } static int heal_fighters(cvector *fgs, int * power, boolean heal_monsters) { int healhp = *power; int healed = 0; void **fig; for (fig = fgs->begin; fig != fgs->end; ++fig) { fighter *df = *fig; if (healhp<=0) break; /* Untote kann man nicht heilen */ if (df->unit->number==0 || fval(df->unit->race, RCF_NOHEAL)) continue; /* wir heilen erstmal keine Monster */ if (heal_monsters || playerrace(df->unit->race)) { int n, hp = df->unit->hp / df->unit->number; int rest = df->unit->hp % df->unit->number; for (n = 0; n < df->unit->number; n++) { int wound = hp - df->person[n].hp; if (rest>n) ++wound; if (wound > 0 && wound < hp) { int heal = MIN(healhp, wound); assert(heal>=0); df->person[n].hp += heal; healhp = MAX(0, healhp - heal); ++healed; if (healhp<=0) break; } } } } *power = healhp; return healed; } int sp_healing(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; int j = 0; int healhp = (int)power * 200; cvector *fgs; message * msg; boolean use_item = get_item(mage, I_AMULET_OF_HEALING) > 0; /* bis zu 11 Personen pro Stufe (einen HP müssen sie ja noch * haben, sonst wären sie tot) können geheilt werden */ if (use_item) { healhp *= 2; } /* gehe alle denen wir helfen der reihe nach durch, heile verwundete, * bis zu verteilende HP aufgebraucht sind */ fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_HELP); v_scramble(fgs->begin, fgs->end); j += heal_fighters(fgs, &healhp, false); j += heal_fighters(fgs, &healhp, true); cv_kill(fgs); free(fgs); if (j <= 0) { level = j; } if (use_item) { msg = msg_message("healing_effect_1", "mage amount item", mage, j, oldresourcetype[R_AMULET_OF_HEALING]); } else { msg = msg_message("healing_effect_0", "mage amount", mage, j); } message_all(b, msg); msg_release(msg); return level; } int sp_undeadhero(fighter * fi, int level, double power, spell * sp) { battle *b = fi->side->battle; unit *mage = fi->unit; region *r = b->region; cvector *fgs; void **fig; int n, undead = 0; message * msg; int force = (int)get_force(power,0); double c = 0.50 + 0.02 * power; /* Liste aus allen Kämpfern */ fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_ENEMY | FS_HELP ); v_scramble(fgs->begin, fgs->end); for (fig = fgs->begin; fig != fgs->end; ++fig) { fighter *df = *fig; unit *du = df->unit; if (force<=0) break; /* keine Monster */ if (!playerrace(du->race)) continue; if (df->alive + df->run.number < du->number) { int j = 0; /* Wieviele Untote können wir aus dieser Einheit wecken? */ for (n = df->alive + df->run.number; n != du->number; n++) { if (chance(c)) { ++j; if (--force<=0) break; } } if (j > 0) { unit * u = create_unit(r, mage->faction, 0, new_race[RC_UNDEAD], 0, du->name, du); /* new units gets some stats from old unit */ if (du->display) { unit_setinfo(u, du->display); } else { unit_setinfo(u, NULL); } setstatus(u, du->status); setguard(u, GUARD_NONE); /* inherit stealth from magician */ if (fval(mage, UFL_ANON_FACTION)) { fset(u, UFL_ANON_FACTION); } /* transfer dead people to new unit, set hitpoints to those of old unit */ transfermen(du, u, j); u->hp = u->number * unit_max_hp(du); assert(j<=df->side->casualties); df->side->casualties -= j; df->side->dead -= j; /* counting total number of undead */ undead += j; } } } cv_kill(fgs); free(fgs); level = MIN(level, undead); if (undead == 0) { msg = msg_message("summonundead_effect_0", "mage region", mage, mage->region); } else { msg = msg_message("summonundead_effect_1", "mage region amount", mage, mage->region, undead); } message_all(b, msg); msg_release(msg); return level; }