static int do_potion(unit * u, region *r, const potion_type * ptype, int amount) { if (ptype == oldpotiontype[P_LIFE]) { int holz = 0; static int tree_type = -1; static int tree_count = -1; if (tree_type < 0) { tree_type = get_param_int(global.parameters, "rules.magic.wol_type", 1); tree_count = get_param_int(global.parameters, "rules.magic.wol_effect", 10); } /* mallorn is required to make mallorn forests, wood for regular ones */ if (fval(r, RF_MALLORN)) { holz = use_pooled(u, rt_find("mallorn"), GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, tree_count * amount); } else { holz = use_pooled(u, rt_find("log"), GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, tree_count * amount); } if (r->land == 0) holz = 0; if (holz < tree_count * amount) { int x = holz / tree_count; if (holz % tree_count) ++x; if (x < amount) amount = x; } rsettrees(r, tree_type, rtrees(r, tree_type) + holz); ADDMSG(&u->faction->msgs, msg_message("growtree_effect", "mage amount", u, holz)); } else if (ptype == oldpotiontype[P_HEILWASSER]) { u->hp = _min(unit_max_hp(u) * u->number, u->hp + 400 * amount); } else if (ptype == oldpotiontype[P_PEOPLE]) { attrib *a = (attrib *) a_find(r->attribs, &at_peasantluck); if (!a) a = a_add(&r->attribs, a_new(&at_peasantluck)); a->data.i += amount; } else if (ptype == oldpotiontype[P_HORSE]) { attrib *a = (attrib *) a_find(r->attribs, &at_horseluck); if (!a) a = a_add(&r->attribs, a_new(&at_horseluck)); a->data.i += amount; } else if (ptype == oldpotiontype[P_WAHRHEIT]) { fset(u, UFL_DISBELIEVES); amount = 1; } else if (ptype == oldpotiontype[P_MACHT]) { /* Verfünffacht die HP von max. 10 Personen in der Einheit */ u->hp += _min(u->number, 10 * amount) * unit_max_hp(u) * 4; } else { change_effect(u, ptype, 10 * amount); } return amount; }
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 transfermen(unit * u, unit * u2, int n) { const attrib *a; int hp = u->hp; region *r = u->region; if (n == 0) return; assert(n > 0); /* "hat attackiert"-status wird übergeben */ if (u2) { skill *sv, *sn; skill_t sk; ship *sh; assert(u2->number + n > 0); for (sk = 0; sk != MAXSKILLS; ++sk) { int weeks, level = 0; sv = get_skill(u, sk); sn = get_skill(u2, sk); if (sv == NULL && sn == NULL) continue; if (sn == NULL && u2->number == 0) { /* new unit, easy to solve */ level = sv->level; weeks = sv->weeks; } else { double dlevel = 0.0; if (sv && sv->level) { dlevel += (sv->level + 1 - sv->weeks / (sv->level + 1.0)) * n; level += sv->level * n; } if (sn && sn->level) { dlevel += (sn->level + 1 - sn->weeks / (sn->level + 1.0)) * u2->number; level += sn->level * u2->number; } dlevel = dlevel / (n + u2->number); level = level / (n + u2->number); if (level <= dlevel) { /* apply the remaining fraction to the number of weeks to go. * subtract the according number of weeks, getting closer to the * next level */ level = (int)dlevel; weeks = (level + 1) - (int)((dlevel - level) * (level + 1)); } else { /* make it harder to reach the next level. * weeks+level is the max difficulty, 1 - the fraction between * level and dlevel applied to the number of weeks between this * and the previous level is the added difficutly */ level = (int)dlevel + 1; weeks = 1 + 2 * level - (int)((1 + dlevel - level) * level); } } if (level) { if (sn == NULL) sn = add_skill(u2, sk); sn->level = (unsigned char)level; sn->weeks = (unsigned char)weeks; assert(sn->weeks > 0 && sn->weeks <= sn->level * 2 + 1); assert(u2->number != 0 || (sn->level == sv->level && sn->weeks == sv->weeks)); } else if (sn) { remove_skill(u2, sk); sn = NULL; } } a = a_find(u->attribs, &at_effect); while (a && a->type == &at_effect) { effect_data *olde = (effect_data *) a->data.v; if (olde->value) change_effect(u2, olde->type, olde->value); a = a->next; } sh = leftship(u); if (sh != NULL) set_leftship(u2, sh); u2->flags |= u->flags & (UFL_LONGACTION | UFL_NOTMOVING | UFL_HUNGER | UFL_MOVED | UFL_ENTER); if (u->attribs) { transfer_curse(u, u2, n); } } scale_number(u, u->number - n); if (u2) { set_number(u2, u2->number + n); hp -= u->hp; u2->hp += hp; /* TODO: Das ist schnarchlahm! und gehört nicht hierhin */ a = a_find(u2->attribs, &at_effect); while (a && a->type == &at_effect) { attrib *an = a->next; effect_data *olde = (effect_data *) a->data.v; int e = get_effect(u, olde->type); if (e != 0) change_effect(u2, olde->type, -e); a = an; } } else if (r->land) { if ((u_race(u)->ec_flags & ECF_REC_ETHEREAL) == 0) { const race *rc = u_race(u); if (rc->ec_flags & ECF_REC_HORSES) { /* Zentauren an die Pferde */ int h = rhorses(r) + n; rsethorses(r, h); } else { int p = rpeasants(r); p += (int)(n * rc->recruit_multi); rsetpeasants(r, p); } } } }
int study_cmd(unit * u, order * ord) { region *r = u->region; int p; int l; int studycost, days; double multi = 1.0; attrib *a = NULL; teaching_info *teach = NULL; int money = 0; skill_t sk; int maxalchemy = 0; int speed_rule = (study_rule_t)config_get_int("study.speedup", 0); static const race *rc_snotling; static int rc_cache; if (rc_changed(&rc_cache)) { rc_snotling = get_race(RC_SNOTLING); } (void)init_order(ord, u->faction->locale); sk = getskill(u->faction->locale); if (!check_student(u, ord, sk)) { return -1; } /* snotlings koennen Talente nur bis T8 lernen */ if (u_race(u) == rc_snotling) { if (get_level(u, sk) >= 8) { cmistake(u, ord, 308, MSG_EVENT); return -1; } } p = studycost = study_cost(u, sk); a = a_find(u->attribs, &at_learning); if (a != NULL) { teach = (teaching_info *)a->data.v; } /* keine kostenpflichtigen Talente fuer Migranten. Vertraute sind * keine Migranten, wird in is_migrant abgefangen. Vorsicht, * studycost darf hier noch nicht durch Akademie erhoeht sein */ if (studycost > 0 && !ExpensiveMigrants() && is_migrant(u)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_migrants_nolearn", "")); return -1; } /* Akademie: */ if (active_building(u, bt_find("academy"))) { studycost = studycost * 2; if (studycost < 50) studycost = 50; } if (sk == SK_MAGIC) { magic_t mtype; if (u->number > 1) { cmistake(u, ord, 106, MSG_MAGIC); return -1; } if (is_familiar(u)) { /* Vertraute zaehlen nicht zu den Magiern einer Partei, * koennen aber nur Graue Magie lernen */ mtype = M_GRAY; } else if (!has_skill(u, SK_MAGIC)) { int mmax = faction_skill_limit(u->faction, SK_MAGIC); /* Die Einheit ist noch kein Magier */ if (faction_count_skill(u->faction, SK_MAGIC) + u->number > mmax) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_magicians", "amount", mmax)); return -1; } mtype = getmagicskill(u->faction->locale); if (mtype == M_NONE || mtype == M_GRAY) { /* wurde kein Magiegebiet angegeben, wird davon * ausgegangen, dass das normal gelernt werden soll */ if (u->faction->magiegebiet != 0) { mtype = u->faction->magiegebiet; } else { /* Es wurde kein Magiegebiet angegeben und die Partei * hat noch keins gewaehlt. */ mtype = getmagicskill(u->faction->locale); if (mtype == M_NONE) { cmistake(u, ord, 178, MSG_MAGIC); return -1; } } } if (mtype != u->faction->magiegebiet) { /* Es wurde versucht, ein anderes Magiegebiet zu lernen * als das der Partei */ if (u->faction->magiegebiet != 0) { cmistake(u, ord, 179, MSG_MAGIC); return -1; } else { /* Lernt zum ersten mal Magie und legt damit das * Magiegebiet der Partei fest */ u->faction->magiegebiet = mtype; } } create_mage(u, mtype); } else { /* ist schon ein Magier und kein Vertrauter */ if (u->faction->magiegebiet == 0) { /* die Partei hat noch kein Magiegebiet gewaehlt. */ mtype = getmagicskill(u->faction->locale); if (mtype == M_NONE) { mtype = getmagicskill(u->faction->locale); if (mtype == M_NONE) { cmistake(u, ord, 178, MSG_MAGIC); return -1; } } /* Legt damit das Magiegebiet der Partei fest */ u->faction->magiegebiet = mtype; } } } if (sk == SK_ALCHEMY) { maxalchemy = effskill(u, SK_ALCHEMY, NULL); if (!has_skill(u, SK_ALCHEMY)) { int amax = faction_skill_limit(u->faction, SK_ALCHEMY); if (faction_count_skill(u->faction, SK_ALCHEMY) + u->number > amax) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_alchemists", "amount", amax)); return -1; } } } if (studycost) { int cost = studycost * u->number; money = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, cost); if (money > cost) money = cost; } if (money < studycost * u->number) { studycost = p; /* Ohne Univertreurung */ if (money > studycost) money = studycost; if (p > 0 && money < studycost * u->number) { cmistake(u, ord, 65, MSG_EVENT); multi = money / (double)(studycost * u->number); } } if (teach == NULL) { a = a_add(&u->attribs, a_new(&at_learning)); teach = (teaching_info *)a->data.v; assert(teach); teach->teachers = NULL; } if (money > 0) { use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, money); ADDMSG(&u->faction->msgs, msg_message("studycost", "unit region cost skill", u, u->region, money, sk)); } if (get_effect(u, oldpotiontype[P_WISE])) { l = get_effect(u, oldpotiontype[P_WISE]); if (l > u->number) l = u->number; teach->days += l * EXPERIENCEDAYS; change_effect(u, oldpotiontype[P_WISE], -l); } if (get_effect(u, oldpotiontype[P_FOOL])) { l = get_effect(u, oldpotiontype[P_FOOL]); if (l > u->number) l = u->number; teach->days -= l * STUDYDAYS; change_effect(u, oldpotiontype[P_FOOL], -l); } if (p != studycost) { /* ist_in_gebaeude(r, u, BT_UNIVERSITAET) == 1) { */ /* p ist Kosten ohne Uni, studycost mit; wenn * p!=studycost, ist die Einheit zwangsweise * in einer Uni */ teach->days += u->number * EXPERIENCEDAYS; } if (is_cursed(r->attribs, &ct_badlearn)) { teach->days -= u->number * EXPERIENCEDAYS; } multi *= study_speedup(u, sk, speed_rule); days = study_days(u, sk); days = (int)((days + teach->days) * multi); /* the artacademy currently improves the learning of entertainment of all units in the region, to be able to make it cumulative with with an academy */ if (sk == SK_ENTERTAINMENT && buildingtype_exists(r, bt_find("artacademy"), false)) { days *= 2; } learn_skill(u, sk, days); if (a != NULL) { if (teach->teachers) { msg_teachers(teach->teachers, u, sk); } a_remove(&u->attribs, a); a = NULL; } u->flags |= (UFL_LONGACTION | UFL_NOTMOVING); /* Anzeigen neuer Traenke */ /* Spruchlistenaktualiesierung ist in Regeneration */ if (sk == SK_ALCHEMY) { faction *f = u->faction; int skill = effskill(u, SK_ALCHEMY, NULL); if (skill > maxalchemy) { show_potions(f, skill); } } init_order(NULL, NULL); return 0; }
int teach_cmd(unit * teacher, struct order *ord) { plane *pl; region *r = teacher->region; skill_t sk_academy = NOSKILL; int teaching, i, j, count, academy_students = 0; if (r->attribs) { if (get_curse(r->attribs, &ct_gbdream)) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "gbdream_noteach", "")); return 0; } } if ((u_race(teacher)->flags & RCF_NOTEACH) || fval(teacher, UFL_WERE)) { cmistake(teacher, ord, 274, MSG_EVENT); return 0; } pl = rplane(r); if (pl && fval(pl, PFL_NOTEACH)) { cmistake(teacher, ord, 273, MSG_EVENT); return 0; } teaching = teacher->number * TEACHNUMBER; if ((i = get_effect(teacher, oldpotiontype[P_FOOL])) > 0) { /* Trank "Dumpfbackenbrot" */ if (i > teaching) i = teaching; /* Trank wirkt pro Schueler, nicht pro Lehrer */ teaching -= i; change_effect(teacher, oldpotiontype[P_FOOL], -i); j = teaching; ADDMSG(&teacher->faction->msgs, msg_message("teachdumb", "teacher amount", teacher, j)); } if (teaching <= 0) return 0; count = 0; init_order_depr(ord); #if TEACH_ALL if (getparam(teacher->faction->locale) == P_ANY) { skill_t sk; unit *scholar; skill_t teachskill[MAXSKILLS]; int t = 0; do { sk = getskill(teacher->faction->locale); teachskill[t] = getskill(teacher->faction->locale); } while (sk != NOSKILL); for (scholar = r->units; teaching > 0 && scholar; scholar = scholar->next) { if (LongHunger(scholar)) { continue; } else if (scholar->faction == teacher->faction) { if (getkeyword(scholar->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ init_order(scholar->thisorder, scholar->faction->locale); sk = getskill(scholar->faction->locale); if (sk != NOSKILL && teachskill[0] != NOSKILL) { for (t = 0; teachskill[t] != NOSKILL; ++t) { if (sk == teachskill[t]) { break; } } sk = teachskill[t]; } if (sk != NOSKILL && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(scholar, sk)) { teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); } } } #ifdef TEACH_FRIENDS else if (alliedunit(teacher, scholar->faction, HELP_GUARD)) { if (getkeyword(scholar->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ init_order(scholar->thisorder, scholar->faction->locale); sk = getskill(scholar->faction->locale); if (sk != NOSKILL && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(scholar, sk, NULL)) { teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); } } } #endif } } else #endif { char zOrder[4096]; size_t sz = sizeof(zOrder); order *new_order; zOrder[0] = '\0'; init_order_depr(ord); while (!parser_end()) { skill_t sk; unit *scholar; bool feedback; getunit(r, teacher->faction, &scholar); ++count; /* Falls die Unit nicht gefunden wird, Fehler melden */ if (!scholar) { char tbuf[20]; const char *uid; const char *token; /* Finde den string, der den Fehler verursacht hat */ parser_pushstate(); init_order_depr(ord); for (j = 0; j != count - 1; ++j) { /* skip over the first 'count' units */ getunit(r, teacher->faction, NULL); } token = getstrtoken(); /* Beginne die Fehlermeldung */ if (isparam(token, teacher->faction->locale, P_TEMP)) { token = getstrtoken(); sprintf(tbuf, "%s %s", LOC(teacher->faction->locale, parameters[P_TEMP]), token); uid = tbuf; } else { uid = token; } ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "unitnotfound_id", "id", uid)); parser_popstate(); continue; } feedback = teacher->faction == scholar->faction || alliedunit(scholar, teacher->faction, HELP_GUARD); /* Neuen Befehl zusammenbauen. TEMP-Einheiten werden automatisch in * ihre neuen Nummern uebersetzt. */ if (zOrder[0]) { strncat(zOrder, " ", sz - 1); --sz; } sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(scholar->no), sz); if (getkeyword(scholar->thisorder) != K_STUDY) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); continue; } /* Input ist nun von student->thisorder !! */ parser_pushstate(); init_order(scholar->thisorder, scholar->faction->locale); sk = getskill(scholar->faction->locale); parser_popstate(); if (sk == NOSKILL) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); continue; } if (effskill_study(scholar, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", "student", scholar)); } continue; } if (sk == SK_MAGIC) { /* ist der Magier schon spezialisiert, so versteht er nur noch * Lehrer seines Gebietes */ magic_t mage2 = unit_get_magic(scholar); if (mage2 != M_GRAY) { magic_t mage1 = unit_get_magic(teacher); if (mage1 != mage2) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "error_different_magic", "target", scholar)); } continue; } } } sk_academy = sk; teaching -= teach_unit(teacher, scholar, teaching, sk, false, &academy_students); } new_order = create_order(K_TEACH, teacher->faction->locale, "%s", zOrder); replace_order(&teacher->orders, ord, new_order); free_order(new_order); /* parse_order & set_order have each increased the refcount */ } if (academy_students > 0 && sk_academy!=NOSKILL) { academy_teaching_bonus(teacher, sk_academy, academy_students); } init_order_depr(NULL); return 0; }
void get_food(region * r) { plane *pl = rplane(r); unit *u; int peasantfood = rpeasants(r) * 10; static int food_rules = -1; static int gamecookie = -1; if (food_rules < 0 || gamecookie != global.cookie) { gamecookie = global.cookie; food_rules = get_param_int(global.parameters, "rules.economy.food", 0); } if (food_rules & FOOD_IS_FREE) { return; } /* 1. Versorgung von eigenen Einheiten. Das vorhandene Silber * wird zunächst so auf die Einheiten aufgeteilt, dass idealerweise * jede Einheit genug Silber für ihren Unterhalt hat. */ for (u = r->units; u; u = u->next) { int need = lifestyle(u); /* Erstmal zurücksetzen */ freset(u, UFL_HUNGER); if (u->ship && (u->ship->flags & SF_FISHING)) { unit *v; int c = 2; for (v = u; c > 0 && v; v = v->next) { if (v->ship == u->ship) { int get = 0; if (v->number <= c) { get = lifestyle(v); } else { get = lifestyle(v) * c / v->number; } if (get) { change_money(v, get); } } c -= v->number; } u->ship->flags -= SF_FISHING; } if (food_rules & FOOD_FROM_PEASANTS) { struct faction *owner = region_get_owner(r); /* if the region is owned, and the owner is nice, then we'll get * food from the peasants - should not be used with WORK */ if (owner != NULL && (get_alliance(owner, u->faction) & HELP_MONEY)) { int rm = rmoney(r); int use = _min(rm, need); rsetmoney(r, rm - use); need -= use; } } need -= get_money(u); if (need > 0) { unit *v; for (v = r->units; need && v; v = v->next) { if (v->faction == u->faction && help_money(v)) { int give = get_money(v) - lifestyle(v); give = _min(need, give); if (give > 0) { change_money(v, -give); change_money(u, give); need -= give; } } } } } /* 2. Versorgung durch Fremde. Das Silber alliierter Einheiten wird * entsprechend verteilt. */ for (u = r->units; u; u = u->next) { int need = lifestyle(u); faction *f = u->faction; need -= _max(0, get_money(u)); if (need > 0) { unit *v; if (food_rules & FOOD_FROM_OWNER) { /* the owner of the region is the first faction to help out when you're hungry */ faction *owner = region_get_owner(r); if (owner && owner != u->faction) { for (v = r->units; v; v = v->next) { if (v->faction == owner && alliedunit(v, f, HELP_MONEY) && help_money(v)) { help_feed(v, u, &need); break; } } } } for (v = r->units; need && v; v = v->next) { if (v->faction != f && alliedunit(v, f, HELP_MONEY) && help_money(v)) { help_feed(v, u, &need); } } /* Die Einheit hat nicht genug Geld zusammengekratzt und * nimmt Schaden: */ if (need > 0) { int lspp = lifestyle(u) / u->number; if (lspp > 0) { int number = (need + lspp - 1) / lspp; if (hunger(number, u)) fset(u, UFL_HUNGER); } } } } /* 3. bestimmen, wie viele Bauern gefressen werden. * bei fehlenden Bauern den Dämon hungern lassen */ for (u = r->units; u; u = u->next) { if (u_race(u) == get_race(RC_DAEMON)) { int hungry = u->number; /* use peasantblood before eating the peasants themselves */ const struct potion_type *pt_blood = 0; const resource_type *rt_blood = rt_find("peasantblood"); if (rt_blood) { pt_blood = rt_blood->ptype; } if (pt_blood) { /* always start with the unit itself, then the first known unit that may have some blood */ unit *donor = u; while (donor != NULL && hungry > 0) { int blut = get_effect(donor, pt_blood); blut = _min(blut, hungry); if (blut) { change_effect(donor, pt_blood, -blut); hungry -= blut; } if (donor == u) donor = r->units; while (donor != NULL) { if (u_race(donor) == get_race(RC_DAEMON) && donor != u) { if (get_effect(donor, pt_blood)) { /* if he's in our faction, drain him: */ if (donor->faction == u->faction) break; } } donor = donor->next; } } } /* remaining demons feed on peasants */ if (pl == NULL || !fval(pl, PFL_NOFEED)) { if (peasantfood >= hungry) { peasantfood -= hungry; hungry = 0; } else { hungry -= peasantfood; peasantfood = 0; } if (hungry > 0) { static int demon_hunger = -1; if (demon_hunger < 0) { demon_hunger = get_param_int(global.parameters, "hunger.demons", 0); } if (demon_hunger == 0) { /* demons who don't feed are hungry */ if (hunger(hungry, u)) fset(u, UFL_HUNGER); } else { /* no damage, but set the hungry-flag */ fset(u, UFL_HUNGER); } } } } } rsetpeasants(r, peasantfood / 10); /* 3. Von den überlebenden das Geld abziehen: */ for (u = r->units; u; u = u->next) { int need = _min(get_money(u), lifestyle(u)); change_money(u, -need); } }
/** Use up resources for building an object. * Build up to 'size' points of 'type', where 'completed' * of the first object have already been finished. return the * actual size that could be built. */ int build(unit * u, const construction * ctype, int completed, int want) { const construction *type = ctype; int skills = INT_MAX; /* number of skill points remainig */ int basesk = 0; int made = 0; if (want <= 0) return 0; if (type == NULL) return ENOMATERIALS; if (type->improvement == NULL && completed == type->maxsize) return ECOMPLETE; if (type->btype != NULL) { building *b; if (!u->building || u->building->type != type->btype) { return EBUILDINGREQ; } b = inside_building(u); if (b == NULL) return EBUILDINGREQ; } if (type->skill != NOSKILL) { int effsk; int dm = get_effect(u, oldpotiontype[P_DOMORE]); assert(u->number); basesk = effskill(u, type->skill); if (basesk == 0) return ENEEDSKILL; effsk = basesk; if (inside_building(u)) { effsk = skillmod(u->building->type->attribs, u, u->region, type->skill, effsk, SMF_PRODUCTION); } effsk = skillmod(type->attribs, u, u->region, type->skill, effsk, SMF_PRODUCTION); if (effsk < 0) return effsk; /* pass errors to caller */ if (effsk == 0) return ENEEDSKILL; skills = effsk * u->number; /* technically, nimblefinge and domore should be in a global set of * "game"-attributes, (as at_skillmod) but for a while, we're leaving * them in here. */ if (dm != 0) { /* Auswirkung Schaffenstrunk */ dm = _min(dm, u->number); change_effect(u, oldpotiontype[P_DOMORE], -dm); skills += dm * effsk; } } for (; want > 0 && skills > 0;) { int c, n; /* skip over everything that's already been done: * type->improvement==NULL means no more improvements, but no size limits * type->improvement==type means build another object of the same time * while material lasts type->improvement==x means build x when type * is finished */ while (type->improvement != NULL && type->improvement != type && type->maxsize > 0 && type->maxsize <= completed) { completed -= type->maxsize; type = type->improvement; } if (type == NULL) { if (made == 0) return ECOMPLETE; break; /* completed */ } /* Hier ist entweder maxsize == -1, oder completed < maxsize. * Andernfalls ist das Datenfile oder sonstwas kaputt... * (enno): Nein, das ist für Dinge, bei denen die nächste Ausbaustufe * die gleiche wie die vorherige ist. z.b. gegenstände. */ if (type->maxsize > 1) { completed = completed % type->maxsize; } else { completed = 0; assert(type->reqsize >= 1); } if (basesk < type->minskill) { if (made == 0) return ELOWSKILL; /* not good enough to go on */ } /* n = maximum buildable size */ if (type->minskill > 1) { n = skills / type->minskill; } else { n = skills; } /* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische) * Talente */ if (skill_limit(u->faction, type->skill) == INT_MAX) { const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER); item *itm = ring ? *i_find(&u->items, ring->itype) : 0; int i = itm ? itm->number : 0; if (i > 0) { int rings = _min(u->number, i); n = n * ((roqf_factor() - 1) * rings + u->number) / u->number; } } if (want < n) n = want; if (type->maxsize > 0) { n = _min(type->maxsize - completed, n); if (type->improvement == NULL) { want = n; } } if (type->materials) for (c = 0; n > 0 && type->materials[c].number; c++) { const struct resource_type *rtype = type->materials[c].rtype; int need, prebuilt; int canuse = get_pooled(u, rtype, GET_DEFAULT, INT_MAX); if (inside_building(u)) { canuse = matmod(u->building->type->attribs, u, rtype, canuse); } if (canuse < 0) return canuse; /* pass errors to caller */ canuse = matmod(type->attribs, u, rtype, canuse); if (type->reqsize > 1) { prebuilt = required(completed, type->reqsize, type->materials[c].number); for (; n;) { need = required(completed + n, type->reqsize, type->materials[c].number); if (need - prebuilt <= canuse) break; --n; /* TODO: optimieren? */ } } else { int maxn = canuse / type->materials[c].number; if (maxn < n) n = maxn; } } if (n <= 0) { if (made == 0) return ENOMATERIALS; else break; } if (type->materials) for (c = 0; type->materials[c].number; c++) { const struct resource_type *rtype = type->materials[c].rtype; int prebuilt = required(completed, type->reqsize, type->materials[c].number); int need = required(completed + n, type->reqsize, type->materials[c].number); int multi = 1; int canuse = 100; /* normalization */ if (inside_building(u)) canuse = matmod(u->building->type->attribs, u, rtype, canuse); if (canuse < 0) return canuse; /* pass errors to caller */ canuse = matmod(type->attribs, u, rtype, canuse); assert(canuse % 100 == 0 || !"only constant multipliers are implemented in build()"); multi = canuse / 100; if (canuse < 0) return canuse; /* pass errors to caller */ use_pooled(u, rtype, GET_DEFAULT, (need - prebuilt + multi - 1) / multi); } made += n; skills -= n * type->minskill; want -= n; completed = completed + n; } /* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */ produceexp(u, ctype->skill, _min(made, u->number)); return made; }
void build_road(region * r, unit * u, int size, direction_t d) { int n, left; region *rn = rconnect(r, d); assert(u->number); if (!eff_skill(u, SK_ROAD_BUILDING, r)) { cmistake(u, u->thisorder, 103, MSG_PRODUCE); return; } if (besieged(u)) { cmistake(u, u->thisorder, 60, MSG_PRODUCE); return; } if (rn == NULL || rn->terrain->max_road < 0) { cmistake(u, u->thisorder, 94, MSG_PRODUCE); return; } if (r->terrain->max_road < 0) { cmistake(u, u->thisorder, 94, MSG_PRODUCE); return; } if (r->terrain == newterrain(T_SWAMP)) { /* wenn kein Damm existiert */ const struct building_type *bt_dam = bt_find("dam"); if (!bt_dam || !buildingtype_exists(r, bt_dam, true)) { cmistake(u, u->thisorder, 132, MSG_PRODUCE); return; } } else if (r->terrain == newterrain(T_DESERT)) { const struct building_type *bt_caravan = bt_find("caravan"); /* wenn keine Karawanserei existiert */ if (!bt_caravan || !buildingtype_exists(r, bt_caravan, true)) { cmistake(u, u->thisorder, 133, MSG_PRODUCE); return; } } else if (r->terrain == newterrain(T_GLACIER)) { const struct building_type *bt_tunnel = bt_find("tunnel"); /* wenn kein Tunnel existiert */ if (!bt_tunnel || !buildingtype_exists(r, bt_tunnel, true)) { cmistake(u, u->thisorder, 131, MSG_PRODUCE); return; } } /* left kann man noch bauen */ left = r->terrain->max_road - rroad(r, d); /* hoffentlich ist r->road <= r->terrain->max_road, n also >= 0 */ if (left <= 0) { ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_roads_finished", "")); return; } if (size > 0) left = _min(size, left); /* baumaximum anhand der rohstoffe */ if (u_race(u) == get_race(RC_STONEGOLEM)) { n = u->number * GOLEM_STONE; } else { n = get_pooled(u, get_resourcetype(R_STONE), GET_DEFAULT, left); if (n == 0) { cmistake(u, u->thisorder, 151, MSG_PRODUCE); return; } } left = _min(n, left); /* n = maximum by skill. try to maximize it */ n = u->number * eff_skill(u, SK_ROAD_BUILDING, r); if (n < left) { const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER); item *itm = ring ? *i_find(&u->items, ring->itype) : 0; if (itm != NULL && itm->number > 0) { int rings = _min(u->number, itm->number); n = n * ((roqf_factor() - 1) * rings + u->number) / u->number; } } if (n < left) { int dm = get_effect(u, oldpotiontype[P_DOMORE]); if (dm != 0) { int sk = eff_skill(u, SK_ROAD_BUILDING, r); int todo = (left - n + sk - 1) / sk; todo = _min(todo, u->number); dm = _min(dm, todo); change_effect(u, oldpotiontype[P_DOMORE], -dm); n += dm * sk; } /* Auswirkung Schaffenstrunk */ } /* make minimum of possible and available: */ n = _min(left, n); /* n is now modified by several special effects, so we have to * minimize it again to make sure the road will not grow beyond * maximum. */ rsetroad(r, d, rroad(r, d) + (short)n); if (u_race(u) == get_race(RC_STONEGOLEM)) { int golemsused = n / GOLEM_STONE; if (n % GOLEM_STONE != 0) { ++golemsused; } scale_number(u, u->number - golemsused); } else { use_pooled(u, get_resourcetype(R_STONE), GET_DEFAULT, n); /* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */ produceexp(u, SK_ROAD_BUILDING, _min(n, u->number)); } ADDMSG(&u->faction->msgs, msg_message("buildroad", "region unit size", r, u, n)); }
int learn_cmd(unit * u, order * ord) { region *r = u->region; int p; magic_t mtyp; int l; int studycost, days; double multi = 1.0; attrib *a = NULL; teaching_info *teach = NULL; int money = 0; skill_t sk; int maxalchemy = 0; int speed_rule = (study_rule_t)get_param_int(global.parameters, "study.speedup", 0); static int learn_newskills = -1; if (learn_newskills < 0) { const char *str = get_param(global.parameters, "study.newskills"); if (str && strcmp(str, "false") == 0) learn_newskills = 0; else learn_newskills = 1; } if (!unit_can_study(u)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_race_nolearn", "race", u_race(u))); return 0; } init_order(ord); sk = getskill(u->faction->locale); if (sk < 0) { cmistake(u, ord, 77, MSG_EVENT); return 0; } if (SkillCap(sk) && SkillCap(sk) <= effskill(u, sk)) { cmistake(u, ord, 771, MSG_EVENT); return 0; } /* Hack: Talente mit Malus -99 koennen nicht gelernt werden */ if (u_race(u)->bonus[sk] == -99) { cmistake(u, ord, 771, MSG_EVENT); return 0; } if (learn_newskills == 0) { skill *sv = unit_skill(u, sk); if (sv == NULL) { /* we can only learn skills we already have */ cmistake(u, ord, 771, MSG_EVENT); return 0; } } /* snotlings koennen Talente nur bis T8 lernen */ if (u_race(u) == get_race(RC_SNOTLING)) { if (get_level(u, sk) >= 8) { cmistake(u, ord, 308, MSG_EVENT); return 0; } } p = studycost = study_cost(u, sk); a = a_find(u->attribs, &at_learning); if (a != NULL) { teach = (teaching_info *)a->data.v; } /* keine kostenpflichtigen Talente fuer Migranten. Vertraute sind * keine Migranten, wird in is_migrant abgefangen. Vorsicht, * studycost darf hier noch nicht durch Akademie erhoeht sein */ if (studycost > 0 && !ExpensiveMigrants() && is_migrant(u)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_migrants_nolearn", "")); return 0; } /* Akademie: */ { struct building *b = inside_building(u); const struct building_type *btype = b ? b->type : NULL; if (btype && btype == bt_find("academy")) { studycost = _max(50, studycost * 2); } } if (sk == SK_MAGIC) { if (u->number > 1) { cmistake(u, ord, 106, MSG_MAGIC); return 0; } if (is_familiar(u)) { /* Vertraute zaehlen nicht zu den Magiern einer Partei, * koennen aber nur Graue Magie lernen */ mtyp = M_GRAY; if (!is_mage(u)) create_mage(u, mtyp); } else if (!has_skill(u, SK_MAGIC)) { int mmax = skill_limit(u->faction, SK_MAGIC); /* Die Einheit ist noch kein Magier */ if (count_skill(u->faction, SK_MAGIC) + u->number > mmax) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_magicians", "amount", mmax)); return 0; } mtyp = getmagicskill(u->faction->locale); if (mtyp == M_NONE || mtyp == M_GRAY) { /* wurde kein Magiegebiet angegeben, wird davon * ausgegangen, dass das normal gelernt werden soll */ if (u->faction->magiegebiet != 0) { mtyp = u->faction->magiegebiet; } else { /* Es wurde kein Magiegebiet angegeben und die Partei * hat noch keins gewaehlt. */ mtyp = getmagicskill(u->faction->locale); if (mtyp == M_NONE) { cmistake(u, ord, 178, MSG_MAGIC); return 0; } } } if (mtyp != u->faction->magiegebiet) { /* Es wurde versucht, ein anderes Magiegebiet zu lernen * als das der Partei */ if (u->faction->magiegebiet != 0) { cmistake(u, ord, 179, MSG_MAGIC); return 0; } else { /* Lernt zum ersten mal Magie und legt damit das * Magiegebiet der Partei fest */ u->faction->magiegebiet = mtyp; } } if (!is_mage(u)) create_mage(u, mtyp); } else { /* ist schon ein Magier und kein Vertrauter */ if (u->faction->magiegebiet == 0) { /* die Partei hat noch kein Magiegebiet gewaehlt. */ mtyp = getmagicskill(u->faction->locale); if (mtyp == M_NONE) { mtyp = getmagicskill(u->faction->locale); if (mtyp == M_NONE) { cmistake(u, ord, 178, MSG_MAGIC); return 0; } } /* Legt damit das Magiegebiet der Partei fest */ u->faction->magiegebiet = mtyp; } } } if (sk == SK_ALCHEMY) { maxalchemy = eff_skill(u, SK_ALCHEMY, r); if (!has_skill(u, SK_ALCHEMY)) { int amax = skill_limit(u->faction, SK_ALCHEMY); if (count_skill(u->faction, SK_ALCHEMY) + u->number > amax) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_alchemists", "amount", amax)); return 0; } } } if (studycost) { int cost = studycost * u->number; money = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, cost); money = _min(money, cost); } if (money < studycost * u->number) { studycost = p; /* Ohne Univertreurung */ money = _min(money, studycost); if (p > 0 && money < studycost * u->number) { cmistake(u, ord, 65, MSG_EVENT); multi = money / (double)(studycost * u->number); } } if (teach == NULL) { a = a_add(&u->attribs, a_new(&at_learning)); teach = (teaching_info *)a->data.v; teach->teachers[0] = 0; } if (money > 0) { use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, money); ADDMSG(&u->faction->msgs, msg_message("studycost", "unit region cost skill", u, u->region, money, sk)); } if (get_effect(u, oldpotiontype[P_WISE])) { l = _min(u->number, get_effect(u, oldpotiontype[P_WISE])); teach->value += l * 10; change_effect(u, oldpotiontype[P_WISE], -l); } if (get_effect(u, oldpotiontype[P_FOOL])) { l = _min(u->number, get_effect(u, oldpotiontype[P_FOOL])); teach->value -= l * 30; change_effect(u, oldpotiontype[P_FOOL], -l); } if (p != studycost) { /* ist_in_gebaeude(r, u, BT_UNIVERSITAET) == 1) { */ /* p ist Kosten ohne Uni, studycost mit; wenn * p!=studycost, ist die Einheit zwangsweise * in einer Uni */ teach->value += u->number * 10; } if (is_cursed(r->attribs, C_BADLEARN, 0)) { teach->value -= u->number * 10; } multi *= study_speedup(u, sk, speed_rule); days = study_days(u, sk); days = (int)((days + teach->value) * multi); /* the artacademy currently improves the learning of entertainment of all units in the region, to be able to make it cumulative with with an academy */ if (sk == SK_ENTERTAINMENT && buildingtype_exists(r, bt_find("artacademy"), false)) { days *= 2; } if (fval(u, UFL_HUNGER)) days /= 2; while (days) { if (days >= u->number * 30) { learn_skill(u, sk, 1.0); days -= u->number * 30; } else { double chance = (double)days / u->number / 30; learn_skill(u, sk, chance); days = 0; } } if (a != NULL) { if (teach != NULL) { int index = 0; while (teach->teachers[index] && index != MAXTEACHERS) { unit *teacher = teach->teachers[index++]; if (teacher->faction != u->faction) { bool feedback = alliedunit(u, teacher->faction, HELP_GUARD); if (feedback) { ADDMSG(&teacher->faction->msgs, msg_message("teach_teacher", "teacher student skill level", teacher, u, sk, effskill(u, sk))); } ADDMSG(&u->faction->msgs, msg_message("teach_student", "teacher student skill", teacher, u, sk)); } } } a_remove(&u->attribs, a); a = NULL; } fset(u, UFL_LONGACTION | UFL_NOTMOVING); /* Anzeigen neuer Traenke */ /* Spruchlistenaktualiesierung ist in Regeneration */ if (sk == SK_ALCHEMY) { const potion_type *ptype; faction *f = u->faction; int skill = eff_skill(u, SK_ALCHEMY, r); if (skill > maxalchemy) { for (ptype = potiontypes; ptype; ptype = ptype->next) { if (skill == ptype->level * 2) { attrib *a = a_find(f->attribs, &at_showitem); while (a && a->type == &at_showitem && a->data.v != ptype) a = a->next; if (a == NULL || a->type != &at_showitem) { a = a_add(&f->attribs, a_new(&at_showitem)); a->data.v = (void *)ptype->itype; } } } } } else if (sk == SK_MAGIC) { sc_mage *mage = get_mage(u); if (!mage) { mage = create_mage(u, u->faction->magiegebiet); } } return 0; }
int teach_cmd(unit * u, struct order *ord) { static const curse_type *gbdream_ct = NULL; plane *pl; region *r = u->region; int teaching, i, j, count, academy = 0; skill_t sk = NOSKILL; if (gbdream_ct == 0) gbdream_ct = ct_find("gbdream"); if (gbdream_ct) { if (get_curse(u->region->attribs, gbdream_ct)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "gbdream_noteach", "")); return 0; } } if ((u_race(u)->flags & RCF_NOTEACH) || fval(u, UFL_WERE)) { cmistake(u, ord, 274, MSG_EVENT); return 0; } pl = rplane(r); if (pl && fval(pl, PFL_NOTEACH)) { cmistake(u, ord, 273, MSG_EVENT); return 0; } teaching = u->number * 30 * TEACHNUMBER; if ((i = get_effect(u, oldpotiontype[P_FOOL])) > 0) { /* Trank "Dumpfbackenbrot" */ i = _min(i, u->number * TEACHNUMBER); /* Trank wirkt pro Schueler, nicht pro Lehrer */ teaching -= i * 30; change_effect(u, oldpotiontype[P_FOOL], -i); j = teaching / 30; ADDMSG(&u->faction->msgs, msg_message("teachdumb", "teacher amount", u, j)); } if (teaching == 0) return 0; count = 0; init_order(ord); #if TEACH_ALL if (getparam(u->faction->locale) == P_ANY) { unit *student = r->units; skill_t teachskill[MAXSKILLS]; int i = 0; do { sk = getskill(u->faction->locale); teachskill[i++] = sk; } while (sk != NOSKILL); while (teaching && student) { if (student->faction == u->faction) { if (LongHunger(student)) continue; if (getkeyword(student->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ init_order(student->thisorder); sk = getskill(student->faction->locale); if (sk != NOSKILL && teachskill[0] != NOSKILL) { for (i = 0; teachskill[i] != NOSKILL; ++i) if (sk == teachskill[i]) break; sk = teachskill[i]; } if (sk != NOSKILL && eff_skill_study(u, sk, r) - TEACHDIFFERENCE > eff_skill_study(student, sk, r)) { teaching -= teach_unit(u, student, teaching, sk, true, &academy); } } } student = student->next; } #ifdef TEACH_FRIENDS while (teaching && student) { if (student->faction != u->faction && alliedunit(u, student->faction, HELP_GUARD)) { if (LongHunger(student)) continue; if (getkeyword(student->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ init_order(student->thisorder); sk = getskill(student->faction->locale); if (sk != NOSKILL && eff_skill_study(u, sk, r) - TEACHDIFFERENCE >= eff_skill(student, sk, r)) { teaching -= teach_unit(u, student, teaching, sk, true, &academy); } } } student = student->next; } #endif } else #endif { char zOrder[4096]; order *new_order; zOrder[0] = '\0'; init_order(ord); while (!parser_end()) { unit *u2; bool feedback; getunit(r, u->faction, &u2); ++count; /* Falls die Unit nicht gefunden wird, Fehler melden */ if (!u2) { char tbuf[20]; const char *uid; const char *token; /* Finde den string, der den Fehler verursacht hat */ parser_pushstate(); init_order(ord); for (j = 0; j != count - 1; ++j) { /* skip over the first 'count' units */ getunit(r, u->faction, NULL); } token = getstrtoken(); /* Beginne die Fehlermeldung */ if (isparam(token, u->faction->locale, P_TEMP)) { token = getstrtoken(); sprintf(tbuf, "%s %s", LOC(u->faction->locale, parameters[P_TEMP]), token); uid = tbuf; } else { uid = token; } ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "unitnotfound_id", "id", uid)); parser_popstate(); continue; } feedback = u->faction == u2->faction || alliedunit(u2, u->faction, HELP_GUARD); /* Neuen Befehl zusammenbauen. TEMP-Einheiten werden automatisch in * ihre neuen Nummern uebersetzt. */ if (zOrder[0]) strcat(zOrder, " "); strcat(zOrder, unitid(u2)); if (getkeyword(u2->thisorder) != K_STUDY) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "teach_nolearn", "student", u2)); continue; } /* Input ist nun von u2->thisorder !! */ parser_pushstate(); init_order(u2->thisorder); sk = getskill(u2->faction->locale); parser_popstate(); if (sk == NOSKILL) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "teach_nolearn", "student", u2)); continue; } /* u is teacher, u2 is student */ if (eff_skill_study(u2, sk, r) > eff_skill_study(u, sk, r) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "teach_asgood", "student", u2)); } continue; } if (sk == SK_MAGIC) { /* ist der Magier schon spezialisiert, so versteht er nur noch * Lehrer seines Gebietes */ sc_mage *mage1 = get_mage(u); sc_mage *mage2 = get_mage(u2); if (!mage2 || !mage1 || (mage2->magietyp != M_GRAY && mage1->magietyp != mage2->magietyp)) { if (feedback) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_different_magic", "target", u2)); } continue; } } teaching -= teach_unit(u, u2, teaching, sk, false, &academy); } new_order = create_order(K_TEACH, u->faction->locale, "%s", zOrder); replace_order(&u->orders, ord, new_order); free_order(new_order); /* parse_order & set_order have each increased the refcount */ } if (academy && sk != NOSKILL) { academy = academy / 30; /* anzahl gelehrter wochen, max. 10 */ learn_skill(u, sk, academy / 30.0 / TEACHNUMBER); } return 0; }