/** * Create a path to the per-server settings directory. * @param path * Path inside the per-server settings directory. * @return * New path. Must be freed. */ char *file_path_server(const char *path) { StringBuffer *sb; HARD_ASSERT(path != NULL); sb = file_path_server_internal(); stringbuffer_append_printf(sb, ".common/%s", path); return stringbuffer_finish(sb); }
/** * Get absolute path to the specified relative path. This path will typically * point to the to client data directory (which is usually located in the user's * home/appdata directory), but depending on the specified mode, extra actions * may be performed. These ensure that if you're trying to access a file that * does not yet exist in the client data directory, it will be read from the * client installation directory instead (unless it's being appended to, in * which case it will be copied to the client data directory first). * * Generally, you should almost always use this when you need to construct a * path, or use one of the many @ref file_wrapper_functions. * @param fname * The file path. * @param mode * File mode. * @return * The absolute path. Must be freed. */ char *file_path(const char *path, const char *mode) { bool is_write, is_append; StringBuffer *sb; char version[MAX_BUF], client_path[HUGE_BUF], *new_path; HARD_ASSERT(path != NULL); HARD_ASSERT(mode != NULL); SOFT_ASSERT_RC(path[0] != '/', estrdup(path), "Path is already absolute: %s", path); sb = stringbuffer_new(); stringbuffer_append_printf(sb, "%s/.atrinik/%s/%s", get_config_dir(), package_get_version_partial(VS(version)), path); new_path = stringbuffer_sub(sb, 0, 0); is_write = is_append = false; if (strchr(mode, 'w') != NULL) { is_write = true; } else if (strchr(mode, '+') != NULL || strchr(mode, 'a') != NULL) { is_append = true; } if (is_write || is_append) { if (access(new_path, W_OK) != 0) { char *dirname; /* Ensure directories exist if we're going to use this path for * writing/appending. */ dirname = path_dirname(new_path); mkdir_recurse(dirname); efree(dirname); if (is_append) { get_data_dir_file(VS(client_path), path); copy_file(client_path, new_path); } } } else { if (access(new_path, R_OK) != 0) { get_data_dir_file(VS(client_path), path); stringbuffer_seek(sb, 0); stringbuffer_append_string(sb, client_path); } } efree(new_path); return stringbuffer_finish(sb); }
/** * Create a path to the per-player settings directory. * @param path * Path inside the per-player settings directory. * @return * New path. Must be freed. */ char *file_path_player(const char *path) { StringBuffer *sb; HARD_ASSERT(path != NULL); sb = file_path_server_internal(); SOFT_ASSERT_LABEL(*cpl.account != '\0', done, "Account name is empty."); SOFT_ASSERT_LABEL(*cpl.name != '\0', done, "Player name is empty."); stringbuffer_append_printf(sb, "%s/%s/%s", cpl.account, cpl.name, path); done: return stringbuffer_finish(sb); }
/** * Constructs a path leading to the chosen server settings directory. Used * internally by file_path_player() and file_path_server(). * @return * */ static StringBuffer *file_path_server_internal(void) { StringBuffer *sb; sb = stringbuffer_new(); stringbuffer_append_string(sb, "settings/"); SOFT_ASSERT_RC(selected_server != NULL, sb, "Selected server is NULL."); SOFT_ASSERT_RC(!string_isempty(selected_server->hostname), sb, "Selected server has empty hostname."); stringbuffer_append_printf(sb, "servers/%s-%d/", selected_server->hostname, selected_server->port); return sb; }
/** * Generate a message detailing the properties of 1-6 artifacts drawn * sequentially from the artifact list. * @param level Level of the book * @param buf Buffer to contain the description. * @param booksize Length of the book. * @return 'buf'. */ static char *artifact_msg(int level, char *buf, size_t booksize) { artifactlist *al; artifact *art; int chance, i, type, index; int book_entries = level > 5 ? RANDOM () % 3 + RANDOM () % 3 + 2 : RANDOM () % level + 1; char *final, *ch; object *tmp = NULL; StringBuffer *desc; /* Values greater than 5 create msg buffers that are too big! */ if (book_entries > 5) { book_entries = 5; } /* Let's determine what kind of artifact type randomly. * Right now legal artifacts only come from those listed * in art_name_array. Also, we check to be sure an artifactlist * for that type exists! */ i = 0; do { index = rndm(1, arraysize(art_name_array)) - 1; type = art_name_array[index].type; al = find_artifactlist(type); i++; } while (al == NULL && i < 10); /* Unable to find a message */ if (i == 10) { snprintf(buf, booksize, "None"); return buf; } /* There is no reason to start on the artifact list at the begining. Lets * take our starting position randomly... */ art = al->items; for (i = rndm(1, level) + rndm(0, 1); i > 0; i--) { /* Out of stuff, loop back around */ if (art == NULL) { art = al->items; } art = art->next; } /* Ok, let's print out the contents */ snprintf(buf, booksize, "<t t=\"Magical %s\">Herein %s detailed %s...\n", art_name_array[index].name, book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact"); /* Artifact msg attributes loop. Let's keep adding entries to the 'book' * as long as we have space up to the allowed max # (book_entires) */ while (book_entries > 0) { if (art == NULL) { art = al->items; } desc = stringbuffer_new(); tmp = get_archetype(art->def_at_name); give_artifact_abilities(tmp, art); SET_FLAG(tmp, FLAG_IDENTIFIED); stringbuffer_append_printf(desc, "\n<t t=\"%s %s\">It is ", tmp->name, tmp->title ? tmp->title : ""); /* Chance of finding. */ chance = 100 * ((float) art->chance / al->total_chance); if (chance >= 20) { stringbuffer_append_string(desc, "an uncommon"); } else if (chance >= 10) { stringbuffer_append_string(desc, "an unusual"); } else if (chance >= 5) { stringbuffer_append_string(desc, "a rare"); } else { stringbuffer_append_string(desc, "a very rare"); } /* Value of artifact. */ stringbuffer_append_printf(desc, " item with a value of %s.", cost_string_from_value(tmp->value)); if ((ch = describe_item(tmp)) && strlen(ch) > 1) { stringbuffer_append_printf(desc, "\nProperties of this artifact include:\n %s", ch); } final = stringbuffer_finish(desc); /* Add the buf if it will fit. */ if (book_overflow(buf, final, booksize)) { free(final); break; } snprintf(buf + strlen(buf), booksize - strlen(buf), "%s", final); free(final); art = art->next; book_entries--; }
/** * Player examines some object. * @param op Player. * @param tmp Object to examine. */ void examine(object *op, object *tmp) { char buf[VERY_BIG_BUF], tmp_buf[64]; int i; if (tmp == NULL || tmp->type == CLOSE_CON) { return; } strcpy(buf, "That is "); strncat(buf, long_desc(tmp, op), VERY_BIG_BUF - strlen(buf) - 1); buf[VERY_BIG_BUF - 1] = '\0'; /* Only add this for usable items, not for objects like walls or * floors for example. */ if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && need_identify(tmp)) { strncat(buf, " (unidentified)", VERY_BIG_BUF - strlen(buf) - 1); } buf[VERY_BIG_BUF - 1] = '\0'; new_draw_info(NDI_UNIQUE, op, buf); buf[0] = '\0'; if (QUERY_FLAG(tmp, FLAG_MONSTER) || tmp->type == PLAYER) { new_draw_info_format(NDI_UNIQUE, op, "%s.", describe_item(tmp->head ? tmp->head : tmp)); examine_living(op, tmp); } /* We don't double use the item_xxx arch commands, so they are always valid */ else if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { if (QUERY_FLAG(tmp, FLAG_IS_GOOD)) { new_draw_info_format(NDI_UNIQUE, op, "It is good aligned."); } else if (QUERY_FLAG(tmp, FLAG_IS_EVIL)) { new_draw_info_format(NDI_UNIQUE, op, "It is evil aligned."); } else if (QUERY_FLAG(tmp, FLAG_IS_NEUTRAL)) { new_draw_info_format(NDI_UNIQUE, op, "It is neutral aligned."); } if (tmp->item_level) { if (tmp->item_skill) { new_draw_info_format(NDI_UNIQUE, op, "It needs a level of %d in %s to use.", tmp->item_level, find_skill_exp_skillname(tmp->item_skill)); } else { new_draw_info_format(NDI_UNIQUE, op, "It needs a level of %d to use.", tmp->item_level); } } if (tmp->item_quality) { if (QUERY_FLAG(tmp, FLAG_INDESTRUCTIBLE)) { new_draw_info_format(NDI_UNIQUE, op, "Qua: %d Con: Indestructible.", tmp->item_quality); } else { new_draw_info_format(NDI_UNIQUE, op, "Qua: %d Con: %d.", tmp->item_quality, tmp->item_condition); } } buf[0] = '\0'; } switch (tmp->type) { case SPELLBOOK: if (QUERY_FLAG(tmp, FLAG_IDENTIFIED) && tmp->stats.sp >= 0 && tmp->stats.sp <= NROFREALSPELLS) { if (tmp->sub_type == ST1_SPELLBOOK_CLERIC) { snprintf(buf, sizeof(buf), "%s is a %d level prayer.", spells[tmp->stats.sp].name, spells[tmp->stats.sp].level); } else { snprintf(buf, sizeof(buf), "%s is a %d level spell.", spells[tmp->stats.sp].name, spells[tmp->stats.sp].level); } } break; case BOOK: if (tmp->msg != NULL) { strcpy(buf, "Something is written in it."); } break; case CONTAINER: if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { if (tmp->race != NULL) { if (tmp->weight_limit) { snprintf(buf, sizeof(buf), "It can hold only %s and its weight limit is %.1f kg.", tmp->race, (float) tmp->weight_limit / 1000.0f); } else { snprintf(buf, sizeof(buf), "It can hold only %s.", tmp->race); } /* Has magic modifier? */ if (tmp->weapon_speed != 1.0f) { new_draw_info(NDI_UNIQUE, op, buf); /* Bad */ if (tmp->weapon_speed > 1.0f) { snprintf(buf, sizeof(buf), "It increases the weight of items inside by %.1f%%.", tmp->weapon_speed * 100.0f); } /* Good */ else { snprintf(buf, sizeof(buf), "It decreases the weight of items inside by %.1f%%.", 100.0f - (tmp->weapon_speed * 100.0f)); } } } else { if (tmp->weight_limit) { snprintf(buf, sizeof(buf), "Its weight limit is %.1f kg.", (float)tmp->weight_limit / 1000.0f); } /* Has magic modifier? */ if (tmp->weapon_speed != 1.0f) { new_draw_info(NDI_UNIQUE, op, buf); /* Bad */ if (tmp->weapon_speed > 1.0f) { snprintf(buf, sizeof(buf), "It increases the weight of items inside by %.1f%%.", tmp->weapon_speed * 100.0f); } /* Good */ else { snprintf(buf, sizeof(buf), "It decreases the weight of items inside by %.1f%%.", 100.0f - (tmp->weapon_speed * 100.0f)); } } } new_draw_info(NDI_UNIQUE, op, buf); if (tmp->weapon_speed == 1.0f) { snprintf(buf, sizeof(buf), "It contains %3.3f kg.", (float) tmp->carrying / 1000.0f); } else if (tmp->weapon_speed > 1.0f) { snprintf(buf, sizeof(buf), "It contains %3.3f kg, increased to %3.3f kg.", (float) tmp->damage_round_tag / 1000.0f, (float) tmp->carrying / 1000.0f); } else { snprintf(buf, sizeof(buf), "It contains %3.3f kg, decreased to %3.3f kg.", (float) tmp->damage_round_tag / 1000.0f, (float) tmp->carrying / 1000.0f); } } break; case WAND: if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { snprintf(buf, sizeof(buf), "It has %d charges left.", tmp->stats.food); } break; case POWER_CRYSTAL: /* Avoid division by zero... */ if (tmp->stats.maxsp == 0) { snprintf(buf, sizeof(buf), "It has capacity of %d.", tmp->stats.maxsp); } else { int i; /* Higher capacity crystals */ if (tmp->stats.maxsp > 1000) { i = (tmp->stats.maxsp % 1000) / 100; if (i) { snprintf(tmp_buf, sizeof(tmp_buf), "It has capacity of %d.%dk and is ", tmp->stats.maxsp / 1000, i); } else { snprintf(tmp_buf, sizeof(tmp_buf), "It has capacity of %dk and is ", tmp->stats.maxsp / 1000); } } else { snprintf(tmp_buf, sizeof(tmp_buf), "It has capacity of %d and is ", tmp->stats.maxsp); } strcat(buf, tmp_buf); i = (tmp->stats.sp * 10) / tmp->stats.maxsp; if (tmp->stats.sp == 0) { strcat(buf, "empty."); } else if (i == 0) { strcat(buf, "almost empty."); } else if (i < 3) { strcat(buf, "partially filled."); } else if (i < 6) { strcat(buf, "half full."); } else if (i < 9) { strcat(buf, "well charged."); } else if (tmp->stats.sp == tmp->stats.maxsp) { strcat(buf, "fully charged."); } else { strcat(buf, "almost full."); } } break; } if (buf[0] != '\0') { new_draw_info(NDI_UNIQUE, op, buf); } if (tmp->material && (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED))) { strcpy(buf, "It is made of: "); for (i = 0; i < NROFMATERIALS; i++) { if (tmp->material & (1 << i)) { strcat(buf, material[i].name); strcat(buf, " "); } } new_draw_info(NDI_UNIQUE, op, buf); } if (tmp->weight) { float weight = (float) (tmp->nrof ? tmp->weight * (int) tmp->nrof : tmp->weight) / 1000.0f; if (tmp->type == MONSTER) { new_draw_info_format(NDI_UNIQUE, op, "%s weighs %3.3f kg.", gender_subjective_upper[object_get_gender(tmp)], weight); } else if (tmp->type == PLAYER) { new_draw_info_format(NDI_UNIQUE, op, "%s weighs %3.3f kg and is carrying %3.3f kg.", gender_subjective_upper[object_get_gender(tmp)], weight, (float) tmp->carrying / 1000.0f); } else { new_draw_info_format(NDI_UNIQUE, op, tmp->nrof > 1 ? "They weigh %3.3f kg." : "It weighs %3.3f kg.", weight); } } if (QUERY_FLAG(tmp, FLAG_STARTEQUIP)) { /* Unpaid clone shop item */ if (QUERY_FLAG(tmp, FLAG_UNPAID)) { new_draw_info_format(NDI_UNIQUE, op, "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", query_cost_string(tmp, op, F_BUY)); } /* God-given item */ else { new_draw_info_format(NDI_UNIQUE, op, "%s god-given item%s.", tmp->nrof > 1 ? "They are" : "It is a", tmp->nrof > 1 ? "s" : ""); if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { if (tmp->value) { new_draw_info_format(NDI_UNIQUE, op, "But %s worth %s.", tmp->nrof > 1 ? "they are" : "it is", query_cost_string(tmp, op, F_TRUE)); } else { new_draw_info_format(NDI_UNIQUE, op, "%s worthless.", tmp->nrof > 1 ? "They are" : "It is"); } } } } else if (tmp->value && !IS_LIVE(tmp)) { if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { if (QUERY_FLAG(tmp, FLAG_UNPAID)) { new_draw_info_format(NDI_UNIQUE, op, "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", query_cost_string(tmp, op, F_BUY)); } else { new_draw_info_format(NDI_UNIQUE, op, "%s worth %s.", tmp->nrof > 1 ? "They are" : "It is", query_cost_string(tmp, op, F_TRUE)); goto dirty_little_jump1; } } else { object *floor; dirty_little_jump1: floor = GET_MAP_OB_LAYER(op->map, op->x, op->y, 0); if (floor && floor->type == SHOP_FLOOR && tmp->type != MONEY) { /* Used for SK_BARGAINING modification */ int charisma = op->stats.Cha; /* This skill gives us a charisma boost */ if (find_skill(op, SK_BARGAINING)) { charisma += 4; if (charisma > MAX_STAT) { charisma = MAX_STAT; } } new_draw_info_format(NDI_UNIQUE, op, "This shop will pay you %s (%0.1f%%).", query_cost_string(tmp, op, F_SELL), 20.0f + 100.0f * cha_bonus[charisma]); } } } else if (!IS_LIVE(tmp)) { if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { if (QUERY_FLAG(tmp, FLAG_UNPAID)) { new_draw_info_format(NDI_UNIQUE, op, "%s would cost nothing.", tmp->nrof > 1 ? "They" : "It"); } else { new_draw_info_format(NDI_UNIQUE, op, "%s worthless.", tmp->nrof > 1 ? "They are" : "It is"); } } } /* Does the object have a message? Don't show message for all object * types - especially if the first entry is a match */ if (tmp->msg && tmp->type != EXIT && tmp->type != BOOK && tmp->type != CORPSE && !QUERY_FLAG(tmp, FLAG_WALK_ON) && strncasecmp(tmp->msg, "@match", 7)) { /* This is just a hack so when identifying the items, we print * out the extra message */ if (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { new_draw_info(NDI_UNIQUE, op, "The object has a story:"); new_draw_info(NDI_UNIQUE, op, tmp->msg); } } /* Blank line */ new_draw_info(NDI_UNIQUE, op, " "); if (QUERY_FLAG(op, FLAG_WIZ)) { StringBuffer *sb = stringbuffer_new(); char *diff; stringbuffer_append_printf(sb, "count %d\n", tmp->count); dump_object(tmp, sb); diff = stringbuffer_finish(sb); new_draw_info(NDI_UNIQUE, op, diff); free(diff); } }