File: exp.c

package info (click to toggle)
crossfire 1.75.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,168 kB
  • sloc: ansic: 83,169; sh: 4,659; perl: 1,736; lex: 1,443; makefile: 1,199; python: 43
file content (264 lines) | stat: -rw-r--r-- 8,653 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/*
 * Crossfire -- cooperative multi-player graphical RPG and adventure game
 *
 * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
 * Copyright (c) 1992 Frank Tore Johansen
 *
 * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
 * welcome to redistribute it under certain conditions. For details, please
 * see COPYING and LICENSE.
 *
 * The authors can be reached via e-mail at <crossfire@metalforge.org>.
 */

/**
 * @file exp.c
 * Experience management. reading data from files and such.
 */

#include "global.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int64_t *levels; /**< Number of levels for which we have experience. */

static const float exp_att_mult[NROFATTACKS] = {
    0.0,    /* AT_PHYSICAL */
    0.0,    /* AT_MAGIC */
    0.0,    /* AT_FIRE */
    0.0,    /* AT_ELECTRICITY */
    0.0,    /* AT_COLD */
    0.0,    /* AT_CONFUSION */
    0.4,    /* AT_ACID */
    1.5,    /* AT_DRAIN */
    0.0,    /* AT_WEAPONMAGIC */
    0.1,    /* AT_GHOSTHIT */
    0.3,    /* AT_POISON */
    0.1,    /* AT_SLOW */
    0.3,    /* AT_PARALYZE */
    0.0,    /* AT_TURN_UNDEAD */
    0.0,    /* AT_FEAR */
    0.0,    /* AT_CANCELLATION */
    0.0,    /* AT_DEPLETE */
    0.0,    /* AT_DEATH */
    0.0,    /* AT_CHAOS */
    0.0,    /* AT_COUNTERSPELL */
    0.5,    /* AT_GODPOWER */
    0.1,    /* AT_HOLYWORD */
    0.2,    /* AT_BLIND */
    0.0,    /* AT_INTERNAL */
    0.5,    /* AT_LIFE_STEALING */
    0.2,    /* AT_DISEASE */
};

static const float exp_prot_mult[NROFATTACKS] = {
    0.4,    /* AT_PHYSICAL */
    0.5,    /* AT_MAGIC */
    0.1,    /* AT_FIRE */
    0.1,    /* AT_ELECTRICITY */
    0.1,    /* AT_COLD */
    0.1,    /* AT_CONFUSION */
    0.1,    /* AT_ACID */
    0.1,    /* AT_DRAIN */
    0.1,    /* AT_WEAPONMAGIC */
    0.1,    /* AT_GHOSTHIT */
    0.1,    /* AT_POISON */
    0.0,    /* AT_SLOW */
    0.1,    /* AT_PARALYZE */
    0.1,    /* AT_TURN_UNDEAD */
    0.1,    /* AT_FEAR */
    0.0,    /* AT_CANCELLATION */
    0.0,    /* AT_DEPLETE */
    0.0,    /* AT_DEATH */
    0.0,    /* AT_CHAOS */
    0.0,    /* AT_COUNTERSPELL */
    0.0,    /* AT_GODPOWER */
    0.1,    /* AT_HOLYWORD */
    0.0,    /* AT_BLIND */
    0.0,    /* AT_INTERNAL */
    0.0,    /* AT_LIFE_STEALING */
    0.1,    /* AT_DISEASE */
};

/**
 * Alternative way to calculate experience based
 * on the ability of a monster.
 *
 * It's far from perfect, and doesn't consider everything which
 * can be considered, thus it's only used in debugging.
 * this is only used with one of the dumpflags,
 * and not anyplace in the code.
 *
 * @param ob
 * object for which to return experience
 * @return
 * experience computed from object's properties.
 */
int64_t new_exp(const object *ob) {
    double att_mult, prot_mult, spec_mult;
    double exp;
    int i;
    long mask = 1;

    att_mult = prot_mult = spec_mult = 1.0;
    for (i = 0; i < NROFATTACKS; i++) {
        mask = 1<<i;
        att_mult += (exp_att_mult[i]*((ob->attacktype&mask) != FALSE));
        /* We multiply & then divide to prevent roundoffs on the floats.
         * the doubling is to take into account the table and resistances
         * are lower than they once were.
         */
        /* prot_mult should increase by fairly minor amounts -
         * for example, if a creature has resist physical 30,
         * and exp mult on that is 0.4, then prot_mult should really
         * go up by 1.2 - still a considerable increase.
         */
        prot_mult += (exp_prot_mult[i]*ob->resist[i])/10.0;
    }

    if (prot_mult < 0)
        prot_mult = 1;

    spec_mult += (0.3*(QUERY_FLAG(ob, FLAG_SEE_INVISIBLE) != FALSE))+
                 (0.5*(QUERY_FLAG(ob, FLAG_SPLITTING) != FALSE))+
                 (0.3*(QUERY_FLAG(ob, FLAG_HITBACK) != FALSE))+
                 (0.1*(QUERY_FLAG(ob, FLAG_REFL_MISSILE) != FALSE))+
                 (0.3*(QUERY_FLAG(ob, FLAG_REFL_SPELL) != FALSE))+
                 (1.0*(QUERY_FLAG(ob, FLAG_NO_MAGIC) != FALSE))+
                 (0.1*(QUERY_FLAG(ob, FLAG_USE_SCROLL) != FALSE))+
                 (0.2*(QUERY_FLAG(ob, FLAG_USE_RANGE) != FALSE))+
                 (0.1*(QUERY_FLAG(ob, FLAG_USE_BOW) != FALSE));

    exp = MAX(ob->stats.maxhp, 5);
    exp *= (QUERY_FLAG(ob, FLAG_CAST_SPELL) && has_ability(ob)) ? (40+MIN(ob->stats.maxsp, 80))/40 : 1;
    exp *= (80.0/(70.0+ob->stats.wc))*(80.0/(70.0+ob->stats.ac))*(50.0+ob->stats.dam)/50.0;
    exp *= att_mult*prot_mult*spec_mult;
/*  exp *= 2.0/(2.0-(MIN(FABS(ob->speed), 0.95)));*/
    exp *= 2.0/(2.0-FABS(ob->speed));
    exp *= (20.0+ob->stats.Con)/20.0;
    if (QUERY_FLAG(ob, FLAG_STAND_STILL))
        exp /= 2;

    return (int64_t)exp;
}

/**
 * Checks whether object has innate abilities (spell/spellbook in inventory).
 * @return
 * 1 if monster has any innate abilities, 0 else
 */
int has_ability(const object *ob) {
    return object_find_by_type(ob, SPELL) != NULL || object_find_by_type(ob, SPELLBOOK) != NULL;
}

/**
 * This loads the experience table from the exp_table
 * file.  This tends to exit on any errors, since it
 * populates the table as it goes along, so if there
 * are errors, the table is likely in an inconsistent
 * state.
 *
 * @note
 * will call exit() if file is invalid or not found.
 */
void init_experience(void) {
    char buf[MAX_BUF], *cp;
    int lastlevel = 0;
    int64_t lastexp = -1, tmpexp;
    FILE *fp;

    snprintf(buf, sizeof(buf), "%s/exp_table", settings.confdir);
    if ((fp = fopen(buf, "r")) == NULL) {
        LOG(llevError, "Fatal error: could not open experience table (%s)\n", buf);
        exit(1);
    }
    while (fgets(buf, MAX_BUF-1, fp) != NULL) {
        if (buf[0] == '#')
            continue;

        /* eliminate newline */
        if ((cp = strrchr(buf, '\n')) != NULL)
            *cp = '\0';

        /* Skip over empty lines and whitespace */
        cp = buf;
        while (isspace(*cp) && *cp != 0)
            cp++;
        if ( *cp == 0 ) continue;

        /* Set max_level */
        if (!strncasecmp(cp, "max_level", 9)) {
            if (settings.max_level) {
                LOG(llevDebug, "Got more than one max_level value from exp_table file?\n");
                free(levels);
            }
            settings.max_level = atoi(cp+9);
            if (!settings.max_level) {
                LOG(llevDebug, "Got invalid max_level from exp_table file? %s\n", buf);
            } else {
                levels = calloc(settings.max_level+1, sizeof(int64_t));
            }
        }
        while (isdigit(*cp) && *cp != 0) {
            if (!settings.max_level) {
                LOG(llevError, "max_level is not set in exp_table file.  Did you remember to update it?\n");
                exit(1);
            }

            tmpexp = atoll(cp);
            /* Do some sanity checking - if value is bogus, just exit because
             * the table otherwise is probably in an inconsistent state
             */
            if (tmpexp <= lastexp) {
                LOG(llevError, "Experience for level %d is lower than previous level (%"FMT64" <= %"FMT64")\n", lastlevel+1, tmpexp, lastexp);
                exit(1);
            }
            lastlevel++;
            if (lastlevel > settings.max_level) {
                LOG(llevError, "Too many levels specified in table (%d > %d)\n", lastlevel, settings.max_level);
                exit(1);
            }
            levels[lastlevel] = tmpexp;
            lastexp = tmpexp;
            /* First, skip over the number we just processed. Then skip over
             * any spaces, commas, etc.
             */
            while (isdigit(*cp) && *cp != 0)
                cp++;
            while (!isdigit(*cp) && *cp != 0)
                cp++;
        }
    }
    fclose(fp);
    if (settings.max_level == 0 || lastlevel != settings.max_level) {
        LOG(llevError, "Fatal: exp_table does not have any level definition or not %d as defined, found %d.\n", settings.max_level, lastlevel);
        exit(1);
    }
    if (lastlevel != settings.max_level && lastlevel != 0) {
        LOG(llevError, "Warning: exp_table does not have %d entries (%d)\n", settings.max_level, lastlevel);
        exit(1);
    }
}

/**
 * Dump the experience table, then calls exit() - useful in terms of debugging to make sure the
 * format of the exp_table is correct.
 */
void dump_experience(void) {
    int i;

    for (i = 1; i <= settings.max_level; i++) {
        fprintf(logfile, "%4d %20"FMT64"\n", i, levels[i]);
    }
    exit(0);
}

/**
 * Frees experience-related memory.
 */
void free_experience(void) {
    FREE_AND_CLEAR(levels);
}