record an achievement (add at end of list unless already present) */
| 2404 | |
| 2405 | /* record an achievement (add at end of list unless already present) */ |
| 2406 | void |
| 2407 | record_achievement(schar achidx) |
| 2408 | { |
| 2409 | int i, absidx; |
| 2410 | int repeat_achievement = 0; |
| 2411 | |
| 2412 | absidx = abs(achidx); |
| 2413 | /* valid achievements range from 1 to N_ACH-1; however, ranks can be |
| 2414 | stored as the complement (ie, negative) to track gender */ |
| 2415 | if ((achidx < 1 && (absidx < ACH_RNK1 || absidx > ACH_RNK8)) |
| 2416 | || achidx >= N_ACH) { |
| 2417 | impossible("Achievement #%d is out of range.", achidx); |
| 2418 | return; |
| 2419 | } |
| 2420 | |
| 2421 | /* the list has an extra slot so there is always at least one 0 at |
| 2422 | its end (more than one unless all N_ACH-1 possible achievements |
| 2423 | have been recorded); find first empty slot or achievement #achidx; |
| 2424 | an attempt to duplicate an achievement can happen if any of Bell, |
| 2425 | Candelabrum, Book, or Amulet is dropped then picked up again */ |
| 2426 | for (i = 0; u.uachieved[i]; ++i) |
| 2427 | if (abs(u.uachieved[i]) == absidx) { |
| 2428 | repeat_achievement = 1; |
| 2429 | break; |
| 2430 | } |
| 2431 | |
| 2432 | /* |
| 2433 | * We do the sound for an achievement, even if it has already been |
| 2434 | * achieved before. Some players might have set up level-based |
| 2435 | * theme music or something. We do let the sound interface know |
| 2436 | * that it's not the original achievement though. |
| 2437 | */ |
| 2438 | SoundAchievement(achidx, 0, repeat_achievement); |
| 2439 | |
| 2440 | if (repeat_achievement) |
| 2441 | return; /* already recorded, don't duplicate it */ |
| 2442 | u.uachieved[i] = achidx; |
| 2443 | |
| 2444 | /* avoid livelog for achievements recorded during final disclosure: |
| 2445 | nudist and blind-from-birth; also ascension which is suppressed |
| 2446 | by this gets logged separately in really_done() */ |
| 2447 | if (program_state.gameover) |
| 2448 | return; |
| 2449 | |
| 2450 | if (absidx >= ACH_RNK1 && absidx <= ACH_RNK8) { |
| 2451 | livelog_printf(achieve_msg[absidx].llflag, |
| 2452 | "attained the rank of %s (level %d)", |
| 2453 | rank_of(rank_to_xlev(absidx - (ACH_RNK1 - 1)), |
| 2454 | Role_switch, (achidx < 0) ? TRUE : FALSE), |
| 2455 | u.ulevel); |
| 2456 | } else if (achidx == ACH_SOKO_PRIZE |
| 2457 | || achidx == ACH_MINE_PRIZE) { |
| 2458 | /* need to supply extra information for these two */ |
| 2459 | short otyp = ((achidx == ACH_SOKO_PRIZE) |
| 2460 | ? svc.context.achieveo.soko_prize_otyp |
| 2461 | : svc.context.achieveo.mines_prize_otyp); |
| 2462 | |
| 2463 | /* note: OBJ_NAME() works here because both "bag of holding" and |
no test coverage detected