Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
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;
}
Exemplo n.º 4
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;
}
Exemplo n.º 5
0
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;
}