forked from angband/angband
-
Notifications
You must be signed in to change notification settings - Fork 0
/
history.c
477 lines (376 loc) · 11.3 KB
/
history.c
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
/*
* File: history.c
* Purpose: Character auto-history creation, management, and display
*
* Copyright (c) 2007 J.D. White
*
* This work is free software; you can redistribute it and/or modify it
* under the terms of either:
*
* a) the GNU General Public License as published by the Free Software
* Foundation, version 2, or
*
* b) the "Angband licence":
* This software may be copied and distributed for educational, research,
* and not for profit purposes provided that this copyright and statement
* are included in all such copies. Other copyrights may also apply.
*/
#include "angband.h"
#include "history.h"
/*
* Number of slots available at birth in the player history list. Defaults to
* 10 and will expand automatically as new history entries are added, up the
* the maximum defined value.
*/
#define HISTORY_BIRTH_SIZE 10
#define HISTORY_MAX 5000
/* The historical list for the character */
history_info *history_list;
/* Index of first writable entry */
static size_t history_ctr;
/* Current size of history list */
static size_t history_size;
#define LIMITLOW(a, b) if (a < b) a = b;
#define LIMITHI(a, b) if (a > b) a = b;
/*
* Initialise an empty history list.
*/
static void history_init(size_t entries)
{
history_ctr = 0;
history_size = entries;
history_list = C_ZNEW(history_size, history_info);
}
/*
* Clear any existing history.
*/
void history_clear(void)
{
if (!history_list) return;
FREE(history_list);
history_ctr = 0;
history_size = 0;
}
/*
* Set the number of history items.
*/
static bool history_set_num(size_t num)
{
history_info *new_list;
if (num > HISTORY_MAX)
num = HISTORY_MAX;
if (num < history_size) return FALSE;
if (num == history_size) return FALSE;
/* Allocate new memory, copy across */
/* XXX Should use mem_realloc() */
new_list = C_ZNEW(num, history_info);
C_COPY(new_list, history_list, history_ctr, history_info);
FREE(history_list);
history_list = new_list;
history_size = num;
return TRUE;
}
/*
* Return the number of history entries.
*/
size_t history_get_num(void)
{
return history_ctr;
}
/*
* Mark artifact number `id` as known.
*/
static bool history_know_artifact(struct artifact *artifact)
{
size_t i = history_ctr;
assert(artifact);
while (i--) {
if (history_list[i].a_idx == artifact->aidx) {
history_list[i].type = HISTORY_ARTIFACT_KNOWN;
return TRUE;
}
}
return FALSE;
}
/*
* Mark artifact number `id` as lost forever, either due to leaving it on a
* level, or due to a store purging its inventory after the player sold it.
*/
bool history_lose_artifact(struct artifact *artifact)
{
size_t i = history_ctr;
assert(artifact);
while (i--) {
if (history_list[i].a_idx == artifact->aidx) {
history_list[i].type |= HISTORY_ARTIFACT_LOST;
return TRUE;
}
}
/* If we lost an artifact that didn't previously have a history, then we missed it */
history_add_artifact(artifact, FALSE, FALSE);
return FALSE;
}
/*
* Add an entry with text `event` to the history list, with type `type`
* ("HISTORY_xxx" in defines.h), and artifact number `id` (0 for everything
* else).
*
* Return TRUE on success.
*/
bool history_add_full(u16b type, struct artifact *artifact, s16b dlev,
s16b clev, s32b turn, const char *text)
{
/* Allocate the history list if needed */
if (!history_list)
history_init(HISTORY_BIRTH_SIZE);
/* Expand the history list if appropriate */
else if ((history_ctr == history_size) && !history_set_num(history_size + 10))
return FALSE;
/* History list exists and is not full. Add an entry at the current counter location. */
history_list[history_ctr].type = type;
history_list[history_ctr].dlev = dlev;
history_list[history_ctr].clev = clev;
history_list[history_ctr].a_idx = artifact ? artifact->aidx : 0;
history_list[history_ctr].turn = turn;
my_strcpy(history_list[history_ctr].event,
text, sizeof(history_list[history_ctr].event));
history_ctr++;
return TRUE;
}
/*
* Add an entry with text `event` to the history list, with type `type`
* ("HISTORY_xxx" in defines.h), and artifact number `id` (0 for everything
* else).
*
* Returne TRUE on success.
*/
bool history_add(const char *event, u16b type, struct artifact *artifact)
{
return history_add_full(type, artifact, p_ptr->depth, p_ptr->lev, turn, event);
}
/*
* Returns TRUE if the artifact denoted by a_idx is KNOWN in the history log.
*/
bool history_is_artifact_known(struct artifact *artifact)
{
size_t i = history_ctr;
assert(artifact);
while (i--) {
if (history_list[i].type & HISTORY_ARTIFACT_KNOWN &&
history_list[i].a_idx == artifact->aidx)
return TRUE;
}
return FALSE;
}
/*
* Returns TRUE if the artifact denoted by a_idx is an active entry in
* the history log (i.e. is not marked HISTORY_ARTIFACT_LOST). This permits
* proper handling of the case where the player loses an artifact but (in
* preserve mode) finds it again later.
*/
static bool history_is_artifact_logged(struct artifact *artifact)
{
size_t i = history_ctr;
assert(artifact);
while (i--) {
/* Don't count ARTIFACT_LOST entries; then we can handle
* re-finding previously lost artifacts in preserve mode */
if (history_list[i].type & HISTORY_ARTIFACT_LOST)
continue;
if (history_list[i].a_idx == artifact->aidx)
return TRUE;
}
return FALSE;
}
/*
* Adding artifacts to the history list is trickier than other operations.
* This is a wrapper function that gets some of the logic out of places
* where it really doesn't belong. Call this to add an artifact to the history
* list or make the history entry visible--history_add_artifact will make that
* determination depending on what object_is_known returns for the artifact.
*/
bool history_add_artifact(struct artifact *artifact, bool known, bool found)
{
object_type object_type_body;
object_type *o_ptr = &object_type_body;
char o_name[80];
char buf[80];
u16b type;
assert(artifact);
/* Make fake artifact for description purposes */
object_wipe(o_ptr);
make_fake_artifact(o_ptr, artifact);
object_desc(o_name, sizeof(o_name), o_ptr,
ODESC_PREFIX | ODESC_BASE | ODESC_SPOIL);
strnfmt(buf, sizeof(buf), (found)?"Found %s":"Missed %s", o_name);
/* Known objects gets different treatment */
if (known) {
/* Try revealing any existing artifact, otherwise log it */
if (history_is_artifact_logged(artifact))
history_know_artifact(artifact);
else
history_add(buf, HISTORY_ARTIFACT_KNOWN, artifact);
} else {
if (!history_is_artifact_logged(artifact)) {
type = HISTORY_ARTIFACT_UNKNOWN |
(found ? 0 : HISTORY_ARTIFACT_LOST);
history_add(buf, type, artifact);
} else {
return FALSE;
}
}
return TRUE;
}
/*
* Convert all ARTIFACT_UNKNOWN history items to HISTORY_ARTIFACT_KNOWN.
* Use only after player retirement/death for the final character dump.
*/
void history_unmask_unknown(void)
{
size_t i = history_ctr;
while (i--)
{
if (history_list[i].type & HISTORY_ARTIFACT_UNKNOWN)
{
history_list[i].type &= ~(HISTORY_ARTIFACT_UNKNOWN);
history_list[i].type |= HISTORY_ARTIFACT_KNOWN;
}
}
}
/*
* Used to determine whether the history entry is visible in the listing or not.
* Returns TRUE if the item is masked -- that is, if it is invisible
*
* All artifacts are now sensed on pickup, so nothing is now invisible. The
* KNOWN / UNKNOWN distinction is if we had fully identified it or not
*/
static bool history_masked(size_t i)
{
return FALSE;
}
/*
* Finds the index of the last printable (non-masked) item in the history list.
*/
static size_t last_printable_item(void)
{
size_t i = history_ctr;
while (i--)
{
if (!history_masked(i))
break;
}
return i;
}
static void print_history_header(void)
{
char buf[80];
/* Print the header (character name and title) */
strnfmt(buf, sizeof(buf), "%s the %s %s",
op_ptr->full_name,
p_ptr->race->name,
p_ptr->class->name);
c_put_str(TERM_WHITE, buf, 0, 0);
c_put_str(TERM_WHITE, "============================================================", 1, 0);
c_put_str(TERM_WHITE, " CHAR. ", 2, 0);
c_put_str(TERM_WHITE, "| TURN | DEPTH |LEVEL| EVENT", 3, 0);
c_put_str(TERM_WHITE, "============================================================", 4, 0);
}
/* Handles all of the display functionality for the history list. */
void history_display(void)
{
int row, wid, hgt, page_size;
char buf[90];
static size_t first_item = 0;
size_t max_item = last_printable_item();
size_t i;
Term_get_size(&wid, &hgt);
/* Six lines provide space for the header and footer */
page_size = hgt - 6;
screen_save();
while (1)
{
struct keypress ch;
Term_clear();
/* Print everything to screen */
print_history_header();
row = 0;
for (i = first_item; row <= page_size && i < history_ctr; i++)
{
/* Skip messages about artifacts not yet IDed. */
if (history_masked(i))
continue;
strnfmt(buf, sizeof(buf), "%10d%7d\'%5d %s",
history_list[i].turn,
history_list[i].dlev * 50,
history_list[i].clev,
history_list[i].event);
if (history_list[i].type & HISTORY_ARTIFACT_LOST)
my_strcat(buf, " (LOST)", sizeof(buf));
/* Size of header = 5 lines */
prt(buf, row + 5, 0);
row++;
}
prt("[Arrow keys scroll, p for previous page, n for next page, ESC to exit.]", hgt - 1, 0);
ch = inkey();
/* XXXmacro we should have a generic "key -> scroll" function */
if (ch.code == 'n')
{
size_t scroll_to = first_item + page_size;
while (history_masked(scroll_to) && scroll_to < history_ctr - 1)
scroll_to++;
first_item = (scroll_to < max_item ? scroll_to : max_item);
}
else if (ch.code == 'p')
{
int scroll_to = first_item - page_size;
while (history_masked(scroll_to) && scroll_to > 0)
scroll_to--;
first_item = (scroll_to >= 0 ? scroll_to : 0);
}
else if (ch.code == ARROW_DOWN)
{
size_t scroll_to = first_item + 1;
while (history_masked(scroll_to) && scroll_to < history_ctr - 1)
scroll_to++;
first_item = (scroll_to < max_item ? scroll_to : max_item);
}
else if (ch.code == ARROW_UP)
{
int scroll_to = first_item - 1;
while (history_masked(scroll_to) && scroll_to > 0)
scroll_to--;
first_item = (scroll_to >= 0 ? scroll_to : 0);
}
else if (ch.code == ESCAPE)
break;
}
screen_load();
return;
}
/* Dump character history to a file, which we assume is already open. */
void dump_history(ang_file *file)
{
size_t i;
char buf[90];
/* We use either ascii or system-specific encoding */
int encoding = OPT(xchars_to_file) ? SYSTEM_SPECIFIC : ASCII;
file_putf(file, "============================================================\n");
file_putf(file, " CHAR.\n");
file_putf(file, "| TURN | DEPTH |LEVEL| EVENT\n");
file_putf(file, "============================================================\n");
for (i = 0; i < (last_printable_item() + 1); i++)
{
/* Skip not-yet-IDd artifacts */
if (history_masked(i)) continue;
strnfmt(buf, sizeof(buf), "%10d%7d\'%5d %s",
history_list[i].turn,
history_list[i].dlev * 50,
history_list[i].clev,
history_list[i].event);
if (history_list[i].type & HISTORY_ARTIFACT_LOST)
my_strcat(buf, " (LOST)", sizeof(buf));
x_file_putf(file, encoding, "%s", buf);
file_put(file, "\n");
}
return;
}