2010-08-08 10:06:34 +02:00
|
|
|
|
/*
|
2015-01-30 22:10:29 +01:00
|
|
|
|
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
2015-01-30 20:37:14 +01:00
|
|
|
|
Katja Zedel <katze@felidae.kn-bremen.de
|
|
|
|
|
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
|
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
#include <platform.h>
|
|
|
|
|
#include <kernel/config.h>
|
|
|
|
|
#include "alchemy.h"
|
|
|
|
|
#include "move.h"
|
2014-08-27 06:40:18 +02:00
|
|
|
|
#include "skill.h"
|
|
|
|
|
|
|
|
|
|
#include <kernel/item.h>
|
|
|
|
|
#include <kernel/faction.h>
|
|
|
|
|
#include <kernel/messages.h>
|
|
|
|
|
#include <kernel/build.h>
|
|
|
|
|
#include <kernel/region.h>
|
|
|
|
|
#include <kernel/pool.h>
|
|
|
|
|
#include <kernel/race.h>
|
|
|
|
|
#include <kernel/unit.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* util includes */
|
|
|
|
|
#include <util/attrib.h>
|
|
|
|
|
#include <util/base36.h>
|
|
|
|
|
#include <util/log.h>
|
|
|
|
|
#include <util/rand.h>
|
2013-12-31 10:06:28 +01:00
|
|
|
|
|
|
|
|
|
#include <storage.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* libc includes */
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void herbsearch(region * r, unit * u, int max)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
int herbsfound;
|
|
|
|
|
const item_type *whichherb;
|
|
|
|
|
|
|
|
|
|
if (eff_skill(u, SK_HERBALISM, r) == 0) {
|
|
|
|
|
cmistake(u, u->thisorder, 59, MSG_PRODUCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_guarded(r, u, GUARD_PRODUCE)) {
|
|
|
|
|
cmistake(u, u->thisorder, 70, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
whichherb = rherbtype(r);
|
|
|
|
|
if (whichherb == NULL) {
|
|
|
|
|
cmistake(u, u->thisorder, 108, MSG_PRODUCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (max)
|
|
|
|
|
max = _min(max, rherbs(r));
|
|
|
|
|
else
|
|
|
|
|
max = rherbs(r);
|
|
|
|
|
herbsfound = ntimespprob(eff_skill(u, SK_HERBALISM, r) * u->number,
|
|
|
|
|
(double)rherbs(r) / 100.0F, -0.01F);
|
|
|
|
|
herbsfound = _min(herbsfound, max);
|
|
|
|
|
rsetherbs(r, rherbs(r) - herbsfound);
|
|
|
|
|
|
|
|
|
|
if (herbsfound) {
|
|
|
|
|
produceexp(u, SK_HERBALISM, u->number);
|
|
|
|
|
i_change(&u->items, whichherb, herbsfound);
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("herbfound",
|
|
|
|
|
"unit region amount herb", u, r, herbsfound, whichherb->rtype));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("researchherb_none",
|
|
|
|
|
"unit region", u, u->region));
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int begin_potion(unit * u, const potion_type * ptype, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
static int rule_multipotion = -1;
|
|
|
|
|
assert(ptype != NULL);
|
|
|
|
|
|
|
|
|
|
if (rule_multipotion < 0) {
|
|
|
|
|
/* should we allow multiple different potions to be used the same turn? */
|
|
|
|
|
rule_multipotion =
|
|
|
|
|
get_param_int(global.parameters, "rules.magic.multipotion", 0);
|
|
|
|
|
}
|
|
|
|
|
if (!rule_multipotion) {
|
|
|
|
|
const potion_type *use = ugetpotionuse(u);
|
|
|
|
|
if (use != NULL && use != ptype) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_message("errusingpotion", "unit using command",
|
|
|
|
|
u, use->itype->rtype, ord));
|
|
|
|
|
return ECUSTOM;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2015-01-30 20:37:14 +01:00
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void end_potion(unit * u, const potion_type * ptype, int amount)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
use_pooled(u, ptype->itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
|
|
|
|
|
amount);
|
|
|
|
|
usetpotionuse(u, ptype);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2015-01-30 20:37:14 +01:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("usepotion",
|
|
|
|
|
"unit potion", u, ptype->itype->rtype));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-06-04 20:19:44 +02:00
|
|
|
|
static int do_potion(unit * u, region *r, const potion_type * ptype, int amount)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
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);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2015-01-30 20:37:14 +01:00
|
|
|
|
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;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2015-01-30 20:37:14 +01:00
|
|
|
|
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;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2015-01-30 20:37:14 +01:00
|
|
|
|
else if (ptype == oldpotiontype[P_WAHRHEIT]) {
|
|
|
|
|
fset(u, UFL_DISBELIEVES);
|
|
|
|
|
amount = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (ptype == oldpotiontype[P_MACHT]) {
|
|
|
|
|
/* Verf<72>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;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
const potion_type *ptype = resource2potion(itype->rtype);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-01-30 20:37:14 +01:00
|
|
|
|
if (oldpotiontype[P_HEAL] && ptype == oldpotiontype[P_HEAL]) {
|
|
|
|
|
return EUNUSABLE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
int result = begin_potion(u, ptype, ord);
|
|
|
|
|
if (result)
|
|
|
|
|
return result;
|
|
|
|
|
amount = do_potion(u, u->region, ptype, amount);
|
|
|
|
|
end_potion(u, ptype, amount);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct potiondelay {
|
2015-01-30 20:37:14 +01:00
|
|
|
|
unit *u;
|
|
|
|
|
region *r;
|
|
|
|
|
const potion_type *ptype;
|
|
|
|
|
int amount;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} potiondelay;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void init_potiondelay(attrib * a)
|
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
a->data.v = malloc(sizeof(potiondelay));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void free_potiondelay(attrib * a)
|
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
free(a->data.v);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int age_potiondelay(attrib * a)
|
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
potiondelay *pd = (potiondelay *)a->data.v;
|
|
|
|
|
pd->amount = do_potion(pd->u, pd->r, pd->ptype, pd->amount);
|
|
|
|
|
return AT_AGE_REMOVE;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_type at_potiondelay = {
|
2015-01-30 20:37:14 +01:00
|
|
|
|
"potiondelay",
|
|
|
|
|
init_potiondelay,
|
|
|
|
|
free_potiondelay,
|
|
|
|
|
age_potiondelay, 0, 0
|
2010-08-08 10:06:34 +02:00
|
|
|
|
};
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static attrib *make_potiondelay(unit * u, const potion_type * ptype, int amount)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
attrib *a = a_new(&at_potiondelay);
|
|
|
|
|
potiondelay *pd = (potiondelay *)a->data.v;
|
|
|
|
|
pd->u = u;
|
|
|
|
|
pd->r = u->region;
|
|
|
|
|
pd->ptype = ptype;
|
|
|
|
|
pd->amount = amount;
|
|
|
|
|
return a;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2011-03-07 08:02:35 +01:00
|
|
|
|
use_potion_delayed(unit * u, const item_type * itype, int amount,
|
2015-01-30 20:37:14 +01:00
|
|
|
|
struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
const potion_type *ptype = resource2potion(itype->rtype);
|
|
|
|
|
int result = begin_potion(u, ptype, ord);
|
|
|
|
|
if (result)
|
|
|
|
|
return result;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-01-30 20:37:14 +01:00
|
|
|
|
a_add(&u->attribs, make_potiondelay(u, ptype, amount));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-01-30 20:37:14 +01:00
|
|
|
|
end_potion(u, ptype, amount);
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************/
|
|
|
|
|
/* at_effect */
|
|
|
|
|
/*****************/
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void a_initeffect(attrib * a)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
a->data.v = calloc(sizeof(effect_data), 1);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void a_finalizeeffect(attrib * a)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
free(a->data.v);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2011-03-07 08:02:35 +01:00
|
|
|
|
a_writeeffect(const attrib * a, const void *owner, struct storage *store)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
effect_data *edata = (effect_data *)a->data.v;
|
|
|
|
|
WRITE_TOK(store, resourcename(edata->type->itype->rtype, 0));
|
|
|
|
|
WRITE_INT(store, edata->value);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int a_readeffect(attrib * a, void *owner, struct storage *store)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
int power;
|
|
|
|
|
const resource_type *rtype;
|
|
|
|
|
effect_data *edata = (effect_data *)a->data.v;
|
|
|
|
|
char zText[32];
|
|
|
|
|
|
|
|
|
|
READ_TOK(store, zText, sizeof(zText));
|
|
|
|
|
rtype = rt_find(zText);
|
|
|
|
|
|
|
|
|
|
READ_INT(store, &power);
|
|
|
|
|
if (rtype == NULL || rtype->ptype == NULL || power <= 0) {
|
|
|
|
|
return AT_READ_FAIL;
|
|
|
|
|
}
|
|
|
|
|
edata->type = rtype->ptype;
|
|
|
|
|
edata->value = power;
|
|
|
|
|
return AT_READ_OK;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_type at_effect = {
|
2015-01-30 20:37:14 +01:00
|
|
|
|
"effect",
|
|
|
|
|
a_initeffect,
|
|
|
|
|
a_finalizeeffect,
|
|
|
|
|
DEFAULT_AGE,
|
|
|
|
|
a_writeeffect,
|
|
|
|
|
a_readeffect,
|
2010-08-08 10:06:34 +02:00
|
|
|
|
};
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int get_effect(const unit * u, const potion_type * effect)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
const attrib *a;
|
|
|
|
|
for (a = a_find(u->attribs, &at_effect); a != NULL && a->type == &at_effect;
|
|
|
|
|
a = a->next) {
|
|
|
|
|
const effect_data *data = (const effect_data *)a->data.v;
|
|
|
|
|
if (data->type == effect)
|
|
|
|
|
return data->value;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int change_effect(unit * u, const potion_type * effect, int delta)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-01-30 20:37:14 +01:00
|
|
|
|
if (delta != 0) {
|
|
|
|
|
attrib *a = a_find(u->attribs, &at_effect);
|
|
|
|
|
effect_data *data = NULL;
|
|
|
|
|
|
|
|
|
|
while (a && a->type == &at_effect) {
|
|
|
|
|
data = (effect_data *)a->data.v;
|
|
|
|
|
if (data->type == effect) {
|
|
|
|
|
if (data->value + delta == 0) {
|
|
|
|
|
a_remove(&u->attribs, a);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
data->value += delta;
|
|
|
|
|
return data->value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
a = a->next;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2015-01-30 20:37:14 +01:00
|
|
|
|
a = a_add(&u->attribs, a_new(&at_effect));
|
|
|
|
|
data = (effect_data *)a->data.v;
|
|
|
|
|
data->type = effect;
|
|
|
|
|
data->value = delta;
|
|
|
|
|
return data->value;
|
|
|
|
|
}
|
|
|
|
|
log_error("change effect with delta==0 for unit %s\n", itoa36(u->no));
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|