bool in_safe_building(unit *u1, unit *u2) { if (u1->building) { building * b = inside_building(u1); if (b && b->type->flags & BTF_FORTIFICATION) { if (!u2->building) { return true; } if (u2->building != b || b != inside_building(u2)) { return true; } } } return false; }
building *active_building(const unit *u, const struct building_type *btype) { if (u->building && u->building->type == btype && building_is_active(u->building)) { return inside_building(u); } return 0; }
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; }
/** 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; }
static int teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, bool report, int *academy) { teaching_info *teach = NULL; attrib *a; int n; /* learning sind die Tage, die sie schon durch andere Lehrer zugute * geschrieben bekommen haben. Total darf dies nicht ueber 30 Tage pro Mann * steigen. * * n ist die Anzahl zusaetzlich gelernter Tage. n darf max. die Differenz * von schon gelernten Tagen zum _max(30 Tage pro Mann) betragen. */ if (magic_lowskill(student)) { cmistake(teacher, teacher->thisorder, 292, MSG_EVENT); return 0; } n = 30 * student->number; a = a_find(student->attribs, &at_learning); if (a != NULL) { teach = (teaching_info *)a->data.v; n -= teach->value; } n = _min(n, nteaching); if (n != 0) { struct building *b = inside_building(teacher); const struct building_type *btype = b ? b->type : NULL; int index = 0; if (teach == NULL) { a = a_add(&student->attribs, a_new(&at_learning)); teach = (teaching_info *)a->data.v; } else { while (teach->teachers[index] && index != MAXTEACHERS) ++index; } if (index < MAXTEACHERS) teach->teachers[index++] = teacher; if (index < MAXTEACHERS) teach->teachers[index] = NULL; teach->value += n; /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ if (btype == bt_find("academy") && student->building && student->building->type == bt_find("academy")) { int j = study_cost(student, sk); j = _max(50, j * 2); /* kann Einheit das zahlen? */ if (get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ teach->value += (n / 30) * 10; /* learning erhoehen */ /* Lehrer zusaetzlich +1 Tag pro Schueler. */ if (academy) *academy += n; } /* sonst nehmen sie nicht am Unterricht teil */ } /* Teaching ist die Anzahl Leute, denen man noch was beibringen kann. Da * hier nicht n verwendet wird, werden die Leute gezaehlt und nicht die * effektiv gelernten Tage. -> FALSCH ? (ENNO) * * Eine Einheit A von 11 Mann mit Talent 0 profitiert vom ersten Lehrer B * also 10x30=300 tage, und der zweite Lehrer C lehrt fuer nur noch 1x30=30 * Tage (damit das Maximum von 11x30=330 nicht ueberschritten wird). * * Damit es aber in der Ausfuehrung nicht auf die Reihenfolge drauf ankommt, * darf der zweite Lehrer C keine weiteren Einheiten D mehr lehren. Also * wird student 30 Tage gutgeschrieben, aber teaching sinkt auf 0 (300-11x30 <= * 0). * * Sonst traete dies auf: * * A: lernt B: lehrt A C: lehrt A D D: lernt * * Wenn B vor C dran ist, lehrt C nur 30 Tage an A (wie oben) und * 270 Tage an D. * * Ist C aber vor B dran, lehrt C 300 tage an A, und 0 tage an D, * und B lehrt auch 0 tage an A. * * Deswegen darf C D nie lehren duerfen. * * -> Das ist wirr. wer hat das entworfen? * Besser waere, man macht erst vorab alle zuordnungen, und dann * die Talentaenderung (enno). */ nteaching = _max(0, nteaching - student->number * 30); } return n; }