1 /**
2 * \file player-history.c
3 * \brief Character auto-history creation, management, and display
4 *
5 * Copyright (c) 2007 J.D. White
6 *
7 * This work is free software; you can redistribute it and/or modify it
8 * under the terms of either:
9 *
10 * a) the GNU General Public License as published by the Free Software
11 * Foundation, version 2, or
12 *
13 * b) the "Angband licence":
14 * This software may be copied and distributed for educational, research,
15 * and not for profit purposes provided that this copyright and statement
16 * are included in all such copies. Other copyrights may also apply.
17 */
18 #include "angband.h"
19 #include "cave.h"
20 #include "game-world.h"
21 #include "obj-desc.h"
22 #include "obj-make.h"
23 #include "obj-pile.h"
24 #include "obj-util.h"
25 #include "player-history.h"
26
27 /**
28 * Memory allocation constants.
29 */
30 #define HISTORY_LEN_INIT 20
31 #define HISTORY_LEN_INCR 20
32
33 /**
34 * Initialise an empty history list.
35 */
history_init(struct player_history * h)36 static void history_init(struct player_history *h)
37 {
38 h->next = 0;
39 h->length = HISTORY_LEN_INIT;
40 h->entries = mem_zalloc(h->length * sizeof(*h->entries));
41 }
42
43 /**
44 * Increase the history array size.
45 */
history_realloc(struct player_history * h)46 static void history_realloc(struct player_history *h)
47 {
48 h->length += HISTORY_LEN_INCR;
49 h->entries = mem_realloc(h->entries,
50 h->length * sizeof *h->entries);
51 }
52
53 /**
54 * Clear any existing history.
55 */
history_clear(struct player * p)56 void history_clear(struct player *p)
57 {
58 struct player_history *h = &p->hist;
59
60 if (h->entries) {
61 mem_free(h->entries);
62 h->entries = NULL;
63 }
64
65 h->next = 0;
66 h->length = 0;
67 }
68
69 /**
70 * Add an entry with text `text` to the history list, with type `type`
71 * ("HIST_xxx" in player-history.h), and artifact number `id` (0 for
72 * everything else).
73 *
74 * Return true on success.
75 */
history_add_full(struct player * p,bitflag * type,int aidx,int dlev,int clev,int turnno,const char * text)76 bool history_add_full(struct player *p,
77 bitflag *type,
78 int aidx,
79 int dlev,
80 int clev,
81 int turnno,
82 const char *text)
83 {
84 struct player_history *h = &p->hist;
85
86 /* Allocate or expand the history list if needed */
87 if (!h->entries)
88 history_init(h);
89 else if (h->next == h->length)
90 history_realloc(h);
91
92 /* Add entry */
93 hist_copy(h->entries[h->next].type, type);
94 h->entries[h->next].dlev = dlev;
95 h->entries[h->next].clev = clev;
96 h->entries[h->next].a_idx = aidx;
97 h->entries[h->next].turn = turnno;
98 my_strcpy(h->entries[h->next].event,
99 text,
100 sizeof(h->entries[h->next].event));
101
102 h->next++;
103
104 return true;
105 }
106
107 /**
108 * Add an entry to the history ledger with specified bitflags.
109 */
history_add_with_flags(struct player * p,const char * text,bitflag flags[HIST_SIZE],const struct artifact * artifact)110 static bool history_add_with_flags(struct player *p,
111 const char *text,
112 bitflag flags[HIST_SIZE],
113 const struct artifact *artifact)
114 {
115 return history_add_full(p,
116 flags,
117 artifact ? artifact->aidx : 0,
118 p->depth,
119 p->lev,
120 p->total_energy / 100,
121 text);
122 }
123
124 /**
125 * Adds an entry to the history ledger.
126 */
history_add(struct player * p,const char * text,int type)127 bool history_add(struct player *p, const char *text, int type)
128 {
129 bitflag flags[HIST_SIZE];
130 hist_wipe(flags);
131 hist_on(flags, type);
132
133 return history_add_with_flags(p, text, flags, NULL);
134 }
135
136 /**
137 * Returns true if the artifact is KNOWN in the history log.
138 */
history_is_artifact_known(struct player * p,const struct artifact * artifact)139 bool history_is_artifact_known(struct player *p, const struct artifact *artifact)
140 {
141 struct player_history *h = &p->hist;
142
143 size_t i = h->next;
144 assert(artifact);
145
146 while (i--) {
147 if (hist_has(h->entries[i].type, HIST_ARTIFACT_KNOWN) &&
148 h->entries[i].a_idx == artifact->aidx)
149 return true;
150 }
151
152 return false;
153 }
154
155 /**
156 * Mark artifact as known.
157 */
history_mark_artifact_known(struct player_history * h,const struct artifact * artifact)158 static bool history_mark_artifact_known(struct player_history *h,
159 const struct artifact *artifact)
160 {
161 assert(artifact);
162
163 size_t i = h->next;
164 while (i--) {
165 if (h->entries[i].a_idx == artifact->aidx) {
166 hist_off(h->entries[i].type, HIST_ARTIFACT_UNKNOWN);
167 hist_on(h->entries[i].type, HIST_ARTIFACT_KNOWN);
168 return true;
169 }
170 }
171
172 return false;
173 }
174
175 /**
176 * Mark artifact as lost.
177 */
history_mark_artifact_lost(struct player_history * h,const struct artifact * artifact)178 static bool history_mark_artifact_lost(struct player_history *h,
179 const struct artifact *artifact)
180 {
181 assert(artifact);
182
183 size_t i = h->next;
184 while (i--) {
185 if (h->entries[i].a_idx == artifact->aidx) {
186 hist_on(h->entries[i].type, HIST_ARTIFACT_LOST);
187 return true;
188 }
189 }
190
191 return false;
192 }
193
194 /**
195 * Utility function for history_add_artifact(): get artifact name
196 */
get_artifact_name(char * buf,size_t len,const struct artifact * artifact)197 static void get_artifact_name(char *buf, size_t len, const struct artifact *artifact)
198 {
199 struct object body = OBJECT_NULL;
200 struct object known_body = OBJECT_NULL;
201
202 struct object *fake = &body;
203 struct object *known_obj = &known_body;
204
205 /* Make fake artifact for description purposes */
206 make_fake_artifact(fake, artifact);
207
208 fake->known = known_obj;
209 object_copy(known_obj, fake);
210 object_desc(buf, len, fake, ODESC_PREFIX | ODESC_BASE | ODESC_SPOIL);
211
212 object_wipe(known_obj);
213 object_wipe(fake);
214 }
215
216 /**
217 * Add an artifact to the history log.
218 *
219 * Call this to add an artifact to the history list or make the history
220 * entry visible.
221 */
history_find_artifact(struct player * p,const struct artifact * artifact)222 void history_find_artifact(struct player *p, const struct artifact *artifact)
223 {
224 assert(artifact != NULL);
225
226 /* Try revealing any existing artifact, otherwise log it */
227 if (!history_mark_artifact_known(&p->hist, artifact)) {
228 char o_name[80];
229 char text[80];
230
231 get_artifact_name(o_name, sizeof(o_name), artifact);
232 strnfmt(text, sizeof(text), "Found %s", o_name);
233
234 bitflag flags[HIST_SIZE];
235 hist_wipe(flags);
236 hist_on(flags, HIST_ARTIFACT_KNOWN);
237
238 history_add_with_flags(p, text, flags, artifact);
239 }
240 }
241
242 /**
243 * Mark artifact number `id` as lost forever.
244 */
history_lose_artifact(struct player * p,const struct artifact * artifact)245 void history_lose_artifact(struct player *p, const struct artifact *artifact)
246 {
247 assert(artifact != NULL);
248
249 /* Try to mark it as lost if it's already in history */
250 if (!history_mark_artifact_lost(&p->hist, artifact)) {
251 /* Otherwise add a new entry */
252 char o_name[80];
253 char text[80];
254
255 get_artifact_name(o_name, sizeof(o_name), artifact);
256 strnfmt(text, sizeof(text), "Missed %s", o_name);
257
258 bitflag flags[HIST_SIZE];
259 hist_wipe(flags);
260 hist_on(flags, HIST_ARTIFACT_UNKNOWN);
261 hist_on(flags, HIST_ARTIFACT_LOST);
262
263 history_add_with_flags(p, text, flags, artifact);
264 }
265 }
266
267 /**
268 * Convert all ARTIFACT_UNKNOWN history items to HIST_ARTIFACT_KNOWN.
269 * Use only after player retirement/death for the final character dump.
270 */
history_unmask_unknown(struct player * p)271 void history_unmask_unknown(struct player *p)
272 {
273 struct player_history *h = &p->hist;
274
275 size_t i = h->next;
276 while (i--) {
277 if (hist_has(h->entries[i].type, HIST_ARTIFACT_UNKNOWN)) {
278 hist_off(h->entries[i].type, HIST_ARTIFACT_UNKNOWN);
279 hist_on(h->entries[i].type, HIST_ARTIFACT_KNOWN);
280 }
281 }
282 }
283
284 /**
285 * Present a copy of the history fot UI use
286 */
history_get_list(struct player * p,struct history_info ** list)287 size_t history_get_list(struct player *p, struct history_info **list)
288 {
289 struct player_history *h = &p->hist;
290
291 *list = h->entries;
292 return h->next;
293 }
294