Bläddra i källkod

Reverted all mercenary changes to mapserver homunculus code

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@13135 54d463be-8e91-2dee-dedb-b68131a5f0ec
ultramage 16 år sedan
förälder
incheckning
d4bcc0a143
4 ändrade filer med 1084 tillägg och 1515 borttagningar
  1. 0 1169
      src/map/homunculus.c
  2. 0 89
      src/map/homunculus.h
  3. 1016 231
      src/map/mercenary.c
  4. 68 26
      src/map/mercenary.h

+ 0 - 1169
src/map/homunculus.c

@@ -1,1169 +0,0 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include "../common/cbasetypes.h"
-#include "../common/malloc.h"
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/mmo.h"
-#include "../common/showmsg.h"
-#include "../common/utils.h"
-
-#include "log.h"
-#include "clif.h"
-#include "chrif.h"
-#include "intif.h"
-#include "itemdb.h"
-#include "map.h"
-#include "pc.h"
-#include "status.h"
-#include "skill.h"
-#include "mob.h"
-#include "pet.h"
-#include "battle.h"
-#include "party.h"
-#include "guild.h"
-#include "atcommand.h"
-#include "script.h"
-#include "npc.h"
-#include "trade.h"
-#include "unit.h"
-
-#include "homunculus.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-//Better equiprobability than rand()% [orn]
-#define rand(a, b) (a+(int) ((float)(b-a+1)*rand()/(RAND_MAX+1.0)))
-
-struct s_homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS];	//[orn]
-struct skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_SKILL_TREE];
-
-static int merc_hom_hungry(int tid, unsigned int tick, int id, intptr data);
-
-static unsigned int hexptbl[MAX_LEVEL];
-
-//For holding the view data of npc classes. [Skotlex]
-static struct view_data hom_viewdb[MAX_HOMUNCULUS_CLASS];
-
-struct view_data* merc_get_hom_viewdata(int class_)
-{	//Returns the viewdata for homunculus
-	if (homdb_checkid(class_))
-		return &hom_viewdb[class_-HM_CLASS_BASE];
-	return NULL;
-}
-
-void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp)
-{
-	clif_hominfo(hd->master,hd,0);
-}
-
-int merc_hom_dead(struct homun_data *hd, struct block_list *src)
-{
-	//There's no intimacy penalties on death (from Tharis)
-	struct map_session_data *sd = hd->master;
-
-	clif_emotion(&hd->bl, 16) ;	//wah
-
-	//Delete timers when dead.
-	merc_hom_hungry_timer_delete(hd);
-	hd->homunculus.hp = 0;
-
-	if (!sd) //unit remove map will invoke unit free
-		return 3;
-
-	clif_emotion(&sd->bl, 28) ; //sob
-	//Remove from map (if it has no intimacy, it is auto-removed from memory)
-	return 3;
-}
-
-//Vaporize a character's homun. If flag, HP needs to be 80% or above.
-int merc_hom_vaporize(struct map_session_data *sd, int flag)
-{
-	struct homun_data *hd;
-
-	nullpo_retr(0, sd);
-
-	hd = sd->hd;
-	if (!hd || hd->homunculus.vaporize)
-		return 0;
-
-	if (status_isdead(&hd->bl))
-		return 0; //Can't vaporize a dead homun.
-
-	if (flag && get_percentage(hd->battle_status.hp, hd->battle_status.max_hp) < 80)
-		return 0;
-
-	hd->regen.state.block = 3; //Block regen while vaporized.
-	//Delete timers when vaporized.
-	merc_hom_hungry_timer_delete(hd);
-	hd->homunculus.vaporize = 1;
-	if(battle_config.hom_setting&0x40)
-		memset(hd->blockskill, 0, sizeof(hd->blockskill));
-	clif_hominfo(sd, sd->hd, 0);
-	merc_save(hd);
-	return unit_remove_map(&hd->bl, 0);
-}
-
-//delete a homunculus, completely "killing it".
-//Emote is the emotion the master should use, send negative to disable.
-int merc_hom_delete(struct homun_data *hd, int emote)
-{
-	struct map_session_data *sd;
-	nullpo_retr(0, hd);
-	sd = hd->master;
-
-	if (!sd)
-		return unit_free(&hd->bl,1);
-
-	if (emote >= 0)
-		clif_emotion(&sd->bl, emote);
-
-	//This makes it be deleted right away.
-	hd->homunculus.intimacy = 0;
-	// Send homunculus_dead to client
-	hd->homunculus.hp = 0;
-	clif_hominfo(sd, hd, 0);
-	return unit_remove_map(&hd->bl,0);
-}
-
-int merc_hom_calc_skilltree(struct homun_data *hd)
-{
-	int i,id=0 ;
-	int j,f=1;
-	int c=0;
-
-	nullpo_retr(0, hd);
-	c = hd->homunculus.class_ - HM_CLASS_BASE;
-
-	for(i=0;i < MAX_SKILL_TREE && (id = hskill_tree[c][i].id) > 0;i++)
-	{
-		if(hd->homunculus.hskill[id-HM_SKILLBASE].id)
-			continue; //Skill already known.
-		if(!battle_config.skillfree)
-		{
-			for(j=0;j<5;j++)
-			{
-				if( hskill_tree[c][i].need[j].id &&
-					merc_hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv)
-				{
-					f=0;
-					break;
-				}
-			}
-		}
-		if (f)
-			hd->homunculus.hskill[id-HM_SKILLBASE].id = id ;
-	}
-	return 0;
-}
-
-int merc_hom_checkskill(struct homun_data *hd,int skill_id)
-{
-	int i = skill_id - HM_SKILLBASE;
-	if(!hd)
-		return 0;
-
-	if(hd->homunculus.hskill[i].id == skill_id)
-		return (hd->homunculus.hskill[i].lv);
-
-	return 0;
-}
-
-int merc_skill_tree_get_max(int id, int b_class){
-	int i, skillid;
-	b_class -= HM_CLASS_BASE;
-	for(i=0;(skillid=hskill_tree[b_class][i].id)>0;i++)
-		if (id == skillid)
-			return hskill_tree[b_class][i].max;
-	return skill_get_max(id);
-}
-
-void merc_hom_skillup(struct homun_data *hd,int skillnum)
-{
-	int i = 0 ;
-	nullpo_retv(hd);
-
-	if(hd->homunculus.vaporize)
-		return;
-
-	i = skillnum - HM_SKILLBASE;
-	if(hd->homunculus.skillpts > 0 &&
-		hd->homunculus.hskill[i].id &&
-		hd->homunculus.hskill[i].flag == 0 && //Don't allow raising while you have granted skills. [Skotlex]
-		hd->homunculus.hskill[i].lv < merc_skill_tree_get_max(skillnum, hd->homunculus.class_)
-		)
-	{
-		hd->homunculus.hskill[i].lv++;
-		hd->homunculus.skillpts-- ;
-		status_calc_homunculus(hd,0);
-		if (hd->master) {
-			clif_homskillup(hd->master, skillnum);
-			clif_hominfo(hd->master,hd,0);
-			clif_homskillinfoblock(hd->master);
-		}
-	}
-}
-
-int merc_hom_levelup(struct homun_data *hd)
-{
-	struct s_homunculus *hom;
-	struct h_stats *min, *max;
-	int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ;
-	int growth_max_hp, growth_max_sp ;
-	char output[256] ;
-
-	if (hd->homunculus.level == MAX_LEVEL || !hd->exp_next || hd->homunculus.exp < hd->exp_next)
-		return 0 ;
-
-	hom = &hd->homunculus;
-	hom->level++ ;
-	if (!(hom->level % 3))
-		hom->skillpts++ ;	//1 skillpoint each 3 base level
-
-	hom->exp -= hd->exp_next ;
-	hd->exp_next = hexptbl[hom->level - 1] ;
-
-	max  = &hd->homunculusDB->gmax;
-	min  = &hd->homunculusDB->gmin;
-
-	growth_max_hp = rand(min->HP, max->HP);
-	growth_max_sp = rand(min->SP, max->SP);
-	growth_str = rand(min->str, max->str);
-	growth_agi = rand(min->agi, max->agi);
-	growth_vit = rand(min->vit, max->vit);
-	growth_dex = rand(min->dex, max->dex);
-	growth_int = rand(min->int_,max->int_);
-	growth_luk = rand(min->luk, max->luk);
-
-	//Aegis discards the decimals in the stat growth values!
-	growth_str-=growth_str%10;
-	growth_agi-=growth_agi%10;
-	growth_vit-=growth_vit%10;
-	growth_dex-=growth_dex%10;
-	growth_int-=growth_int%10;
-	growth_luk-=growth_luk%10;
-
-	hom->max_hp += growth_max_hp;
-	hom->max_sp += growth_max_sp;
-	hom->str += growth_str;
-	hom->agi += growth_agi;
-	hom->vit += growth_vit;
-	hom->dex += growth_dex;
-	hom->int_+= growth_int;
-	hom->luk += growth_luk;
-
-	if ( battle_config.homunculus_show_growth ) {
-		sprintf(output,
-			"Growth: hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ",
-			growth_max_hp, growth_max_sp,
-			growth_str/10.0, growth_agi/10.0, growth_vit/10.0,
-			growth_int/10.0, growth_dex/10.0, growth_luk/10.0);
-		clif_disp_onlyself(hd->master,output,strlen(output));
-	}
-	return 1 ;
-}
-
-int merc_hom_change_class(struct homun_data *hd, short class_)
-{
-	int i;
-	i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
-	if(i < 0)
-		return 0;
-	hd->homunculusDB = &homunculus_db[i];
-	hd->homunculus.class_ = class_;
-	status_set_viewdata(&hd->bl, class_);
-	merc_hom_calc_skilltree(hd);
-	return 1;
-}
-
-int merc_hom_evolution(struct homun_data *hd)
-{
-	struct s_homunculus *hom;
-	struct h_stats *max, *min;
-	struct map_session_data *sd;
-	nullpo_retr(0, hd);
-
-	if(!hd->homunculusDB->evo_class || hd->homunculus.class_ == hd->homunculusDB->evo_class)
-	{
-		clif_emotion(&hd->bl, 4) ;	//swt
-		return 0 ;
-	}
-	sd = hd->master;
-	if (!sd)
-		return 0;
-
-	if (!merc_hom_change_class(hd, hd->homunculusDB->evo_class)) {
-		ShowError("merc_hom_evolution: Can't evolve homunc from %d to %d", hd->homunculus.class_, hd->homunculusDB->evo_class);
-		return 0;
-	}
-
-	//Apply evolution bonuses
-	hom = &hd->homunculus;
-	max = &hd->homunculusDB->emax;
-	min = &hd->homunculusDB->emin;
-	hom->max_hp += rand(min->HP, max->HP);
-	hom->max_sp += rand(min->SP, max->SP);
-	hom->str += 10*rand(min->str, max->str);
-	hom->agi += 10*rand(min->agi, max->agi);
-	hom->vit += 10*rand(min->vit, max->vit);
-	hom->int_+= 10*rand(min->int_,max->int_);
-	hom->dex += 10*rand(min->dex, max->dex);
-	hom->luk += 10*rand(min->luk, max->luk);
-	hom->intimacy = 500;
-
-	unit_remove_map(&hd->bl, 0);
-	map_addblock(&hd->bl);
-
-	clif_spawn(&hd->bl);
-	clif_emotion(&sd->bl, 21);	//no1
-	clif_misceffect2(&hd->bl,568);
-
-	//status_Calc flag&1 will make current HP/SP be reloaded from hom structure
-	hom->hp = hd->battle_status.hp;
-	hom->sp = hd->battle_status.sp;
-	status_calc_homunculus(hd,1);
-
-	if (!(battle_config.hom_setting&0x2))
-		skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
-
-	return 1 ;
-}
-
-int merc_hom_gainexp(struct homun_data *hd,int exp)
-{
-	if(hd->homunculus.vaporize)
-		return 1;
-
-	if( hd->exp_next == 0 ) {
-		hd->homunculus.exp = 0 ;
-		return 0;
-	}
-
-	hd->homunculus.exp += exp;
-
-	if(hd->homunculus.exp < hd->exp_next) {
-		clif_hominfo(hd->master,hd,0);
-		return 0;
-	}
-
- 	//levelup
-	do
-	{
-		merc_hom_levelup(hd) ;
-	}
-	while(hd->homunculus.exp > hd->exp_next && hd->exp_next != 0 );
-
-	if( hd->exp_next == 0 )
-		hd->homunculus.exp = 0 ;
-
-	clif_misceffect2(&hd->bl,568);
-	status_calc_homunculus(hd,0);
-	status_percent_heal(&hd->bl, 100, 100);
-	return 0;
-}
-
-// Return the new value
-int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value)
-{
-	if( battle_config.homunculus_friendly_rate != 100 )
-		value = (value * battle_config.homunculus_friendly_rate) / 100;
-
-	if( hd->homunculus.intimacy + value <= 100000 )
-		hd->homunculus.intimacy += value;
-	else
-		hd->homunculus.intimacy = 100000;
-
-	return hd->homunculus.intimacy;
-}
-
-// Return 0 if decrease fails or intimacy became 0 else the new value
-int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value)
-{
-	if (hd->homunculus.intimacy >= value)
-		hd->homunculus.intimacy -= value;
-	else
-		hd->homunculus.intimacy = 0;
-
-	return hd->homunculus.intimacy;
-}
-
-void merc_hom_heal(struct homun_data *hd,int hp,int sp)
-{
-	clif_hominfo(hd->master,hd,0);
-}
-
-void merc_save(struct homun_data *hd)
-{
-	// copy data that must be saved in homunculus struct ( hp / sp )
-	TBL_PC * sd = hd->master;
-	//Do not check for max_hp/max_sp caps as current could be higher to max due
-	//to status changes/skills (they will be capped as needed upon stat
-	//calculation on login)
-	hd->homunculus.hp = hd->battle_status.hp;
-	hd->homunculus.sp = hd->battle_status.sp;
-	intif_homunculus_requestsave(sd->status.account_id, &hd->homunculus);
-}
-
-int merc_menu(struct map_session_data *sd,int menunum)
-{
-	nullpo_retr(0, sd);
-	if (sd->hd == NULL)
-		return 1;
-
-	switch(menunum) {
-		case 0:
-			break;
-		case 1:
-			merc_hom_food(sd, sd->hd);
-			break;
-		case 2:
-			merc_hom_delete(sd->hd, -1);
-			break;
-		default:
-			ShowError("merc_menu : unknown menu choice : %d\n", menunum) ;
-			break;
-	}
-	return 0;
-}
-
-int merc_hom_food(struct map_session_data *sd, struct homun_data *hd)
-{
-	int i, foodID, emotion;
-	char mes[255];
-
-	if(hd->homunculus.vaporize)
-		return 1 ;
-
-	foodID = hd->homunculusDB->foodID;
-	i = pc_search_inventory(sd,foodID);
-	if(i < 0) {
-		clif_hom_food(sd,foodID,0);
-		return 1;
-	}
-	pc_delitem(sd,i,1,0);
-
-	if ( hd->homunculus.hunger >= 91 ) {
-		merc_hom_decrease_intimacy(hd, 50);
-		emotion = 16;
-	} else if ( hd->homunculus.hunger >= 76 ) {
-		merc_hom_decrease_intimacy(hd, 5);
-		emotion = 19;
-	} else if ( hd->homunculus.hunger >= 26 ) {
-		merc_hom_increase_intimacy(hd, 75);
-		emotion = 2;
-	} else if ( hd->homunculus.hunger >= 11 ) {
-		merc_hom_increase_intimacy(hd, 100);
-		emotion = 2;
-	} else {
-		merc_hom_increase_intimacy(hd, 50);
-		emotion = 2;
-	}
-
-	hd->homunculus.hunger += 10;	//dunno increase value for each food
-	if(hd->homunculus.hunger > 100)
-		hd->homunculus.hunger = 100;
-
-	clif_emotion(&hd->bl,emotion) ;
-	snprintf(mes, sizeof mes,"%s : Gracias!!",hd->homunculus.name);
-	clif_message(&hd->bl,mes);
-
-	clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
-	clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
-	clif_hom_food(sd,foodID,1);
-
-	// Too much food :/
-	if(hd->homunculus.intimacy == 0)
-		return merc_hom_delete(sd->hd, 23); //omg
-
-	return 0;
-}
-
-static int merc_hom_hungry(int tid, unsigned int tick, int id, intptr data)
-{
-	struct map_session_data *sd;
-	struct homun_data *hd;
-
-	sd=map_id2sd(id);
-	if(!sd)
-		return 1;
-
-	if(!sd->status.hom_id || !(hd=sd->hd))
-		return 1;
-
-	if(hd->hungry_timer != tid){
-		ShowError("merc_hom_hungry_timer %d != %d\n",hd->hungry_timer,tid);
-		return 0;
-	}
-
-	hd->hungry_timer = INVALID_TIMER;
-
-	hd->homunculus.hunger-- ;
-	if(hd->homunculus.hunger <= 10) {
-		clif_emotion(&hd->bl, 6) ;	//an
-	} else if(hd->homunculus.hunger == 25) {
-		clif_emotion(&hd->bl, 20) ;	//hmm
-	} else if(hd->homunculus.hunger == 75) {
-		clif_emotion(&hd->bl, 33) ;	//ok
-	}
-
-	if(hd->homunculus.hunger < 0) {
-		hd->homunculus.hunger = 0;
-		// Delete the homunculus if intimacy <= 100
-		if ( !merc_hom_decrease_intimacy(hd, 100) )
-			return merc_hom_delete(hd, 23); //omg
-		clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
-	}
-
-	clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
-	hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); //simple Fix albator
-	return 0;
-}
-
-int merc_hom_hungry_timer_delete(struct homun_data *hd)
-{
-	nullpo_retr(0, hd);
-	if(hd->hungry_timer != -1) {
-		delete_timer(hd->hungry_timer,merc_hom_hungry);
-		hd->hungry_timer = INVALID_TIMER;
-	}
-	return 1;
-}
-
-int merc_hom_change_name(struct map_session_data *sd,char *name)
-{
-	int i;
-	struct homun_data *hd;
-	nullpo_retr(1, sd);
-
-	hd = sd->hd;
-	if (!merc_is_hom_active(hd))
-		return 1;
-	if(hd->homunculus.rename_flag && !battle_config.hom_rename)
-		return 1;
-
-	for(i=0;i<NAME_LENGTH && name[i];i++){
-		if( !(name[i]&0xe0) || name[i]==0x7f)
-			return 1;
-	}
-
-	return intif_rename_hom(sd, name);
-}
-
-int merc_hom_change_name_ack(struct map_session_data *sd, char* name, int flag)
-{
-	struct homun_data *hd = sd->hd;
-	if (!merc_is_hom_active(hd)) return 0;
-	if (!flag) {
-		clif_displaymessage(sd->fd, msg_txt(280)); // You cannot use this name
-		return 0;
-	}
-	strncpy(hd->homunculus.name,name,NAME_LENGTH);
-	clif_charnameack (0,&hd->bl);
-	hd->homunculus.rename_flag = 1;
-	clif_hominfo(sd,hd,0);
-	return 1;
-}
-
-int search_homunculusDB_index(int key,int type)
-{
-	int i;
-
-	for(i=0;i<MAX_HOMUNCULUS_CLASS;i++) {
-		if(homunculus_db[i].base_class <= 0)
-			continue;
-		switch(type) {
-			case HOMUNCULUS_CLASS:
-				if(homunculus_db[i].base_class == key ||
-					homunculus_db[i].evo_class == key)
-					return i;
-				break;
-			case HOMUNCULUS_FOOD:
-				if(homunculus_db[i].foodID == key)
-					return i;
-				break;
-			default:
-				return -1;
-		}
-	}
-	return -1;
-}
-
-// Create homunc structure
-int merc_hom_alloc(struct map_session_data *sd, struct s_homunculus *hom)
-{
-	struct homun_data *hd;
-	int i = 0;
-	nullpo_retr(1, sd);
-
-	Assert((sd->status.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd);
-
-	i = search_homunculusDB_index(hom->class_,HOMUNCULUS_CLASS);
-	if(i < 0) {
-		ShowError("merc_hom_alloc: unknown class [%d] for homunculus '%s', requesting deletion.\n", hom->class_, hom->name);
-		sd->status.hom_id = 0;
-		intif_homunculus_requestdelete(hom->hom_id);
-		return 1;
-	}
-	sd->hd = hd = (struct homun_data*)aCalloc(1,sizeof(struct homun_data));
-	hd->bl.type = BL_HOM;
-	hd->bl.id = npc_get_new_npc_id();
-
-	hd->master = sd;
-	hd->homunculusDB = &homunculus_db[i];
-	memcpy(&hd->homunculus, hom, sizeof(struct s_homunculus));
-	hd->exp_next = hexptbl[hd->homunculus.level - 1];
-
-	status_set_viewdata(&hd->bl, hd->homunculus.class_);
-	status_change_init(&hd->bl);
-	unit_dataset(&hd->bl);
-	hd->ud.dir = sd->ud.dir;
-
-	// Find a random valid pos around the player
-	hd->bl.m = sd->bl.m;
-	hd->bl.x = sd->bl.x;
-	hd->bl.y = sd->bl.y;
-	unit_calc_pos(&hd->bl, sd->bl.x, sd->bl.y, sd->ud.dir);
-	hd->bl.x = hd->ud.to_x;
-	hd->bl.y = hd->ud.to_y;
-
-	map_addiddb(&hd->bl);
-	status_calc_homunculus(hd,1);
-
-	hd->hungry_timer = INVALID_TIMER;
-	return 0;
-}
-
-void merc_hom_init_timers(struct homun_data * hd)
-{
-	if (hd->hungry_timer == -1)
-		hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,merc_hom_hungry,hd->master->bl.id,0);
-	hd->regen.state.block = 0; //Restore HP/SP block.
-}
-
-int merc_call_homunculus(struct map_session_data *sd)
-{
-	struct homun_data *hd;
-
-	if (!sd->status.hom_id) //Create a new homun.
-		return merc_create_homunculus_request(sd, HM_CLASS_BASE + rand(0, 7)) ;
-
-	// If homunc not yet loaded, load it
-	if (!sd->hd)
-		return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
-
-	hd = sd->hd;
-
-	if (!hd->homunculus.vaporize)
-		return 0; //Can't use this if homun wasn't vaporized.
-
-	merc_hom_init_timers(hd);
-	hd->homunculus.vaporize = 0;
-	if (hd->bl.prev == NULL)
-	{	//Spawn him
-		hd->bl.x = sd->bl.x;
-		hd->bl.y = sd->bl.y;
-		hd->bl.m = sd->bl.m;
-		map_addblock(&hd->bl);
-		clif_spawn(&hd->bl);
-		clif_send_homdata(sd,SP_ACK,0);
-		clif_hominfo(sd,hd,1);
-		clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
-		clif_homskillinfoblock(sd);
-		if (battle_config.slaves_inherit_speed&1)
-			status_calc_bl(&hd->bl, SCB_SPEED);
-		merc_save(hd);
-	} else
-		//Warp him to master.
-		unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,0);
-	return 1;
-}
-
-// Recv homunculus data from char server
-int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag)
-{
-	struct map_session_data *sd;
-	struct homun_data *hd;
-
-	sd = map_id2sd(account_id);
-	if(!sd)
-		return 0;
-	if (sd->status.char_id != sh->char_id)
-	{
-		if (sd->status.hom_id == sh->hom_id)
-			sh->char_id = sd->status.char_id; //Correct char id.
-		else
-			return 0;
-	}
-	if(!flag) { // Failed to load
-		sd->status.hom_id = 0;
-		return 0;
-	}
-
-	if (!sd->status.hom_id) //Hom just created.
-		sd->status.hom_id = sh->hom_id;
-	if (sd->hd) //uh? Overwrite the data.
-		memcpy(&sd->hd->homunculus, sh, sizeof(struct s_homunculus));
-	else
-		merc_hom_alloc(sd, sh);
-
-	hd = sd->hd;
-	if(hd && hd->homunculus.hp && !hd->homunculus.vaporize && hd->bl.prev == NULL && sd->bl.prev != NULL)
-	{
-		map_addblock(&hd->bl);
-		clif_spawn(&hd->bl);
-		clif_send_homdata(sd,SP_ACK,0);
-		clif_hominfo(sd,hd,1);
-		clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
-		clif_homskillinfoblock(sd);
-		merc_hom_init_timers(hd);
-	}
-	return 1;
-}
-
-// Ask homunculus creation to char server
-int merc_create_homunculus_request(struct map_session_data *sd, int class_)
-{
-	struct s_homunculus homun;
-	struct h_stats *base;
-	int i;
-
-	nullpo_retr(1, sd);
-
-	i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
-	if(i < 0) return 0;
-
-	memset(&homun, 0, sizeof(struct s_homunculus));
-	//Initial data
-	strncpy(homun.name, homunculus_db[i].name, NAME_LENGTH-1);
-	homun.class_ = class_;
-	homun.level = 1;
-	homun.hunger = 32; //32%
-	homun.intimacy = 2100; //21/1000
-	homun.char_id = sd->status.char_id;
-
-	homun.hp = 10 ;
-	base = &homunculus_db[i].base;
-	homun.max_hp = base->HP;
-	homun.max_sp = base->SP;
-	homun.str = base->str *10;
-	homun.agi = base->agi *10;
-	homun.vit = base->vit *10;
-	homun.int_= base->int_*10;
-	homun.dex = base->dex *10;
-	homun.luk = base->luk *10;
-
-	// Request homunculus creation
-	intif_homunculus_create(sd->status.account_id, &homun);
-	return 1;
-}
-
-int merc_resurrect_homunculus(struct map_session_data* sd, unsigned char per, short x, short y)
-{
-	struct homun_data* hd;
-	nullpo_retr(0, sd);
-
-	if (!sd->status.hom_id)
-		return 0; // no homunculus
-
-	if (!sd->hd) //Load homun data;
-		return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
-
-	hd = sd->hd;
-
-  	if (hd->homunculus.vaporize)
-		return 0; // vaporized homunculi need to be 'called'
-
-	if (!status_isdead(&hd->bl))
-		return 0; // already alive
-
-	merc_hom_init_timers(hd);
-
-	if (!hd->bl.prev)
-	{	//Add it back to the map.
-		hd->bl.m = sd->bl.m;
-		hd->bl.x = x;
-		hd->bl.y = y;
-		map_addblock(&hd->bl);
-		clif_spawn(&hd->bl);
-	}
-	status_revive(&hd->bl, per, 0);
-	return 1;
-}
-
-void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp)
-{
-	struct map_session_data *sd = hd->master;
-	hd->homunculus.hp = hd->battle_status.hp;
-	if (!sd)
-		return;
-	clif_send_homdata(sd,SP_ACK,0);
-	clif_hominfo(sd,hd,1);
-	clif_hominfo(sd,hd,0);
-	clif_homskillinfoblock(sd);
-}
-
-void merc_reset_stats(struct homun_data *hd)
-{	//Resets a homunc stats back to zero (but doesn't touches hunger or intimacy)
-	struct s_homunculus_db *db;
-	struct s_homunculus *hom;
-	struct h_stats *base;
-	hom = &hd->homunculus;
-	db = hd->homunculusDB;
-	base = &db->base;
-	hom->level = 1;
-	hom->hp = 10;
-	hom->max_hp = base->HP;
-	hom->max_sp = base->SP;
-	hom->str = base->str *10;
-	hom->agi = base->agi *10;
-	hom->vit = base->vit *10;
-	hom->int_= base->int_*10;
-	hom->dex = base->dex *10;
-	hom->luk = base->luk *10;
-	hom->exp = 0;
-	hd->exp_next = hexptbl[0];
-	memset(&hd->homunculus.hskill, 0, sizeof hd->homunculus.hskill);
-	hd->homunculus.skillpts = 0;
-}
-
-int merc_hom_shuffle(struct homun_data *hd)
-{
-	struct map_session_data *sd;
-	int lv, i, skillpts;
-	unsigned int exp;
-	struct s_skill b_skill[MAX_HOMUNSKILL];
-
-	if (!merc_is_hom_active(hd))
-		return 0;
-
-	sd = hd->master;
-	lv = hd->homunculus.level;
-	exp = hd->homunculus.exp;
-	memcpy(&b_skill, &hd->homunculus.hskill, sizeof(b_skill));
-	skillpts = hd->homunculus.skillpts;
-	//Reset values to level 1.
-	merc_reset_stats(hd);
-	//Level it back up
-	for (i = 1; i < lv && hd->exp_next; i++){
-		hd->homunculus.exp += hd->exp_next;
-		merc_hom_levelup(hd);
-	}
-
-	if(hd->homunculus.class_ == hd->homunculusDB->evo_class) {
-		//Evolved bonuses
-		struct s_homunculus *hom = &hd->homunculus;
-		struct h_stats *max = &hd->homunculusDB->emax, *min = &hd->homunculusDB->emin;
-		hom->max_hp += rand(min->HP, max->HP);
-		hom->max_sp += rand(min->SP, max->SP);
-		hom->str += 10*rand(min->str, max->str);
-		hom->agi += 10*rand(min->agi, max->agi);
-		hom->vit += 10*rand(min->vit, max->vit);
-		hom->int_+= 10*rand(min->int_,max->int_);
-		hom->dex += 10*rand(min->dex, max->dex);
-		hom->luk += 10*rand(min->luk, max->luk);
-	}
-
-	hd->homunculus.exp = exp;
-	memcpy(&hd->homunculus.hskill, &b_skill, sizeof(b_skill));
-	hd->homunculus.skillpts = skillpts;
-	clif_homskillinfoblock(sd);
-	status_calc_homunculus(hd,0);
-	status_percent_heal(&hd->bl, 100, 100);
-	clif_misceffect2(&hd->bl,568);
-
-	return 1;
-}
-
-int read_homunculusdb(void)
-{
-	FILE *fp;
-	char line[1024], *p;
-	int i, k, classid;
-	int j = 0;
-	const char *filename[]={"homunculus_db.txt","homunculus_db2.txt"};
-	char *str[50];
-	struct s_homunculus_db *db;
-
-	memset(homunculus_db,0,sizeof(homunculus_db));
-	for(i = 0; i<2; i++)
-	{
-		sprintf(line, "%s/%s", db_path, filename[i]);
-		fp = fopen(line,"r");
-		if(!fp){
-			if(i != 0)
-				continue;
-			ShowError("read_homunculusdb : can't read %s\n", line);
-			return -1;
-		}
-
-		while(fgets(line, sizeof(line), fp) && j < MAX_HOMUNCULUS_CLASS)
-		{
-			if(line[0] == '/' && line[1] == '/')
-				continue;
-
-			k = 0;
-			p = strtok (line,",");
-			while (p != NULL && k < 50)
-			{
-				str[k++] = p;
-				p = strtok (NULL, ",");
-			}
-			if (k < 50 )
-			{
-				ShowError("read_homunculusdb : Incorrect number of columns at %s, homunculus %d. Read %d columns, 50 are needed.\n", filename[i], j+1, k);
-				continue;
-			}
-
-			//Base Class,Evo Class
-			classid = atoi(str[0]);
-			if (classid < HM_CLASS_BASE || classid > HM_CLASS_MAX)
-			{
-				ShowError("read_homunculusdb : Invalid class %d (%s)\n", classid, filename[i]);
-				continue;
-			}
-			db = &homunculus_db[j];
-			db->base_class = classid;
-			classid = atoi(str[1]);
-			if (classid < HM_CLASS_BASE || classid > HM_CLASS_MAX)
-			{
-				db->base_class = 0;
-				ShowError("read_homunculusdb : Invalid class %d (%s)\n", classid, filename[i]);
-				continue;
-			}
-			db->evo_class = classid;
-			//Name, Food, Hungry Delay, Base Size, Evo Size, Race, Element, ASPD
-			strncpy(db->name,str[2],NAME_LENGTH-1);
-			db->foodID = atoi(str[3]);
-			db->hungryDelay = atoi(str[4]);
-			db->base_size = atoi(str[5]);
-			db->evo_size = atoi(str[6]);
-			db->race = atoi(str[7]);
-			db->element = atoi(str[8]);
-			db->baseASPD = atoi(str[9]);
-			//base HP, SP, str, agi, vit, int, dex, luk
-			db->base.HP = atoi(str[10]);
-			db->base.SP = atoi(str[11]);
-			db->base.str = atoi(str[12]);
-			db->base.agi = atoi(str[13]);
-			db->base.vit = atoi(str[14]);
-			db->base.int_= atoi(str[15]);
-			db->base.dex = atoi(str[16]);
-			db->base.luk = atoi(str[17]);
-			//Growth Min/Max HP, SP, str, agi, vit, int, dex, luk
-			db->gmin.HP = atoi(str[18]);
-			db->gmax.HP = atoi(str[19]);
-			db->gmin.SP = atoi(str[20]);
-			db->gmax.SP = atoi(str[21]);
-			db->gmin.str = atoi(str[22]);
-			db->gmax.str = atoi(str[23]);
-			db->gmin.agi = atoi(str[24]);
-			db->gmax.agi = atoi(str[25]);
-			db->gmin.vit = atoi(str[26]);
-			db->gmax.vit = atoi(str[27]);
-			db->gmin.int_= atoi(str[28]);
-			db->gmax.int_= atoi(str[29]);
-			db->gmin.dex = atoi(str[30]);
-			db->gmax.dex = atoi(str[31]);
-			db->gmin.luk = atoi(str[32]);
-			db->gmax.luk = atoi(str[33]);
-			//Evolution Min/Max HP, SP, str, agi, vit, int, dex, luk
-			db->emin.HP = atoi(str[34]);
-			db->emax.HP = atoi(str[35]);
-			db->emin.SP = atoi(str[36]);
-			db->emax.SP = atoi(str[37]);
-			db->emin.str = atoi(str[38]);
-			db->emax.str = atoi(str[39]);
-			db->emin.agi = atoi(str[40]);
-			db->emax.agi = atoi(str[41]);
-			db->emin.vit = atoi(str[42]);
-			db->emax.vit = atoi(str[43]);
-			db->emin.int_= atoi(str[44]);
-			db->emax.int_= atoi(str[45]);
-			db->emin.dex = atoi(str[46]);
-			db->emax.dex = atoi(str[47]);
-			db->emin.luk = atoi(str[48]);
-			db->emax.luk = atoi(str[49]);
-
-			//Check that the min/max values really are below the other one.
-			if(db->gmin.HP > db->gmax.HP)
-				db->gmin.HP = db->gmax.HP;
-			if(db->gmin.SP > db->gmax.SP)
-				db->gmin.SP = db->gmax.SP;
-			if(db->gmin.str > db->gmax.str)
-				db->gmin.str = db->gmax.str;
-			if(db->gmin.agi > db->gmax.agi)
-				db->gmin.agi = db->gmax.agi;
-			if(db->gmin.vit > db->gmax.vit)
-				db->gmin.vit = db->gmax.vit;
-			if(db->gmin.int_> db->gmax.int_)
-				db->gmin.int_= db->gmax.int_;
-			if(db->gmin.dex > db->gmax.dex)
-				db->gmin.dex = db->gmax.dex;
-			if(db->gmin.luk > db->gmax.luk)
-				db->gmin.luk = db->gmax.luk;
-
-			if(db->emin.HP > db->emax.HP)
-				db->emin.HP = db->emax.HP;
-			if(db->emin.SP > db->emax.SP)
-				db->emin.SP = db->emax.SP;
-			if(db->emin.str > db->emax.str)
-				db->emin.str = db->emax.str;
-			if(db->emin.agi > db->emax.agi)
-				db->emin.agi = db->emax.agi;
-			if(db->emin.vit > db->emax.vit)
-				db->emin.vit = db->emax.vit;
-			if(db->emin.int_> db->emax.int_)
-				db->emin.int_= db->emax.int_;
-			if(db->emin.dex > db->emax.dex)
-				db->emin.dex = db->emax.dex;
-			if(db->emin.luk > db->emax.luk)
-				db->emin.luk = db->emax.luk;
-
-			j++;
-		}
-		if (j > MAX_HOMUNCULUS_CLASS)
-			ShowWarning("read_homunculusdb: Reached max number of homunculus [%d]. Remaining homunculus were not read.\n ", MAX_HOMUNCULUS_CLASS);
-		fclose(fp);
-		ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' homunculus in '"CL_WHITE"db/%s"CL_RESET"'.\n",j,filename[i]);
-	}
-	return 0;
-}
-
-int read_homunculus_skilldb(void)
-{
-	FILE *fp;
-	char line[1024], *p;
-	int k, classid;
-	int j = 0;
-	char *split[15];
-
-	memset(hskill_tree,0,sizeof(hskill_tree));
-	sprintf(line, "%s/homun_skill_tree.txt", db_path);
-	fp=fopen(line,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n", line);
-		return 1;
-	}
-
-	while(fgets(line, sizeof(line), fp))
-	{
-		int minJobLevelPresent = 0;
-
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-
-		k = 0;
-		p = strtok(line,",");
-		while (p != NULL && k < 15)
-		{
-			split[k++] = p;
-			p = strtok(NULL, ",");
-		}
-
-		if(k < 13)
-			continue;
-
-		if (k == 14)
-			minJobLevelPresent = 1;	// MinJobLvl has been added
-
-		// check for bounds [celest]
-		classid = atoi(split[0]) - HM_CLASS_BASE;
-		if ( classid >= MAX_HOMUNCULUS_CLASS )
-			continue;
-
-		k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
-		// Search an empty line or a line with the same skill_id (stored in j)
-		for(j = 0; j < MAX_SKILL_TREE && hskill_tree[classid][j].id && hskill_tree[classid][j].id != k; j++);
-
-		if (j == MAX_SKILL_TREE)
-		{
-			ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", k, classid);
-			continue;
-		}
-
-		hskill_tree[classid][j].id=k;
-		hskill_tree[classid][j].max=atoi(split[2]);
-		if (minJobLevelPresent)
-			hskill_tree[classid][j].joblv=atoi(split[3]);
-
-		for(k=0;k<5;k++){
-			hskill_tree[classid][j].need[k].id=atoi(split[3+k*2+minJobLevelPresent]);
-			hskill_tree[classid][j].need[k].lv=atoi(split[3+k*2+minJobLevelPresent+1]);
-		}
-	}
-
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","homun_skill_tree.txt");
-	return 0;
-}
-
-void read_homunculus_expdb(void)
-{
-	FILE *fp;
-	char line[1024];
-	int i, j=0;
-	char *filename[]={"exp_homun.txt","exp_homun2.txt"};
-
-	memset(hexptbl,0,sizeof(hexptbl));
-	for(i=0; i<2; i++){
-		sprintf(line, "%s/%s", db_path, filename[i]);
-		fp=fopen(line,"r");
-		if(fp == NULL){
-			if(i != 0)
-				continue;
-			ShowError("can't read %s\n",line);
-			return;
-		}
-		while(fgets(line, sizeof(line), fp) && j < MAX_LEVEL)
-		{
-			if(line[0] == '/' && line[1] == '/')
-				continue;
-
-			hexptbl[j] = strtoul(line, NULL, 10);
-			if (!hexptbl[j++])
-				break;
-		}
-		if (hexptbl[MAX_LEVEL - 1]) // Last permitted level have to be 0!
-		{
-			ShowWarning("read_hexptbl: Reached max level in exp_homun [%d]. Remaining lines were not read.\n ", MAX_LEVEL);
-			hexptbl[MAX_LEVEL - 1] = 0;
-		}
-		fclose(fp);
-		ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' levels in '"CL_WHITE"%s"CL_RESET"'.\n", j, filename[i]);
-	}
-}
-
-void merc_reload(void)
-{
-	read_homunculusdb();
-	read_homunculus_expdb();
-}
-
-void merc_skill_reload(void)
-{
-	read_homunculus_skilldb();
-}
-
-int do_init_merc(void)
-{
-	int class_;
-	read_homunculusdb();
-	read_homunculus_expdb();
-	read_homunculus_skilldb();
-
-	// Add homunc timer function to timer func list [Toms]
-	add_timer_func_list(merc_hom_hungry, "merc_hom_hungry");
-
-	//Stock view data for homuncs
-	memset(&hom_viewdb, 0, sizeof(hom_viewdb));
-	for (class_ = 0; class_ < ARRAYLENGTH(hom_viewdb); class_++)
-		hom_viewdb[class_].class_ = HM_CLASS_BASE+class_;
-	return 0;
-}
-
-int do_final_merc(void);

+ 0 - 89
src/map/homunculus.h

@@ -1,89 +0,0 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _HOMUNCULUS_H_
-#define _HOMUNCULUS_H_
-
-#include "status.h" // struct status_data, struct status_change
-#include "unit.h" // struct unit_data
-
-struct h_stats {
-	unsigned int HP, SP;
-	unsigned short str, agi, vit, int_, dex, luk;
-};
-
-struct s_homunculus_db {
-	int base_class, evo_class;
-	char name[NAME_LENGTH];
-	struct h_stats base, gmin, gmax, emin, emax;
-	int foodID ;
-	int baseASPD ;
-	long hungryDelay ;
-	unsigned char element, race, base_size, evo_size;
-};
-
-extern struct s_homunculus_db homuncumlus_db[MAX_HOMUNCULUS_CLASS];
-enum { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD };
-enum {
-	SP_ACK 	= 0x00,
-	SP_INTIMATE 	= 0x100,
-	SP_HUNGRY 		= 0x200
-};
-
-struct homun_data {
-	struct block_list bl;
-	struct unit_data  ud;
-	struct view_data *vd;
-	struct status_data base_status, battle_status;
-	struct status_change sc;
-	struct regen_data regen;
-	struct s_homunculus_db *homunculusDB;	//[orn]
-	struct s_homunculus homunculus;	//[orn]
-
-	struct map_session_data *master; //pointer back to its master
-	int hungry_timer;	//[orn]
-	unsigned int exp_next;
-	char blockskill[MAX_SKILL];	// [orn]
-};
-
-
-#define homdb_checkid(id) (id >=  HM_CLASS_BASE && id <= HM_CLASS_MAX)
-
-// merc_is_hom_alive(struct homun_data *)
-#define merc_is_hom_active(x) (x && x->homunculus.vaporize != 1 && x->battle_status.hp > 0)
-int do_init_merc(void);
-int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag); //albator
-struct view_data* merc_get_hom_viewdata(int class_);
-void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp);
-int merc_hom_dead(struct homun_data *hd, struct block_list *src);
-void merc_hom_skillup(struct homun_data *hd,int skillnum);
-int merc_hom_calc_skilltree(struct homun_data *hd) ;
-int merc_hom_checkskill(struct homun_data *hd,int skill_id) ;
-int merc_hom_gainexp(struct homun_data *hd,int exp) ;
-int merc_hom_levelup(struct homun_data *hd) ;
-int merc_hom_evolution(struct homun_data *hd) ;
-void merc_hom_heal(struct homun_data *hd,int hp,int sp);
-int merc_hom_vaporize(struct map_session_data *sd, int flag);
-int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y);
-void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp);
-void merc_reset_stats(struct homun_data *hd);
-int merc_hom_shuffle(struct homun_data *hd); // [Zephyrus]
-void merc_save(struct homun_data *hd);
-int merc_call_homunculus(struct map_session_data *sd);
-int merc_create_homunculus_request(struct map_session_data *sd, int class_);
-int search_homunculusDB_index(int key,int type);
-int merc_menu(struct map_session_data *sd,int menunum);
-int merc_hom_food(struct map_session_data *sd, struct homun_data *hd);
-int merc_hom_hungry_timer_delete(struct homun_data *hd);
-int merc_hom_change_name(struct map_session_data *sd,char *name);
-int merc_hom_change_name_ack(struct map_session_data *sd, char* name, int flag);
-#define merc_stop_walking(hd, type) unit_stop_walking(&(hd)->bl, type)
-#define merc_stop_attack(hd) unit_stop_attack(&(hd)->bl)
-int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value);
-int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value);
-int merc_skill_tree_get_max(int id, int b_class);
-void merc_hom_init_timers(struct homun_data * hd);
-void merc_skill_reload(void);
-void merc_reload(void);
-
-#endif /* _HOMUNCULUS_H_ */

+ 1016 - 231
src/map/mercenary.c

@@ -29,6 +29,7 @@
 #include "npc.h"
 #include "trade.h"
 #include "unit.h"
+
 #include "mercenary.h"
 
 #include <stdio.h>
@@ -36,348 +37,1132 @@
 #include <string.h>
 #include <math.h>
 
-struct s_mercenary_db mercenary_db[MAX_MERCENARY_CLASS]; // Mercenary Database
 
-int merc_search_index(int class_)
+//Better equiprobability than rand()% [orn]
+#define rand(a, b) (a+(int) ((float)(b-a+1)*rand()/(RAND_MAX+1.0)))
+
+struct s_homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS];	//[orn]
+struct skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_SKILL_TREE];
+
+static int merc_hom_hungry(int tid, unsigned int tick, int id, intptr data);
+
+static unsigned int hexptbl[MAX_LEVEL];
+
+//For holding the view data of npc classes. [Skotlex]
+static struct view_data hom_viewdb[MAX_HOMUNCULUS_CLASS];
+
+struct view_data* merc_get_hom_viewdata(int class_)
+{	//Returns the viewdata for homunculus
+	if (homdb_checkid(class_))
+		return &hom_viewdb[class_-HM_CLASS_BASE];
+	return NULL;
+}
+
+void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp)
 {
-	int i;
-	ARR_FIND(0, MAX_MERCENARY_CLASS, i, mercenary_db[i].class_ == class_);
-	return (i == MAX_MERCENARY_CLASS)?-1:i;
+	clif_hominfo(hd->master,hd,0);
 }
 
-bool merc_class(int class_)
+int merc_hom_dead(struct homun_data *hd, struct block_list *src)
 {
-	return (bool)(merc_search_index(class_) > -1);
+	//There's no intimacy penalties on death (from Tharis)
+	struct map_session_data *sd = hd->master;
+
+	clif_emotion(&hd->bl, 16) ;	//wah
+
+	//Delete timers when dead.
+	merc_hom_hungry_timer_delete(hd);
+	hd->homunculus.hp = 0;
+
+	if (!sd) //unit remove map will invoke unit free
+		return 3;
+
+	clif_emotion(&sd->bl, 28) ; //sob
+	//Remove from map (if it has no intimacy, it is auto-removed from memory)
+	return 3;
 }
 
-struct view_data * merc_get_viewdata(int class_)
+//Vaporize a character's homun. If flag, HP needs to be 80% or above.
+int merc_hom_vaporize(struct map_session_data *sd, int flag)
 {
-	int i = merc_search_index(class_);
-	if( i < 0 )
+	struct homun_data *hd;
+
+	nullpo_retr(0, sd);
+
+	hd = sd->hd;
+	if (!hd || hd->homunculus.vaporize)
+		return 0;
+	
+	if (status_isdead(&hd->bl))
+		return 0; //Can't vaporize a dead homun.
+
+	if (flag && get_percentage(hd->battle_status.hp, hd->battle_status.max_hp) < 80)
 		return 0;
 
-	return &mercenary_db[i].vd;
+	hd->regen.state.block = 3; //Block regen while vaporized.
+	//Delete timers when vaporized.
+	merc_hom_hungry_timer_delete(hd);
+	hd->homunculus.vaporize = 1;
+	if(battle_config.hom_setting&0x40)
+		memset(hd->blockskill, 0, sizeof(hd->blockskill));
+	clif_hominfo(sd, sd->hd, 0);
+	merc_save(hd);
+	return unit_remove_map(&hd->bl, 0);
 }
 
-int merc_create(struct map_session_data *sd, int class_, unsigned int lifetime)
+//delete a homunculus, completely "killing it". 
+//Emote is the emotion the master should use, send negative to disable.
+int merc_hom_delete(struct homun_data *hd, int emote)
 {
-	struct s_mercenary merc;
-	struct s_mercenary_db *db;
-	int i;
-	nullpo_retr(1,sd);
+	struct map_session_data *sd;
+	nullpo_retr(0, hd);
+	sd = hd->master;
 
-	if( (i = merc_search_index(class_)) < 0 )
-		return 0;
+	if (!sd)
+		return unit_free(&hd->bl,1);
 
-	db = &mercenary_db[i];
-	memset(&merc,0,sizeof(struct s_mercenary));
+	if (emote >= 0)
+		clif_emotion(&sd->bl, emote);
 
-	merc.char_id = sd->status.char_id;
-	merc.class_ = class_;
-	merc.hp = db->status.max_hp;
-	merc.sp = db->status.max_sp;
-	merc.remain_life_time = lifetime;
+	//This makes it be deleted right away.
+	hd->homunculus.intimacy = 0;
+	// Send homunculus_dead to client
+	hd->homunculus.hp = 0;
+	clif_hominfo(sd, hd, 0);
+	return unit_remove_map(&hd->bl,0);
+}
 
-	// Request Char Server to create this mercenary
-	intif_mercenary_create(&merc);
+int merc_hom_calc_skilltree(struct homun_data *hd)
+{
+	int i,id=0 ;
+	int j,f=1;
+	int c=0;
 
-	return 1;
+	nullpo_retr(0, hd);
+	c = hd->homunculus.class_ - HM_CLASS_BASE;
+	
+	for(i=0;i < MAX_SKILL_TREE && (id = hskill_tree[c][i].id) > 0;i++)
+	{
+		if(hd->homunculus.hskill[id-HM_SKILLBASE].id)
+			continue; //Skill already known.
+		if(!battle_config.skillfree)
+		{
+			for(j=0;j<5;j++)
+			{
+				if( hskill_tree[c][i].need[j].id &&
+					merc_hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv) 
+				{
+					f=0;
+					break;
+				}
+			}
+		}
+		if (f)
+			hd->homunculus.hskill[id-HM_SKILLBASE].id = id ;
+	}
+	return 0;
 }
 
-int mercenary_save(struct mercenary_data *md)
+int merc_hom_checkskill(struct homun_data *hd,int skill_id)
 {
-	const struct TimerData * td = get_timer(md->contract_timer);
+	int i = skill_id - HM_SKILLBASE;
+	if(!hd)
+		return 0;
 
-	md->mercenary.hp = md->battle_status.hp;
-	md->mercenary.sp = md->battle_status.sp;
-	if( td != NULL )
-		md->mercenary.remain_life_time = DIFF_TICK(td->tick, gettick());
-	else
+	if(hd->homunculus.hskill[i].id == skill_id)
+		return (hd->homunculus.hskill[i].lv);
+
+	return 0;
+}
+
+int merc_skill_tree_get_max(int id, int b_class){
+	int i, skillid;
+	b_class -= HM_CLASS_BASE;
+	for(i=0;(skillid=hskill_tree[b_class][i].id)>0;i++)
+		if (id == skillid)
+			return hskill_tree[b_class][i].max;
+	return skill_get_max(id);
+}
+
+void merc_hom_skillup(struct homun_data *hd,int skillnum)
+{
+	int i = 0 ;
+	nullpo_retv(hd);
+
+	if(hd->homunculus.vaporize)
+		return;
+	
+	i = skillnum - HM_SKILLBASE;
+	if(hd->homunculus.skillpts > 0 &&
+		hd->homunculus.hskill[i].id &&
+		hd->homunculus.hskill[i].flag == 0 && //Don't allow raising while you have granted skills. [Skotlex]
+		hd->homunculus.hskill[i].lv < merc_skill_tree_get_max(skillnum, hd->homunculus.class_)
+		)
 	{
-		md->mercenary.remain_life_time = 0;
-		ShowWarning("mercenary_save : mercenary without timer (tid %d)\n", md->contract_timer);
+		hd->homunculus.hskill[i].lv++;
+		hd->homunculus.skillpts-- ;
+		status_calc_homunculus(hd,0);
+		if (hd->master) {
+			clif_homskillup(hd->master, skillnum);
+			clif_hominfo(hd->master,hd,0);
+			clif_homskillinfoblock(hd->master);
+		}
+	}
+}
+
+int merc_hom_levelup(struct homun_data *hd)
+{
+	struct s_homunculus *hom;
+	struct h_stats *min, *max;
+	int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ;
+	int growth_max_hp, growth_max_sp ;
+	char output[256] ;
+
+	if (hd->homunculus.level == MAX_LEVEL || !hd->exp_next || hd->homunculus.exp < hd->exp_next)
+		return 0 ;
+	
+	hom = &hd->homunculus;
+	hom->level++ ;
+	if (!(hom->level % 3)) 
+		hom->skillpts++ ;	//1 skillpoint each 3 base level
+
+	hom->exp -= hd->exp_next ;
+	hd->exp_next = hexptbl[hom->level - 1] ;
+	
+	max  = &hd->homunculusDB->gmax;
+	min  = &hd->homunculusDB->gmin;
+
+	growth_max_hp = rand(min->HP, max->HP);
+	growth_max_sp = rand(min->SP, max->SP);
+	growth_str = rand(min->str, max->str);
+	growth_agi = rand(min->agi, max->agi);
+	growth_vit = rand(min->vit, max->vit);
+	growth_dex = rand(min->dex, max->dex);
+	growth_int = rand(min->int_,max->int_);
+	growth_luk = rand(min->luk, max->luk);
+
+	//Aegis discards the decimals in the stat growth values!
+	growth_str-=growth_str%10;
+	growth_agi-=growth_agi%10;
+	growth_vit-=growth_vit%10;
+	growth_dex-=growth_dex%10;
+	growth_int-=growth_int%10;
+	growth_luk-=growth_luk%10;
+
+	hom->max_hp += growth_max_hp;
+	hom->max_sp += growth_max_sp;
+	hom->str += growth_str;
+	hom->agi += growth_agi;
+	hom->vit += growth_vit;
+	hom->dex += growth_dex;
+	hom->int_+= growth_int;
+	hom->luk += growth_luk;
+	
+	if ( battle_config.homunculus_show_growth ) {
+		sprintf(output,
+			"Growth: hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ",
+			growth_max_hp, growth_max_sp,
+			growth_str/10.0, growth_agi/10.0, growth_vit/10.0,
+			growth_int/10.0, growth_dex/10.0, growth_luk/10.0);
+		clif_disp_onlyself(hd->master,output,strlen(output));
 	}
+	return 1 ;
+}
 
-	intif_mercenary_save(&md->mercenary);
+int merc_hom_change_class(struct homun_data *hd, short class_)
+{
+	int i;
+	i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
+	if(i < 0)
+		return 0;
+	hd->homunculusDB = &homunculus_db[i];
+	hd->homunculus.class_ = class_;
+	status_set_viewdata(&hd->bl, class_);
+	merc_hom_calc_skilltree(hd);
 	return 1;
 }
 
-static int merc_contract_end(int tid, unsigned int tick, int id, intptr data)
+int merc_hom_evolution(struct homun_data *hd)
 {
+	struct s_homunculus *hom;
+	struct h_stats *max, *min;
 	struct map_session_data *sd;
-	struct mercenary_data *md;
+	nullpo_retr(0, hd);
 
-	if( (sd = map_id2sd(id)) == NULL )
-		return 1;
-	if( (md = sd->md) == NULL )
+	if(!hd->homunculusDB->evo_class || hd->homunculus.class_ == hd->homunculusDB->evo_class)
+	{
+		clif_emotion(&hd->bl, 4) ;	//swt
+		return 0 ;
+	}
+	sd = hd->master;
+	if (!sd)
+		return 0;
+	
+	if (!merc_hom_change_class(hd, hd->homunculusDB->evo_class)) {
+		ShowError("merc_hom_evolution: Can't evolve homunc from %d to %d", hd->homunculus.class_, hd->homunculusDB->evo_class);
+		return 0;
+	}
+
+	//Apply evolution bonuses
+	hom = &hd->homunculus;
+	max = &hd->homunculusDB->emax;
+	min = &hd->homunculusDB->emin;
+	hom->max_hp += rand(min->HP, max->HP);
+	hom->max_sp += rand(min->SP, max->SP);
+	hom->str += 10*rand(min->str, max->str);
+	hom->agi += 10*rand(min->agi, max->agi);
+	hom->vit += 10*rand(min->vit, max->vit);
+	hom->int_+= 10*rand(min->int_,max->int_);
+	hom->dex += 10*rand(min->dex, max->dex);
+	hom->luk += 10*rand(min->luk, max->luk);
+	hom->intimacy = 500;
+
+	unit_remove_map(&hd->bl, 0);
+	map_addblock(&hd->bl);
+
+	clif_spawn(&hd->bl);
+	clif_emotion(&sd->bl, 21);	//no1
+	clif_misceffect2(&hd->bl,568);
+
+	//status_Calc flag&1 will make current HP/SP be reloaded from hom structure
+	hom->hp = hd->battle_status.hp;
+	hom->sp = hd->battle_status.sp;
+	status_calc_homunculus(hd,1);
+
+	if (!(battle_config.hom_setting&0x2))
+		skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
+				
+	return 1 ;
+}
+
+int merc_hom_gainexp(struct homun_data *hd,int exp)
+{
+	if(hd->homunculus.vaporize)
 		return 1;
 
-	if( md->contract_timer != tid )
-	{
-		ShowError("merc_contract_end %d != %d.\n", md->contract_timer, tid);
+	if( hd->exp_next == 0 ) {
+		hd->homunculus.exp = 0 ;
 		return 0;
 	}
 
-	md->contract_timer = INVALID_TIMER;
-	merc_delete(md, 0); // Mercenary soldier's duty hour is over.
+	hd->homunculus.exp += exp;
+
+	if(hd->homunculus.exp < hd->exp_next) {
+		clif_hominfo(hd->master,hd,0);
+		return 0;
+	}
 
+ 	//levelup
+	do
+	{
+		merc_hom_levelup(hd) ;
+	}
+	while(hd->homunculus.exp > hd->exp_next && hd->exp_next != 0 );
+		
+	if( hd->exp_next == 0 )
+		hd->homunculus.exp = 0 ;
+
+	clif_misceffect2(&hd->bl,568);
+	status_calc_homunculus(hd,0);
+	status_percent_heal(&hd->bl, 100, 100);
 	return 0;
 }
 
-int merc_delete(struct mercenary_data *md, int reply)
+// Return the new value
+int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value)
 {
-	struct map_session_data *sd = md->master;
-	md->mercenary.remain_life_time = 0;
+	if (battle_config.homunculus_friendly_rate != 100)
+		value = (value * battle_config.homunculus_friendly_rate) / 100;
 
-	merc_contract_stop(md);
+	if (hd->homunculus.intimacy + value <= 100000)
+		hd->homunculus.intimacy += value;
+	else
+		hd->homunculus.intimacy = 100000;
+	return hd->homunculus.intimacy;
+}
 
-	if( !sd )
-		return unit_free(&md->bl, 1);
-	clif_mercenary_message(sd->fd, reply);
-	
-	return unit_remove_map(&md->bl, 1);
+// Return 0 if decrease fails or intimacy became 0 else the new value
+int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value)
+{
+	if (hd->homunculus.intimacy >= value)
+		hd->homunculus.intimacy -= value;
+	else
+		hd->homunculus.intimacy = 0;
+
+	return hd->homunculus.intimacy;
 }
 
-void merc_contract_stop(struct mercenary_data *md)
+void merc_hom_heal(struct homun_data *hd,int hp,int sp)
 {
-	nullpo_retv(md);
-	if( md->contract_timer != INVALID_TIMER )
-		delete_timer(md->contract_timer, merc_contract_end);
-	md->contract_timer = INVALID_TIMER;
+	clif_hominfo(hd->master,hd,0);
 }
 
-void merc_contract_init(struct mercenary_data *md)
+void merc_save(struct homun_data *hd)
 {
-	if( md->contract_timer == INVALID_TIMER )
-		md->contract_timer = add_timer(gettick() + md->mercenary.remain_life_time, merc_contract_end, md->master->bl.id, 0);
+	// copy data that must be saved in homunculus struct ( hp / sp )
+	TBL_PC * sd = hd->master;
+	//Do not check for max_hp/max_sp caps as current could be higher to max due
+	//to status changes/skills (they will be capped as needed upon stat 
+	//calculation on login)
+	hd->homunculus.hp = hd->battle_status.hp;
+	hd->homunculus.sp = hd->battle_status.sp;
+	intif_homunculus_requestsave(sd->status.account_id, &hd->homunculus);
+}
 
-	md->regen.state.block = 0;
+int merc_menu(struct map_session_data *sd,int menunum)
+{
+	nullpo_retr(0, sd);
+	if (sd->hd == NULL)
+		return 1;
+	
+	switch(menunum) {
+		case 0:
+			break;
+		case 1:
+			merc_hom_food(sd, sd->hd);
+			break;
+		case 2:
+			merc_hom_delete(sd->hd, -1);
+			break;
+		default:
+			ShowError("merc_menu : unknown menu choice : %d\n", menunum) ;
+			break;
+	}
+	return 0;
 }
 
-int merc_data_received(struct s_mercenary *merc, bool flag)
+int merc_hom_food(struct map_session_data *sd, struct homun_data *hd)
+{
+	int i, foodID, emotion;
+
+	if(hd->homunculus.vaporize)
+		return 1 ;
+
+	foodID = hd->homunculusDB->foodID;
+	i = pc_search_inventory(sd,foodID);
+	if(i < 0) {
+		clif_hom_food(sd,foodID,0);
+		return 1;
+	}
+	pc_delitem(sd,i,1,0);
+
+	if ( hd->homunculus.hunger >= 91 ) {
+		merc_hom_decrease_intimacy(hd, 50);
+		emotion = 16;
+	} else if ( hd->homunculus.hunger >= 76 ) {
+		merc_hom_decrease_intimacy(hd, 5);
+		emotion = 19;
+	} else if ( hd->homunculus.hunger >= 26 ) {
+		merc_hom_increase_intimacy(hd, 75);
+		emotion = 2;
+	} else if ( hd->homunculus.hunger >= 11 ) {
+		merc_hom_increase_intimacy(hd, 100);
+		emotion = 2;
+	} else {
+		merc_hom_increase_intimacy(hd, 50);
+		emotion = 2;
+	}
+
+	hd->homunculus.hunger += 10;	//dunno increase value for each food
+	if(hd->homunculus.hunger > 100)
+		hd->homunculus.hunger = 100;
+
+	clif_emotion(&hd->bl,emotion) ;
+	clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
+	clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
+	clif_hom_food(sd,foodID,1);
+       	
+	// Too much food :/
+	if(hd->homunculus.intimacy == 0)
+		return merc_hom_delete(sd->hd, 23); //omg  
+
+	return 0;
+}
+
+static int merc_hom_hungry(int tid, unsigned int tick, int id, intptr data)
 {
 	struct map_session_data *sd;
-	struct mercenary_data *md;
-	struct s_mercenary_db *db;
-	int i = merc_search_index(merc->class_);
+	struct homun_data *hd;
 
-	if( (sd = map_charid2sd(merc->char_id)) == NULL )
-		return 0;
-	if( !flag || i < 0 )
-	{ // Not created - loaded - DB info
-		sd->status.mer_id = 0;
+	sd=map_id2sd(id);
+	if(!sd)
+		return 1;
+
+	if(!sd->status.hom_id || !(hd=sd->hd))
+		return 1;
+
+	if(hd->hungry_timer != tid){
+		ShowError("merc_hom_hungry_timer %d != %d\n",hd->hungry_timer,tid);
 		return 0;
 	}
 
-	sd->status.mer_id = merc->mercenary_id;
-	db = &mercenary_db[i];
+	hd->hungry_timer = INVALID_TIMER;
+	
+	hd->homunculus.hunger-- ;
+	if(hd->homunculus.hunger <= 10) {
+		clif_emotion(&hd->bl, 6) ;	//an
+	} else if(hd->homunculus.hunger == 25) {
+		clif_emotion(&hd->bl, 20) ;	//hmm
+	} else if(hd->homunculus.hunger == 75) {
+		clif_emotion(&hd->bl, 33) ;	//ok
+	}  
+	
+	if(hd->homunculus.hunger < 0) {
+		hd->homunculus.hunger = 0;
+		// Delete the homunculus if intimacy <= 100
+		if ( !merc_hom_decrease_intimacy(hd, 100) )
+			return merc_hom_delete(hd, 23); //omg  
+		clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
+	}
 
-	if( !sd->md )
-	{
-		sd->md = md = (struct mercenary_data*)aCalloc(1,sizeof(struct mercenary_data));
-		md->bl.type = BL_MER;
-		md->bl.id = npc_get_new_npc_id();
-
-		md->master = sd;
-		md->db = db;
-		memcpy(&md->mercenary, merc, sizeof(struct s_mercenary));
-		status_set_viewdata(&md->bl, md->mercenary.class_);
-		status_change_init(&md->bl);
-		unit_dataset(&md->bl);
-		md->ud.dir = sd->ud.dir;
-
-		md->bl.m = sd->bl.m;
-		md->bl.x = sd->bl.x;
-		md->bl.y = sd->bl.y;
-		unit_calc_pos(&md->bl, sd->bl.x, sd->bl.y, sd->ud.dir);
-		md->bl.x = md->ud.to_x;
-		md->bl.y = md->ud.to_y;
-
-		map_addiddb(&md->bl);
-		status_calc_mercenary(md,1);
-		md->contract_timer = INVALID_TIMER;
-		merc_contract_init(md);
+	clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
+	hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); //simple Fix albator
+	return 0;
+}
+
+int merc_hom_hungry_timer_delete(struct homun_data *hd)
+{
+	nullpo_retr(0, hd);
+	if(hd->hungry_timer != -1) {
+		delete_timer(hd->hungry_timer,merc_hom_hungry);
+		hd->hungry_timer = INVALID_TIMER;
 	}
-	else
-		memcpy(&sd->md->mercenary, merc, sizeof(struct s_mercenary));
+	return 1;
+}
 
-	md = sd->md;
-	if( md && md->bl.prev == NULL && sd->bl.prev != NULL )
-	{
-		map_addblock(&md->bl);
-		clif_spawn(&md->bl);
-		clif_mercenary_info(sd);
-		clif_mercenary_skillblock(sd);
+int merc_hom_change_name(struct map_session_data *sd,char *name)
+{
+	int i;
+	struct homun_data *hd;
+	nullpo_retr(1, sd);
+
+	hd = sd->hd;
+	if (!merc_is_hom_active(hd))
+		return 1;
+	if(hd->homunculus.rename_flag && !battle_config.hom_rename)
+		return 1;
+
+	for(i=0;i<NAME_LENGTH && name[i];i++){
+		if( !(name[i]&0xe0) || name[i]==0x7f)
+			return 1;
 	}
 
+	return intif_rename_hom(sd, name);
+}
+
+int merc_hom_change_name_ack(struct map_session_data *sd, char* name, int flag)
+{
+	struct homun_data *hd = sd->hd;
+	if (!merc_is_hom_active(hd)) return 0;
+	if (!flag) {
+		clif_displaymessage(sd->fd, msg_txt(280)); // You cannot use this name
+		return 0;
+	}
+	strncpy(hd->homunculus.name,name,NAME_LENGTH);
+	clif_charnameack (0,&hd->bl);
+	hd->homunculus.rename_flag = 1;
+	clif_hominfo(sd,hd,0);
 	return 1;
 }
 
-int read_mercenarydb(void)
+int search_homunculusDB_index(int key,int type)
 {
-	FILE *fp;
-	char line[1024], *p;
-	char *str[26];
-	int i, j = 0, k = 0, ele;
-	struct s_mercenary_db *db;
-	struct status_data *status;
+	int i;
+
+	for(i=0;i<MAX_HOMUNCULUS_CLASS;i++) {
+		if(homunculus_db[i].base_class <= 0)
+			continue;
+		switch(type) {
+			case HOMUNCULUS_CLASS:
+				if(homunculus_db[i].base_class == key ||
+					homunculus_db[i].evo_class == key)
+					return i;
+				break;
+			case HOMUNCULUS_FOOD:
+				if(homunculus_db[i].foodID == key)
+					return i;
+				break;
+			default:
+				return -1;
+		}
+	}
+	return -1;
+}
+
+// Create homunc structure
+int merc_hom_alloc(struct map_session_data *sd, struct s_homunculus *hom)
+{
+	struct homun_data *hd;
+	int i = 0;
+	short x,y;
+
+	nullpo_retr(1, sd);
+
+	Assert((sd->status.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd); 
+
+	i = search_homunculusDB_index(hom->class_,HOMUNCULUS_CLASS);
+	if(i < 0) {
+		ShowError("merc_hom_alloc: unknown class [%d] for homunculus '%s', requesting deletion.\n", hom->class_, hom->name);
+		sd->status.hom_id = 0;
+		intif_homunculus_requestdelete(hom->hom_id);
+		return 1;
+	}
+	sd->hd = hd = (struct homun_data*)aCalloc(1,sizeof(struct homun_data));
+	hd->bl.type = BL_HOM;
+	hd->bl.id = npc_get_new_npc_id();
+
+	hd->master = sd;
+	hd->homunculusDB = &homunculus_db[i];
+	memcpy(&hd->homunculus, hom, sizeof(struct s_homunculus));
+	hd->exp_next = hexptbl[hd->homunculus.level - 1];
+
+	status_set_viewdata(&hd->bl, hd->homunculus.class_);
+	status_change_init(&hd->bl);
+	unit_dataset(&hd->bl);
+	hd->ud.dir = sd->ud.dir;
+
+	// Find a random valid pos around the player
+	hd->bl.m = sd->bl.m;
+	hd->bl.x = sd->bl.x;
+	hd->bl.y = sd->bl.y;
+	x = sd->bl.x + 1;
+	y = sd->bl.y + 1;
+	map_random_dir(&hd->bl, &x, &y);
+	hd->bl.x = x;
+	hd->bl.y = y;
+	
+	map_addiddb(&hd->bl);
+	status_calc_homunculus(hd,1);
+
+	hd->hungry_timer = INVALID_TIMER;
+	return 0;
+}
+
+void merc_hom_init_timers(struct homun_data * hd)
+{
+	if (hd->hungry_timer == -1)
+		hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,merc_hom_hungry,hd->master->bl.id,0);
+	hd->regen.state.block = 0; //Restore HP/SP block.
+}
+
+int merc_call_homunculus(struct map_session_data *sd)
+{
+	struct homun_data *hd;
 
-	sprintf(line, "%s/%s", db_path, "mercenary_db.txt");
-	memset(mercenary_db,0,sizeof(mercenary_db));
+	if (!sd->status.hom_id) //Create a new homun.
+		return merc_create_homunculus_request(sd, HM_CLASS_BASE + rand(0, 7)) ;
 
-	fp = fopen(line, "r");
-	if( !fp )
+	// If homunc not yet loaded, load it
+	if (!sd->hd)
+		return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
+
+	hd = sd->hd;
+
+	if (!hd->homunculus.vaporize)
+		return 0; //Can't use this if homun wasn't vaporized.
+
+	merc_hom_init_timers(hd);
+	hd->homunculus.vaporize = 0;
+	if (hd->bl.prev == NULL)
+	{	//Spawn him
+		hd->bl.x = sd->bl.x;
+		hd->bl.y = sd->bl.y;
+		hd->bl.m = sd->bl.m;
+		map_addblock(&hd->bl);
+		clif_spawn(&hd->bl);
+		clif_send_homdata(sd,SP_ACK,0);
+		clif_hominfo(sd,hd,1);
+		clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
+		clif_homskillinfoblock(sd);
+		if (battle_config.slaves_inherit_speed&1)
+			status_calc_bl(&hd->bl, SCB_SPEED);
+		merc_save(hd); 
+	} else
+		//Warp him to master.
+		unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,0);
+	return 1;
+}
+
+// Recv homunculus data from char server
+int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag)
+{
+	struct map_session_data *sd;
+	struct homun_data *hd;
+
+	sd = map_id2sd(account_id);
+	if(!sd)
+		return 0;
+	if (sd->status.char_id != sh->char_id)
 	{
-		ShowError("read_mercenarydb : can't read mercenary_db.txt\n");
-		return -1;
+		if (sd->status.hom_id == sh->hom_id)
+			sh->char_id = sd->status.char_id; //Correct char id.
+		else
+			return 0;
+	}
+	if(!flag) { // Failed to load
+		sd->status.hom_id = 0;
+		return 0;
 	}
 
-	while( fgets(line, sizeof(line), fp) && j < MAX_MERCENARY_CLASS )
+	if (!sd->status.hom_id) //Hom just created.
+		sd->status.hom_id = sh->hom_id;
+	if (sd->hd) //uh? Overwrite the data.
+		memcpy(&sd->hd->homunculus, sh, sizeof(struct s_homunculus));
+	else
+		merc_hom_alloc(sd, sh);
+	
+	hd = sd->hd;
+	if(hd && hd->homunculus.hp && !hd->homunculus.vaporize && hd->bl.prev == NULL && sd->bl.prev != NULL)
 	{
-		k++;
-		if( line[0] == '/' && line[1] == '/' )
-			continue;
+		map_addblock(&hd->bl);
+		clif_spawn(&hd->bl);
+		clif_send_homdata(sd,SP_ACK,0);
+		clif_hominfo(sd,hd,1);
+		clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
+		clif_homskillinfoblock(sd);
+		merc_hom_init_timers(hd);
+	}
+	return 1;
+}
 
-		i = 0;
-		p = strtok(line, ",");
-		while( p != NULL && i < 26 )
-		{
-			str[i++] = p;
-			p = strtok(NULL, ",");
-		}
-		if( i < 26 )
-		{
-			ShowError("read_mercenarydb : Incorrect number of columns at mercenary_db.txt line %d.\n", k);
-			continue;
-		}
+// Ask homunculus creation to char server
+int merc_create_homunculus_request(struct map_session_data *sd, int class_)
+{
+	struct s_homunculus homun;
+	struct h_stats *base;
+	int i;
+
+	nullpo_retr(1, sd);
 
-		db = &mercenary_db[j];
-		db->class_ = atoi(str[0]);
-		strncpy(db->sprite, str[1], NAME_LENGTH);
-		strncpy(db->name, str[2], NAME_LENGTH);
-		db->lv = atoi(str[3]);
-
-		status = &db->status;
-		db->vd.class_ = db->class_;
-
-		status->max_hp = atoi(str[4]);
-		status->max_sp = atoi(str[5]);
-		status->rhw.range = atoi(str[6]);
-		status->rhw.atk = atoi(str[7]);
-		status->rhw.atk2 = atoi(str[8]);
-		status->def = atoi(str[9]);
-		status->mdef = atoi(str[10]);
-		status->str = atoi(str[11]);
-		status->agi = atoi(str[12]);
-		status->vit = atoi(str[13]);
-		status->int_ = atoi(str[14]);
-		status->dex = atoi(str[15]);
-		status->luk = atoi(str[16]);
-		db->range2 = atoi(str[17]);
-		db->range3 = atoi(str[18]);
-		status->size = atoi(str[19]);
-		status->race = atoi(str[20]);
+	i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
+	if(i < 0) return 0;
 	
-		ele = atoi(str[21]);
-		status->def_ele = ele%10;
-		status->ele_lv = ele/20;
-		if( status->def_ele >= ELE_MAX )
-		{
-			ShowWarning("Mercenary %d has invalid element type %d (max element is %d)\n", db->class_, status->def_ele, ELE_MAX - 1);
-			status->def_ele = ELE_NEUTRAL;
+	memset(&homun, 0, sizeof(struct s_homunculus));
+	//Initial data
+	strncpy(homun.name, homunculus_db[i].name, NAME_LENGTH-1);
+	homun.class_ = class_;
+	homun.level = 1;
+	homun.hunger = 32; //32%
+	homun.intimacy = 2100; //21/1000
+	homun.char_id = sd->status.char_id;
+	
+	homun.hp = 10 ;
+	base = &homunculus_db[i].base;
+	homun.max_hp = base->HP;
+	homun.max_sp = base->SP;
+	homun.str = base->str *10;
+	homun.agi = base->agi *10;
+	homun.vit = base->vit *10;
+	homun.int_= base->int_*10;
+	homun.dex = base->dex *10;
+	homun.luk = base->luk *10;
+
+	// Request homunculus creation
+	intif_homunculus_create(sd->status.account_id, &homun); 
+	return 1;
+}
+
+int merc_resurrect_homunculus(struct map_session_data* sd, unsigned char per, short x, short y)
+{
+	struct homun_data* hd;
+	nullpo_retr(0, sd);
+
+	if (!sd->status.hom_id)
+		return 0; // no homunculus
+
+	if (!sd->hd) //Load homun data;
+		return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
+	
+	hd = sd->hd;
+
+  	if (hd->homunculus.vaporize)
+		return 0; // vaporized homunculi need to be 'called'
+
+	if (!status_isdead(&hd->bl))
+		return 0; // already alive
+
+	merc_hom_init_timers(hd);
+
+	if (!hd->bl.prev)
+	{	//Add it back to the map.
+		hd->bl.m = sd->bl.m;
+		hd->bl.x = x;
+		hd->bl.y = y;
+		map_addblock(&hd->bl);
+		clif_spawn(&hd->bl);
+	}
+	status_revive(&hd->bl, per, 0);
+	return 1;
+}
+
+void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp)
+{
+	struct map_session_data *sd = hd->master;
+	hd->homunculus.hp = hd->battle_status.hp;
+	if (!sd)
+		return;
+	clif_send_homdata(sd,SP_ACK,0);
+	clif_hominfo(sd,hd,1);
+	clif_hominfo(sd,hd,0);
+	clif_homskillinfoblock(sd);
+}
+
+void merc_reset_stats(struct homun_data *hd)
+{	//Resets a homunc stats back to zero (but doesn't touches hunger or intimacy)
+	struct s_homunculus_db *db;
+	struct s_homunculus *hom;
+	struct h_stats *base;
+	hom = &hd->homunculus;
+	db = hd->homunculusDB;
+	base = &db->base;
+	hom->level = 1;
+	hom->hp = 10;
+	hom->max_hp = base->HP;
+	hom->max_sp = base->SP;
+	hom->str = base->str *10;
+	hom->agi = base->agi *10;
+	hom->vit = base->vit *10;
+	hom->int_= base->int_*10;
+	hom->dex = base->dex *10;
+	hom->luk = base->luk *10;
+	hom->exp = 0;
+	hd->exp_next = hexptbl[0];
+	memset(&hd->homunculus.hskill, 0, sizeof hd->homunculus.hskill);
+	hd->homunculus.skillpts = 0;
+}
+
+int merc_hom_shuffle(struct homun_data *hd)
+{
+	struct map_session_data *sd;
+	int lv, i, skillpts;
+	unsigned int exp;
+	struct s_skill b_skill[MAX_HOMUNSKILL];
+
+	if (!merc_is_hom_active(hd))
+		return 0;
+
+	sd = hd->master;
+	lv = hd->homunculus.level;
+	exp = hd->homunculus.exp;
+	memcpy(&b_skill, &hd->homunculus.hskill, sizeof(b_skill));
+	skillpts = hd->homunculus.skillpts;
+	//Reset values to level 1.
+	merc_reset_stats(hd);
+	//Level it back up
+	for (i = 1; i < lv && hd->exp_next; i++){
+		hd->homunculus.exp += hd->exp_next;
+		merc_hom_levelup(hd);
+	}
+
+	if(hd->homunculus.class_ == hd->homunculusDB->evo_class) {
+		//Evolved bonuses
+		struct s_homunculus *hom = &hd->homunculus;
+		struct h_stats *max = &hd->homunculusDB->emax, *min = &hd->homunculusDB->emin;
+		hom->max_hp += rand(min->HP, max->HP);
+		hom->max_sp += rand(min->SP, max->SP);
+		hom->str += 10*rand(min->str, max->str);
+		hom->agi += 10*rand(min->agi, max->agi);
+		hom->vit += 10*rand(min->vit, max->vit);
+		hom->int_+= 10*rand(min->int_,max->int_);
+		hom->dex += 10*rand(min->dex, max->dex);
+		hom->luk += 10*rand(min->luk, max->luk);
+	}
+
+	hd->homunculus.exp = exp;
+	memcpy(&hd->homunculus.hskill, &b_skill, sizeof(b_skill));
+	hd->homunculus.skillpts = skillpts;
+	clif_homskillinfoblock(sd);
+	status_calc_homunculus(hd,0);
+	status_percent_heal(&hd->bl, 100, 100);
+	clif_misceffect2(&hd->bl,568);
+
+	return 1;
+}
+
+int read_homunculusdb(void)
+{
+	FILE *fp;
+	char line[1024], *p;
+	int i, k, classid; 
+	int j = 0;
+	const char *filename[]={"homunculus_db.txt","homunculus_db2.txt"};
+	char *str[50];
+	struct s_homunculus_db *db;
+
+	memset(homunculus_db,0,sizeof(homunculus_db));
+	for(i = 0; i<2; i++)
+	{
+		sprintf(line, "%s/%s", db_path, filename[i]);
+		fp = fopen(line,"r");
+		if(!fp){
+			if(i != 0)
+				continue;
+			ShowError("read_homunculusdb : can't read %s\n", line);
+			return -1;
 		}
-		if( status->ele_lv < 1 || status->ele_lv > 4 )
+
+		while(fgets(line, sizeof(line), fp) && j < MAX_HOMUNCULUS_CLASS)
 		{
-			ShowWarning("Mercenary %d has invalid element level %d (max is 4)\n", db->class_, status->ele_lv);
-			status->ele_lv = 1;
-		}
+			if(line[0] == '/' && line[1] == '/')
+				continue;
 
-		status->speed = atoi(str[22]);
-		status->adelay = atoi(str[23]);
-		status->amotion = atoi(str[24]);
-		status->dmotion = atoi(str[25]);
+			k = 0;
+			p = strtok (line,",");
+			while (p != NULL && k < 50)
+			{
+				str[k++] = p;
+				p = strtok (NULL, ",");
+			}
+			if (k < 50 )
+			{
+				ShowError("read_homunculusdb : Incorrect number of columns at %s, homunculus %d. Read %d columns, 50 are needed.\n", filename[i], j+1, k);
+				continue;
+			}
+			
+			//Base Class,Evo Class
+			classid = atoi(str[0]);
+			if (classid < HM_CLASS_BASE || classid > HM_CLASS_MAX)
+			{
+				ShowError("read_homunculusdb : Invalid class %d (%s)\n", classid, filename[i]);
+				continue;
+			}
+			db = &homunculus_db[j];
+			db->base_class = classid;
+			classid = atoi(str[1]);
+			if (classid < HM_CLASS_BASE || classid > HM_CLASS_MAX)
+			{
+				db->base_class = 0;
+				ShowError("read_homunculusdb : Invalid class %d (%s)\n", classid, filename[i]);
+				continue;
+			}
+			db->evo_class = classid;
+			//Name, Food, Hungry Delay, Base Size, Evo Size, Race, Element, ASPD
+			strncpy(db->name,str[2],NAME_LENGTH-1);
+			db->foodID = atoi(str[3]);
+			db->hungryDelay = atoi(str[4]);
+			db->base_size = atoi(str[5]);
+			db->evo_size = atoi(str[6]);
+			db->race = atoi(str[7]);
+			db->element = atoi(str[8]);
+			db->baseASPD = atoi(str[9]);
+			//base HP, SP, str, agi, vit, int, dex, luk
+			db->base.HP = atoi(str[10]);
+			db->base.SP = atoi(str[11]);
+			db->base.str = atoi(str[12]);
+			db->base.agi = atoi(str[13]);
+			db->base.vit = atoi(str[14]);
+			db->base.int_= atoi(str[15]);
+			db->base.dex = atoi(str[16]);
+			db->base.luk = atoi(str[17]);
+			//Growth Min/Max HP, SP, str, agi, vit, int, dex, luk
+			db->gmin.HP = atoi(str[18]);
+			db->gmax.HP = atoi(str[19]);
+			db->gmin.SP = atoi(str[20]);
+			db->gmax.SP = atoi(str[21]);
+			db->gmin.str = atoi(str[22]);
+			db->gmax.str = atoi(str[23]);
+			db->gmin.agi = atoi(str[24]);
+			db->gmax.agi = atoi(str[25]);
+			db->gmin.vit = atoi(str[26]);
+			db->gmax.vit = atoi(str[27]);
+			db->gmin.int_= atoi(str[28]);
+			db->gmax.int_= atoi(str[29]);
+			db->gmin.dex = atoi(str[30]);
+			db->gmax.dex = atoi(str[31]);
+			db->gmin.luk = atoi(str[32]);
+			db->gmax.luk = atoi(str[33]);
+			//Evolution Min/Max HP, SP, str, agi, vit, int, dex, luk
+			db->emin.HP = atoi(str[34]);
+			db->emax.HP = atoi(str[35]);
+			db->emin.SP = atoi(str[36]);
+			db->emax.SP = atoi(str[37]);
+			db->emin.str = atoi(str[38]);
+			db->emax.str = atoi(str[39]);
+			db->emin.agi = atoi(str[40]);
+			db->emax.agi = atoi(str[41]);
+			db->emin.vit = atoi(str[42]);
+			db->emax.vit = atoi(str[43]);
+			db->emin.int_= atoi(str[44]);
+			db->emax.int_= atoi(str[45]);
+			db->emin.dex = atoi(str[46]);
+			db->emax.dex = atoi(str[47]);
+			db->emin.luk = atoi(str[48]);
+			db->emax.luk = atoi(str[49]);
 
-		j++;
-	}
+			//Check that the min/max values really are below the other one.
+			if(db->gmin.HP > db->gmax.HP)
+				db->gmin.HP = db->gmax.HP;
+			if(db->gmin.SP > db->gmax.SP)
+				db->gmin.SP = db->gmax.SP;
+			if(db->gmin.str > db->gmax.str)
+				db->gmin.str = db->gmax.str;
+			if(db->gmin.agi > db->gmax.agi)
+				db->gmin.agi = db->gmax.agi;
+			if(db->gmin.vit > db->gmax.vit)
+				db->gmin.vit = db->gmax.vit;
+			if(db->gmin.int_> db->gmax.int_)
+				db->gmin.int_= db->gmax.int_;
+			if(db->gmin.dex > db->gmax.dex)
+				db->gmin.dex = db->gmax.dex;
+			if(db->gmin.luk > db->gmax.luk)
+				db->gmin.luk = db->gmax.luk;
 
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' mercenaries in '"CL_WHITE"db/mercenary_db.txt"CL_RESET"'.\n",j);
+			if(db->emin.HP > db->emax.HP)
+				db->emin.HP = db->emax.HP;
+			if(db->emin.SP > db->emax.SP)
+				db->emin.SP = db->emax.SP;
+			if(db->emin.str > db->emax.str)
+				db->emin.str = db->emax.str;
+			if(db->emin.agi > db->emax.agi)
+				db->emin.agi = db->emax.agi;
+			if(db->emin.vit > db->emax.vit)
+				db->emin.vit = db->emax.vit;
+			if(db->emin.int_> db->emax.int_)
+				db->emin.int_= db->emax.int_;
+			if(db->emin.dex > db->emax.dex)
+				db->emin.dex = db->emax.dex;
+			if(db->emin.luk > db->emax.luk)
+				db->emin.luk = db->emax.luk;
 
+			j++;
+		}
+		if (j > MAX_HOMUNCULUS_CLASS)
+			ShowWarning("read_homunculusdb: Reached max number of homunculus [%d]. Remaining homunculus were not read.\n ", MAX_HOMUNCULUS_CLASS);
+		fclose(fp);
+		ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' homunculus in '"CL_WHITE"db/%s"CL_RESET"'.\n",j,filename[i]);
+	}
 	return 0;
 }
 
-int read_mercenary_skilldb(void)
+int read_homunculus_skilldb(void)
 {
 	FILE *fp;
 	char line[1024], *p;
-	char *str[3];
-	struct s_mercenary_db *db;
-	int i, j = 0, k = 0, class_;
-	int skillid, skilllv;
-
-	sprintf(line, "%s/%s", db_path, "mercenary_skill_db.txt");
-	fp = fopen(line, "r");
-	if( !fp )
-	{
-		ShowError("read_mercenary_skilldb : can't read mercenary_skill_db.txt\n");
-		return -1;
+	int k, classid; 
+	int j = 0;
+	char *split[15];
+
+	memset(hskill_tree,0,sizeof(hskill_tree));
+	sprintf(line, "%s/homun_skill_tree.txt", db_path);
+	fp=fopen(line,"r");
+	if(fp==NULL){
+		ShowError("can't read %s\n", line);
+		return 1;
 	}
 
-	while( fgets(line, sizeof(line), fp) )
+	while(fgets(line, sizeof(line), fp))
 	{
-		k++;
-		if( line[0] == '/' && line[1] == '/' )
+		int minJobLevelPresent = 0;
+
+		if(line[0]=='/' && line[1]=='/')
 			continue;
 
-		i = 0;
-		p = strtok(line, ",");
-		while( p != NULL && i < 3 )
+		k = 0;
+		p = strtok(line,",");
+		while (p != NULL && k < 15)
 		{
-			str[i++] = p;
+			split[k++] = p;
 			p = strtok(NULL, ",");
 		}
-		if( i < 3 )
-		{
-			ShowError("read_mercenary_skilldb : Incorrect number of columns at mercenary_skill_db.txt line %d.\n", k);
+
+		if(k < 13)
 			continue;
-		}
 
-		class_ = atoi(str[0]);
-		ARR_FIND(0, MAX_MERCENARY_CLASS, i, class_ == mercenary_db[i].class_);
-		if( i == MAX_MERCENARY_CLASS )
-		{
-			ShowError("read_mercenary_skilldb : Class not found in mercenary_db for skill entry, line %d.\n", k);
+		if (k == 14)
+			minJobLevelPresent = 1;	// MinJobLvl has been added
+
+		// check for bounds [celest]
+		classid = atoi(split[0]) - HM_CLASS_BASE;
+		if ( classid >= MAX_HOMUNCULUS_CLASS )
 			continue;
-		}
-		
-		skillid = atoi(str[1]);
-		if( skillid < MC_SKILLBASE || skillid >= MC_SKILLBASE + MAX_MERCSKILL )
+
+		k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
+		// Search an empty line or a line with the same skill_id (stored in j)
+		for(j = 0; j < MAX_SKILL_TREE && hskill_tree[classid][j].id && hskill_tree[classid][j].id != k; j++);
+
+		if (j == MAX_SKILL_TREE)
 		{
-			ShowError("read_mercenary_skilldb : Skill out of range, line %d.\n", k);
+			ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", k, classid);
 			continue;
 		}
 
-		db = &mercenary_db[i];
-		skilllv = atoi(str[2]);
+		hskill_tree[classid][j].id=k;
+		hskill_tree[classid][j].max=atoi(split[2]);
+		if (minJobLevelPresent)
+			hskill_tree[classid][j].joblv=atoi(split[3]);
 
-		i = skillid - MC_SKILLBASE;
-		db->skill[i].id = skillid;
-		db->skill[i].lv = skilllv;
-		j++;
+		for(k=0;k<5;k++){
+			hskill_tree[classid][j].need[k].id=atoi(split[3+k*2+minJobLevelPresent]);
+			hskill_tree[classid][j].need[k].lv=atoi(split[3+k*2+minJobLevelPresent+1]);
+		}
 	}
 
 	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"db/mercenary_skill_db.txt"CL_RESET"'.\n",j);
+	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","homun_skill_tree.txt");
 	return 0;
 }
 
-int do_init_mercenary(void)
+void read_homunculus_expdb(void)
 {
-	read_mercenarydb();
-	read_mercenary_skilldb();
-	
-	//add_timer_func_list(mercenary_contract, "mercenary_contract");
+	FILE *fp;
+	char line[1024];
+	int i, j=0;
+	char *filename[]={"exp_homun.txt","exp_homun2.txt"};
+
+	memset(hexptbl,0,sizeof(hexptbl));
+	for(i=0; i<2; i++){
+		sprintf(line, "%s/%s", db_path, filename[i]);
+		fp=fopen(line,"r");
+		if(fp == NULL){
+			if(i != 0)
+				continue;
+			ShowError("can't read %s\n",line);
+			return;
+		}
+		while(fgets(line, sizeof(line), fp) && j < MAX_LEVEL)
+		{
+			if(line[0] == '/' && line[1] == '/')
+				continue;
+
+			hexptbl[j] = strtoul(line, NULL, 10);
+			if (!hexptbl[j++])
+				break;
+		}
+		if (hexptbl[MAX_LEVEL - 1]) // Last permitted level have to be 0!
+		{
+			ShowWarning("read_hexptbl: Reached max level in exp_homun [%d]. Remaining lines were not read.\n ", MAX_LEVEL);
+			hexptbl[MAX_LEVEL - 1] = 0;
+		}
+		fclose(fp);
+		ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' levels in '"CL_WHITE"%s"CL_RESET"'.\n", j, filename[i]);
+	}
+}
+
+void merc_reload(void)
+{
+	read_homunculusdb();
+	read_homunculus_expdb();
+}
+
+void merc_skill_reload(void)
+{
+	read_homunculus_skilldb();
+}
+
+int do_init_merc(void)
+{
+	int class_;
+	read_homunculusdb();
+	read_homunculus_expdb();
+	read_homunculus_skilldb();
+	// Add homunc timer function to timer func list [Toms]
+	add_timer_func_list(merc_hom_hungry, "merc_hom_hungry");
+
+	//Stock view data for homuncs
+	memset(&hom_viewdb, 0, sizeof(hom_viewdb));
+	for (class_ = 0; class_ < ARRAYLENGTH(hom_viewdb); class_++) 
+		hom_viewdb[class_].class_ = HM_CLASS_BASE+class_;
 	return 0;
 }
 
-int do_final_mercenary(void);
+int do_final_merc(void);

+ 68 - 26
src/map/mercenary.h

@@ -7,42 +7,84 @@
 #include "status.h" // struct status_data, struct status_change
 #include "unit.h" // struct unit_data
 
-struct s_mercenary_db {
-	int class_;
-	char sprite[NAME_LENGTH], name[NAME_LENGTH];
-	unsigned short lv;
-	short range2, range3;
-	struct status_data status;
-	struct view_data vd;
-	struct {
-		unsigned short id, lv;
-	} skill[MAX_MERCSKILL];
+struct h_stats {
+	unsigned int HP, SP;
+	unsigned short str, agi, vit, int_, dex, luk;
+};
+
+struct s_homunculus_db {
+	int base_class, evo_class;
+	char name[NAME_LENGTH];
+	struct h_stats base, gmin, gmax, emin, emax;
+	int foodID ;
+	int baseASPD ;
+	long hungryDelay ;
+	unsigned char element, race, base_size, evo_size;
+};
+
+extern struct s_homunculus_db homuncumlus_db[MAX_HOMUNCULUS_CLASS];
+enum { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD };
+enum {
+	SP_ACK 	= 0x00,
+	SP_INTIMATE 	= 0x100,
+	SP_HUNGRY 		= 0x200
 };
 
-extern struct s_mercenary_db mercenary_db[MAX_MERCENARY_CLASS];
 
-struct mercenary_data {
+struct homun_data {
 	struct block_list bl;
-	struct unit_data ud;
+	struct unit_data  ud;
 	struct view_data *vd;
 	struct status_data base_status, battle_status;
 	struct status_change sc;
 	struct regen_data regen;
+	struct s_homunculus_db *homunculusDB;	//[orn]
+	struct s_homunculus homunculus ;	//[orn]
 
-	struct s_mercenary_db *db;
-	struct s_mercenary mercenary;
-
-	struct map_session_data *master;
-	int contract_timer;
+	struct map_session_data *master; //pointer back to its master
+	int hungry_timer;	//[orn]
+	unsigned int exp_next;
+	char blockskill[MAX_SKILL];	// [orn]
 };
 
-bool merc_class(int class_);
-struct view_data * merc_get_viewdata(int class_);
-int merc_create(struct map_session_data *sd, int class_, unsigned int lifetime);
-int merc_data_received(struct s_mercenary *merc, bool flag);
-int mercenary_save(struct mercenary_data *md);
-int do_init_mercenary(void);
-int merc_delete(struct mercenary_data *md, int reply);
-void merc_contract_stop(struct mercenary_data *md);
+
+#define homdb_checkid(id) (id >=  HM_CLASS_BASE && id <= HM_CLASS_MAX)
+
+// merc_is_hom_alive(struct homun_data *)
+#define merc_is_hom_active(x) (x && x->homunculus.vaporize != 1 && x->battle_status.hp > 0)
+int do_init_merc(void);
+int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag); //albator
+struct view_data* merc_get_hom_viewdata(int class_);
+void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp);
+int merc_hom_dead(struct homun_data *hd, struct block_list *src);
+void merc_hom_skillup(struct homun_data *hd,int skillnum);
+int merc_hom_calc_skilltree(struct homun_data *hd) ;
+int merc_hom_checkskill(struct homun_data *hd,int skill_id) ;
+int merc_hom_gainexp(struct homun_data *hd,int exp) ;
+int merc_hom_levelup(struct homun_data *hd) ;
+int merc_hom_evolution(struct homun_data *hd) ;
+void merc_hom_heal(struct homun_data *hd,int hp,int sp);
+int merc_hom_vaporize(struct map_session_data *sd, int flag);
+int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y);
+void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp);
+void merc_reset_stats(struct homun_data *hd);
+int merc_hom_shuffle(struct homun_data *hd); // [Zephyrus]
+void merc_save(struct homun_data *hd);
+int merc_call_homunculus(struct map_session_data *sd);
+int merc_create_homunculus_request(struct map_session_data *sd, int class_);
+int search_homunculusDB_index(int key,int type);
+int merc_menu(struct map_session_data *sd,int menunum);
+int merc_hom_food(struct map_session_data *sd, struct homun_data *hd);
+int merc_hom_hungry_timer_delete(struct homun_data *hd);
+int merc_hom_change_name(struct map_session_data *sd,char *name);
+int merc_hom_change_name_ack(struct map_session_data *sd, char* name, int flag);
+#define merc_stop_walking(hd, type) unit_stop_walking(&(hd)->bl, type)
+#define merc_stop_attack(hd) unit_stop_attack(&(hd)->bl)
+int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value);
+int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value);
+int merc_skill_tree_get_max(int id, int b_class);
+void merc_hom_init_timers(struct homun_data * hd);
+void merc_skill_reload(void);
+void merc_reload(void);
 
 #endif /* _MERCENARY_H_ */