/** * Housekeeping on leaving a level */ static void on_leave_level(void) { /* Any pending processing */ notice_stuff(player); update_stuff(player); redraw_stuff(player); /* Flush messages */ event_signal(EVENT_MESSAGE_FLUSH); }
/** * Overflow an item from the pack, if it is overfull. */ void pack_overflow(struct object *obj) { int i; char o_name[80]; bool artifact = false; if (!pack_is_overfull()) return; /* Disturbing */ disturb(player, 0); /* Warning */ msg("Your pack overflows!"); /* Get the last proper item */ for (i = 1; i <= z_info->pack_size; i++) if (!player->upkeep->inven[i]) break; /* Drop the last inventory item unless requested otherwise */ if (!obj) { obj = player->upkeep->inven[i - 1]; } /* Rule out weirdness (like pack full, but inventory empty) */ assert(obj != NULL); /* Describe */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); if (obj->artifact) { artifact = true; } /* Message */ msg("You drop %s.", o_name); /* Excise the object and drop it (carefully) near the player */ gear_excise_object(obj); drop_near(cave, &obj, 0, player->py, player->px, false); /* Describe */ if (artifact) msg("You no longer have the %s.", o_name); else msg("You no longer have %s.", o_name); /* Notice, update, redraw */ if (player->upkeep->notice) notice_stuff(player); if (player->upkeep->update) update_stuff(player); if (player->upkeep->redraw) redraw_stuff(player); }
/** * Overflow an item from the pack, if it is overfull. */ void pack_overflow(void) { int i; struct object *obj = NULL; char o_name[80]; if (!pack_is_overfull()) return; /* Disturbing */ disturb(player, 0); /* Warning */ msg("Your pack overflows!"); /* Find the last inventory item */ for (i = 1; i <= z_info->pack_size; i++) if (!player->upkeep->inven[i]) break; /* Last object was the previous index */ obj = player->upkeep->inven[i - 1]; /* Rule out weirdness (like pack full, but inventory empty) */ assert(obj != NULL); /* Describe */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You drop %s (%c).", o_name, I2A(i - 1)); /* Excise the object and drop it (carefully) near the player */ gear_excise_object(obj); drop_near(cave, obj, 0, player->py, player->px, FALSE); /* Describe */ if (obj->artifact) msg("You no longer have the %s (%c).", o_name, I2A(i - 1)); else msg("You no longer have %s (%c).", o_name, I2A(i - 1)); /* Notice stuff (if needed) */ if (player->upkeep->notice) notice_stuff(player->upkeep); /* Update stuff (if needed) */ if (player->upkeep->update) update_stuff(player->upkeep); /* Redraw stuff (if needed) */ if (player->upkeep->redraw) redraw_stuff(player->upkeep); }
/** * Housekeeping on arriving on a new level */ void on_new_level(void) { /* Play ambient sound on change of level. */ play_ambient_sound(); /* Cancel the target */ target_set_monster(0); /* Cancel the health bar */ health_track(player->upkeep, NULL); /* Disturb */ disturb(player, 1); /* Track maximum player level */ if (player->max_lev < player->lev) player->max_lev = player->lev; /* Track maximum dungeon level */ if (player->max_depth < player->depth) player->max_depth = player->depth; /* Flush messages */ event_signal(EVENT_MESSAGE_FLUSH); /* Update display */ event_signal(EVENT_NEW_LEVEL_DISPLAY); /* Update player */ update_player_object_knowledge(player); player->upkeep->update |= (PU_BONUS | PU_HP | PU_SPELLS | PU_INVEN); player->upkeep->notice |= (PN_COMBINE | PN_SEARCH); notice_stuff(player); update_stuff(player); redraw_stuff(player); /* Refresh */ event_signal(EVENT_REFRESH); /* Announce (or repeat) the feeling */ if (player->depth) display_feeling(false); /* Give player minimum energy to start a new level, but do not reduce * higher value from savefile for level in progress */ if (player->energy < z_info->move_energy) player->energy = z_info->move_energy; }
/** * The main game loop. * * This function will run until the player needs to enter a command, or closes * the game, or the character dies. */ void run_game_loop(void) { /* Tidy up after the player's command */ process_player_cleanup(); /* Keep processing the player until they use some energy or * another command is needed */ while (player->upkeep->playing) { process_player(); if (player->upkeep->energy_use) break; else return; } /* The player may still have enough energy to move, so we run another * player turn before processing the rest of the world */ while (player->energy >= z_info->move_energy) { /* Do any necessary animations */ event_signal(EVENT_ANIMATE); /* Process monster with even more energy first */ process_monsters(cave, player->energy + 1); if (player->is_dead || !player->upkeep->playing || player->upkeep->generate_level) break; /* Process the player until they use some energy */ while (player->upkeep->playing) { process_player(); if (player->upkeep->energy_use) break; else return; } } /* Now that the player's turn is fully complete, we run the main loop * until player input is needed again */ while (true) { notice_stuff(player); handle_stuff(player); event_signal(EVENT_REFRESH); /* Process the rest of the world, give the player energy and * increment the turn counter unless we need to stop playing or * generate a new level */ if (player->is_dead || !player->upkeep->playing) return; else if (!player->upkeep->generate_level) { /* Process the rest of the monsters */ process_monsters(cave, 0); /* Mark all monsters as ready to act when they have the energy */ reset_monsters(); /* Refresh */ notice_stuff(player); handle_stuff(player); event_signal(EVENT_REFRESH); if (player->is_dead || !player->upkeep->playing) return; /* Process the world every ten turns */ if (!(turn % 10) && !player->upkeep->generate_level) { process_world(cave); /* Refresh */ notice_stuff(player); handle_stuff(player); event_signal(EVENT_REFRESH); if (player->is_dead || !player->upkeep->playing) return; } /* Give the player some energy */ player->energy += turn_energy(player->state.speed); /* Count game turns */ turn++; } /* Make a new level if requested */ if (player->upkeep->generate_level) { if (character_dungeon) on_leave_level(); cave_generate(&cave, player); on_new_level(); player->upkeep->generate_level = false; } /* If the player has enough energy to move they now do so, after * any monsters with more energy take their turns */ while (player->energy >= z_info->move_energy) { /* Do any necessary animations */ event_signal(EVENT_ANIMATE); /* Process monster with even more energy first */ process_monsters(cave, player->energy + 1); if (player->is_dead || !player->upkeep->playing || player->upkeep->generate_level) break; /* Process the player until they use some energy */ while (player->upkeep->playing) { process_player(); if (player->upkeep->energy_use) break; else return; } } } }
/** * Process player commands from the command queue, finishing when there is a * command using energy (any regular game command), or we run out of commands * and need another from the user, or the character changes level or dies, or * the game is stopped. * * Notice the annoying code to handle "pack overflow", which * must come first just in case somebody manages to corrupt * the savefiles by clever use of menu commands or something. (Can go? NRM) * * Notice the annoying code to handle "monster memory" changes, * which allows us to avoid having to update the window flags * every time we change any internal monster memory field, and * also reduces the number of times that the recall window must * be redrawn. */ void process_player(void) { /* Check for interrupts */ player_resting_complete_special(player); event_signal(EVENT_CHECK_INTERRUPT); /* Repeat until energy is reduced */ do { /* Refresh */ notice_stuff(player); handle_stuff(player); event_signal(EVENT_REFRESH); /* Hack -- Pack Overflow */ pack_overflow(NULL); /* Assume free turn */ player->upkeep->energy_use = 0; /* Dwarves detect treasure */ if (player_has(player, PF_SEE_ORE)) { /* Only if they are in good shape */ if (!player->timed[TMD_IMAGE] && !player->timed[TMD_CONFUSED] && !player->timed[TMD_AMNESIA] && !player->timed[TMD_STUN] && !player->timed[TMD_PARALYZED] && !player->timed[TMD_TERROR] && !player->timed[TMD_AFRAID]) effect_simple(EF_DETECT_GOLD, "3d3", 1, 0, 0, NULL); } /* Paralyzed or Knocked Out player gets no turn */ if ((player->timed[TMD_PARALYZED]) || (player->timed[TMD_STUN] >= 100)) cmdq_push(CMD_SLEEP); /* Prepare for the next command */ if (cmd_get_nrepeats() > 0) event_signal(EVENT_COMMAND_REPEAT); else { /* Check monster recall */ if (player->upkeep->monster_race) player->upkeep->redraw |= (PR_MONSTER); /* Place cursor on player/target */ event_signal(EVENT_REFRESH); } /* Get a command from the queue if there is one */ if (!cmdq_pop(CMD_GAME)) break; if (!player->upkeep->playing) break; process_player_cleanup(); } while (!player->upkeep->energy_use && !player->is_dead && !player->upkeep->generate_level); /* Notice stuff (if needed) */ notice_stuff(player); }
/** * Decreases a monster's hit points by `dam` and handle monster death. * * Hack -- we "delay" fear messages by passing around a "fear" flag. * * We announce monster death (using an optional "death message" (`note`) * if given, and a otherwise a generic killed/destroyed message). * * Returns true if the monster has been killed (and deleted). * * TODO: Consider decreasing monster experience over time, say, by using * "(m_exp * m_lev * (m_lev)) / (p_lev * (m_lev + n_killed))" instead * of simply "(m_exp * m_lev) / (p_lev)", to make the first monster * worth more than subsequent monsters. This would also need to * induce changes in the monster recall code. XXX XXX XXX **/ bool mon_take_hit(struct monster *mon, int dam, bool *fear, const char *note) { s32b div, new_exp, new_exp_frac; struct monster_lore *lore = get_lore(mon->race); /* Redraw (later) if needed */ if (player->upkeep->health_who == mon) player->upkeep->redraw |= (PR_HEALTH); /* Wake it up */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); /* Become aware of its presence */ if (mflag_has(mon->mflag, MFLAG_UNAWARE)) become_aware(mon); /* Hurt it */ mon->hp -= dam; /* It is dead now */ if (mon->hp < 0) { char m_name[80]; char buf[80]; /* Assume normal death sound */ int soundfx = MSG_KILL; /* Play a special sound if the monster was unique */ if (rf_has(mon->race->flags, RF_UNIQUE)) { if (mon->race->base == lookup_monster_base("Morgoth")) soundfx = MSG_KILL_KING; else soundfx = MSG_KILL_UNIQUE; } /* Extract monster name */ monster_desc(m_name, sizeof(m_name), mon, MDESC_DEFAULT); /* Death message */ if (note) { if (strlen(note) <= 1) { /* Death by Spell attack - messages handled by project_m() */ } else { char *str = format("%s%s", m_name, note); my_strcap(str); /* Make sure to flush any monster messages first */ notice_stuff(player); /* Death by Missile attack */ msgt(soundfx, "%s", str); } } else { /* Make sure to flush any monster messages first */ notice_stuff(player); if (!mflag_has(mon->mflag, MFLAG_VISIBLE)) /* Death by physical attack -- invisible monster */ msgt(soundfx, "You have killed %s.", m_name); else if (monster_is_unusual(mon->race)) /* Death by Physical attack -- non-living monster */ msgt(soundfx, "You have destroyed %s.", m_name); else /* Death by Physical attack -- living monster */ msgt(soundfx, "You have slain %s.", m_name); } /* Player level */ div = player->lev; /* Give some experience for the kill */ new_exp = ((long)mon->race->mexp * mon->race->level) / div; /* Handle fractional experience */ new_exp_frac = ((((long)mon->race->mexp * mon->race->level) % div) * 0x10000L / div) + player->exp_frac; /* Keep track of experience */ if (new_exp_frac >= 0x10000L) { new_exp++; player->exp_frac = (u16b)(new_exp_frac - 0x10000L); } else player->exp_frac = (u16b)new_exp_frac; /* When the player kills a Unique, it stays dead */ if (rf_has(mon->race->flags, RF_UNIQUE)) { char unique_name[80]; mon->race->max_num = 0; /* * This gets the correct name if we slay an invisible * unique and don't have See Invisible. */ monster_desc(unique_name, sizeof(unique_name), mon, MDESC_DIED_FROM); /* Log the slaying of a unique */ strnfmt(buf, sizeof(buf), "Killed %s", unique_name); history_add(buf, HIST_SLAY_UNIQUE, 0); } /* Gain experience */ player_exp_gain(player, new_exp); /* Generate treasure */ monster_death(mon, false); /* Recall even invisible uniques or winners */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) || rf_has(mon->race->flags, RF_UNIQUE)) { /* Count kills this life */ if (lore->pkills < SHRT_MAX) lore->pkills++; /* Count kills in all lives */ if (lore->tkills < SHRT_MAX) lore->tkills++; /* Update lore and tracking */ lore_update(mon->race, lore); monster_race_track(player->upkeep, mon->race); } /* Delete the monster */ delete_monster_idx(mon->midx); /* Not afraid */ (*fear) = false; /* Monster is dead */ return (true); } /* Mega-Hack -- Pain cancels fear */ if (!(*fear) && mon->m_timed[MON_TMD_FEAR] && (dam > 0)) { int tmp = randint1(dam); /* Cure a little or all fear */ if (tmp < mon->m_timed[MON_TMD_FEAR]) { /* Reduce fear */ mon_dec_timed(mon, MON_TMD_FEAR, tmp, MON_TMD_FLG_NOMESSAGE, false); } else { /* Cure fear */ mon_clear_timed(mon, MON_TMD_FEAR, MON_TMD_FLG_NOMESSAGE, false); /* No more fear */ (*fear) = false; } } /* Sometimes a monster gets scared by damage */ if (!mon->m_timed[MON_TMD_FEAR] && !rf_has(mon->race->flags, RF_NO_FEAR) && dam > 0) { int percentage; /* Percentage of fully healthy */ percentage = (100L * mon->hp) / mon->maxhp; /* * Run (sometimes) if at 10% or less of max hit points, * or (usually) when hit for half its current hit points */ if ((randint1(10) >= percentage) || ((dam >= mon->hp) && (randint0(100) < 80))) { int timer = randint1(10) + (((dam >= mon->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5)); /* Hack -- note fear */ (*fear) = true; mon_inc_timed(mon, MON_TMD_FEAR, timer, MON_TMD_FLG_NOMESSAGE | MON_TMD_FLG_NOFAIL, false); } } /* Not dead yet */ return (false); }
/** * Handle character death */ void death_screen(void) { const region area = { 51, 2, 0, N_ELEMENTS(death_actions) }; /* Dump bones file */ make_bones(); /* Handle retirement */ if (p_ptr->total_winner) kingly(); /* Save dead player */ if (!old_save()) { msg_print("death save failed!"); message_flush(); } /* Get time of death */ #ifdef _WIN32_WCE { unsigned long fake_time(unsigned long *fake_time_t); fake_time(&death_time); } #else (void) time(&death_time); #endif /* Hack - Know everything upon death */ death_knowledge(); event_signal(EVENT_INVENTORY); event_signal(EVENT_EQUIPMENT); /* Handle stuff */ notice_stuff(); handle_stuff(); /* You are dead */ print_tomb(); /* Enter player in high score list */ enter_score(&death_time); /* Flush all input keys */ flush(); /* Flush messages */ msg_print(NULL); if (!death_menu) { death_menu = menu_new_action(death_actions, N_ELEMENTS(death_actions)); death_menu->flags = MN_CASELESS_TAGS; } menu_layout(death_menu, &area); do { menu_select(death_menu, 0); } while (!get_check("Do you want to quit? ")); }