static void apply_move (Event_move e){ Unit *u = id2unit(e.u); if(find_skill(u, S_IGNR)) u->mv -= e.cost; else u->mv = 0; u->stamina -= e.cost; u->m = neib(u->m, e.dir); fill_map(selected_unit); if(u->player == current_player->id) update_fog_after_move(u); }
/* a - shooting unit, b - target */ static int range_damage (Unit *a, Unit *b){ Skill_range s = find_skill(a, S_RANGE)->range; int hits = 0; int wounds = 0; /*possible wounds(may be blocked by armour)*/ int final = 0; /*final wounds(not blocked by armour)*/ int attacks = a->count; /*chances to hit, to wound and to ignore armour. percents.*/ int to_hit = 2 + s.skill; int to_wound = 5 + (s.strength - utypes[b->t].toughness); int to_as = 10- utypes[b->t].armor; #if 1 int r = rnd(0, 2); to_hit += rnd(-r, r); to_wound += rnd(-r, r); to_as += rnd(-r, r); #endif fixnum(0, 9, &to_hit); fixnum(0, 9, &to_wound); hits = attacks * to_hit / 10; wounds = hits * to_wound / 10; final = wounds * to_as / 10;
/** * Return the price of an item for a character. * @param tmp Object we're querying the price of. * @param who Who is inquiring. Can be NULL, only meaningful if player. * @param flag Combination of @ref F_xxx "F_xxx" flags. * @return The price for the item. */ sint64 query_cost(object *tmp, object *who, int flag) { sint64 val; double diff; int number; int charisma = 11; if ((number = tmp->nrof) == 0) { number = 1; } /* Money is always identified */ if (tmp->type == MONEY) { return (number * tmp->value); } /* Handle identified items */ if (QUERY_FLAG(tmp, FLAG_IDENTIFIED) || !need_identify(tmp)) { if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) { return 0; } else { val = tmp->value * number; } } /* This area deals with objects that are not identified, but can be */ else { if (tmp->arch != NULL) { if (flag == F_BUY) { LOG(llevBug, "BUG: query_cost(): Asking for buy-value of unidentified object %s.\n", query_name(tmp, NULL)); val = tmp->arch->clone.value * number; } /* Trying to sell something, or get true value */ else { /* Selling unidentified gems is *always* stupid */ if (tmp->type == GEM || tmp->type == JEWEL || tmp->type == NUGGET || tmp->type == PEARL) { val = number * 3; } /* Don't want to give anything away */ else if (tmp->type == POTION) { val = number * 50; } else { val = number * tmp->arch->clone.value; } } } else { /* No archetype with this object - we generate some dummy values to avoid server break */ LOG(llevBug, "BUG: query_cost(): Have object with no archetype: %s\n", query_name(tmp, NULL)); if (flag == F_BUY) { LOG(llevBug, "BUG: query_cost(): Asking for buy-value of unidentified object without arch.\n"); val = number * 100; } else { val = number * 80; } } } /* Wands will count special. The base value is for a wand with one charge */ if (tmp->type == WAND) { val += (val * tmp->level) * tmp->stats.food; } else if (tmp->type == ROD || tmp->type == HORN || tmp->type == POTION || tmp->type == SCROLL) { val += val * tmp->level; } /* We are done if we only want get the real value */ if (flag == F_TRUE) { return val; } /* First, we adjust charisma for players and count skills in */ if (who != NULL && who->type == PLAYER) { /* Used for SK_BARGAINING modification */ charisma = who->stats.Cha; /* This skill will give us a charisma boost */ if (find_skill(who, SK_BARGAINING)) { charisma += 4; if (charisma > MAX_STAT) { charisma = MAX_STAT; } } } /* Now adjust for sell or buy multiplier */ if (flag == F_BUY) { diff = (double) (1.0 - (double) cha_bonus[charisma]); } else { diff = (double) (0.20 + (double) cha_bonus[charisma]); } val = (val * (long) (1000 * (diff))) / 1000; /* We want to give at least 1 copper for items which have any * value. */ if (val < 1 && tmp->value > 0) { val = 1; } return val; }
/** * Main part of the alchemy code. From this we call functions that take a * look at the contents of the 'cauldron' and, using these ingredients, * we construct an integer formula value which is referenced (randomly) * against a formula list (the formula list chosen is based on the # * contents of the cauldron). * * If we get a match between the recipe indicated in cauldron contents * and a randomly chosen one, an item is created and experience awarded. * Otherwise various failure effects are possible (getting worse and * worse with # cauldron ingredients). Note that the 'item' to be made * can be *anything* listed on the artifacts list in lib/artifacts which * has a recipe listed in lib/formulae. * * To those wondering why I am using the funky formula index method: * 1) I want to match recipe to ingredients regardless of ordering. * 2) I want a fast search for the 'right' recipe. * * Note: it is just possible that a totally different combination of * ingredients will result in a match with a given recipe. This is not a * bug! There is no good reason (in my mind) why alchemical processes * have to be unique -- such a 'feature' is one reason why players might * want to experiment around. :) * @param caster Who is doing alchemy. * @param cauldron The cauldron in which alchemy should take place. */ static void attempt_do_alchemy(object *caster, object *cauldron) { recipelist *fl; recipe *rp = NULL; float success_chance; int numb, ability = 1; int formula = 0; /* Only players for now */ if (caster->type != PLAYER) { return; } /* If no ingredients, no formula! Let's forget it */ if (!(formula = content_recipe_value(cauldron))) { return; } numb = numb_ob_inside(cauldron); if ((fl = get_formulalist(numb))) { /* The caster only gets an increase in ability if they know * alchemy skill */ if (find_skill(caster, SK_ALCHEMY) != NULL) { change_skill(caster, SK_ALCHEMY); ability += (int) ((float) SK_level(caster) * (float) ((float) (4 + cauldron->magic) / 4.0f)); } #ifdef ALCHEMY_DEBUG LOG(llevDebug, "DEBUG: Got alchemy ability lvl = %d\n", ability); #endif if (QUERY_FLAG(caster, FLAG_WIZ)) { rp = fl->items; while (rp && (formula % rp->index) != 0) { #ifdef EXTREME_ALCHEMY_DEBUG LOG(llevDebug, "DEBUG: found list %d formula: %s of %s (%d)\n", numb, rp->arch_name, rp->title, rp->index); #endif rp = rp->next; } if (rp) { #ifdef ALCHEMY_DEBUG if (rp->title != shstr_cons.NONE) { LOG(llevDebug, "DEBUG: WIZ got formula: %s of %s\n", rp->arch_name, rp->title); } else { LOG(llevDebug, "DEBUG: WIZ got formula: %s (nbatches:%d)\n", rp->arch_name, formula / rp->index); } #endif attempt_recipe(caster, cauldron, ability, rp, formula / rp->index); } else { LOG(llevDebug, "DEBUG: WIZ couldnt find formula for ingredients.\n"); } return; } /* Find the recipe */ for (rp = fl->items; rp != NULL && (formula % rp->index) != 0; rp = rp->next) { } /* If we found a recipe */ if (rp) { float ave_chance = fl->total_chance / (float) fl->number; object *item; /* Create the object **FIRST**, then decide whether to keep it. */ if ((item = attempt_recipe(caster, cauldron, ability, rp, formula / rp->index)) != NULL) { /* Compute base chance of recipe success */ success_chance = ((float) (15 * ability) / (float) (15 * ability + numb * item->level * (numb + item->level + formula / rp->index))); if (ave_chance == 0) { ave_chance = 1; } /* Adjust the success chance by the chance from the recipe list */ if (ave_chance > rp->chance) { success_chance *= ((float) rp->chance + ave_chance) / (2.0f * ave_chance); } else { success_chance = 1.0f - ((1.0f - success_chance) * ((float) rp->chance + ave_chance) / (2.0f * (float) rp->chance)); } #ifdef ALCHEMY_DEBUG LOG(llevDebug, "DEBUG: percent success chance = %f\n", success_chance); #endif /* Roll the dice */ if ((float) (rndm(0, 99)) <= 100.0 * success_chance) { /* We learn from our experience IF we know something of the alchemical arts */ if (caster->chosen_skill && caster->chosen_skill->stats.sp == SK_ALCHEMY) { /* More exp is given for higher ingred number recipes */ sint64 amount = numb * numb * calc_skill_exp(caster, item, -1); add_exp(caster, amount, SK_ALCHEMY); /* So when skill id this item, less xp is awarded */ item->stats.exp = 0; #ifdef EXTREME_ALCHEMY_DEBUG LOG(llevDebug, "DEBUG: %s gains %d experience points.\n", caster->name, amount); #endif } return; } } } } /* If we get here, we failed! */ alchemy_failure_effect(caster, cauldron, rp, calc_alch_danger(caster, cauldron)); }
/** * 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); } }