2010-08-08 10:06:34 +02:00
|
|
|
#include <platform.h>
|
|
|
|
#include <kernel/config.h>
|
|
|
|
#include "alchemy.h"
|
2016-10-26 11:35:20 +02:00
|
|
|
#include "guard.h"
|
2014-08-27 06:40:18 +02:00
|
|
|
#include "skill.h"
|
2016-03-08 14:09:22 +01:00
|
|
|
#include "study.h"
|
2014-08-27 06:40:18 +02:00
|
|
|
|
|
|
|
#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 */
|
2018-09-29 11:37:17 +02:00
|
|
|
#include <kernel/attrib.h>
|
2018-09-29 13:21:46 +02:00
|
|
|
#include <kernel/gamedata.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
#include <util/base36.h>
|
|
|
|
#include <util/log.h>
|
2017-12-29 06:13:28 +01:00
|
|
|
#include <util/macros.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
#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>
|
|
|
|
|
2018-01-12 09:17:01 +01:00
|
|
|
typedef struct potion_type {
|
|
|
|
struct potion_type *next;
|
|
|
|
const struct item_type *itype;
|
|
|
|
int level;
|
|
|
|
} potion_type;
|
|
|
|
|
|
|
|
static potion_type *potiontypes;
|
|
|
|
|
|
|
|
static void pt_register(potion_type * ptype)
|
|
|
|
{
|
|
|
|
ptype->next = potiontypes;
|
|
|
|
potiontypes = ptype;
|
|
|
|
}
|
|
|
|
|
|
|
|
void new_potiontype(item_type * itype, int level)
|
|
|
|
{
|
|
|
|
potion_type *ptype;
|
|
|
|
|
2018-10-22 21:51:11 +02:00
|
|
|
ptype = (potion_type *)calloc(1, sizeof(potion_type));
|
2018-11-17 22:01:15 +01:00
|
|
|
assert(ptype);
|
2018-01-12 09:17:01 +01:00
|
|
|
itype->flags |= ITF_POTION;
|
|
|
|
ptype->itype = itype;
|
|
|
|
ptype->level = level;
|
|
|
|
pt_register(ptype);
|
|
|
|
}
|
|
|
|
|
|
|
|
int potion_level(const item_type *itype)
|
|
|
|
{
|
|
|
|
potion_type *ptype;
|
|
|
|
for (ptype = potiontypes; ptype; ptype = ptype->next) {
|
|
|
|
if (ptype->itype == itype) {
|
|
|
|
return ptype->level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2017-12-29 17:00:16 +01:00
|
|
|
void herbsearch(unit * u, int max_take)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2015-08-27 14:46:08 +02:00
|
|
|
region * r = u->region;
|
2015-01-30 20:37:14 +01:00
|
|
|
int herbsfound;
|
|
|
|
const item_type *whichherb;
|
2019-02-02 20:36:23 +01:00
|
|
|
int effsk = effskill(u, SK_HERBALISM, NULL);
|
2017-12-29 17:00:16 +01:00
|
|
|
int herbs = rherbs(r);
|
2015-01-30 20:37:14 +01:00
|
|
|
|
2015-08-27 14:46:08 +02:00
|
|
|
if (effsk == 0) {
|
2015-01-30 20:37:14 +01:00
|
|
|
cmistake(u, u->thisorder, 59, MSG_PRODUCE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-31 07:45:06 +01:00
|
|
|
if (is_guarded(r, u)) {
|
2015-01-30 20:37:14 +01:00
|
|
|
cmistake(u, u->thisorder, 70, MSG_EVENT);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
whichherb = rherbtype(r);
|
|
|
|
if (whichherb == NULL) {
|
|
|
|
cmistake(u, u->thisorder, 108, MSG_PRODUCE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-29 17:00:16 +01:00
|
|
|
if (max_take < herbs) {
|
|
|
|
herbs = max_take;
|
|
|
|
}
|
2015-08-27 14:46:08 +02:00
|
|
|
herbsfound = ntimespprob(effsk * u->number,
|
2015-01-30 20:37:14 +01:00
|
|
|
(double)rherbs(r) / 100.0F, -0.01F);
|
2017-12-28 18:55:45 +01:00
|
|
|
|
2017-12-29 17:00:16 +01:00
|
|
|
if (herbsfound > herbs) herbsfound = herbs;
|
2018-01-21 11:14:55 +01:00
|
|
|
rsetherbs(r, rherbs(r) - herbsfound);
|
2015-01-30 20:37:14 +01:00
|
|
|
|
|
|
|
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",
|
2015-08-27 14:46:08 +02:00
|
|
|
"unit region", u, r));
|
2015-01-30 20:37:14 +01:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-01-12 09:17:01 +01:00
|
|
|
void show_potions(faction *f, int sklevel)
|
|
|
|
{
|
|
|
|
const potion_type *ptype;
|
|
|
|
for (ptype = potiontypes; ptype; ptype = ptype->next) {
|
|
|
|
if (ptype->level > 0 && sklevel == 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-22 14:36:05 +01:00
|
|
|
static int potion_luck(unit *u, region *r, attrib_type *atype, int amount) {
|
2015-11-22 14:28:15 +01:00
|
|
|
attrib *a = (attrib *)a_find(r->attribs, atype);
|
2017-01-10 18:07:36 +01:00
|
|
|
UNUSED_ARG(u);
|
2015-11-22 14:28:15 +01:00
|
|
|
if (!a) {
|
|
|
|
a = a_add(&r->attribs, a_new(atype));
|
|
|
|
}
|
|
|
|
a->data.i += amount;
|
|
|
|
return amount;
|
|
|
|
}
|
|
|
|
|
2018-02-17 15:17:05 +01:00
|
|
|
int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-02-17 15:17:05 +01:00
|
|
|
region *r = u->region;
|
|
|
|
|
2018-02-17 10:39:23 +01:00
|
|
|
if (itype == oldpotiontype[P_PEOPLE]) {
|
2018-02-17 15:30:38 +01:00
|
|
|
amount = potion_luck(u, r, &at_peasantluck, amount);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2018-01-12 09:17:01 +01:00
|
|
|
else if (itype == oldpotiontype[P_HORSE]) {
|
2018-02-17 15:30:38 +01:00
|
|
|
amount = potion_luck(u, r, &at_horseluck, amount);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2015-01-30 20:37:14 +01:00
|
|
|
else {
|
2018-01-12 09:17:01 +01:00
|
|
|
change_effect(u, itype, 10 * amount);
|
2015-01-30 20:37:14 +01:00
|
|
|
}
|
2018-02-17 15:30:38 +01:00
|
|
|
if (amount > 0) {
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("use_item",
|
|
|
|
"unit amount item", u, amount, itype->rtype));
|
|
|
|
}
|
2015-01-30 20:37:14 +01:00
|
|
|
return amount;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************/
|
|
|
|
/* at_effect */
|
|
|
|
/*****************/
|
|
|
|
|
2018-02-09 21:20:43 +01:00
|
|
|
static void a_initeffect(variant *var)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-10-22 21:51:11 +02:00
|
|
|
var->v = calloc(1, sizeof(effect_data));
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-02-09 21:20:43 +01:00
|
|
|
a_writeeffect(const variant *var, const void *owner, struct storage *store)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-02-09 21:20:43 +01:00
|
|
|
effect_data *edata = (effect_data *)var->v;
|
2017-01-10 18:07:36 +01:00
|
|
|
UNUSED_ARG(owner);
|
2018-01-12 09:17:01 +01:00
|
|
|
WRITE_TOK(store, resourcename(edata->type->rtype, 0));
|
2015-01-30 20:37:14 +01:00
|
|
|
WRITE_INT(store, edata->value);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-09 21:20:43 +01:00
|
|
|
static int a_readeffect(variant *var, void *owner, struct gamedata *data)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2016-02-13 13:42:02 +01:00
|
|
|
struct storage *store = data->store;
|
2015-01-30 20:37:14 +01:00
|
|
|
int power;
|
|
|
|
const resource_type *rtype;
|
2018-02-09 21:20:43 +01:00
|
|
|
effect_data *edata = (effect_data *)var->v;
|
2015-01-30 20:37:14 +01:00
|
|
|
char zText[32];
|
|
|
|
|
2017-01-10 18:07:36 +01:00
|
|
|
UNUSED_ARG(owner);
|
2015-01-30 20:37:14 +01:00
|
|
|
READ_TOK(store, zText, sizeof(zText));
|
|
|
|
rtype = rt_find(zText);
|
|
|
|
|
|
|
|
READ_INT(store, &power);
|
2018-01-12 09:17:01 +01:00
|
|
|
if (rtype == NULL || rtype->itype == NULL || power <= 0) {
|
2015-01-30 20:37:14 +01:00
|
|
|
return AT_READ_FAIL;
|
|
|
|
}
|
2018-01-12 09:17:01 +01:00
|
|
|
if (data->version < NOLANDITEM_VERSION) {
|
|
|
|
if (rtype->itype == oldpotiontype[P_HEAL]) {
|
|
|
|
/* healing potions used to have long-term effects */
|
|
|
|
return AT_READ_FAIL;
|
|
|
|
}
|
2016-06-17 21:49:31 +02:00
|
|
|
}
|
2018-01-12 09:17:01 +01:00
|
|
|
edata->type = rtype->itype;
|
2015-01-30 20:37:14 +01:00
|
|
|
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,
|
2018-02-09 21:20:43 +01:00
|
|
|
a_free_voidptr,
|
2015-01-30 20:37:14 +01:00
|
|
|
DEFAULT_AGE,
|
|
|
|
a_writeeffect,
|
|
|
|
a_readeffect,
|
2010-08-08 10:06:34 +02:00
|
|
|
};
|
|
|
|
|
2018-01-12 09:17:01 +01:00
|
|
|
int get_effect(const unit * u, const item_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
|
|
|
}
|
|
|
|
|
2018-01-12 09:17:01 +01:00
|
|
|
int change_effect(unit * u, const item_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
|
|
|
}
|
2018-01-12 10:01:06 +01:00
|
|
|
|
|
|
|
bool display_potions(struct unit *u)
|
|
|
|
{
|
2019-02-02 20:36:23 +01:00
|
|
|
int skill = effskill(u, SK_ALCHEMY, NULL);
|
2018-01-12 10:01:06 +01:00
|
|
|
int c = 0;
|
|
|
|
const potion_type *ptype;
|
|
|
|
for (ptype = potiontypes; ptype != NULL; ptype = ptype->next) {
|
|
|
|
if (ptype->level * 2 <= skill) {
|
|
|
|
show_item(u, ptype->itype);
|
|
|
|
++c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (c > 0);
|
|
|
|
}
|