asgard/
asgard/.settings/
asgard/area/
asgard/data/clans/
asgard/data/clans/history/
asgard/data/rosters/
asgard/src/notice/
/*
 * global_mods.c
 *
 *  Created on: 6 May 2011
 *      Author: deryck
 */

#include <stdlib.h>
#include <stdio.h>
#include "merc.h"
#include "global_mods.h"

DECLARE_DO_FUN(do_announce);

#define GMOD_FILE				"globalmods.txt"
#define GMOD_MAX_MULT			3.0f
#define GMOD_UPDATE_INTERVAL	2*PULSE_TICK		// Roughly every minute.
#define GMOD_TICKET_DURATION	60*60				// One hour.

GMOD_TICKET *gmod_tickets;
static GMOD_TICKET *gmod_free_tickets;

static float gmod_current[GMOD_MAX];
static bool gmod_initialised = FALSE;
static int gmod_update_counter = GMOD_UPDATE_INTERVAL;	// Ensures it runs on first update.

static GMOD_TICKET *gmod_new_ticket(GMOD_TYPE type, float multiplier, time_t expiry_time) {
	GMOD_TICKET *ticket;

	if (gmod_free_tickets == NULL)
		ticket = malloc(sizeof(GMOD_TICKET));
	else {
		ticket = gmod_free_tickets;
		gmod_free_tickets = gmod_free_tickets->next;
	}

	if (ticket != NULL) {
		ticket->type = type;
		ticket->multiplier = multiplier;
		ticket->expiry_time = expiry_time;
	}

	return ticket;
}

static void gmod_free_ticket(GMOD_TICKET *ticket) {
	if (ticket != NULL) {
		ticket->next = gmod_free_tickets;
		gmod_free_tickets = ticket;
	}
}

static void gmod_save() {
	FILE *fp;

	if ((fp = get_temp_file()) == NULL) {
		printf_debug("gmod_save: could not open temporary file.");
		return;
	}

	GMOD_TICKET *ticket;
	for (ticket = gmod_tickets; ticket != NULL; ticket = ticket->next) {
		fprintf(fp, "%d %d %ld\n", ticket->type,
			(int)(ticket->multiplier*100.0f), ticket->expiry_time);
	}
	fprintf(fp, "-1\n");

	if (!close_write_file(GMOD_FILE))
		printf_debug("gmod_save: could not save file as "GMOD_FILE".");
}

static void gmod_load() {
	FILE *fp;

	gmod_initialised = TRUE;
	if ((fp = fopen(GMOD_FILE, "r")) == NULL) {
		printf_debug("gmod_load: could not open "GMOD_FILE".");
		return;
	}

	GMOD_TICKET *ticket;
	int type, time;
	float multiplier;
	while ((type = fread_number(fp)) >= 0) {
		multiplier = ((float)fread_number(fp))/100.0f;
		time = fread_number(fp);
		ticket = gmod_new_ticket(type, multiplier, time);
		if (ticket == NULL) {
			printf_debug("gmod_load: couldn't allocate memory for a ticket.");
			return;
		}
		ticket->next = gmod_tickets;
		gmod_tickets = ticket;
	}

	fclose(fp);
}

void gmod_update(bool force) {
	GMOD_TICKET *ticket, *ticket_prev = NULL, *ticket_next;

	// Update every GMOD_UPDATE_INTERVAL.
	if (++gmod_update_counter >= GMOD_UPDATE_INTERVAL || force)
		gmod_update_counter = 0;
	else
		return;

	// Load any saved values.
	if (!gmod_initialised)
		gmod_load();

	// Reset multipliers to 1x.
	int i;
	for (i = 0; i < GMOD_MAX; i++)
		gmod_current[i] = 1.0f;

	// Loop through tickets and update the modifiers.
	bool changed = FALSE;
	for (ticket = gmod_tickets; ticket != NULL; ticket = ticket_next) {
		ticket_next = ticket->next;

		if (ticket->expiry_time <= current_time || ticket->multiplier <= 0.0f
		||  ticket->type < 0 || ticket->type >= GMOD_MAX) {
			char buf[MAX_STRING_LENGTH];
			// Remove expired/invalid ticket.
			if (ticket_prev != NULL)
				ticket_prev->next = ticket_next;
			else
				gmod_tickets = ticket_next;
			sprintf(buf, "A {G+%.2f{Yx %s modifier ticket has {rexpired{Y.",
				ticket->multiplier, gmod_get_name(ticket->type));
			do_announce(NULL, buf);
			gmod_free_ticket(ticket);
			changed = TRUE;
			continue;
		}

		gmod_current[ticket->type] += ticket->multiplier;
		ticket_prev = ticket;
	}

	// Save any changes.
	if (changed || force)
		gmod_save();
}

#define CHECK_TYPE_BOUNDS(Type, RetVal)					\
	if ((Type) < 0 || (Type) >= GMOD_MAX) {				\
		printf_debug("Invalid GMOD_TYPE: %d.", (Type));	\
		return RetVal;									\
	}

float gmod_get_mult(GMOD_TYPE type) {
	CHECK_TYPE_BOUNDS(type, 1.0f);
	return gmod_current[type];
}

char * gmod_get_name(GMOD_TYPE type) {
	static char *typenames[] = {
		"experience",
		"quest rewards",
	};
	CHECK_TYPE_BOUNDS(type, "unknown");
	return typenames[type];
}

bool gmod_apply(GMOD_TYPE type, int *value) {
	float mult = gmod_get_mult(type);
	if (mult > 1.0f) {
		if (value != NULL && *value > 0)
			*value = (int) (((float)*value) * mult);
		return TRUE;
	}
	return FALSE;
}

bool gmod_add(GMOD_TYPE type, float multiplier) {
	CHECK_TYPE_BOUNDS(type, FALSE);
	float current_mult = gmod_get_mult(type);

	if (current_mult + multiplier > GMOD_MAX_MULT)
		return FALSE;

	GMOD_TICKET *ticket = gmod_new_ticket(type, multiplier, current_time+GMOD_TICKET_DURATION);
	if (ticket == NULL) {
		printf_debug("gmod_add: couldn't allocate memory for a ticket.");
		return FALSE;
	}

	// Add ticket to queue, force an update and save state.
	ticket->next = gmod_tickets;
	gmod_tickets = ticket;
	gmod_update(TRUE);
	return TRUE;
}