int rc_specialdamage(const race * ar, const race * dr, const struct weapon_type *wtype) { race_t art = old_race(ar); int m, modifier = 0; if (wtype != NULL && wtype->modifiers != NULL) for (m = 0; wtype->modifiers[m].value; ++m) { /* weapon damage for this weapon, possibly by race */ if (wtype->modifiers[m].flags & WMF_DAMAGE) { race_list *rlist = wtype->modifiers[m].races; if (rlist != NULL) { while (rlist) { if (rlist->data == ar) break; rlist = rlist->next; } if (rlist == NULL) continue; } modifier += wtype->modifiers[m].value; } } switch (art) { case RC_HALFLING: if (wtype != NULL && dragonrace(dr)) { modifier += 5; } break; default: break; } return modifier; }
static order *monster_move(region * r, unit * u) { direction_t d = NODIRECTION; if (monster_is_waiting(u)) { return NULL; } switch (old_race(u_race(u))) { case RC_FIREDRAGON: case RC_DRAGON: case RC_WYRM: d = richest_neighbour(r, u->faction, 1); break; case RC_TREEMAN: d = treeman_neighbour(r); break; default: d = random_neighbour(r, u); break; } /* falls kein geld gefunden wird, zufaellig verreisen, aber nicht in * den ozean */ if (d == NODIRECTION) return NULL; reduce_weight(u); return create_order(K_MOVE, u->faction->locale, "%s", LOC(u->faction->locale, directions[d])); }
static void scared_by_monster(unit * u) { int n; switch (old_race(u_race(u))) { case RC_FIREDRAGON: n = rng_int() % 160 * u->number; break; case RC_DRAGON: n = rng_int() % 400 * u->number; break; case RC_WYRM: n = rng_int() % 1000 * u->number; break; default: n = rng_int() % (u->number / 4 + 1); } if (n > 0) { n = lovar(n); n = _min(rpeasants(u->region), n); if (n > 0) { n = scareaway(u->region, n); if (n > 0) { ADDMSG(&u->region->msgs, msg_message("fleescared", "amount unit", n, u)); } } } }
static order *monster_seeks_target(region * r, unit * u) { direction_t d; unit *target = NULL; int dist, dist2; direction_t i; region *nr; /* Das Monster sucht ein bestimmtes Opfer. Welches, steht * in einer Referenz/attribut * derzeit gibt es nur den alp */ switch (old_race(u_race(u))) { case RC_ALP: target = alp_target(u); break; default: assert(!"Seeker-Monster gibt kein Ziel an"); } /* TODO: prüfen, ob target überhaupt noch existiert... */ if (!target) { log_error("Monster '%s' hat kein Ziel!\n", unitname(u)); return NULL; /* this is a bug workaround! remove!! */ } if (r == target->region) { /* Wir haben ihn! */ if (u_race(u) == get_race(RC_ALP)) { alp_findet_opfer(u, r); } else { assert(!"Seeker-Monster hat keine Aktion fuer Ziel"); } return NULL; } /* Simpler Ansatz: Nachbarregion mit gerinster Distanz suchen. * Sinnvoll momentan nur bei Monstern, die sich nicht um das * Terrain kümmern. Nebelwände & Co machen derzeit auch nix... */ dist2 = distance(r, target->region); d = NODIRECTION; for (i = 0; i < MAXDIRECTIONS; i++) { nr = rconnect(r, i); assert(nr); dist = distance(nr, target->region); if (dist < dist2) { dist2 = dist; d = i; } } assert(d != NODIRECTION); return create_order(K_MOVE, u->faction->locale, "%s", LOC(u->faction->locale, directions[d])); }
static void eaten_by_monster(unit * u) { /* adjustment for smaller worlds */ static double multi = 0.0; int n = 0; int horse = 0; if (multi == 0.0) { multi = RESOURCE_QUANTITY * newterrain(T_PLAIN)->size / 10000.0; } switch (old_race(u_race(u))) { case RC_FIREDRAGON: n = rng_int() % 80 * u->number; horse = get_item(u, I_HORSE); break; case RC_DRAGON: n = rng_int() % 200 * u->number; horse = get_item(u, I_HORSE); break; case RC_WYRM: n = rng_int() % 500 * u->number; horse = get_item(u, I_HORSE); break; default: n = rng_int() % (u->number / 20 + 1); } n = (int)(n * multi); if (n > 0) { n = lovar(n); n = MIN(rpeasants(u->region), n); if (n > 0) { deathcounts(u->region, n); rsetpeasants(u->region, rpeasants(u->region) - n); ADDMSG(&u->region->msgs, msg_message("eatpeasants", "unit amount", u, n)); } } if (horse > 0) { set_item(u, I_HORSE, 0); ADDMSG(&u->region->msgs, msg_message("eathorse", "unit amount", u, horse)); } }
static void equip_newunits(const struct equipment *eq, struct unit *u) { struct region *r = u->region; const struct resource_type *rtype; switch (old_race(u_race(u))) { case RC_ELF: rtype = rt_find("fairyboot"); set_show_item(u->faction, rtype->itype); break; case RC_GOBLIN: rtype = rt_find("roi"); set_show_item(u->faction, rtype->itype); set_number(u, 10); break; case RC_HUMAN: if (u->building == NULL) { const building_type *btype = bt_find("castle"); if (btype != NULL) { building *b = new_building(btype, r, u->faction->locale); b->size = 10; u_set_building(u, b); building_set_owner(u); } } break; case RC_CAT: rtype = rt_find("roi"); set_show_item(u->faction, rtype->itype); break; case RC_AQUARIAN: { ship *sh = new_ship(st_find("boat"), r, u->faction->locale); sh->size = sh->type->construction->maxsize; u_set_ship(u, sh); } break; case RC_CENTAUR: rsethorses(r, 250 + rng_int() % 51 + rng_int() % 51); break; default: break; } }
static void absorbed_by_monster(unit * u) { int n; switch (old_race(u_race(u))) { default: n = rng_int() % (u->number / 20 + 1); } if (n > 0) { n = lovar(n); n = _min(rpeasants(u->region), n); if (n > 0) { rsetpeasants(u->region, rpeasants(u->region) - n); scale_number(u, u->number + n); ADDMSG(&u->region->msgs, msg_message("absorbpeasants", "unit race amount", u, u_race(u), n)); } } }
summary *make_summary(void) { faction *f; region *r; unit *u; summary *s = calloc(1, sizeof(summary)); const struct resource_type *rhorse = get_resourcetype(R_HORSE); for (f = factions; f; f = f->next) { const struct locale *lang = f->locale; struct language *plang = s->languages; while (plang && plang->locale != lang) plang = plang->next; if (!plang) { plang = calloc(sizeof(struct language), 1); plang->next = s->languages; s->languages = plang; plang->locale = lang; } ++plang->number; f->nregions = 0; f->num_total = 0; f->money = 0; if (f->alive && f->units) { s->factions++; /* Problem mit Monsterpartei ... */ if (!is_monsters(f)) { s->factionrace[old_race(f->race)]++; } } } /* count everything */ for (r = regions; r; r = r->next) { s->pferde += rhorses(r); s->schiffe += listlen(r->ships); s->gebaeude += listlen(r->buildings); if (!fval(r->terrain, SEA_REGION)) { s->landregionen++; if (r->units) { s->landregionen_mit_spielern++; } if (fval(r, RF_ORCIFIED)) { s->orkifizierte_regionen++; } if (r->terrain == newterrain(T_VOLCANO)) { s->inactive_volcanos++; } else if (r->terrain == newterrain(T_VOLCANO_SMOKING)) { s->active_volcanos++; } } if (r->units) { s->regionen_mit_spielern++; } if (rpeasants(r) || r->units) { s->inhabitedregions++; s->peasants += rpeasants(r); s->peasantmoney += rmoney(r); /* Einheiten Info. nregions darf nur einmal pro Partei * incrementiert werden. */ for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); for (u = r->units; u; u = u->next) { f = u->faction; if (!is_monsters(u->faction)) { skill *sv; item *itm; s->nunits++; s->playerpop += u->number; if (u->flags & UFL_HERO) { s->heroes += u->number; } s->spielerpferde += i_get(u->items, rhorse->itype); s->playermoney += get_money(u); s->armed_men += armedmen(u, true); for (itm = u->items; itm; itm = itm->next) { if (itm->type->rtype->wtype) { s->waffen += itm->number; } if (itm->type->rtype->atype) { s->ruestungen += itm->number; } } s->spielerpferde += i_get(u->items, rhorse->itype); for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) { skill_t sk = sv->id; int aktskill = eff_skill(u, sk, r); if (aktskill > s->maxskill) s->maxskill = aktskill; } if (!fval(f, FFL_SELECT)) { f->nregions++; fset(f, FFL_SELECT); } } f->num_total += u->number; f->money += get_money(u); s->poprace[old_race(u_race(u))] += u->number; } } } return s; }
static int damage_unit(unit * u, const char *dam, bool physical, bool magic) { int *hp = malloc(u->number * sizeof(int)); int h; int i, dead = 0, hp_rem = 0, heiltrank; double magres = magic_resistance(u); assert(u->number); if (fval(u_race(u), RCF_ILLUSIONARY) || u_race(u) == get_race(RC_SPELL)) { return 0; } h = u->hp / u->number; /* HP verteilen */ for (i = 0; i < u->number; i++) hp[i] = h; h = u->hp - (u->number * h); for (i = 0; i < h; i++) hp[i]++; /* Schaden */ for (i = 0; i < u->number; i++) { int damage = dice_rand(dam); if (magic) damage = (int)(damage * (1.0 - magres)); if (physical) damage -= nb_armor(u, i); hp[i] -= damage; } /* Auswirkungen */ for (i = 0; i < u->number; i++) { if (hp[i] <= 0) { heiltrank = 0; /* Sieben Leben */ if (old_race(u_race(u)) == RC_CAT && (chance(1.0 / 7))) { hp[i] = u->hp / u->number; hp_rem += hp[i]; continue; } /* Heiltrank */ if (oldpotiontype[P_HEAL]) { if (get_effect(u, oldpotiontype[P_HEAL]) > 0) { change_effect(u, oldpotiontype[P_HEAL], -1); heiltrank = 1; } else if (i_get(u->items, oldpotiontype[P_HEAL]->itype) > 0) { i_change(&u->items, oldpotiontype[P_HEAL]->itype, -1); change_effect(u, oldpotiontype[P_HEAL], 3); heiltrank = 1; } if (heiltrank && (chance(0.50))) { hp[i] = u->hp / u->number; hp_rem += hp[i]; continue; } } dead++; } else { hp_rem += hp[i]; } } scale_number(u, u->number - dead); u->hp = hp_rem; free(hp); return dead; }
void plan_monsters(faction * f) { region *r; assert(f); attack_chance = get_param_flt(global.parameters, "rules.monsters.attack_chance", 0.4); f->lastorders = turn; for (r = regions; r; r = r->next) { unit *u; bool attacking = false; for (u = r->units; u; u = u->next) { attrib *ta; order *long_order = NULL; /* Ab hier nur noch Befehle für NPC-Einheiten. */ if (!is_monsters(u->faction)) continue; /* Befehle müssen jede Runde neu gegeben werden: */ free_orders(&u->orders); if (skill_enabled(SK_PERCEPTION)) { /* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */ /* TODO: this only works for playerrace */ produceexp(u, SK_PERCEPTION, u->number); } if (!attacking) { if (chance(attack_chance)) attacking = true; } if (u->status > ST_BEHIND) { setstatus(u, ST_FIGHT); /* all monsters fight */ } if (attacking && (!r->land || is_guard(u, GUARD_TAX))) { monster_attacks(u); } /* units with a plan to kill get ATTACK orders: */ ta = a_find(u->attribs, &at_hate); if (ta && !monster_is_waiting(u)) { unit *tu = (unit *)ta->data.v; if (tu && tu->region == r) { order * ord = monster_attack(u, tu); if (ord) { addlist(&u->orders, ord); } } else if (tu) { tu = findunitg(ta->data.i, NULL); if (tu != NULL) { long_order = make_movement_order(u, tu->region, 2, allowed_walk); } } else a_remove(&u->attribs, ta); } /* All monsters guard the region: */ if (!monster_is_waiting(u) && r->land) { addlist(&u->orders, create_order(K_GUARD, u->faction->locale, NULL)); } /* Einheiten mit Bewegungsplan kriegen ein NACH: */ if (long_order == NULL) { attrib *ta = a_find(u->attribs, &at_targetregion); if (ta) { if (u->region == (region *)ta->data.v) { a_remove(&u->attribs, ta); } } else if (u_race(u)->flags & RCF_MOVERANDOM) { if (rng_int() % 100 < MOVECHANCE || check_overpopulated(u)) { long_order = monster_move(r, u); } } } if (long_order == NULL && unit_can_study(u)) { /* Einheiten, die Waffenlosen Kampf lernen könnten, lernen es um * zu bewachen: */ if (u_race(u)->bonus[SK_WEAPONLESS] != -99) { if (effskill(u, SK_WEAPONLESS, 0) < 1) { long_order = create_order(K_STUDY, f->locale, "'%s'", skillname(SK_WEAPONLESS, f->locale)); } } } if (long_order == NULL) { /* Ab hier noch nicht generalisierte Spezialbehandlungen. */ if (!u->orders) { handle_event(u->attribs, "ai_move", u); } switch (old_race(u_race(u))) { case RC_SEASERPENT: long_order = create_order(K_PIRACY, f->locale, NULL); break; #ifdef TODO_ALP case RC_ALP: long_order = monster_seeks_target(r, u); break; #endif case RC_FIREDRAGON: case RC_DRAGON: case RC_WYRM: long_order = plan_dragon(u); break; default: if (u_race(u)->flags & RCF_LEARN) { long_order = monster_learn(u); } break; } } if (long_order) { addlist(&u->orders, long_order); } } } pathfinder_cleanup(); }
static order *plan_dragon(unit * u) { attrib *ta = a_find(u->attribs, &at_targetregion); region *r = u->region; region *tr = NULL; bool move = false; order *long_order = NULL; if (ta == NULL) { move |= (r->land == 0 || r->land->peasants == 0); /* when no peasants, move */ move |= (r->land == 0 || r->land->money == 0); /* when no money, move */ } move |= chance(0.04); /* 4% chance to change your mind */ if (u_race(u) == get_race(RC_WYRM) && !move) { unit *u2; for (u2 = r->units; u2; u2 = u2->next) { /* wyrme sind einzelgänger */ if (u2 == u) { /* we do not make room for newcomers, so we don't need to look at them */ break; } if (u2 != u && u_race(u2) == u_race(u) && chance(0.5)) { move = true; break; } } } if (move) { /* dragon gets bored and looks for a different place to go */ ta = set_new_dragon_target(u, u->region, DRAGON_RANGE); } else ta = a_find(u->attribs, &at_targetregion); if (ta != NULL) { tr = (region *)ta->data.v; if (tr == NULL || !path_exists(u->region, tr, DRAGON_RANGE, allowed_dragon)) { ta = set_new_dragon_target(u, u->region, DRAGON_RANGE); if (ta) tr = findregion(ta->data.sa[0], ta->data.sa[1]); } } if (tr != NULL) { assert(long_order == NULL); switch (old_race(u_race(u))) { case RC_FIREDRAGON: long_order = make_movement_order(u, tr, 4, allowed_dragon); break; case RC_DRAGON: long_order = make_movement_order(u, tr, 3, allowed_dragon); break; case RC_WYRM: long_order = make_movement_order(u, tr, 1, allowed_dragon); break; default: break; } if (long_order) { reduce_weight(u); } if (rng_int() % 100 < 15) { const struct locale *lang = u->faction->locale; /* do a growl */ if (rname(tr, lang)) { addlist(&u->orders, create_order(K_MAIL, lang, "%s '%s... %s %s %s'", LOC(lang, parameters[P_REGION]), random_growl(), u->number == 1 ? "Ich rieche" : "Wir riechen", "etwas in", rname(tr, u->faction->locale))); } } } else { /* we have no target. do we like it here, then? */ long_order = get_money_for_dragon(u->region, u, income(u)); if (long_order == NULL) { /* money is gone, need a new target */ set_new_dragon_target(u, u->region, DRAGON_RANGE); } else if (u_race(u) != get_race(RC_FIREDRAGON)) { /* neue dracoiden! */ if (r->land && !fval(r->terrain, FORBIDDEN_REGION)) { int ra = 20 + rng_int() % 100; if (get_money(u) > ra * 50 + 100 && rng_int() % 100 < 50) { recruit_dracoids(u, ra); } } } } if (long_order == NULL) { skill_t sk = SK_PERCEPTION; /* study perception (or a random useful skill) */ while (!skill_enabled(sk) || u_race(u)->bonus[sk] < -5) { sk = (skill_t)(rng_int() % MAXSKILLS); } long_order = create_order(K_STUDY, u->faction->locale, "'%s'", skillname(sk, u->faction->locale)); } return long_order; }