asgard/
asgard/.settings/
asgard/area/
asgard/data/clans/
asgard/data/clans/history/
asgard/data/rosters/
asgard/src/notice/
/*
 * newrandom.c
 *
 *  Created on: 2 Feb 2011
 *      Author: Nico and Ishamael
 *  Based on random object code by Chris Langlois(tas@intrepid.inetsolve.com) and Gabe Volker.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "newclan.h"
#include "newrandom.h"
#include "recycle.h"

// TODO: move the uniques/randoms into separate areas,
// remembering to edit pfiles for the new vnums.
#define VNUM_UNIQUE_LOWER	850
#define VNUM_UNIQUE_UPPER	999
#define VNUM_RANDOM_LOWER	24500
#define VNUM_RANDOM_UPPER	25499

// 400 each for prefixes/suffixes
#define VNUM_PREFIX_LOWER	25500
#define VNUM_PREFIX_UPPER	25899
#define VNUM_SUFFIX_LOWER	25900
#define VNUM_SUFFIX_UPPER	26299
//#define VNUM_PREFIX_LOWER	700
//#define VNUM_PREFIX_UPPER	849
//#define VNUM_SUFFIX_LOWER	700
//#define VNUM_SUFFIX_UPPER	849

// Default maximum equipment (not pop) level for items.
#define DEFAULT_MAX_LEVEL	101

// Quality types.
#define QUALITY_WORST		-3
#define QUALITY_WORSE		-2
#define QUALITY_BAD			-1
#define QUALITY_NORMAL		0
#define QUALITY_GOOD		1
#define QUALITY_BETTER		2
#define QUALITY_BEST		3

// Range around pop_level to use for min/max prefix/suffix levels.
#define POP_RANGE			20

OBJ_INDEX_LIST *obj_index_list_free;

static const struct quality_type quality_names[] = {
	{ "broken", QUALITY_WORST, "{r" },
	{ "dented", QUALITY_WORSE, "{y" },
	{ "scratched", QUALITY_BAD, "{D" },
	{ "normal", QUALITY_NORMAL, "" },
	{ "improved", QUALITY_GOOD, "{C" },
	{ "exceptional", QUALITY_BETTER, "{M" },
	{ "flawless", QUALITY_BEST, "{G" },
	{NULL},
};

// Start of helper functions.
OBJ_INDEX_LIST *new_obj_index_list(OBJ_INDEX_DATA *object_index) {
	OBJ_INDEX_LIST *oil;
	if (obj_index_list_free != NULL) {
		oil = obj_index_list_free;
		obj_index_list_free = obj_index_list_free->next;
	} else
		oil = malloc(sizeof(OBJ_INDEX_LIST));

	oil->object_index = object_index;
	oil->next = NULL;
	return oil;
}
void free_obj_index_list(OBJ_INDEX_LIST *oil, bool all) {
	if (oil == NULL)
		return;
	// Recursive free.
	if (all)
		free_obj_index_list(oil->next, TRUE);
	oil->next = obj_index_list_free;
	obj_index_list_free = oil;
}
bool is_in_obj_index_list(OBJ_INDEX_DATA *objIndex, OBJ_INDEX_LIST *oil) {
	for (; oil != NULL; oil = oil->next)
		if (oil->object_index->vnum == objIndex->vnum)
			return TRUE;
	return FALSE;
}

// Util functions mainly for specific loads in act_wiz.c
static OBJ_INDEX_DATA *ritem_lookup(char *name, int min_vnum, int max_vnum) {
	OBJ_INDEX_DATA *objIndex;
	int vnum = min_vnum;

	for (vnum = min_vnum; vnum <= max_vnum; vnum++) {
		if ((objIndex = get_obj_index(vnum)) != NULL && is_name(name, objIndex->name))
			return objIndex;
	}

	return NULL;
}

OBJ_INDEX_LIST *prefix_lookup(char *name) {
	OBJ_INDEX_DATA *objIndex = ritem_lookup(name, VNUM_PREFIX_LOWER, VNUM_PREFIX_UPPER);

	if (objIndex == NULL)
		return NULL;
	return new_obj_index_list(objIndex);
}

OBJ_INDEX_LIST *suffix_lookup(char *name) {
	OBJ_INDEX_DATA *objIndex = ritem_lookup(name, VNUM_SUFFIX_LOWER, VNUM_SUFFIX_UPPER);

	if (objIndex == NULL)
		return NULL;
	return new_obj_index_list(objIndex);
}

OBJ_INDEX_DATA *rbase_object_lookup(char *name) {
	return ritem_lookup(name, VNUM_RANDOM_LOWER, VNUM_RANDOM_UPPER);
}

const struct quality_type *quality_lookup_name(char *name) {
	const struct quality_type *quality_struct;
	for (quality_struct = quality_names; quality_struct->name != NULL; quality_struct++)
		if (!str_cmp(quality_struct->name, name))
			return quality_struct;
	return NULL;
}

const struct quality_type *quality_lookup_index(int index) {
	const struct quality_type *quality_struct;
	for (quality_struct = quality_names; quality_struct->name != NULL; quality_struct++)
		if (quality_struct->index == index)
			return quality_struct;
	return NULL;
}

// Amount to drop the level by on unsuccessful searches by level_based_load.
#define LEVEL_STEP	10
static OBJ_INDEX_DATA *level_based_load(int vnum_lower, int vnum_upper, int level_lower, int level_upper, bool try_lower) {
	OBJ_INDEX_DATA *object_index;
	OBJ_INDEX_LIST *list = NULL, *elem;
	int count = 0, vnum;

	// Why do we use a list? Stops having to redo the hash list lookup/level checking all over again.

	// Build a list of object indexes that pass level conditions.
	for (vnum = vnum_lower; vnum <= vnum_upper; vnum++) {
		object_index = get_obj_index(vnum);
		if (object_index == NULL
		|| object_index->level < level_lower
		|| object_index->level > level_upper)
			continue;
		// Add to list, increment count.
		elem = new_obj_index_list(object_index);
		elem->next = list; list = elem;
		count++;
	}

	// No objects that match conditions.
	if (count == 0) {
		if (try_lower && level_lower > 1) {
			// Lets drop the lower bound and try again.
			return level_based_load(vnum_lower, vnum_upper, UMAX(1, level_lower-LEVEL_STEP), level_upper, try_lower);
		}
		return NULL;
	}

	// Get an index into the built list and find the corresponding object index.
	int random_pick = number_range(0, count-1);
	for (elem = list; random_pick-- > 0; elem = elem->next);
	object_index = elem->object_index;

	free_obj_index_list(list, TRUE);
	return object_index;
}

#define assign_field(Field, Value)	\
	free_string(Field);				\
	Field = str_dup(Value);

#define sprintf_field(TempBuf, Field, Args...)	\
	sprintf(TempBuf, Args);						\
	assign_field(Field, TempBuf);

// Start of the proper meat.

static void add_modifier(OBJ_INDEX_DATA *modifier, OBJ_DATA *object, bool prefix_not_suffix, char *connector, int mob_tier, int base_tier_bonus) {
	char name[MAX_STRING_LENGTH];
	// Set keywords.
	sprintf_field(name, object->name, "%s %s", strip_bespaces(process_name(modifier->name)), object->name);

	// Set short description.
	if (prefix_not_suffix)	// Prefix, modifier's short description comes first.
		sprintf(name, "%s%s%s", modifier->short_descr, connector, object->short_descr);
	else					// Suffix, modifier's short description comes last.
		sprintf(name, "%s%s%s", object->short_descr, connector, modifier->short_descr);
	assign_field(object->short_descr, name);
	assign_field(object->description, name);

	// Add effects and whatnot.
	if (modifier->item_type == ITEM_WEAPON && object->item_type == ITEM_WEAPON)
		SET_BIT(object->value[4], modifier->value[4]);
	SET_BIT(object->extra_flags, modifier->extra_flags);
	AFFECT_DATA *affect, temp_affect;
	for (affect = modifier->affected; affect != NULL; affect = affect->next) {
		if (affect->location != APPLY_MORPH_FORM) {
			temp_affect = *affect;
			temp_affect.modifier = affect->modifier + number_range((affect->modifier * mob_tier / 99 / base_tier_bonus), (affect->modifier * mob_tier / 99));
			affect_to_obj(object, &temp_affect);
		}
	}
}

static void combine_fuzz_effects(OBJ_DATA *object) {
	AFFECT_DATA *current, *search, *search_next, *search_prev;

	for (current = object->affected; current != NULL; current = current->next) {
		if (current->location == APPLY_MORPH_FORM)
			continue;
		// Combine.
		for (search = current->next, search_prev = current; search != NULL; search = search_next) {
			search_next = search->next;
			if (current->where == search->where && current->type == search->type
			&&  current->location == search->location && current->bitvector == search->bitvector) {
				search_prev->next = search_next;
				current->modifier += search->modifier;
				free_affect(search);
			} else
				search_prev = search;
		}
		// Fuzzy
		current->modifier = number_range((current->modifier*9)/10, (current->modifier*11)/10);
	}

	// Remove zero effects.
	for (search = object->affected, search_prev = NULL; search != NULL; search = search_next) {
		search_next = search->next;
		if (search->modifier == 0 && search->bitvector == 0) {
			if (search_prev == NULL)
				object->affected = search_next;
			else
				search_prev->next = search_next;
			free_affect(search);
		} else
			search_prev = search;
	}
}

// Apply effects from prefixes, suffixes and modifiers to object chosen.
// Slightly random in terms of effect numbers.
OBJ_DATA *specific_object(int quality, OBJ_DATA *object, OBJ_INDEX_LIST *prefixes, OBJ_INDEX_LIST *suffixes, bool unique, int mob_tier) {
	// Calculate the alignment of the object - currently held in the weight field of the object.
	int alignment = 0, count = 0, chance = 0, modifier = 0, levelr = 0;
	OBJ_INDEX_LIST *elem;
	char name[MAX_STRING_LENGTH];

	chance = number_range(2,5); // Set a lower cap for tier bonus on object. Higher number is worse.

	if ((object->item_type == ITEM_ARMOR) && (unique == FALSE) && (mob_tier > 2)) {
		object->value[0] = object->value[0] + number_range(object->value[0] * mob_tier / 99 * ( 10 - chance ) / 10, object->value[0] * mob_tier / 99);
		object->value[1] = object->value[1] + number_range(object->value[1] * mob_tier / 99 * ( 10 - chance ) / 10, object->value[1] * mob_tier / 99);
		object->value[2] = object->value[2] + number_range(object->value[2] * mob_tier / 99 * ( 10 - chance ) / 10, object->value[2] * mob_tier / 99);
		object->value[3] = object->value[3] + number_range(object->value[3] * mob_tier / 99 * ( 10 - chance ) / 10, object->value[3] * mob_tier / 99);
	}
	if ((object->item_type == ITEM_ARMOR) && (unique == FALSE) && (quality < QUALITY_NORMAL)) {
		// Let's reduce base AC on broken/cracked things.
		// Higher chance = more corrosive effect!
		// BAD = -1, WORSE = -2, WORST = -3
		//modifier = -((quality * 5) - chance + 7); // -13 - 5 = -18.  -5 - -2 = -7 + 7 = 0
						       // Range is -11 to 0
		modifier = number_range((-quality * 4), (-quality * 5));

		if (modifier > 0) {
			object->value[0] = object->value[0] * (15 - modifier) / 15; // ( 1 - 11 )
			object->value[1] = object->value[1] * (15 - modifier) / 15; // ( 1 - 11 )
			object->value[2] = object->value[2] * (15 - modifier) / 15; // ( 1 - 11 )
			object->value[3] = object->value[3] * (15 - modifier) / 15; // ( 1 - 11 )
		}
	}
	if ((object->item_type == ITEM_ARMOR) && ((object->value[0] < 0) || (object->value[1] < 0) || (object->value[2] < 0) || (object->value[3] < 0))) {
		object->value[0] = 0;
		object->value[1] = 0;
		object->value[2] = 0;
		object->value[3] = 0;
	}
	if ((object->item_type == ITEM_WEAPON) && (unique == FALSE) && (quality < QUALITY_NORMAL)) {
		if ((quality < QUALITY_BAD) || ((quality == QUALITY_BAD) && (number_range(0,1) == 1)))
			object->value[1] = object->value[1] - number_range((-quality - 1), ((-quality - 1) * 2));
		object->value[2] = object->value[2] - number_range((-quality - 1), ((-quality - 1) * 2));

		if (object->value[1] < 1)
			object->value[1] = 1;
		if (object->value[2] < 1)
			object->value[2] = 1;
	}

	if (mob_tier > 1) {
		if (quality >= QUALITY_NORMAL)
			levelr = mob_tier;
		else if (quality == QUALITY_BAD)
			levelr = number_range(mob_tier * 7 / 10, mob_tier);
		else if (quality == QUALITY_WORSE)
			levelr = number_range(mob_tier * 5 / 10, mob_tier * 7 / 10);
		else if (quality == QUALITY_WORST)
			levelr = number_range(mob_tier * 2 / 10, mob_tier * 4 / 10);
	}

	if ((unique == TRUE) || (levelr < 0) || (mob_tier <= 0))
		object->tier_level = 0;
	else
		object->tier_level = levelr;


	if (prefixes != NULL) {
		// Add all the prefixes.
		for (elem = prefixes; elem != NULL; elem = elem->next) {
			alignment += elem->object_index->weight;
			add_modifier(elem->object_index, object, TRUE, " ", mob_tier, chance);
		}
		free_obj_index_list(prefixes, TRUE);
	}
	if (suffixes != NULL) {
		char *connector;
		// Add all the suffixes.
		count = 0;
		for (elem = suffixes; elem != NULL; elem = elem->next, count++) {
			// We want it to be <item name> of <suffix>, <suffix> and <suffix>.
			if (count == 0)
				connector = " of ";
			else if (elem->next == NULL)
				connector = " and ";
			else
				connector = ", ";
			alignment += elem->object_index->weight;
			add_modifier(elem->object_index, object, FALSE, connector, mob_tier, chance);
		}
		free_obj_index_list(suffixes, TRUE);
	}

	// Too evil or too good, make it anti-neutral.
	if (alignment <= -600 || alignment >= 600)
		SET_BIT(object->extra_flags, ITEM_ANTI_NEUTRAL);

	// Opposite alignment flags.
	if (alignment <= -300)
		SET_BIT(object->extra_flags, ITEM_ANTI_GOOD);
	else if (alignment >= 300)
		SET_BIT(object->extra_flags, ITEM_ANTI_EVIL);

	// Set level.
	object->level = URANGE(1, object->level, DEFAULT_MAX_LEVEL);

	// Decode quality index.
	const struct quality_type *quality_struct = quality_lookup_index(quality);
	if (quality_struct == NULL) {
		printf_debug("specific_object: invalid quality (%d).", quality);
		// Skip application of quality modifier.
		goto afterModifier;
	}

	// Apply some extra flags.
	SET_BIT(object->extra_flags, ITEM_NORESTRING);

	//if (quality >= QUALITY_GOOD)
	//	SET_BIT(object->extra_flags, ITEM_NOPURGE);
	//else 
		REMOVE_BIT(object->extra_flags, ITEM_NOPURGE);

	if (quality >= QUALITY_BETTER)
		SET_BIT(object->extra_flags, ITEM_NOSAC);
	else
		REMOVE_BIT(object->extra_flags, ITEM_NOSAC);

	chance = number_percent();

	if ((chance < 35) && (quality == QUALITY_NORMAL))
		SET_BIT(object->extra_flags, ITEM_BURN_PROOF);
	else if ((chance < 45) && (quality == QUALITY_GOOD))
		SET_BIT(object->extra_flags, ITEM_BURN_PROOF);
	else if ((chance < 65) && (quality == QUALITY_BETTER))
		SET_BIT(object->extra_flags, ITEM_BURN_PROOF);
	else if ((chance < 80) && (quality == QUALITY_BEST))
		SET_BIT(object->extra_flags, ITEM_BURN_PROOF);
	else
		REMOVE_BIT(object->extra_flags, ITEM_BURN_PROOF);

	// Add quality word to the keywords, as well as a tag for easy identification.
	sprintf_field(name, object->name, "%s %s %s", quality_struct->name, object->name, (unique ? "unique" : "random"));

	// Apply quality modifier to the item effects.
	// Also, we don't like the word "normal" popping up on items that are normal.
	if (quality != QUALITY_NORMAL) {
		sprintf_field(name, object->short_descr, "%s %s", quality_struct->name, object->short_descr);
		sprintf_field(name, object->description, "%s %s", quality_struct->name, object->description);

		// Adjust price of equipment.
		object->cost = (object->cost) + ((object->cost * quality) / 4); // almost double or 0

		AFFECT_DATA *affect;
		int modifier;
		for (affect = object->affected; affect != NULL; affect = affect->next) {
			if (affect->location == APPLY_MORPH_FORM
			||  affect->where != TO_OBJECT)
				continue;

			modifier = (abs(affect->modifier)*quality)/10;

			// AC/Saves are backwards scales so we need to subtract the modifier.
			if (affect->location == APPLY_AC || affect->location == APPLY_SAVES)
				affect->modifier = affect->modifier-modifier;
			else
				affect->modifier = affect->modifier+modifier;
		}
	}

afterModifier:
	if (!unique) {
		// Format the description, putting a/an or nothing depending on whether the object is plural
		// and whether the first letter is a vowel.
		char *check_plural = strip_spec_char_col(strip_bespaces(object->pIndexData->short_descr));

		if (check_plural[strlen(check_plural)-1] != 's') {
			char *check_vowel = strip_spec_char_col(strip_bespaces(object->short_descr));
			char first = UPPER(check_vowel[0]);
			bool vowel_first = first == 'A' || first == 'E' || first == 'I' || first == 'O' || first == 'U';

			sprintf_field(name, object->description, "A%s %s", vowel_first ? "n" : "", object->description);
			sprintf_field(name, object->short_descr, "a%s %s", vowel_first ? "n" : "", object->short_descr);
		}
		// Colourise and format names.
		char *quality_colour = quality_struct ? quality_struct->colour : "{x";

		sprintf_field(name, object->short_descr, "%s%s{x", quality_colour, object->short_descr);
		sprintf_field(name, object->description, "%s%s lie%s here.{x",
				quality_colour, object->description, check_plural[strlen(check_plural)-1] == 's' ? "" : "s");
	}

	combine_fuzz_effects(object);
	return object;
}

// Random(ish) load capped at pop_level.
OBJ_DATA *random_object(int pop_level, int mob_tier) {
	OBJ_INDEX_LIST *prefixes = NULL, *suffixes = NULL;
	OBJ_INDEX_DATA *obj_index;
	OBJ_DATA *object;
	int chance, pop_min, pop_max;
	int quality;

	// Drop level a bit.
	pop_level -= number_range(0, 40);

	// Always pop at level one at the very least.
	pop_level	= UMAX(1, pop_level);

	// Calculate pop range for unique/object/prefix/suffix loads.
	pop_min		= UMAX(1, pop_level - POP_RANGE);
	pop_max		= pop_level + POP_RANGE/2;

	// Level to unique pop chance:
	// <140 : 2%
	// 141-152 : 3%
	// 153-164 : 4%
	// 165+ : 5%
	chance = UMAX(0, pop_level-140);	// x = 0 or pop_level-140 (whichever higher).
	chance = 2 + UMIN(3, chance/12);	// Min of 2%, max of 5%(2+3) or 2% + 1% per 12 levels over 140.
	if (number_percent() <= chance) {
		// Lets pop a unique!
		char name[MAX_STRING_LENGTH];

		obj_index = level_based_load(VNUM_UNIQUE_LOWER, VNUM_UNIQUE_UPPER, pop_min, pop_max, TRUE);
		object = create_object(obj_index, DEFAULT_MAX_LEVEL);
		sprintf_field(name, object->name, "unique %s", object->name);

		return specific_object(QUALITY_NORMAL, object, NULL, NULL, TRUE, mob_tier);
	}

	// Calculate the quality of the item (ugly, yes, but practical).
	chance = number_range(1,200); //number_percent();
/*	if (chance <= 5)
		quality = QUALITY_WORST;	// 5%
	else if (chance <= 12)
		quality = QUALITY_WORSE;	// 7%
	else if (chance <= 27)
		quality = QUALITY_BAD;		// 15%
	else if (chance <= 85)
		quality = QUALITY_NORMAL;	// 58%
	else if (chance <= 93)
		quality = QUALITY_GOOD;		// 8%
	else if (chance <= 98)
		quality = QUALITY_BETTER;	// 5%
	else
		quality = QUALITY_BEST;		// 2% */

	if (chance <= 10)
		quality = QUALITY_WORST;	// 5%
	else if (chance <= 40)
		quality = QUALITY_WORSE;	// 15%
	else if (chance <= 110)
		quality = QUALITY_BAD;		// 35%
	else if (chance <= 170)
		quality = QUALITY_NORMAL;	// 30%
	else if (chance <= 190)
		quality = QUALITY_GOOD;		// 10%
	else if (chance <= 198)
		quality = QUALITY_BETTER;	// 4%
	else
		quality = QUALITY_BEST;		// 1%

	// Find index data first.
	obj_index = level_based_load(VNUM_RANDOM_LOWER, VNUM_RANDOM_UPPER, pop_min, pop_max, TRUE);

	if (obj_index == NULL) {
		printf_debug("random_object: could not find any objects between %d and %d (level %d).", pop_min, pop_max, pop_level);
		return NULL;
	}

	// Load up the object.
	object = create_object(obj_index, DEFAULT_MAX_LEVEL);
/*
	if (mob_tier > 1) {
		if (quality >= QUALITY_NORMAL)
			levelr = mob_tier;
		else if (quality == QUALITY_BAD)
			levelr = number_range(mob_tier - 10, mob_tier);
		else if (quality == QUALITY_WORSE)
			levelr = number_range(mob_tier - 20, mob_tier - 10);
		else if (quality == QUALITY_WORST)
			levelr = number_range(mob_tier - 30, mob_tier - 20);
	}

	if ((levelr < 0) || (mob_tier <= 0))
		object->tier_level = 0;
	else
		object->tier_level = levelr;
*/

#define MAKE_PS(Index, Vmin, Vmax, List)															\
	case Index:																						\
		obj_index = level_based_load(Vmin, Vmax, 1, pop_max, TRUE);									\
		if (obj_index != NULL && !is_in_obj_index_list(obj_index, List)								\
		&&  (obj_index->item_type == ITEM_TRASH || obj_index->item_type == object->item_type)) {	\
			elem = new_obj_index_list(obj_index);													\
			elem->next = List;																		\
			List = elem;																			\
			/* Not a linear scale - lots of bad prefixes is more expensive than one good prefix. */	\
			pop_max = pop_max - obj_index->level - obj_index->level*ps_count++/6;					\
			break;																					\
		}

	// This should loop round till we've finally filled chosen as many prefixes/suffixes as we can.
	// Case 3 is for repetition - if we fail to find a prefix, we try finding a suffix, then if we
	// can't find a suffix, we fall through to case 4 where we set ps_count to ps_max and end the
	// loop. Vice versa for suffixes for cases 2 and 5.
	OBJ_INDEX_LIST *elem;
	int ps_count = 0, ps_max = number_range(-3, 3) + quality;

	// Drop the min pop level again, so theres more chance of cheaper tags being added.
//	pop_min = UMAX(1, pop_min/2);
	while (ps_count < ps_max && pop_max > 0) {
		// Make sure pop_min is equal or less than pop_max.
//		pop_min = UMIN(pop_min, pop_max);
		// Flip a coin to see what we should add.
		switch (number_bits(1)) {
		MAKE_PS(0, VNUM_PREFIX_LOWER, VNUM_PREFIX_UPPER, prefixes)
		MAKE_PS(2, VNUM_SUFFIX_LOWER, VNUM_SUFFIX_UPPER, suffixes)	// Fall through to here if unsuccessful.
		case 4:
			ps_count = ps_max;	// Exit loop.
			break;
		MAKE_PS(1, VNUM_SUFFIX_LOWER, VNUM_SUFFIX_UPPER, suffixes)
		MAKE_PS(3, VNUM_PREFIX_LOWER, VNUM_PREFIX_UPPER, prefixes)	// Fall through to here if unsuccessful.
		case 5:
			ps_count = ps_max;	// Exit loop.
			break;
		}
	}

	// Set as enchanted.
	object->enchanted = TRUE;

	// specific_object does all the fancy stuff with the object, prefixes and suffixes.
	return specific_object(quality, object, prefixes, suffixes, FALSE, mob_tier);
}