home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / kernel / history.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  13.0 KB  |  563 lines  |  [TEXT/R*ch]

  1. /* Management of historical information about an Xconq game.
  2.    Copyright (C) 1992, 1993, 1994, 1995 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10. extern int find_event_type PARAMS ((Obj *sym));
  11.  
  12. static void play_event_messages PARAMS ((Side *side, HistEvent *hevt));
  13.  
  14. static void rewrite_unit_references PARAMS ((int oldid, int newid));
  15. static void rewrite_unit_references_in_event PARAMS ((HistEvent *hevt, int oldid, int newid));
  16.  
  17. HevtDefn hevtdefns[] = {
  18.  
  19. #undef  DEF_HEVT
  20. #define DEF_HEVT(NAME, code, DATADESCS) { NAME, DATADESCS },
  21.  
  22. #include "history.def"
  23.  
  24.     { NULL, NULL }
  25. };
  26.  
  27. /* Head of the list of events. */
  28.  
  29. HistEvent *history;
  30.  
  31. int tmphevtdata1;
  32.  
  33. /* The list of all past unit records. */
  34.  
  35. PastUnit *past_unit_list;
  36.  
  37. PastUnit *last_past_unit;
  38.  
  39. /* The id of the next past unit to be synthesized.  -1 is reserved, so
  40.    start counting down from -2. */
  41.  
  42. int next_past_unit_id = -2;
  43.  
  44. /* Buffers for descriptions of past units. */
  45.  
  46. #define NUMPASTBUFS 3
  47.  
  48. int curpastbuf = 0;
  49.  
  50. char *pastbufs[NUMPASTBUFS] = { NULL, NULL, NULL };
  51.  
  52. /* True if events are being recorded into the history. */
  53.  
  54. static int recording_events;
  55.  
  56. /* True if statistics dump is wanted. */
  57.  
  58. int statistics_wanted = TRUE;
  59.  
  60. /* True after the statistics file has been written. */
  61.  
  62. int statistics_dumped;
  63.  
  64. void
  65. init_history()
  66. {
  67.     /* The first "event" is just a marker. */
  68.     history = create_historical_event(H_LOG_HEAD);
  69.     /* Give it an impossible date. */
  70.     history->startdate = -1;
  71.     history->next = history->prev = history;
  72.     /* Initialize the past unit record. */
  73.     past_unit_list = last_past_unit = NULL;
  74.     next_past_unit_id = -2;
  75. }
  76.  
  77. void
  78. start_history()
  79. {
  80.     /* Ignore multiple starts. */
  81.     if (recording_events)
  82.       return;
  83.     recording_events = TRUE;
  84.     record_event(H_LOG_STARTED, ALLSIDES);
  85. }
  86.  
  87. HistEvent *
  88. create_historical_event(type)
  89. HistEventType type;
  90. {
  91.     HistEvent *hevt = (HistEvent *) xmalloc(sizeof(HistEvent));
  92.  
  93.     if (type >= NUMHEVTTYPES)
  94.       run_warning("unknown hist event type %d", type);
  95.     hevt->type = type;
  96.     hevt->observers = ALLSIDES;
  97.     hevt->next = hevt->prev = NULL;
  98.     return hevt;
  99. }
  100.  
  101. HistEvent *
  102. #ifdef __STDC__
  103. record_event(HistEventType type, SideMask observers, ...)
  104. #else
  105. record_event(type, observers, d1, d2, d3, d4)
  106. HistEventType type;
  107. SideMask observers;
  108. int d1, d2, d3, d4;
  109. #endif
  110. {
  111.     int i, val;
  112.     char *descs;
  113.     HistEvent *hevt;
  114.     Side *side;
  115.  
  116.     if (!recording_events)
  117.       return NULL;
  118.     hevt = create_historical_event(type);
  119.     hevt->startdate = g_turn();
  120.     hevt->enddate = g_turn();
  121.     hevt->observers = observers;
  122.     descs = hevtdefns[type].datadescs;
  123. #ifdef __STDC__
  124.     {
  125.     va_list ap;
  126.  
  127.     va_start(ap, observers);
  128.     for (i = 0; descs[i] != '\0'; ++i) {
  129.         val = va_arg(ap, int);
  130.         hevt->data[i] = val;
  131.     }
  132.     va_end(ap);
  133.     }
  134. #else
  135.     hevt->data[0] = d1;
  136.     hevt->data[1] = d2;
  137.     hevt->data[2] = d3;
  138.     hevt->data[3] = d4;
  139. #endif
  140.     /* Check on plausibility of event data. */
  141.     for (i = 0; descs[i] != '\0'; ++i) {
  142.     val = hevt->data[i];
  143.     switch (descs[i]) {
  144.       case 'S':
  145.         if (!between(0, val, numsides))
  146.           run_warning("invalid side number %d in hist event", val);
  147.         break;
  148.       case 'U':
  149.         /* (should check unit validity?) */
  150.         break;
  151.     }
  152.     }
  153.     /* Insert the newly created event. */
  154.     hevt->next = history;
  155.     hevt->prev = history->prev;
  156.     history->prev->next = hevt;
  157.     history->prev = hevt;
  158.     Dprintf("Recorded event %s (observed by %d)\n",
  159.         hevtdefns[hevt->type].name, hevt->observers);
  160.     if (observers != NOSIDES) {
  161.     /* Let all the observers' interfaces look at this event. */
  162.     for_all_sides(side) {
  163.         if (side_in_set(side, observers)) {
  164.         update_event_display(side, hevt, TRUE);
  165.         if (g_event_messages() != lispnil) {
  166.             play_event_messages(side, hevt);
  167.         }
  168.         }
  169.     }
  170.     }
  171.     if (any_post_event_scores)
  172.       check_post_event_scores(hevt);
  173.     return hevt;
  174. }
  175.  
  176. static void
  177. play_event_messages(side, hevt)
  178. Side *side;
  179. HistEvent *hevt;
  180. {
  181.     int found = FALSE;
  182.     char *soundname;
  183.     Obj *rest, *head, *parms, *msgdesc;
  184.  
  185.     for_all_list(g_event_messages(), rest) {
  186.     head = car(rest);
  187.     if (consp(head)
  188.         && symbolp(car(head))
  189.         && hevt->type == find_event_type(car(head))) {
  190.         found = TRUE;
  191.         break;
  192.     }
  193.     if (consp(head)
  194.         && consp(car(head))
  195.         && symbolp(car(car(head)))
  196.         && hevt->type == find_event_type(car(car(head)))) {
  197.         parms = cdr(car(head));
  198.         if (parms == lispnil) {
  199.         found = TRUE;
  200.         break;
  201.         }
  202. #if 0
  203.         if (((symbolp(car(parms))
  204.            && strcmp(c_string(car(parms)),
  205.                  u_type_name(unit->type)) == 0)
  206.           || match_keyword(car(parms), K_USTAR)
  207.           || (symbolp(car(parms))
  208.               && boundp(car(parms))
  209.               && ((symbolp(symbol_value(car(parms)))
  210.                   && strcmp(c_string(symbol_value(car(parms))),
  211.                     u_type_name(unit->type)) == 0)
  212.                   || (numberp(symbol_value(car(parms)))
  213.                       && c_number(symbol_value(car(parms)))
  214.                   == unit->type)))
  215.           )) {
  216.         found = TRUE;
  217.         break;
  218.         }
  219.         /* (should be able to match on particular data also) */
  220. #endif
  221.     }
  222.     }
  223.     /* If we have a match, do something with it. */
  224.     if (found) {
  225.     msgdesc = cadr(head);
  226.     if (stringp(msgdesc)) {
  227.         notify(side, "%s", c_string(msgdesc));
  228.     } else if (consp(msgdesc)
  229.            && symbolp(car(msgdesc))
  230.            && strcmp(c_string(car(msgdesc)), "sound") == 0
  231.            && stringp(cadr(msgdesc))) {
  232.         soundname = c_string(cadr(msgdesc));
  233.         /* (should not be passing ptrs to schedule_movie) */
  234.         schedule_movie(side, movie_extra_0, soundname);
  235.         play_movies(add_side_to_set(side, NOSIDES));
  236.     } else {
  237.     }
  238.     }
  239. }
  240.  
  241. void
  242. record_unit_death(unit, reason)
  243. Unit *unit;
  244. HistEventType reason;
  245. {
  246.     enum loss_reasons lossreason;
  247.     PastUnit *pastunit;
  248.  
  249.     pastunit = create_past_unit(unit->type, 0);
  250.     pastunit->name = unit->name;
  251.     pastunit->number = unit->number;
  252.     pastunit->x = unit->x;  pastunit->y = unit->y;  pastunit->z = unit->z;
  253.     pastunit->side = unit->side;
  254.     rewrite_unit_references(unit->id, pastunit->id);
  255.     if (reason >= 0) {
  256.     switch (reason) {
  257.       case H_UNIT_GARRISONED:
  258.         record_event(reason, add_side_to_set(unit->side, NOSIDES), pastunit->id, tmphevtdata1);
  259.         break;
  260.       default:
  261.         record_event(reason, add_side_to_set(unit->side, NOSIDES), pastunit->id);
  262.         break;
  263.     }
  264.     }
  265.     if (unit->side) {
  266.     switch (reason) {
  267.       case H_UNIT_KILLED:
  268.       case H_UNIT_WRECKED:
  269.         lossreason = combat_loss;
  270.         break;
  271.       case H_UNIT_STARVED:
  272.         lossreason = starvation_loss;
  273.         break;
  274.       case H_UNIT_DIED_IN_ACCIDENT:
  275.       case H_UNIT_WRECKED_IN_ACCIDENT:
  276.       case H_UNIT_VANISHED:
  277.         lossreason = accident_loss;
  278.         break;
  279.       case H_UNIT_DISBANDED:
  280.         lossreason = disband_loss;
  281.         break;
  282.       default:
  283.         lossreason = other_loss;
  284.         break;
  285.     }
  286.     count_loss(unit->side, unit->type, lossreason);
  287.     }
  288. }
  289.  
  290. PastUnit *
  291. change_unit_to_past_unit(unit)
  292. Unit *unit;
  293. {
  294.     PastUnit *pastunit;
  295.  
  296.     pastunit = create_past_unit(unit->type, 0);
  297.     pastunit->name = unit->name;
  298.     pastunit->number = unit->number;
  299.     pastunit->x = unit->x;  pastunit->y = unit->y;  pastunit->z = unit->z;
  300.     pastunit->side = unit->side;
  301.     rewrite_unit_references(unit->id, pastunit->id);
  302.     return pastunit;
  303. }
  304.  
  305. void
  306. record_unit_side_change(unit, newside, reason, agent)
  307. Unit *unit, *agent;
  308. Side *newside;
  309. HistEventType reason;
  310. {
  311.     enum loss_reasons lossreason;
  312.     enum gain_reasons gainreason;
  313.     SideMask observers;
  314.     PastUnit *pastunit;
  315.  
  316.     pastunit = change_unit_to_past_unit(unit);
  317.     observers = NOSIDES;
  318.     observers = add_side_to_set(unit->side, observers);
  319.     observers = add_side_to_set(newside, observers);
  320.     if (agent != NULL)
  321.       observers = add_side_to_set(agent->side, observers);
  322.     record_event(reason, observers, pastunit->id, (agent ? agent->id : 0),
  323.          side_number(newside));
  324.     lossreason = (reason == H_UNIT_CAPTURED ? capture_loss : other_loss);
  325.     count_loss(unit->side, unit->type, lossreason);
  326.     gainreason = (reason == H_UNIT_CAPTURED ? capture_gain : other_gain);
  327.     count_gain(newside, unit->type, gainreason);
  328. }
  329.  
  330. void
  331. record_unit_name_change(unit, newname)
  332. Unit *unit;
  333. char *newname;
  334. {
  335.     PastUnit *pastunit;
  336.  
  337.     pastunit = change_unit_to_past_unit(unit);
  338.     record_event(H_UNIT_NAME_CHANGED, ALLSIDES, pastunit->id, unit->id);
  339. }
  340.  
  341. void
  342. count_gain(side, u, reason)
  343. Side *side;
  344. int u;
  345. enum gain_reasons reason;
  346. {
  347.     if (side)
  348.       ++(side->gaincounts[num_gain_reasons * u + reason]);
  349. }
  350.  
  351. void
  352. count_loss(side, u, reason)
  353. Side *side;
  354. int u;
  355. enum loss_reasons reason;
  356. {
  357.     if (side)
  358.       ++(side->losscounts[num_loss_reasons * u + reason]);
  359. }
  360.  
  361. PastUnit *
  362. create_past_unit(type, id)
  363. int type, id;
  364. {
  365.     PastUnit *pastunit = (PastUnit *) xmalloc(sizeof(PastUnit));
  366.  
  367.     pastunit->type = type;
  368.     if (id == 0)
  369.       id = next_past_unit_id--;
  370.     else
  371.       next_past_unit_id = min(next_past_unit_id, id - 1);
  372.     pastunit->id = id;
  373.     /* Glue at the end of the list, so all stays sorted. */
  374.     if (past_unit_list == NULL) {
  375.     past_unit_list = last_past_unit = pastunit;
  376.     } else {
  377.     last_past_unit->next = pastunit;
  378.     last_past_unit = pastunit;
  379.     }
  380.     return pastunit;
  381. }
  382.  
  383. PastUnit *
  384. find_past_unit(n)
  385. int n;
  386. {
  387.     PastUnit *pastunit;
  388.  
  389.     for (pastunit = past_unit_list; pastunit != NULL; pastunit = pastunit->next) {
  390.     if (pastunit->id == n)
  391.       return pastunit;
  392.     }
  393.     return NULL;
  394. }
  395.  
  396. static void
  397. rewrite_unit_references(oldid, newid)
  398. int oldid, newid;
  399. {
  400.     HistEvent *hevt;
  401.  
  402.     rewrite_unit_references_in_event(history, oldid, newid);
  403.     for (hevt = history->next; hevt != history; hevt = hevt->next) {
  404.     rewrite_unit_references_in_event(hevt, oldid, newid);
  405.     }
  406. }
  407.  
  408. static void
  409. rewrite_unit_references_in_event(hevt, oldid, newid)
  410. HistEvent *hevt;
  411. int oldid, newid;
  412. {
  413.     int i;
  414.     char *descs;
  415.  
  416.     descs = hevtdefns[hevt->type].datadescs;
  417.     /* Scan through the data description, looking for
  418.        values that are references to actual units. */
  419.     for (i = 0; descs[i] != '\0'; ++i) {
  420.     if (descs[i] == 'U' && hevt->data[i] == oldid)
  421.       hevt->data[i] = newid;
  422.     }
  423. }
  424.  
  425. /* Indicate that history is no longer being recorded. */
  426.  
  427. void
  428. end_history()
  429. {
  430.     record_event(H_LOG_ENDED, ALLSIDES);
  431.     recording_events = FALSE;
  432. }
  433.  
  434. HistEvent *
  435. get_nth_history_line(side, n, nextevt)
  436. Side *side;
  437. int n;
  438. HistEvent **nextevt;
  439. {
  440.     int i = 0;
  441.     HistEvent *hevt;
  442.     
  443.     for (hevt = history->next; hevt != history; hevt = hevt->next) {
  444.     if (side_in_set(side, hevt->observers)) {
  445.         if (n == 0) {
  446.         *nextevt = hevt;
  447.         return NULL;
  448.         }
  449.         if (i == n) {
  450.         *nextevt = hevt->next;
  451.         return hevt;
  452.         }
  453.         if (hevt->startdate != hevt->prev->startdate) {
  454.         ++i;
  455.         if (i == n) {
  456.             *nextevt = hevt->next;
  457.             return NULL;
  458.         }
  459.         }
  460.         ++i;
  461.     }
  462.     }
  463.     /* Return the last event. */
  464.     *nextevt = history;
  465.     return history->prev;
  466. }
  467.  
  468. /* Summarize various aspects of performance in the game, writing it
  469.    all to a file. */
  470.  
  471. void
  472. dump_statistics()
  473. {
  474.     Side *side;
  475.     FILE *fp;
  476.  
  477.     if (!statistics_wanted)
  478.       return;
  479.     fp = fopen(statistics_filename(), "w");
  480.     if (fp != NULL) {
  481.     if (1 /* records exist */) {
  482.         write_side_results(fp, NULL);
  483.         write_unit_record(fp, NULL);
  484.         write_combat_results(fp, NULL);
  485.         fprintf(fp, "\f\n");
  486.         for_all_sides(side) {
  487.         write_side_results(fp, side);
  488.         write_unit_record(fp, side);
  489.         write_combat_results(fp, side);
  490.         if (side->next != NULL)
  491.           fprintf(fp, "\f\n");
  492.         }
  493.     } else {
  494.         fprintf(fp, "No statistics were kept.\n");
  495.     }
  496.     statistics_dumped = TRUE;
  497.     fclose(fp);
  498.     } else {
  499.     run_warning("Can't open statistics file \"%s\"",
  500.             statistics_filename());
  501.     }
  502. }
  503.  
  504. /* Short, unreadable, but greppable listing of past unit.  Primarily useful
  505.    for debugging and warnings.  We use several buffers and rotate between
  506.    them so we can call this more than once in a single printf. */
  507.  
  508. char *
  509. past_unit_desig(pastunit)
  510. PastUnit *pastunit;
  511. {
  512.     char *shortbuf;
  513.  
  514.     if (pastunit == NULL)
  515.       return "no pastunit";
  516.     /* Allocate if not yet done so. */
  517.     if (pastbufs[curpastbuf] == NULL)
  518.       pastbufs[curpastbuf] = xmalloc(BUFSIZE);
  519.     shortbuf = pastbufs[curpastbuf];
  520.     curpastbuf = (curpastbuf + 1) % NUMPASTBUFS;
  521.     if (pastunit->id == -1) {
  522.     sprintf(shortbuf, "s%d head", side_number(pastunit->side));
  523.     return shortbuf;
  524.     } else if (is_unit_type(pastunit->type)) {
  525.     sprintf(shortbuf, "s%d %-3.3s %d (%d,%d",
  526.         side_number(pastunit->side),
  527.         shortest_unique_name(pastunit->type),
  528.         pastunit->id, pastunit->x, pastunit->y);
  529.     if (pastunit->z != 0)
  530.       tprintf(shortbuf, ",%d", pastunit->z);
  531.     strcat(shortbuf, ")");  /* close out the pastunit location */
  532.     return shortbuf;
  533.     } else {
  534.     return "!garbage pastunit!";
  535.     }
  536. }
  537.  
  538. /* Would be faster to stash these, but enough difference to care? */
  539.  
  540. int
  541. total_gain(side, u)
  542. Side *side;
  543. int u;
  544. {
  545.     int i, total = 0;
  546.  
  547.     for (i = 0; i < num_gain_reasons; ++i)
  548.       total += side_gain_count(side, u, i);
  549.     return total;
  550. }
  551.  
  552. int
  553. total_loss(side, u)
  554. Side *side;
  555. int u;
  556. {
  557.     int i, total = 0;
  558.  
  559.     for (i = 0; i < num_loss_reasons; ++i)
  560.       total += side_loss_count(side, u, i);
  561.     return total;
  562. }
  563.