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