瀏覽代碼

Orn's and Albator's Homunculus system, finally, YAY!!

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@7706 54d463be-8e91-2dee-dedb-b68131a5f0ec
DracoRPG 19 年之前
父節點
當前提交
15dd140846

+ 4 - 0
Changelog-Trunk.txt

@@ -4,6 +4,10 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK.  ALL UNTESTED BUGFIXES/FEATURES GO
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 
 2006/07/17
+	* Added Homunculus system, enjoy! [orn aka Nylou]
+	  - WARNING: This currently only works with the SQL version, as the TXT char-server
+	    has no code for it at the moment. You TXT server won't break, but the char-server
+	    will complain of an unknown packet if you try to get an Homunculus
 	* Updated nomemo mapflags based on info from http://ro.doddlercon.com/images/memo.jpg [Poki#3]
 
 2006/07/15

+ 9 - 0
conf-tmpl/battle/misc.conf

@@ -130,3 +130,12 @@ duel_time_interval: 60
 // NOTE: For this setting to make effect you have to use a server compiled with 
 // Cell Stack Limit support (see src/map/map.h)
 cell_stack_limit: 1
+
+
+// Homunculus settings: I put them there at the moment, they will certainly get their own file later [DracoRPG]
+
+// Intimacy needed to use Evolved Vanilmirth's Bio Explosion
+hvan_explosion_intimate: 45000
+
+// Show stat growth to the owner when an Homunculus levels up
+homunculus_show_growth: 0

+ 100 - 0
db/exp_homun.txt

@@ -0,0 +1,100 @@
+//Homunculus LvUp experience value table
+50
+110
+180
+260
+350
+630
+950
+1310
+1710
+2150
+3180
+4300
+5510
+6810
+8200
+10800
+13560
+16480
+19560
+22800
+28090
+33630
+39420
+45460
+51750
+6150
+70910
+81030
+91510
+102350
+117580
+133300
+149510
+166210
+183400
+206480
+230200
+254560
+279560
+305200
+338450
+372510
+407380
+443060
+479550
+525590
+572630
+620670
+669710
+719750
+781500
+844460
+908630
+974010
+1040600
+1121280
+1203400
+1286960
+1371960
+1458400
+1561530
+1666350
+1772860
+1881060
+1990950
+2120350
+2251710
+2385030
+2520310
+2657550
+2817340
+2979380
+3143670
+3310210
+3479000
+3673600
+3870760
+4070480
+4272760
+4477600
+4711730
+4948750
+5188660
+5431460
+5677150
+5955830
+6237750
+6522910
+6811310
+7102950
+7431500
+7763660
+8099430
+8438810
+8781800
+9165840
+9553880
+9945920
+0    

+ 73 - 0
db/homun_skill_tree.txt

@@ -0,0 +1,73 @@
+//CLass,Skill-ID,MaxLV,Prerequisite Skill-ID-1,Prerequisite Skill-ID-1-Lv,PrereqSkill-ID-2,PrereqSkill-ID-2-Lv,PrereqSkill-ID-3,PrereqSkill-ID-3-Lv,PrereqSkill-ID-4,PrereqSkill-ID-4-Lv,PrereqSkill-ID-5,PrereqSkill-ID-5-Lv//CLASS_SKILLNAME#Skill Name#
+//Lif
+6001,8001,5,0,0,0,0,0,0,0,0,0,0 //HLIF_HEAL
+6001,8002,5,8001,3,0,0,0,0,0,0,0,0 //HLIF_AVOID
+6001,8003,5,8001,5,0,0,0,0,0,0,0,0 //HLIF_BRAIN
+//Amistr
+6002,8005,5,0,0,0,0,0,0,0,0,0,0 //HAMI_CASTLE
+6002,8006,5,8005,5,0,0,0,0,0,0,0,0 //HAMI_DEFENCE
+6002,8007,5,8006,3,0,0,0,0,0,0,0,0 //HAMI_SKIN
+//Filir
+6003,8009,5,0,0,0,0,0,0,0,0,0,0 //HFLI_MOON
+6003,8010,5,8009,3,0,0,0,0,0,0,0,0 //HFLI_FLEET
+6003,8011,5,8010,3,0,0,0,0,0,0,0,0 //HFLI_SPEED
+//Vanilmirth
+6004,8013,5,0,0,0,0,0,0,0,0,0,0 //HVAN_CAPRICE,
+6004,8014,5,8013,3,0,0,0,0,0,0,0,0 //HVAN_CHAOTIC
+6004,8015,5,8013,5,0,0,0,0,0,0,0,0 //HVAN_INSTRUCT
+//Lif2
+6005,8001,5,0,0,0,0,0,0,0,0,0,0 //HLIF_HEAL
+6005,8002,5,8001,3,0,0,0,0,0,0,0,0 //HLIF_AVOID
+6005,8003,5,8001,5,0,0,0,0,0,0,0,0 //HLIF_BRAIN
+//Amistr2
+6006,8005,5,0,0,0,0,0,0,0,0,0,0 //HAMI_CASTLE
+6006,8006,5,8005,5,0,0,0,0,0,0,0,0 //HAMI_DEFENCE
+6006,8007,5,8006,3,0,0,0,0,0,0,0,0 //HAMI_SKIN
+//Filir2
+6007,8009,5,0,0,0,0,0,0,0,0,0,0 //HFLI_MOON
+6007,8010,5,8009,3,0,0,0,0,0,0,0,0 //HFLI_FLEET
+6007,8011,5,8010,3,0,0,0,0,0,0,0,0 //HFLI_SPEED
+//Vanilmirth2
+6008,8013,5,0,0,0,0,0,0,0,0,0,0 //HVAN_CAPRICE,
+6008,8014,5,8013,3,0,0,0,0,0,0,0,0 //HVAN_CHAOTIC
+6008,8015,5,8013,5,0,0,0,0,0,0,0,0 //HVAN_INSTRUCT
+//Lif_H
+6009,8001,5,0,0,0,0,0,0,0,0,0,0 //HLIF_HEAL
+6009,8002,5,8001,3,0,0,0,0,0,0,0,0 //HLIF_AVOID
+6009,8003,5,8001,5,0,0,0,0,0,0,0,0 //HLIF_BRAIN
+6009,8004,3,0,0,0,0,0,0,0,0,0,0 //HLIF_CHANGE
+//Amistr_H
+6010,8005,5,0,0,0,0,0,0,0,0,0,0 //HAMI_CASTLE
+6010,8006,5,8005,5,0,0,0,0,0,0,0,0 //HAMI_DEFENCE
+6010,8007,5,8006,3,0,0,0,0,0,0,0,0 //HAMI_SKIN
+6010,8008,3,0,0,0,0,0,0,0,0,0,0 //HAMI_BLOODLUST
+//Filir_H
+6011,8009,5,0,0,0,0,0,0,0,0,0,0 //HFLI_MOON
+6011,8010,5,8009,3,0,0,0,0,0,0,0,0 //HFLI_FLEET
+6011,8011,5,8010,3,0,0,0,0,0,0,0,0 //HFLI_SPEED
+6011,8012,3,0,0,0,0,0,0,0,0,0,0 //HFLI_SBR44
+//Vanilmirth_H
+6012,8013,5,0,0,0,0,0,0,0,0,0,0 //HVAN_CAPRICE,
+6012,8014,5,8013,3,0,0,0,0,0,0,0,0 //HVAN_CHAOTIC
+6012,8015,5,8013,5,0,0,0,0,0,0,0,0 //HVAN_INSTRUCT
+6012,8016,3,0,0,0,0,0,0,0,0,0,0 //HVAN_EXPLOSION
+//Lif2_H
+6013,8001,5,0,0,0,0,0,0,0,0,0,0 //HLIF_HEAL
+6013,8002,5,8001,3,0,0,0,0,0,0,0,0 //HLIF_AVOID
+6013,8003,5,8001,5,0,0,0,0,0,0,0,0 //HLIF_BRAIN
+6013,8004,3,0,0,0,0,0,0,0,0,0,0 //HLIF_CHANGE
+//Amistr2_H
+6014,8005,5,0,0,0,0,0,0,0,0,0,0 //HAMI_CASTLE
+6014,8006,5,8005,5,0,0,0,0,0,0,0,0 //HAMI_DEFENCE
+6014,8007,5,8006,3,0,0,0,0,0,0,0,0 //HAMI_SKIN
+6014,8008,3,0,0,0,0,0,0,0,0,0,0 //HAMI_BLOODLUST
+//Filir2_H
+6015,8009,5,0,0,0,0,0,0,0,0,0,0 //HFLI_MOON
+6015,8010,5,8009,3,0,0,0,0,0,0,0,0 //HFLI_FLEET
+6015,8011,5,8010,3,0,0,0,0,0,0,0,0 //HFLI_SPEED
+6015,8012,3,0,0,0,0,0,0,0,0,0,0 //HFLI_SBR44
+//Vanilmirth2_H
+6016,8013,5,0,0,0,0,0,0,0,0,0,0 //HVAN_CAPRICE,
+6016,8014,5,8013,3,0,0,0,0,0,0,0,0 //HVAN_CHAOTIC
+6016,8015,5,8013,5,0,0,0,0,0,0,0,0 //HVAN_INSTRUCT
+6016,8016,3,0,0,0,0,0,0,0,0,0,0 //HVAN_EXPLOSION

+ 17 - 0
db/homunculus_db.txt

@@ -0,0 +1,17 @@
+//class,name,baseHP,baseSP,baseSTR,baseAGI,baseVIT,baseINT,baseDEX,baseLUK,baseIntimacy,baseHungry,hungryDelay,foodID,gminHP,gmaxHP,gminSP,gmaxSP,gminSTR,gmaxSTR,gminAGI,gmaxAGI,gminVIT,gmaxVIT,gminINT,gmaxINT,gminDEX,gmaxDEX,gminLUK,gmaxLUK,evol_class,baseASPD,size,race,element,accessID
+6001,Lif,150,40,17,20,15,35,24,12,21,32,60000,537,60,100,4,9,1,8,3,8,1,14,1,14,1,14,3,14,6009,700,0,0,0,0
+6002,Amistr,320,10,20,17,35,11,24,12,21,32,60000,912,80,130,1,4,3,14,3,12,1,14,1,2,2,8,3,10,6010,700,0,0,0,0
+6003,Filir,90,25,29,35,9,8,30,9,21,32,60000,910,45,75,3,6,1,14,1,20,1,1,2,8,1,14,3,10,6011,700,0,0,0,0
+6004,Vanilmirth,80,11,11,11,11,11,11,11,21,32,60000,911,30,150,0,7,1,20,1,20,1,20,1,20,1,20,3,20,6012,700,0,0,0,0
+6005,Lif2,150,40,17,20,15,35,24,12,21,32,60000,537,60,100,4,9,1,8,3,8,1,14,1,14,1,14,3,14,6013,700,0,0,0,0
+6006,Amistr2,320,10,20,17,35,11,24,12,21,32,60000,912,80,130,1,4,3,14,3,12,1,14,1,2,2,8,3,10,6014,700,0,0,0,0
+6007,Filir2,90,25,29,35,9,8,30,9,21,32,60000,910,45,75,3,6,1,14,1,20,1,1,2,8,1,14,3,10,6015,700,0,0,0,0
+6008,Vanilmirth2,80,11,11,11,11,11,11,11,21,32,60000,911,30,150,0,7,1,20,1,20,1,20,1,20,1,20,3,20,6016,700,0,0,0,0
+6009,Lif_H,150,40,17,20,15,35,24,12,21,32,60000,537,60,100,4,9,1,8,3,8,1,14,1,14,1,14,3,14,0,700,1,0,0,0
+6010,Amistr_H,320,10,20,17,35,11,24,12,21,32,60000,912,80,130,1,4,3,14,3,12,1,14,1,2,2,8,3,10,0,700,1,0,0,0
+6011,Filir_H,90,25,29,35,9,8,30,9,21,32,60000,910,45,75,3,6,1,14,1,20,1,1,2,8,1,14,3,10,0,700,1,0,0,0
+6012,Vanilmirth_H,80,11,11,11,11,11,11,11,21,32,60000,911,30,150,0,7,1,20,1,20,1,20,1,20,1,20,3,20,0,700,1,0,0,0
+6013,Lif2_H,150,40,17,20,15,35,24,12,21,32,60000,537,60,100,4,9,1,8,3,8,1,14,1,14,1,14,3,14,0,700,1,0,0,0
+6014,Amistr2_H,320,10,20,17,35,11,24,12,21,32,60000,912,80,130,1,4,3,14,3,12,1,14,1,2,2,8,3,10,0,700,1,0,0,0
+6015,Filir2_H,90,25,29,35,9,8,30,9,21,32,60000,910,45,75,3,6,1,14,1,20,1,1,2,8,1,14,3,10,0,700,1,0,0,0
+6016,Vanilmirth2_H,80,11,11,11,11,11,11,11,21,32,60000,911,30,150,0,7,1,20,1,20,1,20,1,20,1,20,3,20,0,700,1,0,0,0

+ 1 - 1
db/item_db.txt

@@ -2433,7 +2433,7 @@
 12037,Lotto_Box_03,Lotto Box 03,2,,10,20,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem rand(7381,7390),1; },{},{}
 12038,Lotto_Box_04,Lotto Box 04,2,,10,20,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem groupranditem(IG_LottoBox),1; },{},{}
 12039,Lotto_Box_05,Lotto Box 05,2,,10,20,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem rand(7542,7546),1; },{},{}
-12040,Philosopher's_Stone,Philosopher's Stone,2,,50000,300,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
+12040,Philosopher's_Stone,Philosopher's Stone,2,,50000,300,,,,,0xFFFFFFFF,7,2,,,,,,{ homevolution ; },{},{}
 // Mixed Foods (+1 to +5)
 12041,Boiled_Down_Locust,Boiled Down Locust,0,2000,,60,,,,,0xFFFFFFFF,7,2,,,,,,{ sc_start SC_STRFood, 1200000, 1; percentheal 5,0; },{},{}
 12042,Seasoned_Webs,Seasoned Webs,0,4000,,500,,,,,0xFFFFFFFF,7,2,,,,,,{ sc_start SC_STRFood, 1200000, 2; percentheal 5,0; },{},{}

+ 16 - 16
db/mob_db.txt

@@ -831,19 +831,19 @@
 1796,G_AUNOE,Aunoe,Aunoe,1,50,0,2,1,1,7,10,0,5,1,1,1,1,6,30,10,12,1,3,21,131,400,1872,672,480,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
 //Homunculi (Don't uncomment) (Why? :P [Poki#3])
-//6001,LIF,Lif,Lif,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6002,AMISTR,Amistr,Amistr,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6003,FILIR,Filir,Filir,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6004,VANILMIRTH,Vanilmirth,Vanilmirth,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6005,LIF2,Lif,Lif,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6006,AMISTR2,Amistr,Amistr,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6007,FILIR2,Filir,Filir,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6008,VANILMIRTH2,Vanilmirth,Vanilmirth,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6009,LIF_H,Lif,Lif,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6010,AMISTR_H,Amistr,Amistr,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6011,FILIR_H,Filir,Filir,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6012,VANILMIRTH_H,Vanilmirth,Vanilmirth,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6013,LIF_H2,Lif,Lif,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6014,AMISTR_H2,Amistr,Amistr,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6015,FILIR_H2,Filir,Filir,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-//6016,VANILMIRTH_H2,Vanilmirth,Vanilmirth,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+6001,LIF,Lif,Lif,50,8010,708,465,94,1,170,325,48,21,1,94,48,129,99,90,10,12,1,0,60,145,300,1608,816,396,0,0,0,0,0,0,0,0,999,150,953,5500,912,220,757,61,1003,120,715,200,998,350,0,0,0,0,4072,5//1Lif
+6002,AMISTR,Amistr,Amistr,50,12022,255,465,94,1,208,416,83,12,1,89,84,25,76,75,10,12,1,0,60,145,300,1608,816,396,0,0,0,0,0,0,0,0,999,150,953,5500,912,220,757,61,1003,120,715,200,998,350,0,0,0,0,4072,5//1Amistr
+6003,FILIR,Filir,Filir,50,5820,515,3872,2695,1,200,401,18,23,1,129,19,95,111,69,10,12,1,0,80,145,250,1608,864,288,0,0,0,0,0,0,0,0,999,150,953,5500,912,220,757,61,1003,120,715,200,998,350,0,0,0,0,4072,5//1Filir
+6004,VANILMIRTH,Vanilmirth,Vanilmirth,50,9026,358,3872,2695,2,226,452,74,32,1,131,75,155,112,120,10,12,1,0,80,145,250,1264,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Vanilmirth
+6005,LIF2,Lif,Lif,50,8010,708,465,94,1,170,325,48,21,1,94,48,129,99,90,10,12,1,0,60,145,300,1608,816,396,0,0,0,0,0,0,0,0,999,150,953,5500,912,220,757,61,1003,120,715,200,998,350,0,0,0,0,4072,5//1Lif
+6006,AMISTR2,Amistr,Amistr,50,12022,255,465,94,1,208,416,83,12,1,89,84,25,76,75,10,12,1,0,80,145,200,1264,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Amistr
+6007,FILIR2,Filir,Filir,50,5820,515,3872,2695,1,200,401,18,23,1,129,19,95,111,69,10,12,1,0,80,145,200,1024,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Filir
+6008,VANILMIRTH2,Vanimirth,Vanilmirth,50,9026,358,3872,2695,2,226,452,74,32,1,131,75,155,112,120,10,12,1,0,80,145,150,1024,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Vanilmirth
+6009,LIF_H,Lif,Lif,50,8010,708,465,94,1,200,410,48,21,1,97,48,155,110,99,10,12,1,0,60,145,300,1608,816,396,0,0,0,0,0,0,0,0,999,150,953,5500,912,220,757,61,1003,120,715,200,998,350,0,0,0,0,4072,5//1Lif
+6010,AMISTR_H,Amistr,Amistr,50,12022,255,465,94,1,230,460,83,12,1,93,84,30,85,83,10,12,2,0,60,145,300,1608,816,396,0,0,0,0,0,0,0,0,999,150,953,5500,912,220,757,61,1003,120,715,200,998,350,0,0,0,0,4072,5//1Amistr
+6011,FILIR_H,Filir,Filir,50,5820,515,3872,2695,1,220,440,18,23,1,138,19,105,126,80,10,12,2,0,80,145,250,1608,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Filir
+6012,VANILMIRTH_H,Vanilmirth,Vanilmirth,50,9026,358,3872,2695,2,280,550,74,32,1,135,75,162,120,126,10,12,2,0,80,145,250,1264,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Vanilmirth
+6013,LIF_H2,Lif,Lif,50,8010,708,465,94,1,200,410,48,21,1,97,48,155,110,99,10,12,1,0,60,145,300,1608,816,396,0,0,0,0,0,0,0,0,999,150,953,5500,912,220,757,61,1003,120,715,200,998,350,0,0,0,0,4072,5//1Lif
+6014,AMISTR_H2,Amistr,Amistr,50,12022,255,465,94,1,230,460,83,12,1,93,84,30,85,83,10,12,2,0,80,145,200,1264,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Amistr
+6015,FILIR_H2,Filir,Filir,50,5820,515,3872,2695,1,220,440,18,23,1,138,19,105,126,80,10,12,2,0,80,145,200,1024,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Filir
+6016,VANILMIRTH_H2,Vanimirth,Vanilmirth,50,9026,358,3872,2695,2,280,550,74,32,1,135,75,162,120,126,10,12,2,0,80,145,150,1024,864,288,0,0,0,0,0,0,0,0,7004,2000,7054,5500,1000,250,997,30,512,0,757,250,985,163,0,0,0,0,4072,5//1Vanilmirth

+ 13 - 9
db/packet_db.txt

@@ -676,9 +676,9 @@ packet_ver: 17
 0x022b,57
 0x022c,64
 0x022d,5
-0x0232,9
-0x0233,11
-0x0234,-1
+0x0232,9,hommoveto,6
+0x0233,11,homattack,0
+0x0234,6,hommovetomaster,0
 
 //2005-06-28aSakexe by Sara
 packet_ver: 18
@@ -695,18 +695,22 @@ packet_ver: 18
 0x00f3,-1,globalmessage,2:4
 0x00f5,13,takeitem,9
 0x00f7,18,movefromkafra,11:14
+0x0112,4,homuseskillpoint,0
 0x0113,33,useskilltopos,12:15:18:31
 0x0116,12,dropitem,3:10
 0x0143,10,npcamountinput,2:6
 0x0190,24,actionrequest,11:23
 0x0193,2,closekafra,0
 0x20e,10
-//Homon Status
-0x22e,71
-//Start Homunculus?
-0x230,12
-//Homon Skill list
-0x235,115
+0x022d,5,hommenu,4 // Menu (1 = feed ; 2 = dead) 
+0x022e,71 // Status 
+0x022f,5 // Feeding reply 
+0x0230,12 // Is Homunculus 
+0x0231,26,changehomunculusname,0 
+0x0232,9,hommoveto,6 
+0x0233,11,homattack,0 
+0x0234,6,hommovetomaster,0 
+0x0235,115 // Skills
 0x237,2,rankingpk,0
 0x23d,-1
 0x23e,4

+ 31 - 1
db/skill_cast_db.txt

@@ -376,7 +376,13 @@
 //-- AM_CP_HELM
 237,2000,0,0,120000:240000:360000:480000:600000,0
 //==========================================
-
+//-- AM_CALLHOMUNCULUS
+243,2000,0,0,0,0
+//-- AM_REST
+244,2000,0,0,0,0
+//-- AM_RESURRECTHOMUN
+247,2000,0,0,0,0
+//==========================================
 
 //===== Crusader ===========================
 //-- CR_AUTOGUARD
@@ -959,6 +965,30 @@
 1019,2000,1000,0,1800000,0
 //==========================================
 
+//==========================================
+//-- HLIF_HEAL
+8001,0,1000,0,0,1000
+//-- HLIF_AVOID
+8002,0,0,0,40000:35000:30000:25000:20000,0
+//-- HLIF_CHANGE
+8004,0,0,0,60000:180000:300000,600000:900000:1200000
+//-- HAMI_DEFENCE
+8006,0,0,0,40000:35000:30000:25000:20000,30000
+//-- HAMI_BLOODLUST
+8008,0,0,0,60000:180000:300000,300000:600000:900000
+//-- HFLI_MOON
+8009,0,0,1000,0,0	
+//-- HFLI_FLEET
+8010,0,0,0,60000:55000:50000:45000:40000,60000:70000:80000:90000:120000
+//-- HFLI_SPEED
+8011,0,0,0,60000:55000:50000:45000:40000,60000:70000:80000:90000:120000
+//-- HVAN_CAPRICE
+8013,700:1400:2100:2800:3500,0,1000:1200:1400:1600:1800,0,1000:1200:1400:1600:1800
+//-- HVAN_CHAOTIC
+8014,0,0,1000,0,1000
+//-- HVAN_EXPLOSION
+8016,0,0,1000,0,0
+//==========================================
 
 //===== Guild Skills =======================
 //-- GD_LEADERSHIP

+ 20 - 2
db/skill_db.txt

@@ -263,8 +263,8 @@
 //240,0,0,0,0,0,0,5,0,no,0,0,0,none,0	//AM_CREATECREATURE#Life Creation#
 //241,0,0,0,0,0,0,5,0,no,0,0,0,none,0	//AM_CULTIVATION#Cultivation#
 //242,0,0,0,0,0,0,5,0,no,0,0,0,none,0	//AM_FLAMECONTROL#Flame Control#
-243,0,6,4,0,1,0,1,0,no,0,0,0,none,0	//AM_CALLHOMUN#Call Homunculus#
-244,0,6,4,0,1,0,1,0,no,0,0,0,none,0	//AM_REST#Peaceful Rest#
+243,0,0,4,0,1,0,1,0,no,0,0,0,none,0	//AM_CALLHOMUN#Call Homunculus#
+244,0,0,4,0,1,0,1,0,no,0,0,0,none,0	//AM_REST#Peaceful Rest#
 //245,0,0,0,0,0,0,10,0,no,0,0,0,none,0	//AM_DRILLMASTER#Drillmaster#
 //246,9,0,0,0,0,0,10,0,no,0,0,0,none,0	//AM_HEALHOMUN#Heal Homunculus#
 247,9,6,4,0,1,0,5,0,no,0,0,0,none,0	//AM_RESURRECTHOMUN#Ressurect Homunculus#
@@ -587,6 +587,24 @@
 1018,9,6,1,3,1,0,1,1,yes,0,1,0,magic,0	//SA_ELEMENTFIRE#Elemental Change Fire#
 1019,9,6,1,4,1,0,1,1,yes,0,1,0,magic,0	//SA_ELEMENTWIND#Elemental Change Wind#
 
+//homunculus
+8001,9,6,16,0,1,0,5,1,no,0,0,0,magic,0	//HLIF_HEAL#Healing hand#
+8002,0,6,4,0,3,-1,5,1,no,0,0,0,none,0	//HLIF_AVOID
+8003,0,0,0,0,0,1,5,0,no,0,0,0,none,0	//HLIF_BRAIN
+8004,0,6,4,0,1,0,3,0,no,0,0,0,none,0	//HLIF_CHANGE
+8005,0,6,4,0,1,0,5,0,no,0,0,0,none,0	//HAMI_CASTLE
+8006,0,6,4,0,1,0,5,0,no,0,0,0,none,0	//HAMI_DEFENCE
+8007,0,0,0,0,1,0,5,0,no,0,0,0,none,0	//HAMI_SKIN
+8008,0,6,4,0,1,0,3,0,no,0,0,0,none,0	//HAMI_BLOODLUST
+8009,1,8,1,0,0,0,5,1:2:2:2:3,no,0,0,0,weapon,0	//HFLI_MOON
+8010,0,6,4,0,1,0,5,0,no,0,0,0,none,0	//HFLI_FLEET
+8011,0,6,4,0,1,0,5,0,yes,0,0,0,misc,0	//HFLI_SPEED
+8012,1,6,1,0,0,0,3,0,no,0,0,0,none,0	//HFLI_SBR44
+8013,9,6,1,0,0,0,5,1:2:3:4:5,no,0,0,0,magic,0	//HVAN_CAPRICE
+8014,0,6,4,0,1,0,5,0,no,0,0,0,none,0	//HVAN_CHAOTIC
+8015,0,0,0,0,1,0,5,0,no,0,0,0,none,0	//HVAN_INSTRUCT
+8016,0,6,4,-1,2,2,3,1,no,0,0,0,misc,0	//HVAN_EXPLOSION
+
 //id,range,hit,inf,pl,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count
 10000,0,0,0,0,0,0,1,0,no,0,16,0,none,0	//GD_APPROVAL#Approval#
 10001,0,0,0,0,0,0,1,0,no,0,16,0,none,0	//GD_KAFRACONTRACT#Kafra Contract#

+ 18 - 2
db/skill_require_db.txt

@@ -172,6 +172,10 @@
 236,0,0,25,0,0,0,99,0,0,none,0,7139,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//AM_CP_ARMOR#ケミカルア?マ?チャ?ジ#
 237,0,0,25,0,0,0,99,0,0,none,0,7139,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//AM_CP_HELM#ケミカルヘルムチャ?ジ#
 
+243,0,0,10,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//AM_CALLHOMUNCULUS
+244,0,0,50,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//AM_REST
+247,0,0,74:68:62:56:50,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//AM_RESURRECTHOMUN
+
 249,0,0,12:14:16:18:20:22:24:26:28:30,0,0,0,99,0,0,shield,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//CR_AUTOGUARD#オ?トガ?ド#
 250,0,0,10,0,0,0,99,0,0,shield,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//CR_SHIELDCHARGE#シ?ルドチャ?ジ#
 251,0,0,12,0,0,0,99,0,0,shield,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//CR_SHIELDBOOMERANG#シ?ルドブ?メラン#
@@ -456,17 +460,29 @@
 10013,0,0,1,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//GD_EMERGENCYCALL##
 
 //Current SP values for Homunculi devs
-//AM_HEALHOMUN 12#14#16#18#20#22#24#26#28#30#
-//AM_RESURRECTHOMUN 74#68#62#56#50#
 //HLIF_HEAL 13#16#19#22#25#
+8001,0,0,13:16:19:22:25,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HLIF_HEAL
 //HLIF_AVOID 20#25#30#35#40#
+8002,0,0,20:25:30:35:40,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HLIF_AVOID
+//HLIF_CHANGE 1#1#1#
+8004,0,1,0,0,0,99,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HLIF_CHANGE
 //HAMI_CASTLE 10#10#10#10#10#
+8005,0,0,10,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HAMI_CASTLE
 //HAMI_DEFENCE 20#25#30#35#40#
+8006,0,0,20:25:30:35:40,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HAMI_DEFENCE
 //HAMI_BLOODLUST 120#120#120#
+8008,0,0,120,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HAMI_BLOODLUST
 //HFLI_MOON 4#8#12#16#20#
+8009,0,0,4:8:12:16:20,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HFLI_MOON
 //HFLI_FLEET 30#40#50#60#70#
+8010,0,0,30:40:50:60:70,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HFLI_FLEET
 //HFLI_SPEED 30#40#50#60#70#
+8011,0,0,30:40:50:60:70,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HFLI_SPEED
 //HFLI_SBR44 1#1#1#
+8012,0,0,1,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HFLI_SBR44
 //HVAN_CAPRICE 22#24#26#28#30#
+8013,0,0,22:24:26:28:30,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HVAN_CAPRICE
 //HVAN_CHAOTIC 40#40#40#40#40#
+8014,0,0,40,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HVAN_CHAOTIC
 //HVAN_EXPLOSION 1#1#1# 
+8016,0,0,1,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//HVAN_EXPLOSION

+ 0 - 21
sql-files/homunculus.sql

@@ -1,21 +0,0 @@
--- Table for the Homunculus.
--- [blackhole89]
-
-CREATE TABLE `homunculus` (
-  `id` int(11) NOT NULL auto_increment,
-  `char_id` int(11) unsigned NOT NULL default '0',
-  `class` mediumint(9) unsigned NOT NULL default '0',
-  `name` varchar(24) NOT NULL default '',
-  `level` smallint(4) NOT NULL default '0',
-  `exp` int(12) NOT NULL default '0',
-  `hunger` tinyint(10) NOT NULL default '0',
-  `hp` int(12) NOT NULL default '1',
-  `sp` int(12) NOT NULL default '1',
-  `skill1lv` smallint(4) NOT NULL default '0',
-  `skill2lv` smallint(4) NOT NULL default '0',
-  `skill3lv` smallint(4) NOT NULL default '0',
-  `skill4lv` smallint(4) NOT NULL default '0',
-  `skillpts` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`id`),
-  KEY `char_id` (`char_id`)
-)

+ 584 - 540
sql-files/main.sql

@@ -1,540 +1,584 @@
--- MySQL dump 9.11
--- Server version	4.0.24
-
---
--- Table structure for table `cart_inventory`
---
-
-DROP TABLE IF EXISTS `cart_inventory`;
-CREATE TABLE `cart_inventory` (
-  `id` int(11) NOT NULL auto_increment,
-  `char_id` int(11) NOT NULL default '0',
-  `nameid` int(11) NOT NULL default '0',
-  `amount` int(11) NOT NULL default '0',
-  `equip` mediumint(8) unsigned NOT NULL default '0',
-  `identify` smallint(6) NOT NULL default '0',
-  `refine` tinyint(3) unsigned NOT NULL default '0',
-  `attribute` tinyint(4) NOT NULL default '0',
-  `card0` int(11) NOT NULL default '0',
-  `card1` int(11) NOT NULL default '0',
-  `card2` int(11) NOT NULL default '0',
-  `card3` int(11) NOT NULL default '0',
-  PRIMARY KEY  (`id`),
-  KEY `char_id` (`char_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `char`
---
-
-DROP TABLE IF EXISTS `char`;
-CREATE TABLE `char` (
-  `char_id` int(11) unsigned NOT NULL auto_increment,
-  `account_id` int(11) unsigned NOT NULL default '0',
-  `char_num` tinyint(1) NOT NULL default '0',
-  `name` varchar(30) NOT NULL default '',
-  `class` smallint(6) unsigned NOT NULL default '0',
-  `base_level` smallint(6) unsigned NOT NULL default '1',
-  `job_level` smallint(6) unsigned NOT NULL default '1',
-  `base_exp` bigint(20) unsigned NOT NULL default '0',
-  `job_exp` bigint(20) unsigned NOT NULL default '0',
-  `zeny` int(11) unsigned NOT NULL default '0',
-  `str` smallint(4) unsigned NOT NULL default '0',
-  `agi` smallint(4) unsigned NOT NULL default '0',
-  `vit` smallint(4) unsigned NOT NULL default '0',
-  `int` smallint(4) unsigned NOT NULL default '0',
-  `dex` smallint(4) unsigned NOT NULL default '0',
-  `luk` smallint(4) unsigned NOT NULL default '0',
-  `max_hp` mediumint(8) unsigned NOT NULL default '0',
-  `hp` mediumint(8) unsigned NOT NULL default '0',
-  `max_sp` mediumint(6) unsigned NOT NULL default '0',
-  `sp` mediumint(6) unsigned NOT NULL default '0',
-  `status_point` smallint(4) unsigned NOT NULL default '0',
-  `skill_point` smallint(4) unsigned NOT NULL default '0',
-  `option` int(11) NOT NULL default '0',
-  `karma` tinyint(3) NOT NULL default '0',
-  `manner` tinyint(3) NOT NULL default '0',
-  `party_id` int(11) unsigned NOT NULL default '0',
-  `guild_id` int(11) unsigned NOT NULL default '0',
-  `pet_id` int(11) unsigned NOT NULL default '0',
-  `hair` tinyint(4) unsigned NOT NULL default '0',
-  `hair_color` smallint(5) unsigned NOT NULL default '0',
-  `clothes_color` smallint(5) unsigned NOT NULL default '0',
-  `weapon` smallint(6) unsigned NOT NULL default '1',
-  `shield` smallint(6) unsigned NOT NULL default '0',
-  `head_top` smallint(6) unsigned NOT NULL default '0',
-  `head_mid` smallint(6) unsigned NOT NULL default '0',
-  `head_bottom` smallint(6) unsigned NOT NULL default '0',
-  `last_map` varchar(20) NOT NULL default 'prontera.gat',
-  `last_x` smallint(4) unsigned NOT NULL default '53',
-  `last_y` smallint(4) unsigned NOT NULL default '111',
-  `save_map` varchar(20) NOT NULL default 'prontera.gat',
-  `save_x` smallint(4) unsigned NOT NULL default '53',
-  `save_y` smallint(4) unsigned NOT NULL default '111',
-  `partner_id` int(11) unsigned NOT NULL default '0',
-  `online` tinyint(2) NOT NULL default '0',
-  `father` int(11) unsigned NOT NULL default '0',
-  `mother` int(11) unsigned NOT NULL default '0',
-  `child` int(11) unsigned NOT NULL default '0',
-  `fame` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`char_id`),
-  KEY `account_id` (`account_id`),
-  KEY `party_id` (`party_id`),
-  KEY `guild_id` (`guild_id`)
-) TYPE=InnoDB AUTO_INCREMENT=150000; 
-
---
--- Table structure for table `charlog`
---
-
-DROP TABLE IF EXISTS `charlog`;
-CREATE TABLE `charlog` (
-  `time` datetime NOT NULL default '0000-00-00 00:00:00',
-  `char_msg` varchar(255) NOT NULL default 'char select',
-  `account_id` int(11) NOT NULL default '0',
-  `char_num` tinyint(4) NOT NULL default '0',
-  `name` varchar(255) NOT NULL default '',
-  `str` int(11) unsigned NOT NULL default '0',
-  `agi` int(11) unsigned NOT NULL default '0',
-  `vit` int(11) unsigned NOT NULL default '0',
-  `int` int(11) unsigned NOT NULL default '0',
-  `dex` int(11) unsigned NOT NULL default '0',
-  `luk` int(11) unsigned NOT NULL default '0',
-  `hair` tinyint(4) NOT NULL default '0',
-  `hair_color` int(11) NOT NULL default '0'
-) TYPE=MyISAM; 
-
---
--- Table structure for table `friends`
---
-
-DROP TABLE IF EXISTS `friends`;
-CREATE TABLE `friends` (
-  `char_id` int(11) NOT NULL default '0',
-  `friend_account` int(11) NOT NULL default '0',
-  `friend_id` int(11) NOT NULL default '0'
-) TYPE=MyISAM;
-
---
--- Table structure for table `global_reg_value`
---
-
-DROP TABLE IF EXISTS `global_reg_value`;
-CREATE TABLE `global_reg_value` (
-  `char_id` int(11) unsigned NOT NULL default '0',
-  `str` varchar(255) NOT NULL default '',
-  `value` varchar(255) NOT NULL default '0',
-  `type` int(11) NOT NULL default '3',
-  `account_id` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`char_id`,`str`,`account_id`),
-  KEY `account_id` (`account_id`),
-  KEY `char_id` (`char_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `guild`
---
-
-DROP TABLE IF EXISTS `guild`;
-CREATE TABLE `guild` (
-  `guild_id` int(11) unsigned NOT NULL auto_increment,
-  `name` varchar(24) NOT NULL default '',
-  `char_id` int(11) unsigned NOT NULL default '0',
-  `master` varchar(24) NOT NULL default '',
-  `guild_lv` tinyint(6) unsigned NOT NULL default '0',
-  `connect_member` tinyint(6) unsigned NOT NULL default '0',
-  `max_member` tinyint(6) unsigned NOT NULL default '0',
-  `average_lv` smallint(6) unsigned NOT NULL default '1',
-  `exp` int(11) unsigned NOT NULL default '0',
-  `next_exp` int(11) unsigned NOT NULL default '0',
-  `skill_point` tinyint(11) unsigned NOT NULL default '0',
-  `mes1` varchar(60) NOT NULL default '',
-  `mes2` varchar(120) NOT NULL default '',
-  `emblem_len` int(11) unsigned NOT NULL default '0',
-  `emblem_id` int(11) unsigned NOT NULL default '0',
-  `emblem_data` blob NOT NULL,
-  PRIMARY KEY  (`guild_id`,`char_id`),
-  UNIQUE KEY `guild_id` (`guild_id`),
-  KEY `char_id` (`char_id`),
-  CONSTRAINT `guild_ibfk_1` FOREIGN KEY (`char_id`) REFERENCES `char` (`char_id`) ON DELETE CASCADE
-) TYPE=InnoDB;
-
---
--- Table structure for table `guild_alliance`
---
-
-DROP TABLE IF EXISTS `guild_alliance`;
-CREATE TABLE `guild_alliance` (
-  `guild_id` int(11) unsigned NOT NULL default '0',
-  `opposition` int(11) unsigned NOT NULL default '0',
-  `alliance_id` int(11) unsigned NOT NULL default '0',
-  `name` varchar(24) NOT NULL default '',
-  PRIMARY KEY  (`guild_id`,`alliance_id`),
-  KEY `alliance_id` (`alliance_id`),
-  CONSTRAINT `guild_alliance_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE,
-  CONSTRAINT `guild_alliance_ibfk_2` FOREIGN KEY (`alliance_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
-) TYPE=InnoDB;
-
---
--- Table structure for table `guild_castle`
---
-
-DROP TABLE IF EXISTS `guild_castle`;
-CREATE TABLE `guild_castle` (
-  `castle_id` int(11) unsigned NOT NULL default '0',
-  `guild_id` int(11) unsigned NOT NULL default '0',
-  `economy` int(11) unsigned NOT NULL default '0',
-  `defense` int(11) unsigned NOT NULL default '0',
-  `triggerE` int(11) unsigned NOT NULL default '0',
-  `triggerD` int(11) unsigned NOT NULL default '0',
-  `nextTime` int(11) unsigned NOT NULL default '0',
-  `payTime` int(11) unsigned NOT NULL default '0',
-  `createTime` int(11) unsigned NOT NULL default '0',
-  `visibleC` int(11) unsigned NOT NULL default '0',
-  `visibleG0` int(11) unsigned NOT NULL default '0',
-  `visibleG1` int(11) unsigned NOT NULL default '0',
-  `visibleG2` int(11) unsigned NOT NULL default '0',
-  `visibleG3` int(11) unsigned NOT NULL default '0',
-  `visibleG4` int(11) unsigned NOT NULL default '0',
-  `visibleG5` int(11) unsigned NOT NULL default '0',
-  `visibleG6` int(11) unsigned NOT NULL default '0',
-  `visibleG7` int(11) unsigned NOT NULL default '0',
-  `gHP0` int(11) unsigned NOT NULL default '0',
-  `ghP1` int(11) unsigned NOT NULL default '0',
-  `gHP2` int(11) unsigned NOT NULL default '0',
-  `gHP3` int(11) unsigned NOT NULL default '0',
-  `gHP4` int(11) unsigned NOT NULL default '0',
-  `gHP5` int(11) unsigned NOT NULL default '0',
-  `gHP6` int(11) unsigned NOT NULL default '0',
-  `gHP7` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`castle_id`),
-  KEY `guild_id` (`guild_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `guild_expulsion`
---
-
-DROP TABLE IF EXISTS `guild_expulsion`;
-CREATE TABLE `guild_expulsion` (
-  `guild_id` int(11) unsigned NOT NULL default '0',
-  `name` varchar(24) NOT NULL default '',
-  `mes` varchar(40) NOT NULL default '',
-  `acc` varchar(40) NOT NULL default '',
-  `account_id` int(11) unsigned NOT NULL default '0',
-  `rsv1` int(11) unsigned NOT NULL default '0',
-  `rsv2` int(11) unsigned NOT NULL default '0',
-  `rsv3` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`guild_id`,`name`),
-  CONSTRAINT `guild_expulsion_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
-) TYPE=InnoDB;
-
---
--- Table structure for table `guild_member`
---
-
-DROP TABLE IF EXISTS `guild_member`;
-CREATE TABLE `guild_member` (
-  `guild_id` int(11) unsigned NOT NULL default '0',
-  `account_id` int(11) unsigned NOT NULL default '0',
-  `char_id` int(11) unsigned NOT NULL default '0',
-  `hair` tinyint(6) unsigned NOT NULL default '0',
-  `hair_color` smallint(6) unsigned NOT NULL default '0',
-  `gender` tinyint(6) unsigned NOT NULL default '0',
-  `class` smallint(6) unsigned NOT NULL default '0',
-  `lv` smallint(6) unsigned NOT NULL default '0',
-  `exp` bigint(20) unsigned NOT NULL default '0',
-  `exp_payper` tinyint(11) unsigned NOT NULL default '0',
-  `online` tinyint(4) unsigned NOT NULL default '0',
-  `position` tinyint(6) unsigned NOT NULL default '0',
-  `rsv1` int(11) unsigned NOT NULL default '0',
-  `rsv2` int(11) unsigned NOT NULL default '0',
-  `name` varchar(24) NOT NULL default '',
-  PRIMARY KEY  (`guild_id`,`char_id`),
-  KEY `char_id` (`char_id`),
-  CONSTRAINT `guild_member_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE,
-  CONSTRAINT `guild_member_ibfk_2` FOREIGN KEY (`char_id`) REFERENCES `char` (`char_id`) ON DELETE CASCADE
-) TYPE=InnoDB;
-
---
--- Table structure for table `guild_position`
---
-
-DROP TABLE IF EXISTS `guild_position`;
-CREATE TABLE `guild_position` (
-  `guild_id` int(9) unsigned NOT NULL default '0',
-  `position` tinyint(6) unsigned NOT NULL default '0',
-  `name` varchar(24) NOT NULL default '',
-  `mode` tinyint(11) unsigned NOT NULL default '0',
-  `exp_mode` tinyint(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`guild_id`,`position`),
-  KEY `guild_id` (`guild_id`),
-  CONSTRAINT `guild_position_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
-) TYPE=InnoDB;
-
---
--- Table structure for table `guild_skill`
---
-
-DROP TABLE IF EXISTS `guild_skill`;
-CREATE TABLE `guild_skill` (
-  `guild_id` int(11) unsigned NOT NULL default '0',
-  `id` smallint(11) unsigned NOT NULL default '0',
-  `lv` tinyint(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`guild_id`,`id`),
-  CONSTRAINT `guild_skill_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
-) TYPE=InnoDB;
-
---
--- Table structure for table `guild_storage`
---
-
-DROP TABLE IF EXISTS `guild_storage`;
-CREATE TABLE `guild_storage` (
-  `id` int(10) unsigned NOT NULL auto_increment,
-  `guild_id` int(11) unsigned NOT NULL default '0',
-  `nameid` int(11) unsigned NOT NULL default '0',
-  `amount` int(11) unsigned NOT NULL default '0',
-  `equip` mediumint(8) unsigned NOT NULL default '0',
-  `identify` smallint(6) unsigned NOT NULL default '0',
-  `refine` tinyint(3) unsigned NOT NULL default '0',
-  `attribute` tinyint(4) unsigned NOT NULL default '0',
-  `card0` smallint(11) NOT NULL default '0',
-  `card1` smallint(11) NOT NULL default '0',
-  `card2` smallint(11) NOT NULL default '0',
-  `card3` smallint(11) NOT NULL default '0',
-  PRIMARY KEY  (`id`),
-  KEY `guild_id` (`guild_id`),
-  CONSTRAINT `guild_storage_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
-) TYPE=InnoDB;
-
--- Database: Ragnarok
--- Table: 'interlog'
---
-
-DROP TABLE IF EXISTS `interlog`;
-CREATE TABLE `interlog` (
-  `time` datetime NOT NULL default '0000-00-00 00:00:00',
-  `log` varchar(255) NOT NULL default ''
-) TYPE=MyISAM; 
-
---
--- Table structure for table `inventory`
---
-
-DROP TABLE IF EXISTS `inventory`;
-CREATE TABLE `inventory` (
-  `id` int(11) unsigned NOT NULL auto_increment,
-  `char_id` int(11) unsigned NOT NULL default '0',
-  `nameid` int(11) unsigned NOT NULL default '0',
-  `amount` int(11) unsigned NOT NULL default '0',
-  `equip` mediumint(8) unsigned NOT NULL default '0',
-  `identify` smallint(6) NOT NULL default '0',
-  `refine` tinyint(3) unsigned NOT NULL default '0',
-  `attribute` tinyint(4) unsigned NOT NULL default '0',
-  `card0` smallint(11) NOT NULL default '0',
-  `card1` smallint(11) NOT NULL default '0',
-  `card2` smallint(11) NOT NULL default '0',
-  `card3` smallint(11) NOT NULL default '0',
-  PRIMARY KEY  (`id`),
-  KEY `char_id` (`char_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `ipbanlist`
---
-
-DROP TABLE IF EXISTS `ipbanlist`;
-CREATE TABLE `ipbanlist` (
-  `list` varchar(255) NOT NULL default '',
-  `btime` datetime NOT NULL default '0000-00-00 00:00:00',
-  `rtime` datetime NOT NULL default '0000-00-00 00:00:00',
-  `reason` varchar(255) NOT NULL default '',
-  KEY (`list`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `login`
---
-
-DROP TABLE IF EXISTS `login`;
-CREATE TABLE `login` (
-  `account_id` int(11) unsigned NOT NULL auto_increment,
-  `userid` varchar(255) NOT NULL default '',
-  `user_pass` varchar(32) NOT NULL default '',
-  `lastlogin` datetime NOT NULL default '0000-00-00 00:00:00',
-  `sex` char(1) NOT NULL default 'M',
-  `logincount` mediumint(9) unsigned NOT NULL default '0',
-  `email` varchar(60) NOT NULL default '',
-  `level` tinyint(3) NOT NULL default '0',
-  `error_message` smallint(11) unsigned NOT NULL default '0',
-  `connect_until` smallint(11) unsigned NOT NULL default '0',
-  `last_ip` varchar(100) NOT NULL default '',
-  `memo` smallint(11) unsigned NOT NULL default '0',
-  `ban_until` int(11) unsigned NOT NULL default '0',
-  `state` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`account_id`),
-  KEY `name` (`userid`)
-) TYPE=InnoDB AUTO_INCREMENT=2000000; 
-
--- added standard accounts for servers, VERY INSECURE!!!
--- inserted into the table called login which is above
-
-INSERT INTO `login` (`account_id`, `userid`, `user_pass`, `sex`, `email`) VALUES ('1', 's1', 'p1', 'S','athena@athena.com');
-
---
--- Table structure for table `sc_data`
---
-
-DROP TABLE IF EXISTS `sc_data`;
-CREATE TABLE `sc_data` (
-  `account_id` int(11) unsigned NOT NULL,
-  `char_id` int(11) unsigned NOT NULL,
-  `type` smallint(11) unsigned NOT NULL,
-  `tick` int(11) NOT NULL,
-  `val1` int(11) NOT NULL default '0',
-  `val2` int(11) NOT NULL default '0',
-  `val3` int(11) NOT NULL default '0',
-  `val4` int(11) NOT NULL default '0',
-  KEY (`account_id`),
-  KEY (`char_id`),
-  CONSTRAINT `scdata_ibfk_1` FOREIGN KEY (`account_id`) REFERENCES `login` (`account_id`) ON DELETE CASCADE,
-  CONSTRAINT `scdata_ibfk_2` FOREIGN KEY (`char_id`) REFERENCES `char` (`char_id`) ON DELETE CASCADE
-) TYPE=InnoDB;
-
---
--- Table structure for table `loginlog`
---
-
-DROP TABLE IF EXISTS `loginlog`;
-CREATE TABLE `loginlog` (
-  `time` datetime NOT NULL default '0000-00-00 00:00:00',
-  `ip` int(10) unsigned NOT NULL default '0',
-  `user` varchar(32) NOT NULL default '',
-  `rcode` tinyint(4) NOT NULL default '0',
-  `log` varchar(255) NOT NULL default '',
-  INDEX (`ip`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `memo`
---
-
-DROP TABLE IF EXISTS `memo`;
-CREATE TABLE `memo` (
-  `memo_id` int(11) unsigned NOT NULL auto_increment,
-  `char_id` int(11) unsigned NOT NULL default '0',
-  `map` varchar(255) NOT NULL default '',
-  `x` smallint(9) unsigned NOT NULL default '0',
-  `y` smallint(9) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`memo_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `party`
---
-
-DROP TABLE IF EXISTS `party`;
-CREATE TABLE `party` (
-  `party_id` int(11) unsigned NOT NULL auto_increment,
-  `name` char(100) NOT NULL default '',
-  `exp` tinyint(11) unsigned NOT NULL default '0',
-  `item` tinyint(11) unsigned NOT NULL default '0',
-  `leader_id` int(11) unsigned NOT NULL default '0',
-  `leader_char` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`party_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `pet`
---
-
-DROP TABLE IF EXISTS `pet`;
-CREATE TABLE `pet` (
-  `pet_id` int(11) unsigned NOT NULL auto_increment,
-  `class` mediumint(9) unsigned NOT NULL default '0',
-  `name` varchar(24) NOT NULL default '',
-  `account_id` int(11) unsigned NOT NULL default '0',
-  `char_id` int(11) unsigned NOT NULL default '0',
-  `level` smallint(4) unsigned NOT NULL default '0',
-  `egg_id` smallint(11) unsigned NOT NULL default '0',
-  `equip` mediumint(8) unsigned NOT NULL default '0',
-  `intimate` smallint(9) unsigned NOT NULL default '0',
-  `hungry` smallint(9) unsigned NOT NULL default '0',
-  `rename_flag` tinyint(4) unsigned NOT NULL default '0',
-  `incuvate` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`pet_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `ragsrvinfo`
---
-
-DROP TABLE IF EXISTS `ragsrvinfo`;
-CREATE TABLE `ragsrvinfo` (
-  `index` int(11) NOT NULL default '0',
-  `name` varchar(255) NOT NULL default '',
-  `exp` int(11) unsigned NOT NULL default '0',
-  `jexp` int(11) unsigned NOT NULL default '0',
-  `drop` int(11) unsigned NOT NULL default '0',
-  `motd` varchar(255) NOT NULL default ''
-) TYPE=MyISAM;
-
---
--- Table structure for table `skill`
---
-
-DROP TABLE IF EXISTS `skill`;
-CREATE TABLE `skill` (
-  `char_id` int(11) unsigned NOT NULL default '0',
-  `id` smallint(11) unsigned NOT NULL default '0',
-  `lv` tinyint(4) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`char_id`,`id`),
-  KEY `char_id` (`char_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `sstatus`
---
-
-DROP TABLE IF EXISTS `sstatus`;
-CREATE TABLE `sstatus` (
-  `index` tinyint(4) unsigned NOT NULL default '0',
-  `name` varchar(255) NOT NULL default '',
-  `user` int(11) unsigned NOT NULL default '0'
-) TYPE=MyISAM;
-
---
--- Table structure for table `storage`
---
-
-DROP TABLE IF EXISTS `storage`;
-CREATE TABLE `storage` (
-  `id` int(11) unsigned NOT NULL auto_increment,
-  `account_id` int(11) unsigned NOT NULL default '0',
-  `nameid` int(11) unsigned NOT NULL default '0',
-  `amount` smallint(11) unsigned NOT NULL default '0',
-  `equip` mediumint(8) unsigned NOT NULL default '0',
-  `identify` smallint(6) unsigned NOT NULL default '0',
-  `refine` tinyint(3) unsigned NOT NULL default '0',
-  `attribute` tinyint(4) unsigned NOT NULL default '0',
-  `card0` smallint(11) NOT NULL default '0',
-  `card1` smallint(11) NOT NULL default '0',
-  `card2` smallint(11) NOT NULL default '0',
-  `card3` smallint(11) NOT NULL default '0',
-  PRIMARY KEY  (`id`),
-  KEY `account_id` (`account_id`)
-) TYPE=MyISAM;
-
---
--- Table structure for table `mapreg`
---
-
-DROP TABLE IF EXISTS `mapreg`;
-CREATE TABLE `mapreg` (
-  `varname` varchar(32) NOT NULL,
-  `index` int(11) unsigned NOT NULL default '0',
-  `value` varchar(255) NOT NULL,
-  KEY `varname` (`varname`),
-  KEY `index` (`index`)
-) TYPE=MyISAM;
+-- MySQL dump 9.11
+-- Server version	4.0.24
+
+--
+-- Table structure for table `cart_inventory`
+--
+
+DROP TABLE IF EXISTS `cart_inventory`;
+CREATE TABLE `cart_inventory` (
+  `id` int(11) NOT NULL auto_increment,
+  `char_id` int(11) NOT NULL default '0',
+  `nameid` int(11) NOT NULL default '0',
+  `amount` int(11) NOT NULL default '0',
+  `equip` mediumint(8) unsigned NOT NULL default '0',
+  `identify` smallint(6) NOT NULL default '0',
+  `refine` tinyint(3) unsigned NOT NULL default '0',
+  `attribute` tinyint(4) NOT NULL default '0',
+  `card0` int(11) NOT NULL default '0',
+  `card1` int(11) NOT NULL default '0',
+  `card2` int(11) NOT NULL default '0',
+  `card3` int(11) NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  KEY `char_id` (`char_id`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `char`
+--
+
+DROP TABLE IF EXISTS `char`;
+CREATE TABLE `char` (
+  `char_id` int(11) unsigned NOT NULL auto_increment,
+  `account_id` int(11) unsigned NOT NULL default '0',
+  `char_num` tinyint(1) NOT NULL default '0',
+  `name` varchar(30) NOT NULL default '',
+  `class` smallint(6) unsigned NOT NULL default '0',
+  `base_level` smallint(6) unsigned NOT NULL default '1',
+  `job_level` smallint(6) unsigned NOT NULL default '1',
+  `base_exp` bigint(20) unsigned NOT NULL default '0',
+  `job_exp` bigint(20) unsigned NOT NULL default '0',
+  `zeny` int(11) unsigned NOT NULL default '0',
+  `str` smallint(4) unsigned NOT NULL default '0',
+  `agi` smallint(4) unsigned NOT NULL default '0',
+  `vit` smallint(4) unsigned NOT NULL default '0',
+  `int` smallint(4) unsigned NOT NULL default '0',
+  `dex` smallint(4) unsigned NOT NULL default '0',
+  `luk` smallint(4) unsigned NOT NULL default '0',
+  `max_hp` mediumint(8) unsigned NOT NULL default '0',
+  `hp` mediumint(8) unsigned NOT NULL default '0',
+  `max_sp` mediumint(6) unsigned NOT NULL default '0',
+  `sp` mediumint(6) unsigned NOT NULL default '0',
+  `status_point` smallint(4) unsigned NOT NULL default '0',
+  `skill_point` smallint(4) unsigned NOT NULL default '0',
+  `option` int(11) NOT NULL default '0',
+  `karma` tinyint(3) NOT NULL default '0',
+  `manner` tinyint(3) NOT NULL default '0',
+  `party_id` int(11) unsigned NOT NULL default '0',
+  `guild_id` int(11) unsigned NOT NULL default '0',
+  `pet_id` int(11) unsigned NOT NULL default '0',
+  `homun_id` int(11) unsigned NOT NULL default '0',
+  `hair` tinyint(4) unsigned NOT NULL default '0',
+  `hair_color` smallint(5) unsigned NOT NULL default '0',
+  `clothes_color` smallint(5) unsigned NOT NULL default '0',
+  `weapon` smallint(6) unsigned NOT NULL default '1',
+  `shield` smallint(6) unsigned NOT NULL default '0',
+  `head_top` smallint(6) unsigned NOT NULL default '0',
+  `head_mid` smallint(6) unsigned NOT NULL default '0',
+  `head_bottom` smallint(6) unsigned NOT NULL default '0',
+  `last_map` varchar(20) NOT NULL default 'prontera.gat',
+  `last_x` smallint(4) unsigned NOT NULL default '53',
+  `last_y` smallint(4) unsigned NOT NULL default '111',
+  `save_map` varchar(20) NOT NULL default 'prontera.gat',
+  `save_x` smallint(4) unsigned NOT NULL default '53',
+  `save_y` smallint(4) unsigned NOT NULL default '111',
+  `partner_id` int(11) unsigned NOT NULL default '0',
+  `online` tinyint(2) NOT NULL default '0',
+  `father` int(11) unsigned NOT NULL default '0',
+  `mother` int(11) unsigned NOT NULL default '0',
+  `child` int(11) unsigned NOT NULL default '0',
+  `fame` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`char_id`),
+  KEY `account_id` (`account_id`),
+  KEY `party_id` (`party_id`),
+  KEY `guild_id` (`guild_id`)
+) TYPE=InnoDB AUTO_INCREMENT=150000; 
+
+--
+-- Table structure for table `charlog`
+--
+
+DROP TABLE IF EXISTS `charlog`;
+CREATE TABLE `charlog` (
+  `time` datetime NOT NULL default '0000-00-00 00:00:00',
+  `char_msg` varchar(255) NOT NULL default 'char select',
+  `account_id` int(11) NOT NULL default '0',
+  `char_num` tinyint(4) NOT NULL default '0',
+  `name` varchar(255) NOT NULL default '',
+  `str` int(11) unsigned NOT NULL default '0',
+  `agi` int(11) unsigned NOT NULL default '0',
+  `vit` int(11) unsigned NOT NULL default '0',
+  `int` int(11) unsigned NOT NULL default '0',
+  `dex` int(11) unsigned NOT NULL default '0',
+  `luk` int(11) unsigned NOT NULL default '0',
+  `hair` tinyint(4) NOT NULL default '0',
+  `hair_color` int(11) NOT NULL default '0'
+) TYPE=MyISAM; 
+
+--
+-- Table structure for table `friends`
+--
+
+DROP TABLE IF EXISTS `friends`;
+CREATE TABLE `friends` (
+  `char_id` int(11) NOT NULL default '0',
+  `friend_account` int(11) NOT NULL default '0',
+  `friend_id` int(11) NOT NULL default '0'
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `global_reg_value`
+--
+
+DROP TABLE IF EXISTS `global_reg_value`;
+CREATE TABLE `global_reg_value` (
+  `char_id` int(11) unsigned NOT NULL default '0',
+  `str` varchar(255) NOT NULL default '',
+  `value` varchar(255) NOT NULL default '0',
+  `type` int(11) NOT NULL default '3',
+  `account_id` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`char_id`,`str`,`account_id`),
+  KEY `account_id` (`account_id`),
+  KEY `char_id` (`char_id`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `guild`
+--
+
+DROP TABLE IF EXISTS `guild`;
+CREATE TABLE `guild` (
+  `guild_id` int(11) unsigned NOT NULL auto_increment,
+  `name` varchar(24) NOT NULL default '',
+  `char_id` int(11) unsigned NOT NULL default '0',
+  `master` varchar(24) NOT NULL default '',
+  `guild_lv` tinyint(6) unsigned NOT NULL default '0',
+  `connect_member` tinyint(6) unsigned NOT NULL default '0',
+  `max_member` tinyint(6) unsigned NOT NULL default '0',
+  `average_lv` smallint(6) unsigned NOT NULL default '1',
+  `exp` int(11) unsigned NOT NULL default '0',
+  `next_exp` int(11) unsigned NOT NULL default '0',
+  `skill_point` tinyint(11) unsigned NOT NULL default '0',
+  `mes1` varchar(60) NOT NULL default '',
+  `mes2` varchar(120) NOT NULL default '',
+  `emblem_len` int(11) unsigned NOT NULL default '0',
+  `emblem_id` int(11) unsigned NOT NULL default '0',
+  `emblem_data` blob NOT NULL,
+  PRIMARY KEY  (`guild_id`,`char_id`),
+  UNIQUE KEY `guild_id` (`guild_id`),
+  KEY `char_id` (`char_id`),
+  CONSTRAINT `guild_ibfk_1` FOREIGN KEY (`char_id`) REFERENCES `char` (`char_id`) ON DELETE CASCADE
+) TYPE=InnoDB;
+
+--
+-- Table structure for table `guild_alliance`
+--
+
+DROP TABLE IF EXISTS `guild_alliance`;
+CREATE TABLE `guild_alliance` (
+  `guild_id` int(11) unsigned NOT NULL default '0',
+  `opposition` int(11) unsigned NOT NULL default '0',
+  `alliance_id` int(11) unsigned NOT NULL default '0',
+  `name` varchar(24) NOT NULL default '',
+  PRIMARY KEY  (`guild_id`,`alliance_id`),
+  KEY `alliance_id` (`alliance_id`),
+  CONSTRAINT `guild_alliance_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE,
+  CONSTRAINT `guild_alliance_ibfk_2` FOREIGN KEY (`alliance_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
+) TYPE=InnoDB;
+
+--
+-- Table structure for table `guild_castle`
+--
+
+DROP TABLE IF EXISTS `guild_castle`;
+CREATE TABLE `guild_castle` (
+  `castle_id` int(11) unsigned NOT NULL default '0',
+  `guild_id` int(11) unsigned NOT NULL default '0',
+  `economy` int(11) unsigned NOT NULL default '0',
+  `defense` int(11) unsigned NOT NULL default '0',
+  `triggerE` int(11) unsigned NOT NULL default '0',
+  `triggerD` int(11) unsigned NOT NULL default '0',
+  `nextTime` int(11) unsigned NOT NULL default '0',
+  `payTime` int(11) unsigned NOT NULL default '0',
+  `createTime` int(11) unsigned NOT NULL default '0',
+  `visibleC` int(11) unsigned NOT NULL default '0',
+  `visibleG0` int(11) unsigned NOT NULL default '0',
+  `visibleG1` int(11) unsigned NOT NULL default '0',
+  `visibleG2` int(11) unsigned NOT NULL default '0',
+  `visibleG3` int(11) unsigned NOT NULL default '0',
+  `visibleG4` int(11) unsigned NOT NULL default '0',
+  `visibleG5` int(11) unsigned NOT NULL default '0',
+  `visibleG6` int(11) unsigned NOT NULL default '0',
+  `visibleG7` int(11) unsigned NOT NULL default '0',
+  `gHP0` int(11) unsigned NOT NULL default '0',
+  `ghP1` int(11) unsigned NOT NULL default '0',
+  `gHP2` int(11) unsigned NOT NULL default '0',
+  `gHP3` int(11) unsigned NOT NULL default '0',
+  `gHP4` int(11) unsigned NOT NULL default '0',
+  `gHP5` int(11) unsigned NOT NULL default '0',
+  `gHP6` int(11) unsigned NOT NULL default '0',
+  `gHP7` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`castle_id`),
+  KEY `guild_id` (`guild_id`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `guild_expulsion`
+--
+
+DROP TABLE IF EXISTS `guild_expulsion`;
+CREATE TABLE `guild_expulsion` (
+  `guild_id` int(11) unsigned NOT NULL default '0',
+  `name` varchar(24) NOT NULL default '',
+  `mes` varchar(40) NOT NULL default '',
+  `acc` varchar(40) NOT NULL default '',
+  `account_id` int(11) unsigned NOT NULL default '0',
+  `rsv1` int(11) unsigned NOT NULL default '0',
+  `rsv2` int(11) unsigned NOT NULL default '0',
+  `rsv3` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`guild_id`,`name`),
+  CONSTRAINT `guild_expulsion_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
+) TYPE=InnoDB;
+
+--
+-- Table structure for table `guild_member`
+--
+
+DROP TABLE IF EXISTS `guild_member`;
+CREATE TABLE `guild_member` (
+  `guild_id` int(11) unsigned NOT NULL default '0',
+  `account_id` int(11) unsigned NOT NULL default '0',
+  `char_id` int(11) unsigned NOT NULL default '0',
+  `hair` tinyint(6) unsigned NOT NULL default '0',
+  `hair_color` smallint(6) unsigned NOT NULL default '0',
+  `gender` tinyint(6) unsigned NOT NULL default '0',
+  `class` smallint(6) unsigned NOT NULL default '0',
+  `lv` smallint(6) unsigned NOT NULL default '0',
+  `exp` bigint(20) unsigned NOT NULL default '0',
+  `exp_payper` tinyint(11) unsigned NOT NULL default '0',
+  `online` tinyint(4) unsigned NOT NULL default '0',
+  `position` tinyint(6) unsigned NOT NULL default '0',
+  `rsv1` int(11) unsigned NOT NULL default '0',
+  `rsv2` int(11) unsigned NOT NULL default '0',
+  `name` varchar(24) NOT NULL default '',
+  PRIMARY KEY  (`guild_id`,`char_id`),
+  KEY `char_id` (`char_id`),
+  CONSTRAINT `guild_member_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE,
+  CONSTRAINT `guild_member_ibfk_2` FOREIGN KEY (`char_id`) REFERENCES `char` (`char_id`) ON DELETE CASCADE
+) TYPE=InnoDB;
+
+--
+-- Table structure for table `guild_position`
+--
+
+DROP TABLE IF EXISTS `guild_position`;
+CREATE TABLE `guild_position` (
+  `guild_id` int(9) unsigned NOT NULL default '0',
+  `position` tinyint(6) unsigned NOT NULL default '0',
+  `name` varchar(24) NOT NULL default '',
+  `mode` tinyint(11) unsigned NOT NULL default '0',
+  `exp_mode` tinyint(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`guild_id`,`position`),
+  KEY `guild_id` (`guild_id`),
+  CONSTRAINT `guild_position_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
+) TYPE=InnoDB;
+
+--
+-- Table structure for table `guild_skill`
+--
+
+DROP TABLE IF EXISTS `guild_skill`;
+CREATE TABLE `guild_skill` (
+  `guild_id` int(11) unsigned NOT NULL default '0',
+  `id` smallint(11) unsigned NOT NULL default '0',
+  `lv` tinyint(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`guild_id`,`id`),
+  CONSTRAINT `guild_skill_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
+) TYPE=InnoDB;
+
+--
+-- Table structure for table `guild_storage`
+--
+
+DROP TABLE IF EXISTS `guild_storage`;
+CREATE TABLE `guild_storage` (
+  `id` int(10) unsigned NOT NULL auto_increment,
+  `guild_id` int(11) unsigned NOT NULL default '0',
+  `nameid` int(11) unsigned NOT NULL default '0',
+  `amount` int(11) unsigned NOT NULL default '0',
+  `equip` mediumint(8) unsigned NOT NULL default '0',
+  `identify` smallint(6) unsigned NOT NULL default '0',
+  `refine` tinyint(3) unsigned NOT NULL default '0',
+  `attribute` tinyint(4) unsigned NOT NULL default '0',
+  `card0` smallint(11) NOT NULL default '0',
+  `card1` smallint(11) NOT NULL default '0',
+  `card2` smallint(11) NOT NULL default '0',
+  `card3` smallint(11) NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  KEY `guild_id` (`guild_id`),
+  CONSTRAINT `guild_storage_ibfk_1` FOREIGN KEY (`guild_id`) REFERENCES `guild` (`guild_id`) ON DELETE CASCADE
+) TYPE=InnoDB;
+
+--
+-- Table structure for table `homunculus`
+--
+
+DROP TABLE IF EXISTS `homunculus`;
+CREATE TABLE `homunculus` (
+  `homun_id` int(11) NOT NULL auto_increment,
+  `char_id` int(11) NOT NULL,
+  `class` mediumint(9) unsigned NOT NULL default '0',
+  `name` varchar(24) NOT NULL default '',
+  `level` smallint(4) NOT NULL default '0',
+  `exp` int(12) NOT NULL default '0',
+  `intimacy` int(12) NOT NULL default '0',
+  `hunger` smallint(4) NOT NULL default '0',
+  `str` smallint(4) unsigned NOT NULL default '0',
+  `agi` smallint(4) unsigned NOT NULL default '0',
+  `vit` smallint(4) unsigned NOT NULL default '0',
+  `int` smallint(4) unsigned NOT NULL default '0',
+  `dex` smallint(4) unsigned NOT NULL default '0',
+  `luk` smallint(4) unsigned NOT NULL default '0',
+  `hp` int(12) NOT NULL default '1',
+  `max_hp` int(12) NOT NULL default '1',
+  `sp` int(12) NOT NULL default '1',
+  `max_sp` int(12) NOT NULL default '1',
+  `skill_point` smallint(4) unsigned NOT NULL default '0',
+  `alive` tinyint(2) NOT NULL default '1',
+  `rename_flag` tinyint(2) NOT NULL default '0',
+  `vaporize` tinyint(2) NOT NULL default '0',
+  PRIMARY KEY  (`homun_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+-- 
+-- Table structure for table `interlog`
+--
+
+DROP TABLE IF EXISTS `interlog`;
+CREATE TABLE `interlog` (
+  `time` datetime NOT NULL default '0000-00-00 00:00:00',
+  `log` varchar(255) NOT NULL default ''
+) TYPE=MyISAM; 
+
+--
+-- Table structure for table `inventory`
+--
+
+DROP TABLE IF EXISTS `inventory`;
+CREATE TABLE `inventory` (
+  `id` int(11) unsigned NOT NULL auto_increment,
+  `char_id` int(11) unsigned NOT NULL default '0',
+  `nameid` int(11) unsigned NOT NULL default '0',
+  `amount` int(11) unsigned NOT NULL default '0',
+  `equip` mediumint(8) unsigned NOT NULL default '0',
+  `identify` smallint(6) NOT NULL default '0',
+  `refine` tinyint(3) unsigned NOT NULL default '0',
+  `attribute` tinyint(4) unsigned NOT NULL default '0',
+  `card0` smallint(11) NOT NULL default '0',
+  `card1` smallint(11) NOT NULL default '0',
+  `card2` smallint(11) NOT NULL default '0',
+  `card3` smallint(11) NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  KEY `char_id` (`char_id`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `ipbanlist`
+--
+
+DROP TABLE IF EXISTS `ipbanlist`;
+CREATE TABLE `ipbanlist` (
+  `list` varchar(255) NOT NULL default '',
+  `btime` datetime NOT NULL default '0000-00-00 00:00:00',
+  `rtime` datetime NOT NULL default '0000-00-00 00:00:00',
+  `reason` varchar(255) NOT NULL default '',
+  KEY (`list`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `login`
+--
+
+DROP TABLE IF EXISTS `login`;
+CREATE TABLE `login` (
+  `account_id` int(11) unsigned NOT NULL auto_increment,
+  `userid` varchar(255) NOT NULL default '',
+  `user_pass` varchar(32) NOT NULL default '',
+  `lastlogin` datetime NOT NULL default '0000-00-00 00:00:00',
+  `sex` char(1) NOT NULL default 'M',
+  `logincount` mediumint(9) unsigned NOT NULL default '0',
+  `email` varchar(60) NOT NULL default '',
+  `level` tinyint(3) NOT NULL default '0',
+  `error_message` smallint(11) unsigned NOT NULL default '0',
+  `connect_until` smallint(11) unsigned NOT NULL default '0',
+  `last_ip` varchar(100) NOT NULL default '',
+  `memo` smallint(11) unsigned NOT NULL default '0',
+  `ban_until` int(11) unsigned NOT NULL default '0',
+  `state` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`account_id`),
+  KEY `name` (`userid`)
+) TYPE=InnoDB AUTO_INCREMENT=2000000; 
+
+-- added standard accounts for servers, VERY INSECURE!!!
+-- inserted into the table called login which is above
+
+INSERT INTO `login` (`account_id`, `userid`, `user_pass`, `sex`, `email`) VALUES ('1', 's1', 'p1', 'S','athena@athena.com');
+
+--
+-- Table structure for table `mapreg`
+--
+
+DROP TABLE IF EXISTS `mapreg`;
+CREATE TABLE `mapreg` (
+  `varname` varchar(32) NOT NULL,
+  `index` int(11) unsigned NOT NULL default '0',
+  `value` varchar(255) NOT NULL,
+  KEY `varname` (`varname`),
+  KEY `index` (`index`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `sc_data`
+--
+
+DROP TABLE IF EXISTS `sc_data`;
+CREATE TABLE `sc_data` (
+  `account_id` int(11) unsigned NOT NULL,
+  `char_id` int(11) unsigned NOT NULL,
+  `type` smallint(11) unsigned NOT NULL,
+  `tick` int(11) NOT NULL,
+  `val1` int(11) NOT NULL default '0',
+  `val2` int(11) NOT NULL default '0',
+  `val3` int(11) NOT NULL default '0',
+  `val4` int(11) NOT NULL default '0',
+  KEY (`account_id`),
+  KEY (`char_id`),
+  CONSTRAINT `scdata_ibfk_1` FOREIGN KEY (`account_id`) REFERENCES `login` (`account_id`) ON DELETE CASCADE,
+  CONSTRAINT `scdata_ibfk_2` FOREIGN KEY (`char_id`) REFERENCES `char` (`char_id`) ON DELETE CASCADE
+) TYPE=InnoDB;
+
+--
+-- Table structure for table `loginlog`
+--
+
+DROP TABLE IF EXISTS `loginlog`;
+CREATE TABLE `loginlog` (
+  `time` datetime NOT NULL default '0000-00-00 00:00:00',
+  `ip` int(10) unsigned NOT NULL default '0',
+  `user` varchar(32) NOT NULL default '',
+  `rcode` tinyint(4) NOT NULL default '0',
+  `log` varchar(255) NOT NULL default '',
+  INDEX (`ip`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `memo`
+--
+
+DROP TABLE IF EXISTS `memo`;
+CREATE TABLE `memo` (
+  `memo_id` int(11) unsigned NOT NULL auto_increment,
+  `char_id` int(11) unsigned NOT NULL default '0',
+  `map` varchar(255) NOT NULL default '',
+  `x` smallint(9) unsigned NOT NULL default '0',
+  `y` smallint(9) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`memo_id`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `party`
+--
+
+DROP TABLE IF EXISTS `party`;
+CREATE TABLE `party` (
+  `party_id` int(11) unsigned NOT NULL auto_increment,
+  `name` char(100) NOT NULL default '',
+  `exp` tinyint(11) unsigned NOT NULL default '0',
+  `item` tinyint(11) unsigned NOT NULL default '0',
+  `leader_id` int(11) unsigned NOT NULL default '0',
+  `leader_char` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`party_id`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `pet`
+--
+
+DROP TABLE IF EXISTS `pet`;
+CREATE TABLE `pet` (
+  `pet_id` int(11) unsigned NOT NULL auto_increment,
+  `class` mediumint(9) unsigned NOT NULL default '0',
+  `name` varchar(24) NOT NULL default '',
+  `account_id` int(11) unsigned NOT NULL default '0',
+  `char_id` int(11) unsigned NOT NULL default '0',
+  `level` smallint(4) unsigned NOT NULL default '0',
+  `egg_id` smallint(11) unsigned NOT NULL default '0',
+  `equip` mediumint(8) unsigned NOT NULL default '0',
+  `intimate` smallint(9) unsigned NOT NULL default '0',
+  `hungry` smallint(9) unsigned NOT NULL default '0',
+  `rename_flag` tinyint(4) unsigned NOT NULL default '0',
+  `incuvate` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`pet_id`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `ragsrvinfo`
+--
+
+DROP TABLE IF EXISTS `ragsrvinfo`;
+CREATE TABLE `ragsrvinfo` (
+  `index` int(11) NOT NULL default '0',
+  `name` varchar(255) NOT NULL default '',
+  `exp` int(11) unsigned NOT NULL default '0',
+  `jexp` int(11) unsigned NOT NULL default '0',
+  `drop` int(11) unsigned NOT NULL default '0',
+  `motd` varchar(255) NOT NULL default ''
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `skill`
+--
+
+DROP TABLE IF EXISTS `skill`;
+CREATE TABLE `skill` (
+  `char_id` int(11) unsigned NOT NULL default '0',
+  `id` smallint(11) unsigned NOT NULL default '0',
+  `lv` tinyint(4) unsigned NOT NULL default '0',
+  PRIMARY KEY  (`char_id`,`id`),
+  KEY `char_id` (`char_id`)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `skill_homunculus`
+--
+
+DROP TABLE IF EXISTS `skill_homunculus`;
+CREATE TABLE `skill_homunculus` (
+  `homun_id` int(11) NOT NULL,
+  `id` int(11) NOT NULL,
+  `lv` smallint(6) NOT NULL,
+  PRIMARY KEY  (`homun_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `sstatus`
+--
+
+DROP TABLE IF EXISTS `sstatus`;
+CREATE TABLE `sstatus` (
+  `index` tinyint(4) unsigned NOT NULL default '0',
+  `name` varchar(255) NOT NULL default '',
+  `user` int(11) unsigned NOT NULL default '0'
+) TYPE=MyISAM;
+
+--
+-- Table structure for table `storage`
+--
+
+DROP TABLE IF EXISTS `storage`;
+CREATE TABLE `storage` (
+  `id` int(11) unsigned NOT NULL auto_increment,
+  `account_id` int(11) unsigned NOT NULL default '0',
+  `nameid` int(11) unsigned NOT NULL default '0',
+  `amount` smallint(11) unsigned NOT NULL default '0',
+  `equip` mediumint(8) unsigned NOT NULL default '0',
+  `identify` smallint(6) unsigned NOT NULL default '0',
+  `refine` tinyint(3) unsigned NOT NULL default '0',
+  `attribute` tinyint(4) unsigned NOT NULL default '0',
+  `card0` smallint(11) NOT NULL default '0',
+  `card1` smallint(11) NOT NULL default '0',
+  `card2` smallint(11) NOT NULL default '0',
+  `card3` smallint(11) NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  KEY `account_id` (`account_id`)
+) TYPE=MyISAM;

+ 36 - 0
sql-files/upgrade_svn7706.sql

@@ -0,0 +1,36 @@
+ALTER TABLE `char` ADD `homun_id` int(11) unsigned NOT NULL default '0' AFTER `pet_id`;
+
+DROP TABLE IF EXISTS `homunculus`;
+CREATE TABLE `homunculus` (
+  `homun_id` int(11) NOT NULL auto_increment,
+  `char_id` int(11) NOT NULL,
+  `class` mediumint(9) unsigned NOT NULL default '0',
+  `name` varchar(24) NOT NULL default '',
+  `level` smallint(4) NOT NULL default '0',
+  `exp` int(12) NOT NULL default '0',
+  `intimacy` int(12) NOT NULL default '0',
+  `hunger` smallint(4) NOT NULL default '0',
+  `str` smallint(4) unsigned NOT NULL default '0',
+  `agi` smallint(4) unsigned NOT NULL default '0',
+  `vit` smallint(4) unsigned NOT NULL default '0',
+  `int` smallint(4) unsigned NOT NULL default '0',
+  `dex` smallint(4) unsigned NOT NULL default '0',
+  `luk` smallint(4) unsigned NOT NULL default '0',
+  `hp` int(12) NOT NULL default '1',
+  `max_hp` int(12) NOT NULL default '1',
+  `sp` int(12) NOT NULL default '1',
+  `max_sp` int(12) NOT NULL default '1',
+  `skill_point` smallint(4) unsigned NOT NULL default '0',
+  `alive` tinyint(2) NOT NULL default '1',
+  `rename_flag` tinyint(2) NOT NULL default '0',
+  `vaporize` tinyint(2) NOT NULL default '0',
+  PRIMARY KEY  (`homun_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+
+CREATE TABLE `skill_homunculus` (
+  `homun_id` int(11) NOT NULL,
+  `id` int(11) NOT NULL,
+  `lv` smallint(6) NOT NULL,
+  PRIMARY KEY  (`homun_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;

+ 4 - 3
src/char_sql/Makefile

@@ -10,7 +10,7 @@ COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h
 	../common/malloc.h ../common/showmsg.h ../common/utils.h ../common/strlib.h \
 	../common/graph.h ../common/grfio.h ../common/mapindex.h
 
-char-server_sql: char.o inter.o int_party.o int_guild.o int_storage.o int_pet.o itemdb.o $(COMMON_OBJ)
+char-server_sql: char.o inter.o int_party.o int_guild.o int_storage.o int_pet.o int_homun.o itemdb.o $(COMMON_OBJ)
 	$(CC) -o ../../$@ $^ $(LIB_S)
 
 clean:
@@ -19,9 +19,10 @@ clean:
 # DO NOT DELETE
 
 char.o: char.c char.h ../common/strlib.h itemdb.h ../common/showmsg.h
-inter.o: inter.c inter.h int_party.h int_guild.h int_storage.h int_pet.h ../common/mmo.h char.h ../common/socket.h ../common/showmsg.h
+inter.o: inter.c inter.h int_party.h int_guild.h int_storage.h int_pet.h int_homun.h ../common/mmo.h char.h ../common/socket.h ../common/showmsg.h
 int_party.o: int_party.c int_party.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/timer.h ../common/db.h ../common/showmsg.h
 int_guild.o: int_guild.c int_guild.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/db.h ../common/showmsg.h
 int_storage.o: int_storage.c int_storage.h char.h itemdb.h ../common/showmsg.h
 int_pet.o: int_pet.c int_pet.h inter.h char.h ../common/mmo.h ../common/socket.h ../common/db.h ../common/showmsg.h
-itemdb.o: itemdb.c itemdb.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+int_homun.o: int_homun.c int_homun.h inter.h char.h ../common/mmo.h ../common/socket.h ../common/db.h ../common/showmsg.h
+itemdb.o: itemdb.c itemdb.h ../common/db.h ../common/mmo.h ../common/showmsg.h

+ 5 - 4
src/char_sql/char.c

@@ -491,7 +491,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus *p){
 			"`base_exp`='%u', `job_exp`='%u', `zeny`='%d',"
 			"`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
 			"`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
-			"`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',"
+			"`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',"	//[orn] add homun_id (homunculus id)
 			"`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
 			"`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'"
 			" WHERE  `account_id`='%d' AND `char_id` = '%d'",
@@ -499,7 +499,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus *p){
 			p->base_exp, p->job_exp, p->zeny,
 			p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point,
 			p->str, p->agi, p->vit, p->int_, p->dex, p->luk,
-			p->option, p->party_id, p->guild_id, p->pet_id,
+			p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id,	//[orn] add homun_id (homunculus id)
 			p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom,
 			mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
 			mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y,
@@ -956,7 +956,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus *p){
 
 	sprintf(tmp_sql, "SELECT `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`,"
 		"`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,"
-		"`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`"
+		"`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id`"	//[orn] homun_id
 		"FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR
 	if (mysql_query(&mysql_handle, tmp_sql)) {
 		ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
@@ -977,6 +977,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus *p){
 		p->save_point.map = mapindex_name2id(sql_row[17]); p->save_point.x = atoi(sql_row[18]);	p->save_point.y = atoi(sql_row[19]);
 		p->partner_id = atoi(sql_row[20]); p->father = atoi(sql_row[21]); p->mother = atoi(sql_row[22]); p->child = atoi(sql_row[23]);
 		p->fame = atoi(sql_row[24]);
+		p->hom_id = atoi(sql_row[25]);	//[orn] homunculus id
 
 		strcat (t_msg, " status2");
 	} else
@@ -3036,7 +3037,7 @@ int parse_frommap(int fd) {
 			}
 
 			// no inter server packet. no char server packet -> disconnect
-			ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0));
+			ShowError("Unknown packet 0x%04x ( %s ) from map server, disconnecting.\n", RFIFOW(fd,0), (char*)RFIFOP(fd,0));
 			session[fd]->eof = 1;
 			return 0;
 		}

+ 300 - 0
src/char_sql/int_homun.c

@@ -0,0 +1,300 @@
+// Homunculus saving by Albator and Orn for eAthena.
+// GNU/GPL rulez !
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "char.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+
+struct s_homunculus *homun_pt;
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+
+int inter_homunculus_sql_init(void){
+	//memory alloc
+	homun_pt = (struct s_homunculus*)aCalloc(sizeof(struct s_homunculus), 1);
+	return 0;
+}
+void inter_homunculus_sql_final(void){
+	if (homun_pt) aFree(homun_pt);
+	return;
+}
+
+int mapif_saved_homunculus(int fd, short flag)
+{
+	WFIFOW(fd,0) = 0x3892;
+	if(flag==1)
+		WFIFOB(fd,2) = 1;
+	else
+		WFIFOB(fd,2) = 0;
+	WFIFOSET(fd, 3);
+	return 0;
+}
+int mapif_info_homunculus(int fd, int account_id, struct s_homunculus *hd)
+{
+	WFIFOW(fd,0) = 0x3891;
+	WFIFOW(fd,2) = sizeof(struct s_homunculus)+9;
+	WFIFOL(fd,4) = account_id;
+	WFIFOB(fd,8) = 1; // account loaded with success
+
+	memcpy(WFIFOP(fd,9), hd, sizeof(struct s_homunculus));
+	WFIFOSET(fd, sizeof(struct s_homunculus)+9);
+	return 0;
+}
+
+int mapif_homunculus_deleted(int fd, int flag)
+{
+	WFIFOW(fd, 0) = 0x3893;
+	if(flag == 1)
+		WFIFOB(fd,2) = 1; // Homunculus deleted
+	else
+		WFIFOB(fd,2) = 0; /* Fail /!\ */
+
+    WFIFOSET(fd, 3);
+
+	return 0;
+
+}
+int mapif_homunculus_created(int fd, int account_id, struct s_homunculus *sh, short flag)
+{
+	WFIFOW(fd, 0) =0x3890;
+	WFIFOL(fd,2) = account_id;
+	WFIFOL(fd,6) = sh->char_id;
+	if(flag==1){
+		WFIFOW(fd, 10)=1;
+		WFIFOL(fd,12) = sh->hom_id;
+    }
+    else{
+		WFIFOW(fd, 10)=0;
+		WFIFOL(fd,12) = 0;
+	}
+	WFIFOSET(fd, 16);
+
+	return 0;
+}
+void init_homun_skills(struct s_homunculus *hd)
+{
+	int i;
+	for(i=0;i<MAX_HOMUNSKILL;i++)
+		hd->hskill[i].id = hd->hskill[i].lv = hd->hskill[i].flag = 0;
+}
+
+// Save/Update Homunculus Skills
+int mapif_save_homunculus_skills(struct s_homunculus *hd)
+{
+	int i;
+
+	for(i=0; i<MAX_HOMUNSKILL; i++)
+	{
+		if(hd->hskill[i].id != 0 && hd->hskill[i].lv != 0 )
+		{
+			sprintf(tmp_sql,"REPLACE INTO `skill_homunculus` (`homun_id`, `id`, `lv`) VALUES (%d, %d, %d)",
+				hd->hom_id, hd->hskill[i].id, hd->hskill[i].lv);
+
+			if(mysql_query(&mysql_handle, tmp_sql)){
+				ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+				ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
+
+int mapif_save_homunculus(int fd, int account_id, struct s_homunculus *hd)
+{
+	int flag =1;
+
+	if(hd->hom_id==0) // new homunculus
+	{
+		ShowInfo("New homunculus name : %s\n",hd->name);
+
+		sprintf(tmp_sql, "INSERT INTO `homunculus` "
+			"(`char_id`, `class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`, `alive`, `rename_flag`, `vaporize`) "
+			"VALUES ('%d', '%d', '%s', '%d', '%lu', '%lu', '%d', '%d', %d, '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+			hd->char_id, hd->class_,hd->name,hd->level,hd->exp,hd->intimacy,hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
+			hd->hp,hd->max_hp,hd->sp,hd->max_sp, hd->skillpts, hd->alive, hd->rename_flag, hd->vaporize);
+
+	}
+	else
+	{
+		sprintf(tmp_sql, "UPDATE `homunculus` SET `char_id`='%d', `class`='%d',`name`='%s',`level`='%d',`exp`='%lu',`intimacy`='%lu',`hunger`='%d', `agi`='%d', `vit`='%d', `int`='%d', `dex`='%d', `luk`='%d', `hp`='%d',`max_hp`='%d',`sp`='%d',`max_sp`='%d',`skill_point`='%d', `alive`='%d', `rename_flag`='%d', `vaporize`='%d', `str`='%d' WHERE `homun_id`='%d'",
+			hd->char_id, hd->class_,hd->name,hd->level,hd->exp,hd->intimacy,hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
+			hd->hp,hd->max_hp,hd->sp,hd->max_sp, hd->skillpts, hd->alive, hd->rename_flag, hd->vaporize);
+	}
+
+	if(mysql_query(&mysql_handle, tmp_sql)){
+		ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+		ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+		flag =  0;
+	}
+
+	if(hd->hom_id==0 && flag!=0)
+		hd->hom_id = (int)mysql_insert_id(&mysql_handle); // new homunculus
+	else
+	{
+		flag = mapif_save_homunculus_skills(hd);
+		mapif_saved_homunculus(fd, flag);
+	}
+	return flag;
+}
+
+
+
+// Load an homunculus
+int mapif_load_homunculus(int fd){
+	int i;
+	memset(homun_pt, 0, sizeof(struct s_homunculus));
+
+	sprintf(tmp_sql,"SELECT `homun_id`,`char_id`,`class`,`name`,`level`,`exp`,`intimacy`,`hunger`,, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`alive`,`rename_flag`, `vaporize` FROM `homunculus` WHERE `homun_id`='%lu'", RFIFOL(fd,6));
+	if(mysql_query(&mysql_handle, tmp_sql) ) {
+		ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+		ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+
+		return 0;
+	}
+	sql_res = mysql_store_result(&mysql_handle) ;
+	if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+		sql_row = mysql_fetch_row(sql_res);
+
+		homun_pt->hom_id = RFIFOL(fd,6) ; //RFIFOL(fd,2);
+		homun_pt->class_ = atoi(sql_row[2]);
+		memcpy(homun_pt->name, sql_row[3],NAME_LENGTH-1);
+		homun_pt->char_id = atoi(sql_row[1]);
+		homun_pt->level = atoi(sql_row[4]);
+		homun_pt->exp = atoi(sql_row[5]);
+		homun_pt->intimacy = atoi(sql_row[6]);
+		homun_pt->hunger = atoi(sql_row[7]);
+		homun_pt->str = atoi(sql_row[8]);
+		homun_pt->agi = atoi(sql_row[9]);
+		homun_pt->vit = atoi(sql_row[10]);
+		homun_pt->int_ = atoi(sql_row[11]);
+		homun_pt->dex = atoi(sql_row[12]);
+		homun_pt->luk = atoi(sql_row[13]);
+		homun_pt->hp = atoi(sql_row[14]);
+		homun_pt->max_hp = atoi(sql_row[15]);
+		homun_pt->sp = atoi(sql_row[16]);
+		homun_pt->max_sp = atoi(sql_row[17]);
+		homun_pt->skillpts = atoi(sql_row[18]);
+		homun_pt->alive = atoi(sql_row[19]);
+		homun_pt->rename_flag = atoi(sql_row[20]);
+		homun_pt->vaporize = atoi(sql_row[21]);
+	}
+	if(homun_pt->hunger < 0)
+		homun_pt->hunger = 0;
+	else if(homun_pt->hunger > 100)
+		homun_pt->hunger = 100;
+	if(homun_pt->intimacy < 0)
+		homun_pt->intimacy = 0;
+	else if(homun_pt->intimacy > 100000)
+		homun_pt->intimacy = 100000;
+
+	mysql_free_result(sql_res);
+
+	// Load Homunculus Skill
+	init_homun_skills(homun_pt); //bousille homun_pt !!!
+
+	sprintf(tmp_sql,"SELECT `id`,`lv` FROM `skill_homunculus` WHERE `homun_id`=%d",homun_pt->hom_id);
+	if(mysql_query(&mysql_handle, tmp_sql) ) {
+		ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+		ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+		return 0;
+	}
+	sql_res = mysql_store_result(&mysql_handle);
+         if(sql_res){
+         	while((sql_row = mysql_fetch_row(sql_res))){
+						i = (atoi(sql_row[0])-HM_SKILLBASE-1);
+						homun_pt->hskill[i].id = atoi(sql_row[0]);
+                        homun_pt->hskill[i].lv = atoi(sql_row[1]);
+                 }
+         }
+
+	mysql_free_result(sql_res);
+
+	ShowInfo("Homunculus loaded (%d - %s).\n", homun_pt->hom_id, homun_pt->name);
+	return mapif_info_homunculus(fd, RFIFOL(fd,2), homun_pt);
+}
+
+
+
+int mapif_delete_homunculus(int fd)
+{
+	sprintf(tmp_sql, "DELETE FROM `homunculus` WHERE `homun_id` = '%lu'", RFIFOL(fd,2));
+   if(mysql_query(&mysql_handle, tmp_sql)){
+     ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+     ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+   return mapif_homunculus_deleted(fd, 0);
+   }
+
+	 return mapif_homunculus_deleted(fd, 1);
+}
+
+
+
+int mapif_parse_CreateHomunculus(int fd)
+{
+	memset(homun_pt, 0, sizeof(struct s_homunculus));
+
+	/* Data from packet */
+	homun_pt->char_id = RFIFOL(fd,6);
+	homun_pt->class_ = RFIFOW(fd,10);
+	homun_pt->max_hp = RFIFOL(fd,12);
+	homun_pt->max_sp = RFIFOL(fd,16);
+	memcpy(homun_pt->name, (char*)RFIFOP(fd, 20), NAME_LENGTH-1);
+	homun_pt->str = RFIFOL(fd,44);
+	homun_pt->agi = RFIFOL(fd,48);
+	homun_pt->vit = RFIFOL(fd,52);
+	homun_pt->int_ = RFIFOL(fd,56);
+	homun_pt->dex = RFIFOL(fd,60);
+	homun_pt->luk = RFIFOL(fd,64);
+
+	/* Const data for each creation*/
+	homun_pt->hom_id = 0;
+	homun_pt->exp =0;
+	homun_pt->hp = 10 ;
+	homun_pt->sp = 0 ;
+	homun_pt->rename_flag = 0;
+	homun_pt->skillpts =0;
+	homun_pt->hunger = 32;
+	homun_pt->level=1;
+	homun_pt->intimacy = 21;
+
+	// Save in sql db
+	if(mapif_save_homunculus(fd,RFIFOL(fd,2), homun_pt))
+		return mapif_homunculus_created(fd, RFIFOL(fd,2), homun_pt, 1); // send homun_id
+	else
+		return mapif_homunculus_created(fd, RFIFOL(fd,2), homun_pt, 0); // fail
+
+
+}
+
+
+
+
+int inter_homunculus_parse_frommap(int fd){
+	switch(RFIFOW(fd, 0)){
+	case 0x3090: mapif_parse_CreateHomunculus(fd); break;
+	case 0x3091: mapif_load_homunculus(fd); break;
+	case 0x3092: mapif_save_homunculus(fd, RFIFOL(fd,6), (struct s_homunculus*) RFIFOP(fd, 10)); break;
+	case 0x3093: mapif_delete_homunculus(fd); break;  // doesn't need to be parse, very simple packet...
+	// rename homunculus is just like save... Map server check the rename flag before, send the save packet
+	// case 0x3094: mapif_parse_rename_homunculus(fd); break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}

+ 14 - 0
src/char_sql/int_homun.h

@@ -0,0 +1,14 @@
+// Homunculus saving by Albator and Orn for eAthena.
+// GNU/GPL rulez !
+
+#ifndef _INT_HOMUN_H_
+#define _INT_HOMUN_H_
+
+int inter_homunculus_sql_init(void);
+void inter_homunculus_sql_final(void);
+int mapif_save_homunculus(struct s_homunculus *hd);
+int mapif_load_homunculus(int fd);
+int mapif_delete_homunculus(int fd);
+int inter_homunculus_parse_frommap(int fd);
+
+#endif

+ 10 - 1
src/char_sql/inter.c

@@ -15,6 +15,7 @@
 #include "int_guild.h"
 #include "int_storage.h"
 #include "int_pet.h"
+#include "int_homun.h" //albator
 
 #define WISDATA_TTL (60*1000)	// Wisデータの生存時間(60秒)
 #define WISDELLIST_MAX 256			// Wisデータ削除リストの要素数
@@ -79,6 +80,10 @@ int inter_recv_packet_length[]={
 	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
 	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
 	48,14,-1, 6, 35, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0, //0x3080-0x308f
+//	44,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3090 - 0x309f  Homunculus packets [albator]
+	68,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3090 - 0x309f  Homunculus packets [albator]
+	
+
 };
 
 struct WisData {
@@ -362,6 +367,7 @@ int inter_init(const char *file)
 	inter_party_sql_init();
 
 	inter_pet_sql_init();
+	inter_homunculus_sql_init(); // albator
 	inter_accreg_sql_init();
 
 	//printf ("interserver timer initializing : %d sec...\n",autosave_interval);
@@ -419,6 +425,7 @@ void inter_final(void) {
 	inter_storage_sql_final();
 	inter_party_sql_final();
 	inter_pet_sql_final();
+	inter_homunculus_sql_final();	//[orn]
 	
 	if (accreg_pt) aFree(accreg_pt);
 	return;
@@ -753,7 +760,7 @@ int inter_parse_frommap(int fd)
 	int cmd=RFIFOW(fd,0);
 	int len=0;
 
-	// inter鯖管轄かを調べ
+	// inter鯖管轄かを調べ
 	if(cmd < 0x3000 || cmd >= 0x3000 + (sizeof(inter_recv_packet_length)/
 		sizeof(inter_recv_packet_length[0]) ) )
 		return 0;
@@ -781,6 +788,8 @@ int inter_parse_frommap(int fd)
 			break;
 		if(inter_pet_parse_frommap(fd))
 			break;
+		if(inter_homunculus_parse_frommap(fd)) //albator
+			break;
 		return 0;
 	}
 

+ 29 - 3
src/common/mmo.h

@@ -117,6 +117,11 @@
 
 #define CHAR_CONF_NAME  "conf/char_athena.conf"
 
+//Base Homun skill.
+#define HM_SKILLBASE 8000
+#define MAX_HOMUNSKILL 16
+#define MAX_HOMUNCULUS_CLASS	16	//[orn]
+
 struct item {
 	int id;
 	short nameid;
@@ -163,6 +168,29 @@ struct s_pet {
 	char incuvate;
 };
 
+struct s_homunculus {	//[orn]
+	char name[NAME_LENGTH];
+	int hom_id;
+	int char_id;
+	short class_;
+	int hp,max_hp,sp,max_sp;
+	short alive; //albator
+	unsigned long intimacy;	//[orn]
+	short hunger;
+	struct skill hskill[MAX_HOMUNSKILL]; //albator
+	short skillpts;
+	short level;
+	unsigned long exp;
+	short rename_flag;
+	short vaporize; //albator
+	int str ;
+	int agi ;
+	int vit ;
+	int int_ ;
+	int dex ;
+	int luk ;
+};
+
 struct friend {
 	int account_id;
 	int char_id;
@@ -188,6 +216,7 @@ struct mmo_charstatus {
 	unsigned char karma;
 	short hair,hair_color,clothes_color;
 	int party_id,guild_id,pet_id;
+	int hom_id;	//[orn]
 	int fame;
 
 	short weapon,shield;
@@ -385,9 +414,6 @@ enum {
 	GD_DEVELOPMENT=10014,
 };
 
-//Base Homun skill.
-#define HM_SKILLBASE 8001
-#define MAX_HOMUNSKILL 16
 
 //These mark the ID of the jobs, as expected by the client. [Skotlex]
 enum {

+ 169 - 0
src/map/atcommand.c

@@ -27,6 +27,7 @@
 #include "skill.h"
 #include "mob.h"
 #include "pet.h"
+#include "mercenary.h"	//[orn]
 #include "battle.h"
 #include "party.h"
 #include "guild.h"
@@ -298,6 +299,13 @@ ACMD_FUNC(commands); // [Skotlex]
 ACMD_FUNC(noask); //LuzZza
 ACMD_FUNC(request); //[Skotlex]
 
+ACMD_FUNC(homlevel);	//[orn]
+ACMD_FUNC(homevolution);	//[orn]
+ACMD_FUNC(makehomun);	//[orn]
+ACMD_FUNC(homfriendly);	//[orn]
+ACMD_FUNC(homhungry);	//[orn]
+ACMD_FUNC(homtalk);	//[orn]
+
 /*==========================================
  *AtCommandInfo atcommand_info[]�\‘¢‘̂̒è‹`
  *------------------------------------------
@@ -616,6 +624,13 @@ static AtCommandInfo atcommand_info[] = {
 	{ AtCommand_NoAsk,				"@noask",			1, atcommand_noask }, // [LuzZza]
 	{ AtCommand_Request,				"@request",			20, atcommand_request }, // [Skotlex]
 
+	{ AtCommand_HomLevel,				"@homlvup",		60, atcommand_homlevel	},
+	{ AtCommand_HomEvolution,				"@homevolution",	60, atcommand_homevolution	},
+	{ AtCommand_MakeHomun,				"@makehomun",	60, atcommand_makehomun	},
+	{ AtCommand_HomFriendly,			"@homfriendly",		60, atcommand_homfriendly },
+	{ AtCommand_HomHungry,			"@homhungry",		60, atcommand_homhungry },
+	{ AtCommand_HomTalk,			"@homtalk",		0, atcommand_homtalk },
+
 // add new commands before this line
 	{ AtCommand_Unknown,			NULL,				 1, NULL }
 };
@@ -9425,6 +9440,160 @@ int atcommand_mobinfo(
 	return 0;
 }
 
+/*==========================================
+ * homunculus level up [orn]
+ *------------------------------------------
+ */
+int atcommand_homlevel(
+	const int fd, struct map_session_data* sd,
+	const char* command, const char* message)
+{
+	int level = 0, i = 0;
+
+	nullpo_retr(-1, sd);
+
+	if (!message || !*message)
+		return -1;
+		
+	if ( sd->status.hom_id == 0 || !sd->homunculus.alive || sd->homunculus.vaporize )
+		return 1 ;
+
+	level = atoi(message);
+	if ( ( level + sd->homunculus.level ) > MAX_LEVEL )
+		level = MAX_LEVEL - sd->homunculus.level ;
+	if (level >= 1) {
+		for (i = 1; i <= level ; i++){
+			merc_hom_levelup(sd->hd) ;
+		}
+		clif_misceffect2(&sd->hd->bl,568) ;
+	}
+	
+	return 0;
+}
+
+/*==========================================
+ * homunculus evolution H [orn]
+ *------------------------------------------
+ */
+int atcommand_homevolution(
+	const int fd, struct map_session_data* sd,
+	const char* command, const char* message)
+{
+	nullpo_retr(-1, sd);
+	
+	if (sd->hd && sd->hd->homunculusDB->evo_class)
+	{
+		merc_hom_evolution(sd->hd) ;
+	}
+	return 0;
+}
+
+/*==========================================
+ * call choosen homunculus [orn]
+ *------------------------------------------
+ */
+int
+atcommand_makehomun(
+	const int fd, struct map_session_data* sd,
+	const char* command, const char* message)
+{
+	int homunid;
+	nullpo_retr(-1, sd);
+	if(sscanf(message, "%d", &homunid)<1)
+		return -1;
+	if( homunid < 6001 || homunid > 6016 )
+		return -1;
+	if(sd->status.hom_id == 0)
+	{
+		merc_create_homunculus(sd,homunid);
+	}
+	else
+	{
+		clif_displaymessage(fd,msg_txt(144));
+	}
+	return 0;
+}
+
+/*==========================================
+ * modify homunculus intimacy [orn]
+ *------------------------------------------
+ */
+int atcommand_homfriendly(
+	const int fd, struct map_session_data* sd,
+	const char* command, const char* message)
+{
+	int friendly = 0;
+
+	nullpo_retr(-1, sd);
+
+	if (!message || !*message)
+		return -1;
+
+	friendly = atoi(message);
+	if (sd->status.hom_id > 0 && sd->hd) {
+		if (friendly > 0 && friendly <= 1000) {
+			sd->homunculus.intimacy = friendly * 100 ;
+			clif_send_homdata(sd,SP_INTIMATE,friendly);
+		} else {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*==========================================
+ * modify homunculus hunger [orn]
+ *------------------------------------------
+ */
+int atcommand_homhungry(
+	const int fd, struct map_session_data* sd,
+	const char* command, const char* message)
+{
+	int hungry = 0;
+
+	nullpo_retr(-1, sd);
+
+	if (!message || !*message)
+		return -1;
+
+	hungry = atoi(message);
+	if (sd->status.hom_id > 0 && sd->hd) {
+		if (hungry >= 0 && hungry <= 100) {
+			sd->homunculus.hunger = hungry;
+			clif_send_homdata(sd,SP_HUNGRY,sd->homunculus.hunger);
+		} else {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*==========================================
+ * modify homunculus hunger [orn]
+ *------------------------------------------
+ */
+int atcommand_homtalk(
+	const int fd, struct map_session_data* sd,
+	const char* command, const char* message)
+{
+	char mes[100],temp[100];
+
+	nullpo_retr(-1, sd);
+
+	if(!sd->status.hom_id || !sd->hd || !sd->homunculus.alive )
+		return -1;
+
+	if (sscanf(message, "%99[^\n]", mes) < 1)
+		return -1;
+
+	snprintf(temp, sizeof temp ,"%s : %s",sd->homunculus.name,mes);
+	clif_message(&sd->hd->bl, temp);
+
+	return 0;
+}
+
 /*==========================================
  * Show Items DB Info   v 1.0
  * originally by [Lupus] eAthena

+ 6 - 0
src/map/atcommand.h

@@ -271,6 +271,12 @@ enum AtCommandType {
 	AtCommand_Commands, // [Skotlex]
 	AtCommand_NoAsk, // [LuzZza]
 	AtCommand_Request, // [Skotlex], supposedly taken from Freya (heard the command was there, but I haven't seen the code yet)
+	AtCommand_HomLevel, //[orn]
+	AtCommand_HomEvolution, //[orn]
+	AtCommand_MakeHomun, //[orn]
+	AtCommand_HomFriendly, //[orn]
+	AtCommand_HomHungry, //[orn]
+	AtCommand_HomTalk, //[orn]
 	// end <- Ahem, guys, don't place AtCommands after AtCommand_Unknown! [Skotlex]
 	AtCommand_Unknown,
 	AtCommand_MAX

+ 28 - 1
src/map/battle.c

@@ -761,6 +761,7 @@ static struct Damage battle_calc_weapon_attack(
 	short i;
 
 	struct map_session_data *sd, *tsd;
+	struct homun_data *hd;	//[orn]
 	struct Damage wd;
 	struct status_change *sc = status_get_sc(src);
 	struct status_change *tsc = status_get_sc(target);
@@ -813,6 +814,7 @@ static struct Damage battle_calc_weapon_attack(
 
 	BL_CAST(BL_PC, src, sd);
 	BL_CAST(BL_PC, target, tsd);
+	BL_CAST(BL_HOMUNCULUS, src, hd);	//[orn]
 
 	if(sd) {
 		if (sd->skillblown[0].id != 0)
@@ -1029,6 +1031,7 @@ static struct Damage battle_calc_weapon_attack(
 				case NPC_MENTALBREAKER:
 				case GS_GROUNDDRIFT:
 				case NJ_TATAMIGAESHI:
+				case HVAN_EXPLOSION:	//[orn]
 					flag.hit = 1;
 					break;
 				case CR_SHIELDBOOMERANG:
@@ -1177,6 +1180,14 @@ static struct Damage battle_calc_weapon_attack(
 						ATK_ADD(sd->inventory_data[index]->weight/10);
 					break;
 				}
+			case HFLI_SBR44:	//[orn]
+				if(hd){
+					wd.damage = hd->master->homunculus.intimacy ;
+					wd.damage2 = hd->master->homunculus.intimacy ;
+					hd->master->homunculus.intimacy = 200;
+					clif_send_homdata(hd->master,0x100,hd->master->homunculus.intimacy/100);
+				}
+				break;
 			default:
 			{
 				i = (flag.cri?1:0)|(flag.arrow?2:0)|(skill_num == HW_MAGICCRASHER?4:0)|(skill_num == MO_EXTREMITYFIST?8:0);
@@ -1499,6 +1510,12 @@ static struct Damage battle_calc_weapon_attack(
 				case MO_BALKYOUNG:
 					skillratio += 200;
 					break;
+				case HFLI_MOON:	//[orn]
+					skillratio += ( 110 * (skill_lv + 1) ) - 100 ;
+					skillratio /= wd.div_ ;
+					break;
+				case HFLI_SBR44:	//[orn]
+					skillratio += 100 * skill_lv ;
 			}
 
 			ATK_RATE(skillratio);
@@ -2468,6 +2485,7 @@ struct Damage  battle_calc_misc_attack(
 	case CR_ACIDDEMONSTRATION:
 		md.flag = (md.flag&~BF_RANGEMASK)|BF_LONG;
 		break;
+	case HVAN_EXPLOSION:	//[orn]
 	case NPC_SELFDESTRUCTION:
 	case NPC_SMOKING:
 		flag.elefix = flag.cardfix = 0;
@@ -2553,6 +2571,9 @@ struct Damage  battle_calc_misc_attack(
 	case GS_FLING:
 		md.damage = sd?sd->status.job_level:status_get_lv(src);
 		break;
+	case HVAN_EXPLOSION:	//[orn]
+		md.damage = sstatus->hp * (50 + 50 * skill_lv) / 100 ;
+		break ;
 	}
 	
 	damage_div_fix(md.damage, md.div_);
@@ -3619,6 +3640,7 @@ static const struct battle_data_short {
 	{ "autospell_stacking", 				&battle_config.autospell_stacking },
 	{ "override_mob_names", 				&battle_config.override_mob_names },
 	{ "min_chat_delay",						&battle_config.min_chat_delay },
+	{ "homunculus_show_growth",					&battle_config.homunculus_show_growth },	//[orn]
 };
 
 static const struct battle_data_int {
@@ -3662,7 +3684,7 @@ static const struct battle_data_int {
 	{ "max_heal",                          &battle_config.max_heal },
 	{ "mob_remove_delay",                  &battle_config.mob_remove_delay	},
 	{ "sg_miracle_skill_duration",				&battle_config.sg_miracle_skill_duration },
-
+	{ "hvan_explosion_intimate",					&battle_config.hvan_explosion_intimate },	//[orn]
 };
 
 int battle_set_value(char *w1, char *w2) {
@@ -4045,6 +4067,8 @@ void battle_set_defaults() {
 	battle_config.autospell_stacking = 0;
 	battle_config.override_mob_names = 0;
 	battle_config.min_chat_delay = 0;
+	battle_config.hvan_explosion_intimate = 45000;	//[orn]
+	battle_config.homunculus_show_growth = 0;	//[orn]
 }
 
 void battle_validate_conf() {
@@ -4257,6 +4281,9 @@ void battle_validate_conf() {
 	if (battle_config.cell_stack_limit != 1)
 		ShowWarning("Battle setting 'cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n");
 #endif
+
+	if(battle_config.hvan_explosion_intimate > 100000)	//[orn]
+		battle_config.hvan_explosion_intimate = 100000;
 }
 
 /*==========================================

+ 2 - 0
src/map/battle.h

@@ -436,6 +436,8 @@ extern struct Battle_Config {
 	unsigned short autospell_stacking; //Enables autospell cards to stack. [Skotlex]
 	unsigned short override_mob_names; //Enables overriding spawn mob names with the mob_db names. [Skotlex]
 	unsigned short min_chat_delay; //Minimum time between client messages. [Skotlex]
+	unsigned int hvan_explosion_intimate ;	// fix [albator]
+	unsigned short homunculus_show_growth ;	//[orn]
 
 } battle_config;
 

+ 23 - 18
src/map/charsave.c

@@ -26,6 +26,7 @@ struct mmo_charstatus *charsave_loadchar(int charid){
 	char *str_p;
 	friends = 0;
 
+	ShowDebug("charsave_loadchar : charid = %d | hd->master->status.char_id = %d\n", charid) ;
          c = (struct mmo_charstatus *)aCalloc(1,sizeof(struct mmo_charstatus));
 
          if(charid <= 0){
@@ -33,9 +34,9 @@ struct mmo_charstatus *charsave_loadchar(int charid){
 				aFree(c);
          	return NULL;
          }
-
+    // add homun_id [albator]
 	//Tested, Mysql 4.1.9+ has no problems with the long query, the buf is 65k big and the sql server needs for it 0.00009 secs on an athlon xp 2400+ WinXP (1GB Mem) ..  [Sirius]
-         sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame` FROM `char` WHERE `char_id` = '%d'", charid);
+         sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id` FROM `char` WHERE `char_id` = '%d'", charid);
     	if(mysql_query(&charsql_handle, tmp_sql)){
 				ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
 				ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
@@ -102,9 +103,10 @@ struct mmo_charstatus *charsave_loadchar(int charid){
          c->mother = atoi(charsql_row[44]);
          c->child = atoi(charsql_row[45]);
          c->fame = atoi(charsql_row[46]);
-
+		 c->hom_id = atoi(charsql_row[47]); // albator
         	mysql_free_result(charsql_res);
 
+
 	//Check for '0' Savepoint / LastPoint
 	if (c->last_point.x == 0 || c->last_point.y == 0 || c->last_point.map == 0){
 		c->last_point.map = mapindex_name2id(MAP_PRONTERA);
@@ -237,8 +239,10 @@ struct mmo_charstatus *charsave_loadchar(int charid){
 				c->global_reg_num = i;
          }
 */
+	
+		 
 	//Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex]
-	//Friend list 
+	//Friend list
 	sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM friends f LEFT JOIN `char` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", charid);
 
 	if(mysql_query(&charsql_handle, tmp_sql)){
@@ -248,7 +252,7 @@ struct mmo_charstatus *charsave_loadchar(int charid){
 	}
 	else
 		sql_res = mysql_store_result(&charsql_handle);
-	
+
 	if(sql_res)
 	{
 		for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && i<MAX_FRIENDS; i++)
@@ -273,6 +277,7 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){
 //	char tmp_str[64];
 //	char tmp_str2[512];
          //First save the 'char'
+	ShowDebug("charsave_savechar : charid = %d | hd->master->status.char_id = %d\n", charid) ;
 	sprintf(tmp_sql ,"UPDATE `char` SET `class`='%d', `base_level`='%d', `job_level`='%d',"
 		"`base_exp`='%d', `job_exp`='%d', `zeny`='%d',"
 		"`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
@@ -280,7 +285,7 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){
 		"`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',"
 		"`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
 		"`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d',"
-		"`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d'"
+		"`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d', `homun_id`='%d'"
 		"WHERE  `account_id`='%d' AND `char_id` = '%d'",
 		c->class_, c->base_level, c->job_level,
 		c->base_exp, c->job_exp, c->zeny,
@@ -291,7 +296,7 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){
 		c->weapon, c->shield, c->head_top, c->head_mid, c->head_bottom,
 		mapindex_id2name(c->last_point.map), c->last_point.x, c->last_point.y,
 		mapindex_id2name(c->save_point.map), c->save_point.x, c->save_point.y, c->partner_id, c->father, c->mother,
-		c->child, c->fame, c->account_id, c->char_id
+		c->child, c->fame, c->hom_id, c->account_id, c->char_id
 	);
          if(mysql_query(&charsql_handle, tmp_sql)){
 				ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
@@ -311,16 +316,16 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){
 				str_p += sprintf(str_p, "INSERT INTO `inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
 			for (j = 0; j < MAX_SLOTS; j++)
 				str_p += sprintf(str_p, ", `card%d`", j);
-				
+
 			str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
 				charid, c->inventory[i].nameid, c->inventory[i].amount, c->inventory[i].equip,
 				c->inventory[i].identify, c->inventory[i].refine, c->inventory[i].attribute);
 
 			for (j = 0; j < MAX_SLOTS; j++)
 				str_p += sprintf(str_p, ", '%d'", c->inventory[i].card[j]);
-			
+
 			strcat(tmp_sql,")");
-			
+
 			if(mysql_query(&charsql_handle, tmp_sql)){
 				ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
 				ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
@@ -340,16 +345,16 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){
 				str_p += sprintf(str_p, "INSERT INTO `cart_inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
 				for (j = 0; j < MAX_SLOTS; j++)
 					str_p += sprintf(str_p, ", `card%d`", j);
-				
+
 				str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
 					charid, c->cart[i].nameid, c->cart[i].amount, c->cart[i].equip,
 					c->cart[i].identify, c->cart[i].refine, c->cart[i].attribute);
 
 				for (j = 0; j < MAX_SLOTS; j++)
 					str_p += sprintf(str_p, ", '%d'", c->cart[i].card[j]);
-			
+
 				strcat(tmp_sql,")");
-				
+
 				if(mysql_query(&charsql_handle, tmp_sql)){
 					ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
 					ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
@@ -436,7 +441,7 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){
 int charsave_load_scdata(int account_id, int char_id)
 {	//Loads character's sc_data
 	struct map_session_data *sd;
-	
+
 	sd = map_id2sd(account_id);
 	if (!sd)
 	{
@@ -450,7 +455,7 @@ int charsave_load_scdata(int account_id, int char_id)
 	}
 	sprintf(tmp_sql, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` FROM `sc_data`"
 		"WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
-	
+
 	if(mysql_query(&charsql_handle, tmp_sql)){
 		ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
 		ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
@@ -490,7 +495,7 @@ void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_
 	char *p = tmp_sql;
 
 	p += sprintf(p, "INSERT INTO `sc_data` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ");
-			
+
 	for(i = 0; i < max_sc; i++)
 	{
 		if (sc_data->data[i].timer == -1)
@@ -498,10 +503,10 @@ void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_
 		timer = get_timer(sc_data->data[i].timer);
 		if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
 			continue;
-		
+
 		p += sprintf(p, " ('%d','%d','%hu','%d','%d','%d','%d','%d'),", account_id, char_id,
 			i, DIFF_TICK(timer->tick,tick), sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4);
-		
+
 		count++;
 	}
 	if (count > 0)

+ 5 - 0
src/map/charsave.h

@@ -7,6 +7,11 @@
 #include "status.h"
 
 #ifndef TXT_ONLY
+	int charsave_loadHomunculus(int hom_id, struct homun_data *p);
+	int charsave_saveHomunculus(struct homun_data *hd);
+	int charsave_saveHomunculusSkills(struct homun_data *hd);
+	int charsave_deleteHomunculus(struct homun_data *hd);
+
 	struct mmo_charstatus *charsave_loadchar(int charid);
 	int charsave_savechar(int charid, struct mmo_charstatus *c);
 	int charsave_load_scdata(int account_id, int char_id);

+ 1 - 0
src/map/chrif.c

@@ -22,6 +22,7 @@
 #include "npc.h"
 #include "pc.h"
 #include "status.h"
+#include "mercenary.h"
 #ifndef TXT_ONLY
 #include "charsave.h"
 #endif

+ 217 - 80
src/map/clif.c

@@ -41,6 +41,7 @@
 #include "guild.h"
 #include "vending.h"
 #include "pet.h"
+#include "mercenary.h"	//[orn]
 #include "log.h"
 
 #include "irc.h"
@@ -1449,93 +1450,89 @@ int clif_spawn(struct block_list *bl)
 	}
 	return 0;
 }
-/*==========================================
- * Homunculus [blackhole89]
- *------------------------------------------
- */
-// Can somebody tell me why exactly I have commented this lot of stuff out?
-// acknowledge client it has a homunculus
-int clif_homunack(struct map_session_data *sd)
-{
-	struct homun_data *hd = sd->hd;
-	unsigned char buf[64];
-
-	nullpo_retr(0, sd);
-	nullpo_retr(0, sd->hd);
 
-	//memset(buf,0,packet_len_table[0x230]);
-	memset(buf,0,12); //not yet set that stuff
-	WBUFW(buf,0)=0x230;
-	WBUFL(buf,4)=hd->bl.id;
-	ShowError("in clif_homunack~\n");
-	clif_send(buf,/*packet_len_table[0x230]*/12,&sd->bl,SELF);
-
-	return 0;
-}
-
-// homunculus stats et al
-int clif_homuninfo(struct map_session_data *sd)
+//[orn]
+int clif_hominfo(struct map_session_data *sd, int flag)
 {
 	struct homun_data *hd = sd->hd;
 	struct status_data *status;
 	unsigned char buf[128];
 	
 	nullpo_retr(0, hd);
+
+//	if ( sd->hd ) 
+//		return 0 ;
+
 	status = &hd->battle_status;
 	memset(buf,0,71); //packet_len_table[0x22e]);
 	WBUFW(buf,0)=0x22e;
-	memcpy(WBUFP(buf,2),hd->name,NAME_LENGTH);
-	WBUFW(buf,27)=hd->level;
-	WBUFW(buf,29)=hd->hunger_rate;
-	WBUFL(buf,31)=0xFF;	//intimacy, leave it as is
-	WBUFW(buf,35)=status->batk;
+	memcpy(WBUFP(buf,2),hd->master->homunculus.name,NAME_LENGTH);
+	WBUFB(buf,26)=hd->master->homunculus.rename_flag * 2;
+	WBUFW(buf,27)=hd->master->homunculus.level;
+	WBUFW(buf,29)=hd->master->homunculus.hunger;
+	WBUFW(buf,31)=(unsigned short) (hd->master->homunculus.intimacy / 100) ;
+	WBUFW(buf,33)=0;			// equip id
+	WBUFW(buf,35)=status->rhw.atk2;
 	WBUFW(buf,37)=status->matk_max;
 	WBUFW(buf,39)=status->hit;
 	WBUFW(buf,41)=status->cri/10;	//crit is a +1 decimal value!
-	WBUFW(buf,43)=status->def;
+	WBUFW(buf,43)=status->def + status->vit ;
 	WBUFW(buf,45)=status->mdef;
 	WBUFW(buf,47)=status->flee;
-	WBUFW(buf,49)=status->amotion;
+	WBUFW(buf,49)=(flag)?0:status->amotion;
 	WBUFW(buf,51)=status->hp;
 	WBUFW(buf,53)=status->max_hp;
 	WBUFW(buf,55)=status->sp;
 	WBUFW(buf,57)=status->max_sp;
-	WBUFL(buf,59)=hd->exp;
+	WBUFL(buf,59)=hd->master->homunculus.exp;
 	WBUFL(buf,63)=hd->exp_next;
-	WBUFW(buf,67)=hd->skillpts;
-	WBUFW(buf,69)=0x21;
+	WBUFW(buf,67)=hd->master->homunculus.skillpts;
+	WBUFW(buf,69)=hd->attackable;
 	clif_send(buf,/*packet_len_table[0x22e]*/71,&sd->bl,SELF); 
 	return 0;
 }
 
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_send_homdata(struct map_session_data *sd, int type, int param) {	//[orn]
+	int fd;
+	nullpo_retv(sd);
+	nullpo_retv(sd->hd);
+
+	fd=sd->fd;
+	WFIFOW(fd,0)=0x230;
+	WFIFOW(fd,2)=type;
+	WFIFOL(fd,4)=sd->hd->bl.id;
+	WFIFOL(fd,8)=param;
+	WFIFOSET(fd,packet_len_table[0x230]);
+
+	return;
+}
 // like skillinfoblock, just for homunculi.
-int clif_homunskillinfoblock(struct map_session_data *sd)
-{
+int clif_homskillinfoblock(struct map_session_data *sd) {	//[orn]
 	int fd;
-	int i,c,len=4,id/*, inf2*/;
+	int i,j,c,len=4,id/*, inf2*/;
 
 	nullpo_retr(0, sd);
 	nullpo_retr(0, sd->hd);
 
+	if ( !sd->hd ) 
+		return 0 ;
+
 	fd=sd->fd;
-	WFIFOHEAD(fd, 4 * 37 + 4);
 	WFIFOW(fd,0)=0x235;
-	for ( i = c = 0; i < 4; i++){
-		if( (id=sd->hd->hskill[i].id)!=0 ){
-			WFIFOW(fd,len  ) = id;
-			WFIFOW(fd,len+2) = skill_get_inf(id-7300);		// H. skills mapped to 700 and above
-			WFIFOW(fd,len+4) = 0;
-			WFIFOW(fd,len+6) = sd->hd->hskill[i].level;
-			WFIFOW(fd,len+8) = skill_get_sp(id,sd->hd->hskill[i].level);
-			WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->hd->hskill[i].level);
-			strncpy(WFIFOP(fd,len+12), /*merc_skill_get_name(id)*/ "", NAME_LENGTH); // can somebody tell me what exactly that function was good for anyway
-		/*	inf2 = skill_get_inf2(id);
-			if(((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
-				!(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL))) ||
-				(battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill) )
-				//WFIFOB(fd,len+36)= (sd->status.skill[i].lv < skill_get_max(id) && sd->status.skill[i].flag ==0 )? 1:0;
-				WFIFOB(fd,len+36)= (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_) && sd->status.skill[i].flag ==0 )? 1:0;
-			else */
+	for ( i = c = 0; i < MAX_HOMUNSKILL; i++){
+		if( (id = sd->homunculus.hskill[i].id) != 0 ){
+			j = id - HM_SKILLBASE - 1 ;
+			WFIFOW(fd,len  ) = id ;
+			WFIFOW(fd,len+2) = skill_get_inf(id) ;
+			WFIFOW(fd,len+4) = 0 ;
+			WFIFOW(fd,len+6) = sd->homunculus.hskill[j].lv ;
+			WFIFOW(fd,len+8) = skill_get_sp(id,sd->homunculus.hskill[j].lv) ;
+			WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,sd->homunculus.hskill[j].lv) ;
+			strncpy(WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH) ;
 				WFIFOB(fd,len+36) = 1;//0;
 			len+=37;
 			c++;
@@ -1547,49 +1544,146 @@ int clif_homunskillinfoblock(struct map_session_data *sd)
 	return 0;
 }
 
+void clif_homskillup(struct map_session_data *sd, int skill_num) {	//[orn]
+	int range,fd,skillid;
+
+	nullpo_retv(sd);
+	skillid = skill_num - HM_SKILLBASE ;
+
+	fd=sd->fd;
+	WFIFOW(fd,0) = 0x10e;
+	WFIFOW(fd,2) = skill_num;
+	WFIFOW(fd,4) = sd->homunculus.hskill[skillid].lv;
+	WFIFOW(fd,6) = skill_get_sp(skill_num,sd->homunculus.hskill[skillid].lv);
+	range = skill_get_range(skill_num,sd->homunculus.hskill[skillid].lv);
+	if(range < 0)
+		range = status_get_range(&sd->bl) - (range + 1);
+	WFIFOW(fd,8) = range;
+	WFIFOB(fd,10) = (sd->homunculus.hskill[skillid].lv < skill_get_max(sd->homunculus.hskill[skillid].id)) ? 1 : 0;
+	WFIFOSET(fd,packet_len_table[0x10e]);
+
+	return;
+}
+
 // Request a Homunculus name change
-void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd) {
+void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd) {	//[orn]
 	RFIFOHEAD(fd);
 	nullpo_retv(sd);
 	nullpo_retv(sd->hd);
-	memcpy(sd->hd->name,RFIFOP(fd,2),24);
-	clif_homuninfo(sd);
+	memcpy(sd->homunculus.name,RFIFOP(fd,2),24);
+	sd->homunculus.rename_flag = 1 ;
+	clif_hominfo(sd,0);
 	clif_charnameack(sd->fd,&sd->hd->bl);
 }
 
 // Somebody who is less lazy than me rename this to ReturnToMaster or something
-void clif_parse_QueryHomunPos(int fd, struct map_session_data *sd) {
+void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd) {	//[orn]
 	RFIFOHEAD(fd);
 	nullpo_retv(sd);
 	nullpo_retv(sd->hd);
+
+	if ( sd->hd && status_isdead(&sd->hd->bl) ) 
+		return ;
+
 	unit_walktoxy(&sd->hd->bl, sd->bl.x,sd->bl.y-1, 0); //move to master
-	//clif_homunposack(sd->hd);
+}
+
+// player spend a skillpoint for homunculus
+void clif_parse_HomUseSKillPoint(int fd, struct map_session_data *sd) {	//[orn]
+	int skillid ;
+	nullpo_retv(sd);
+	nullpo_retv(sd->hd);
+
+	if ( !sd->hd ) 
+		return ;
+	skillid = RFIFOW(fd,2);
+
+	merc_hom_skillup(sd->hd, skillid);
 }
 
 // Request a Homunculus move-to-position
-void clif_parse_HMoveTo(int fd,struct map_session_data *sd) {
+void clif_parse_HomMoveTo(int fd,struct map_session_data *sd) {	//[orn]
 	int x,y,cmd;
 
 	nullpo_retv(sd);
 	nullpo_retv(sd->hd);
 
+	if ( sd->hd && status_isdead(&sd->hd->bl) ) 
+		return ;
+
 	cmd = RFIFOW(fd,0);
 	x = RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]) * 4 +
 		(RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0] + 1) >> 6);
 	y = ((RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]+1) & 0x3f) << 4) +
 		(RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0] + 2) >> 4);
 
-	unit_walktoxy(&sd->hd->bl,x,y,0);
+	unit_walktoxy(&(sd->hd->bl),x,y,0);
 }
 
 // Request the Homunculus attacking a bl
-void clif_parse_HAttack(int fd,struct map_session_data *sd) {
+void clif_parse_HomAttack(int fd,struct map_session_data *sd) {	//[orn]
+	struct block_list *target;
 	nullpo_retv(sd);
 	nullpo_retv(sd->hd);
+	
+	target=map_id2bl(RFIFOL(fd,6));
+	
+	if ( sd->hd && target && ( status_isdead(&sd->hd->bl) || status_isdead(target) ) ) 
+		return ;
 
 	if(sd->hd->bl.id != RFIFOL(fd,2)) return;
-	
-	printf("unit_attack returned: %d\n",unit_attack(&sd->hd->bl,RFIFOL(fd,6),0));
+	merc_stop_walking(sd->hd, 1);
+	merc_stop_attack(sd->hd);
+	if ( sd->hd && target ) {
+		sd->hd->target_id = RFIFOL(fd,6) ;
+		unit_attack(&sd->hd->bl,RFIFOL(fd,6),1) ;
+	}
+}
+
+void clif_parse_HomMenu(int fd, struct map_session_data *sd) {	//[orn]
+	int cmd;
+	cmd = RFIFOW(fd,0);
+	RFIFOHEAD(fd);
+	if ( sd->hd && status_isdead(&sd->hd->bl) ) 
+		return ;
+	merc_menu(sd,RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]));
+}
+
+int clif_hom_food(struct map_session_data *sd,int foodid,int fail)	//[orn]
+{
+	int fd;
+
+	nullpo_retr(0, sd);
+
+	fd=sd->fd;
+	WFIFOHEAD(fd,packet_len_table[0x22f]);
+	WFIFOW(fd,0)=0x22f;
+	WFIFOB(fd,2)=fail;
+	WFIFOW(fd,3)=foodid;
+	WFIFOSET(fd,packet_len_table[0x22f]);
+
+	return 0;
+}
+
+/*==========================================
+ * orn
+ *------------------------------------------
+ */
+int clif_hwalkok(struct homun_data *hd)
+{
+	int fd;
+
+	nullpo_retr(0, hd);
+
+	fd=hd->master->fd;
+	WFIFOHEAD(fd, packet_len_table[0x87]);
+	WFIFOW(fd,0)=0x87;
+	WFIFOL(fd,2)=gettick();
+	WFIFOPOS2(fd,6,hd->bl.x,hd->bl.y,hd->ud.to_x,hd->ud.to_y);
+	WFIFOB(fd,11)=0x88;
+	WFIFOSET(fd,packet_len_table[0x87]);
+
+	return 0;
 }
 
 /*==========================================
@@ -7751,7 +7845,7 @@ int clif_charnameack (int fd, struct block_list *bl)
 		break;
 	//[blackhole89]
 	case BL_HOMUNCULUS:
-		memcpy(WBUFP(buf,6), ((struct homun_data*)bl)->name, NAME_LENGTH);
+		memcpy(WBUFP(buf,6), ((struct homun_data*)bl)->master->homunculus.name, NAME_LENGTH);
 		break;
 	case BL_PET:
 		memcpy(WBUFP(buf,6), ((struct pet_data*)bl)->name, NAME_LENGTH);
@@ -8210,10 +8304,11 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 	if(sd->hd && sd->hd->battle_status.hp) {
 		map_addblock(&sd->hd->bl);
 		clif_spawn(&sd->hd->bl);
-		clif_homunack(sd);
-		clif_homuninfo(sd);
-		clif_homuninfo(sd); //for some reason, at least older clients want this sent twice
-		clif_homunskillinfoblock(sd);
+//		clif_homunack(sd);
+		clif_hominfo(sd,1);
+		clif_hominfo(sd,0); //for some reason, at least older clients want this sent twice
+		clif_send_homdata(sd,0,0);
+		clif_homskillinfoblock(sd);
 	}
 
 	if(sd->state.connect_new) {
@@ -9612,6 +9707,9 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) {
 	if (skillnotok(skillnum, sd))
 		return;
 
+	if (sd->hd && skillnotok_hom(skillnum, sd->hd))	//[orn]
+		return;
+
 	if (sd->bl.id != target_id &&
 		!sd->state.skill_flag &&
 		skill_get_inf(skillnum)&INF_SELF_SKILL)
@@ -9680,12 +9778,19 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) {
 		if (skillnum >= GD_SKILLBASE && sd->state.gmaster_flag)
 			skilllv = guild_checkskill(sd->state.gmaster_flag, skillnum);
 		
-		if ((lv = pc_checkskill(sd, skillnum)) > 0) {
-			if (skilllv > lv)
-				skilllv = lv;
-			unit_skilluse_id(&sd->bl, target_id, skillnum, skilllv);
-			if (sd->state.skill_flag)
-				sd->state.skill_flag = 0;
+		if ( ( skillnum >= HM_SKILLBASE ) && sd->status.hom_id && sd->homunculus.alive && !sd->homunculus.vaporize ) {	//[orn]
+			if ( ( lv = merc_hom_checkskill(sd, skillnum) ) > 0 )
+				if (skilllv > lv)
+					skilllv = lv;
+			unit_skilluse_id(&sd->hd->bl, target_id, skillnum, skilllv);
+		} else {
+			if ((lv = pc_checkskill(sd, skillnum)) > 0) {
+				if (skilllv > lv)
+					skilllv = lv;
+				unit_skilluse_id(&sd->bl, target_id, skillnum, skilllv);
+				if (sd->state.skill_flag)
+					sd->state.skill_flag = 0;
+			}
 		}
 	}
 }
@@ -11559,6 +11664,36 @@ int clif_parse(int fd) {
 
 	#if DUMP_ALL_PACKETS
 		dump = 1;
+				int i;
+				FILE *fp;
+				char packet_txt[256] = "save/packet.txt";
+				time_t now;
+				dump = 1;
+
+				if ((fp = fopen(packet_txt, "a")) == NULL) {
+					ShowError("clif.c: cant write [%s] !!! data is lost !!!\n", packet_txt);
+					return 1;
+				} else {
+					time(&now);
+					if (sd && sd->state.auth) {
+						if (sd->status.name != NULL)
+							fprintf(fp, "%sPlayer with account ID %d (character ID %d, player name %s) sent packet:\n",
+							        asctime(localtime(&now)), sd->status.account_id, sd->status.char_id, sd->status.name);
+						else
+							fprintf(fp, "%sPlayer with account ID %d sent wrong packet:\n", asctime(localtime(&now)), sd->bl.id);
+					} else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified)
+						fprintf(fp, "%sPlayer with account ID %d sent wrong packet:\n", asctime(localtime(&now)), sd->bl.id);
+
+					fprintf(fp, "\tsession #%d, packet 0x%04x, length %d, version %d\n", fd, cmd, packet_len, packet_ver);
+					fprintf(fp, "\t---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
+					for(i = 0; i < packet_len; i++) {
+						if ((i & 15) == 0)
+							fprintf(fp, "\n\t%04X ", i);
+						fprintf(fp, "%02X ", RFIFOB(fd,i));
+					}
+					fprintf(fp, "\n\n");
+					fclose(fp);
+				}
 	#endif
 
 	if (sd && sd->state.auth == 1 && sd->state.waitingdisconnect == 1) { // 切断待ちの場合パケットを処理しない
@@ -11777,11 +11912,13 @@ static int packetdb_readdb(void)
 		{clif_parse_FeelSaveOk,"feelsaveok"},
 		{clif_parse_AdoptRequest,"adopt"},
 		{clif_parse_debug,"debug"},
-		//[blackhole89]
+		//[blackhole89]	//[orn]
 		{clif_parse_ChangeHomunculusName,"changehomunculusname"},
-		{clif_parse_QueryHomunPos,"queryhomunpos"},
-		{clif_parse_HMoveTo,"hmoveto"},
-		{clif_parse_HAttack,"hattack"},
+		{clif_parse_HomMoveToMaster,"hommovetomaster"},
+		{clif_parse_HomMoveTo,"hommoveto"},
+		{clif_parse_HomAttack,"homattack"},
+		{clif_parse_HomUseSKillPoint,"homuseskillpoint"},
+		{clif_parse_HomMenu,"hommenu"},
 		{NULL,NULL}
 	};
 

+ 7 - 3
src/map/clif.h

@@ -354,9 +354,13 @@ void clif_mission_mob(struct map_session_data *sd, unsigned short mob_id, unsign
 
 // [blackhole89]
 int clif_spawnhomun(struct homun_data *hd);
-int clif_homunack(struct map_session_data *sd);
-int clif_homuninfo(struct map_session_data *sd);
-int clif_homunskillinfoblock(struct map_session_data *sd);
+int clif_hominfo(struct map_session_data *sd, int flag);
+int clif_homskillinfoblock(struct map_session_data *sd);
+void clif_homskillup(struct map_session_data *sd, int skill_num) ;	//[orn]
+int clif_hom_food(struct map_session_data *sd,int foodid,int fail);	//[orn]
+void clif_send_homdata(struct map_session_data *sd, int type, int param);	//[orn]
+int clif_hwalkok(struct homun_data *hd);	//[orn]
+
 #endif
 
 

+ 155 - 15
src/map/intif.c

@@ -24,6 +24,7 @@
 #include "guild.h"
 #include "pet.h"
 #include "atcommand.h"
+#include "mercenary.h" //albator
 
 static const int packet_len_table[]={
 	-1,-1,27,-1, -1, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0, //0x3800-0x380f
@@ -35,6 +36,7 @@ static const int packet_len_table[]={
 	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
 	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
 	11,-1, 7, 3, 36, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0, //0x3880
+	16,-1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3890  Homunculus [albator]
 };
 
 extern int char_fd;		// inter serverのfdはchar_fdを使う
@@ -132,13 +134,13 @@ int intif_GMmessage(char* mes,int len,int flag)
 
 	// Send to the local players
 	clif_GMmessage(NULL, mes, len, flag);
-	
+
 	if (CheckForCharServer())
 		return 0;
-	
+
 	if (other_mapserver_count < 1)
 		return 0; //No need to send.
-	
+
 	WFIFOHEAD(inter_fd,lp + len + 4);
 	WFIFOW(inter_fd,0) = 0x3000;
 	WFIFOW(inter_fd,2) = lp + len + 4;
@@ -156,13 +158,13 @@ int intif_announce(char* mes,int len, unsigned long color, int flag)
 		clif_MainChatMessage(mes);
 	else
 		clif_announce(NULL, mes, len, color, flag);
-	
+
 	if (CheckForCharServer())
 		return 0;
 
 	if (other_mapserver_count < 1)
 		return 0; //No need to send.
-	
+
 	WFIFOHEAD(inter_fd, 8 + len);
 	WFIFOW(inter_fd,0) = 0x3000;
 	WFIFOW(inter_fd,2) = 8 + len;
@@ -182,8 +184,8 @@ int intif_wis_message(struct map_session_data *sd, char *nick, char *mes, int me
 	{	//Character not found.
 		clif_wis_end(sd->fd, 1);
 		return 0;
-	}	
-		
+	}
+
 	WFIFOHEAD(inter_fd,mes_len + 52);
 	WFIFOW(inter_fd,0) = 0x3001;
 	WFIFOW(inter_fd,2) = mes_len + 52;
@@ -236,7 +238,7 @@ int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes) {
 
 int intif_regtostr(char* str, struct global_reg *reg, int qty) {
 	int len =0, i;
-	
+
 	for (i = 0; i < qty; i++) {
 		len+= sprintf(str+len, "%s", reg[i].str)+1; //We add 1 to consider the '\0' in place.
 		len+= sprintf(str+len, "%s", reg[i].value)+1;
@@ -252,7 +254,7 @@ int intif_saveregistry(struct map_session_data *sd, int type)
 
 	if (CheckForCharServer())
 		return -1;
-	
+
 	switch (type) {
 	case 3: //Character reg
 		reg = sd->save_reg.global;
@@ -311,7 +313,7 @@ int intif_request_registry(struct map_session_data *sd, int flag)
 	WFIFOW(inter_fd,0) = 0x3005;
 	WFIFOL(inter_fd,2) = sd->status.account_id;
 	WFIFOL(inter_fd,6) = sd->status.char_id;
-	WFIFOB(inter_fd,10) = (flag&1?1:0); //Request Acc Reg 2 
+	WFIFOB(inter_fd,10) = (flag&1?1:0); //Request Acc Reg 2
 	WFIFOB(inter_fd,11) = (flag&2?1:0); //Request Acc Reg
 	WFIFOB(inter_fd,12) = (flag&4?1:0); //Request Char Reg
 	WFIFOSET(inter_fd,13);
@@ -405,7 +407,7 @@ int intif_party_addmember(int party_id,struct party_member *member)
 {
 	if (CheckForCharServer())
 		return 0;
-	
+
 	WFIFOHEAD(inter_fd,42);
 	WFIFOW(inter_fd,0)=0x3022;
 	WFIFOW(inter_fd,2)=8+sizeof(struct party_member);
@@ -448,7 +450,7 @@ int intif_party_changemap(struct map_session_data *sd,int online)
 		return 0;
 	if(!sd)
 		return 0;
-	
+
 	WFIFOHEAD(inter_fd,19);
 	WFIFOW(inter_fd,0)=0x3025;
 	WFIFOL(inter_fd,2)=sd->status.party_id;
@@ -776,6 +778,72 @@ int intif_guild_castle_datasave(int castle_id,int index, int value)
 	return 0;
 }
 
+//-----------------------------------------------------------------
+// Homunculus Packets send to Inter server [albator]
+//-----------------------------------------------------------------
+
+int intif_homunculus_create(int account_id, struct s_homunculus *sh)
+{
+	if (CheckForCharServer())
+		return 0;
+	WFIFOHEAD(inter_fd, 44+NAME_LENGHT);
+	WFIFOW(inter_fd, 0) = 0x3090;
+	WFIFOL(inter_fd, 2) = account_id;
+	WFIFOL(inter_fd, 6) = sh->char_id;
+	WFIFOW(inter_fd, 10) = sh->class_;
+	WFIFOL(inter_fd,12) = sh->max_hp;
+	WFIFOL(inter_fd,16) = sh->max_sp;
+	memcpy(WFIFOP(inter_fd,20), sh->name, NAME_LENGTH);
+	WFIFOL(inter_fd,44) = sh->str;
+	WFIFOL(inter_fd,48) = sh->agi;
+	WFIFOL(inter_fd,52) = sh->vit;
+	WFIFOL(inter_fd,56) = sh->int_;
+	WFIFOL(inter_fd,60) = sh->dex;
+	WFIFOL(inter_fd,64) = sh->luk;
+	WFIFOSET(inter_fd, 44+NAME_LENGTH);
+	
+	return 0;
+}
+
+int intif_homunculus_requestload(int account_id, int homun_id)
+{
+	if (CheckForCharServer())
+		return 0;
+	WFIFOHEAD(inter_fd, 10);
+	WFIFOW(inter_fd,0) = 0x3091;
+	WFIFOL(inter_fd,2) = account_id;
+	WFIFOL(inter_fd,6) = homun_id;
+	WFIFOSET(inter_fd, 10);
+	return 0;
+}
+
+int intif_homunculus_requestsave(int account_id, struct s_homunculus* sh)
+{
+	if (CheckForCharServer())
+		return 0;
+	WFIFOHEAD(inter_fd, sizeof(struct s_homunculus)+10);
+	WFIFOW(inter_fd,0) = 0x3092;
+	WFIFOL(inter_fd,2) =  sizeof(struct s_homunculus)+10;
+	WFIFOL(inter_fd,6) = account_id;
+	memcpy(WFIFOP(inter_fd,10),sh,sizeof(struct s_homunculus));
+	WFIFOSET(inter_fd, sizeof(struct s_homunculus)+10);
+	return 0;
+
+}
+
+int intif_homunculus_requestdelete(int homun_id)
+{
+	if (CheckForCharServer())
+		return 0;
+	WFIFOHEAD(inter_fd, 6);
+	WFIFOW(inter_fd, 0) = 0x3093;
+	WFIFOL(inter_fd,2) = homun_id;
+	WFIFOSET(inter_fd,6);
+	return 0;
+
+}
+
+
 //-----------------------------------------------------------------
 // Packets receive from inter server
 
@@ -881,9 +949,9 @@ int intif_parse_Registers(int fd) {
 
 	if (RFIFOB(fd,12) == 3 && sd->status.char_id != RFIFOL(fd,8))
 		return 1; //Character registry from another character.
-	
+
 	flag = (sd->save_reg.global_num == -1 || sd->save_reg.account_num == -1 || sd->save_reg.account2_num == -1);
-	
+
 	switch (RFIFOB(fd,12)) {
 		case 3: //Character Registry
 			reg = sd->save_reg.global;
@@ -1370,6 +1438,74 @@ int intif_parse_RenamePetOk(int fd)
 	return 0;
 }
 
+//----------------------------------------------------------------
+// Homunculus recv packets [albator]
+
+int intif_parse_CreateHomunculus(int fd)
+{
+	struct map_session_data *sd = NULL;
+	RFIFOHEAD(fd);
+
+	if((sd=map_id2sd(RFIFOL(fd,2)))==NULL  ||
+		sd->status.char_id != RFIFOL(fd,6))
+		return 0;
+
+	if(RFIFOW(fd,10)==1)
+	{
+		ShowInfo("Homunculus created successfully\n");
+		sd->status.hom_id = sd->homunculus.hom_id = RFIFOL(fd,12);
+		merc_hom_recv_data(RFIFOL(fd,2), &sd->homunculus, 1) ;
+	}
+	else
+	{
+		ShowError("intif_parse_CreateHomunculus: failed to create homunculus\n");
+		clif_displaymessage(sd->fd, "[debug] fail to create homunculus"); // display error message..
+	}
+
+	return 0;
+}
+
+int intif_parse_RecvHomunculusData(int fd)
+{
+	struct s_homunculus sh;
+	int len;
+
+	RFIFOHEAD(fd);
+	len=RFIFOW(fd,2);
+
+	if(sizeof(struct s_homunculus)!=len-9) {
+		if(battle_config.etc_log)
+			ShowError("intif: homun data: data size error %d %d\n",sizeof(struct s_homunculus),len-9);
+	}
+	else{
+		memcpy(&sh,RFIFOP(fd,9),sizeof(struct s_homunculus));
+		merc_hom_recv_data(RFIFOL(fd,4),&sh,RFIFOB(fd,8));
+	}
+
+	return 0;
+
+}
+
+int intif_parse_SaveHomunculusOk(int fd)
+{
+	RFIFOHEAD(fd);
+	if(RFIFOB(fd,2) != 1) {
+		if(battle_config.error_log)
+			ShowError("homunculus data save failure\n");
+	}
+	return 0;
+}
+
+int intif_parse_DeleteHomunculusOk(int fd)
+{
+	RFIFOHEAD(fd);
+	if(RFIFOB(fd,2) != 1) {
+		if(battle_config.error_log)
+			ShowError("Homunculus data delete failure\n");
+	}
+
+	return 0;
+}
 //-----------------------------------------------------------------
 // inter serverからの通信
 // エラーがあれば0(false)を返すこと
@@ -1398,7 +1534,7 @@ int intif_parse(int fd)
 	}
 	// 処理分岐
 	switch(cmd){
-	case 0x3800:	
+	case 0x3800:
 		if (RFIFOL(fd,4) == 0xFF000000) //Normal announce.
 			clif_GMmessage(NULL,(char *) RFIFOP(fd,8),packet_len-8,0);
 		else if (RFIFOL(fd,4) == 0xFE000000) //Main chat message [LuzZza]
@@ -1445,6 +1581,10 @@ int intif_parse(int fd)
 	case 0x3882:	intif_parse_SavePetOk(fd); break;
 	case 0x3883:	intif_parse_DeletePetOk(fd); break;
 	case 0x3884:   intif_parse_RenamePetOk(fd); break;
+	case 0x3890:	intif_parse_CreateHomunculus(fd); break;
+	case 0x3891:	intif_parse_RecvHomunculusData(fd); break;
+	case 0x3892:	intif_parse_SaveHomunculusOk(fd); break;
+	case 0x3893:	intif_parse_DeleteHomunculusOk(fd); break;
 	default:
 		if(battle_config.error_log)
 			ShowError("intif_parse : unknown packet %d %x\n",fd,RFIFOW(fd,0));

+ 7 - 0
src/map/intif.h

@@ -60,6 +60,13 @@ int intif_save_petdata(int account_id, struct s_pet *p);
 int intif_delete_petdata(int pet_id);
 int intif_rename_pet(struct map_session_data *sd, char *name);
 
+
+int intif_homunculus_create(int account_id, struct s_homunculus *sh);
+int intif_homunculus_requestload(int account_id, int homun_id);
+int intif_homunculus_requestsave(int account_id, struct s_homunculus* sh);
+int intif_homunculus_requestdelete(int homun_id);
+
+
 int CheckForCharServer(void);
 
 #endif

+ 4 - 0
src/map/map.c

@@ -39,6 +39,7 @@
 #include "script.h"
 #include "guild.h"
 #include "pet.h"
+#include "mercenary.h"	//[orn]
 #include "atcommand.h"
 #include "charcommand.h"
 
@@ -1664,6 +1665,8 @@ int map_quit(struct map_session_data *sd) {
 
 		sd->state.waitingdisconnect = 1;
 		if (sd->pd) unit_free(&sd->pd->bl);
+		if(sd->status.hom_id > 0 && sd->hd)	//[orn]
+			merc_hom_delete(sd->hd, 0) ;
 		unit_free(&sd->bl);
 		chrif_save(sd,1);
 	} else { //Try to free some data, without saving anything (this could be invoked on map server change. [Skotlex]
@@ -3886,6 +3889,7 @@ int do_init(int argc, char *argv[]) {
 	do_init_storage();
 	do_init_skill();
 	do_init_pet();
+	do_init_merc();	//[orn]
 	do_init_npc();
 	do_init_unit();
 #ifndef TXT_ONLY /* mail system [Valaris] */

+ 24 - 13
src/map/map.h

@@ -485,6 +485,7 @@ struct party_data {
 
 struct npc_data;
 struct pet_db;
+struct homunculus_db;	//[orn]
 struct item_data;
 struct square;
 
@@ -747,6 +748,7 @@ struct map_session_data {
 	struct s_pet pet;
 	struct pet_data *pd;
 
+	struct s_homunculus homunculus ;	//[orn]
 	struct homun_data *hd;	// [blackhole89]
 
 	struct{
@@ -932,26 +934,35 @@ struct homun_data {
 	struct view_data *vd;
 	struct status_data base_status, battle_status;
 	struct status_change sc;
-
-	char name[NAME_LENGTH];
-	int id;
-	short class_;
+	struct homunculus_db *homunculusDB;	//[orn]
 
 	struct map_session_data *master; //pointer back to its master
 
-	short hunger_rate;
+	int hungry_timer;	//[orn]
 
-	struct {
-		int id;		//0 = none
-		int level;
-	} hskill[4];	//skills (max. 4 for now)
 
 	int target_id,attacked_id;
+	short attackable;
 
-	short level;
-	short regenhp,regensp;
-	unsigned long exp,exp_next;
-	short skillpts;
+	int natural_heal_timer;	//[orn]
+	int hp_sub,sp_sub;
+	int inchealhptick,inchealsptick;
+	int nhealhp,nhealsp,nshealhp,nshealsp,nsshealhp,nsshealsp;
+	short hp_loss_value;
+	short sp_loss_value;
+	short hp_loss_type;
+	short sp_gain_value;
+	short hp_gain_value;
+	int hp_loss_tick;
+	int sp_loss_tick;
+	int hp_loss_rate;
+	int sp_loss_rate;
+	unsigned int canregen_tick;
+
+	
+	unsigned short regenhp,regensp;
+	unsigned long exp_next;
+	char blockskill[MAX_SKILL];	// [orn]
 };
 
 struct pet_data {

+ 1020 - 197
src/map/mercenary.c

@@ -32,29 +32,19 @@
 #include "unit.h"
 
 #include "mercenary.h"
-// Homunculus and future Mercenary system code go here [Celest]
+#include "charsave.h"
+
 typedef char char32[32];
 
-// everything below is crappy code by [blackhole89].
-
-/*
-HLIF_HEAL#Ä¡À¯ÀÇ _Õ+æ(Èú)#
-HLIF_AVOID#+ä+ÞȸÇÇ#
-HLIF_BRAIN#_ú_ö_ú#
-HLIF_CHANGE#¸àÅ> Ã_ÀÎÁö#
-HAMI_CASTLE#Ä___¸÷#
-HAMI_DEFENCE#÷ðÆæ_º#
-HAMI_SKIN#_Æ_Ù¸¸Æ_¿ò _ºÅ_#
-HAMI_BLOODLUST#ºí·¯÷å ·¯_ºÆR#
-HFLI_MOON#¹R¶óÀÌÆR#
-HFLI_FLEET#Çø_ ¹<ºê#
-HFLI_SPEED#¿À¹ö÷å _ºÇÇ÷å#
-HFLI_SBR44#S.B.R.44#
-HVAN_CAPRICE#Ä<ÇÁ¸R_º#
-HVAN_CHAOTIC#Ä<¿ÀÆ_ º__×÷ñ_Ç#
-HVAN_INSTRUCT#Ã_ÀÎÁö ÀÎ_ºÆR·°_Ç#
-HVAN_EXPLOSION#¹ÙÀÌ¿À ÀÍ_ºÇ÷ÎÁ¯#
-*/
+static int dirx[8]={0,-1,-1,-1,0,1,1,1};	//[orn]
+static int diry[8]={1,1,0,-1,-1,-1,0,1};	//[orn]
+
+//Better equiprobability than rand()% [orn]
+#define rand(a, b) a+(int) ((float)(b-a+1)*rand()/(RAND_MAX+1.0))
+
+struct homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS];	//[orn]
+struct skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_SKILL_TREE];
+
 char32 merc_skillname[20] = {"NULL","HLIF_HEAL","HLIF_AVOID","HLIF_BRAIN","HLIF_CHANGE",
 							"HAMI_CASTLE","HAMI_DEFENCE","HAMI_SKIN","HAMI_BLOODLUST",
 							"HFLI_MOON","HFLI_FLEET","HFLI_SPEED","HFLI_SBR44",
@@ -62,106 +52,509 @@ char32 merc_skillname[20] = {"NULL","HLIF_HEAL","HLIF_AVOID","HLIF_BRAIN","HLIF_
 
 void merc_load_exptables(void);
 int mercskill_castend_id( int tid, unsigned int tick, int id,int data );
+static int merc_hom_hungry(int tid,unsigned int tick,int id,int data);
 
 int do_init_merc (void)
 {
 	merc_load_exptables();
+	memset(homunculus_db,0,sizeof(homunculus_db));	//[orn]
+	read_homunculusdb();	//[orn]
 	return 0;
 }
 
-static unsigned long hexptbl[126];
+static unsigned long hexptbl[MAX_LEVEL+1];
 
 void merc_load_exptables(void)
 {
-	FILE *fl;
-	int i;
+	FILE *fp;
+	char line[1024];
+	int i,k; 
+	int j=0;
+	int lines;
+	char *filename[]={"exp_homun.txt","exp_homun2.txt"};
+	char *str[32],*h,*nh;
 
-	fl=fopen("db/hexptbl.txt","rb");
-	if(!fl) return;
-	
-	ShowInfo("reading db/hexptbl.txt\n");
 
-	for(i=0;i<125;++i)
-	{
-		fscanf(fl,"%lu,",&(hexptbl[i]));
+	j = 0;
+	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 ;
+		}
+		lines = 0;
+		while(fgets(line,sizeof(line)-1,fp) && j <= MAX_LEVEL){
+			
+			lines++;
+
+			if(line[0] == '/' && line[1] == '/')
+				continue;
+
+			for(k=0,h=line;k<20;k++){
+				if((nh=strchr(h,','))!=NULL){
+					str[k]=h;
+					*nh=0;
+					h=nh+1;
+				} else {
+					str[k]=h;
+					h+=strlen(h);
+				}
+			}
+
+			hexptbl[j]= atoi(str[0]);
+			
+			j++;
+		}
+		if (j >= MAX_LEVEL)
+			ShowWarning("read_hexptbl: Reached max level in exp_homun [%d]. Remaining lines were not read.\n ", MAX_HOMUNCULUS_CLASS);
+		fclose(fp);
+		ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[i]);
 	}
-	fclose(fl);
+	
+	return ;
+
 }
 
-char *merc_skill_get_name(int id)
+char *merc_hom_skill_get_name(int id)
 {
 	return merc_skillname[id-HM_SKILLBASE];
 }
 
 void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp)
 {
-	clif_homuninfo(hd->master);
+	nullpo_retv(hd);
+
+	if( hd->battle_status.hp < 0)
+		hd->battle_status.hp = 0;
+	if( hd->battle_status.sp < 0)
+		hd->battle_status.sp = 0;
+	hd->master->homunculus.hp = hd->battle_status.hp ;
+	hd->master->homunculus.sp = hd->battle_status.sp ;
+	clif_hominfo(hd->master,0);
 }
 
-int merc_dead(struct homun_data *hd, struct block_list *src)
+int merc_hom_dead(struct homun_data *hd, struct block_list *src)
 {
-	//dead lol
-	merc_save(hd);
+	hd->master->homunculus.intimacy -= 100 ;
+	hd->master->homunculus.alive = 0 ;
+	if(hd->master->homunculus.intimacy <= 0) {
+		merc_stop_walking(hd, 1);
+		merc_stop_attack(hd);
+		clif_emotion(&hd->master->bl, 23) ;	//omg
+		merc_hom_delete(hd,1) ;
+		return 1 ;
+	}
+	clif_emotion(&hd->bl, 16) ;	//wah
+	clif_emotion(&hd->master->bl, 28) ;	//sob
+	merc_hom_delete(hd, 0);
+
 	return 3; //Remove it from map.
 }
 
-void merc_skillup(struct map_session_data *sd,short skillnum)
+int merc_hom_delete(struct homun_data *hd, int flag)
+{
+	nullpo_retr(0, hd);
+	// Delete homunculus
+	if ( flag&1 ) {	//sabbath
+		intif_homunculus_requestdelete(hd->master->homunculus.hom_id) ;
+		merc_stop_walking(hd, 1);
+		merc_stop_attack(hd);
+		clif_emotion(&hd->bl, 28) ;	//sob
+		hd->master->status.hom_id = 0;
+		hd->master->homunculus.hom_id = 0;
+		chrif_save(hd->master,0);
+	} 
+	else {
+		merc_save(hd) ;
+	}
+	if ( !unit_free(&hd->bl) ) 
+		return 0 ;
+	aFree(hd);
+    	
+	return 1;
+}
+
+int merc_hom_calc_skilltree(struct map_session_data *sd)
+{
+	int i,id=0 ;
+	int j,f=1;
+	int c=0;
+
+	nullpo_retr(0, sd);
+	c = sd->homunculus.class_ - 6001 ;
+	
+	for(i=0;i < MAX_SKILL_TREE && (id = hskill_tree[c][i].id) > 0;i++){
+		if(sd->homunculus.hskill[id-HM_SKILLBASE-1].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(sd,hskill_tree[c][i].need[j].id) <
+					hskill_tree[c][i].need[j].lv) {
+					f=0;
+					break;
+				}
+			}
+		}
+		if (f){
+			sd->homunculus.hskill[id-HM_SKILLBASE-1].id = id ;
+		}
+	}
+
+	return 0;
+}
+
+int merc_hom_checkskill(struct map_session_data *sd,int skill_id)
+{
+	int i = skill_id - HM_SKILLBASE - 1 ;
+	if(sd == NULL) return 0;
+
+	if(sd->homunculus.hskill[i].id == skill_id)
+		return (sd->homunculus.hskill[i].lv);
+
+	return 0;
+}
+
+static int merc_skill_tree_get_max(int id, int b_class){
+	int i, skillid;
+	for(i=0;(skillid=hskill_tree[b_class-6001][i].id)>0;i++)
+		if (id == skillid) return hskill_tree[b_class-6001][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->master->homunculus.vaporize == 0) {
+		i = skillnum - HM_SKILLBASE - 1 ;
+		if( hd->master->homunculus.skillpts > 0 &&
+			hd->master->homunculus.hskill[i].id &&
+			( hd->master->homunculus.hskill[i].flag == 0 ) && //Don't allow raising while you have granted skills. [Skotlex]
+			hd->master->homunculus.hskill[i].lv < merc_skill_tree_get_max(skillnum, hd->master->homunculus.class_)
+			)
+		{
+			hd->master->homunculus.hskill[i].lv++ ;
+			hd->master->homunculus.skillpts-- ;
+			status_calc_homunculus(hd,1) ;
+			clif_homskillup(hd->master, skillnum) ;
+			clif_hominfo(hd->master,0) ;
+			clif_homskillinfoblock(hd->master) ;
+		}
+	}
+}
+
+int merc_hom_levelup(struct homun_data *hd)
 {
-	nullpo_retv(sd);
-	nullpo_retv(sd->hd);
-	if(!sd->hd->skillpts) return;	//no skill points left
+	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->master->homunculus.level == MAX_LEVEL) return 0 ;
+	
+	hd->master->homunculus.level++ ;
+	if ( ( (hd->master->homunculus.level) % 3 ) == 0 ) hd->master->homunculus.skillpts++ ;	//1 skillpoint each 3 base level
+
+	hd->master->homunculus.exp -= hd->exp_next ;
+	hd->exp_next = hexptbl[hd->master->homunculus.level - 1] ;
+	
+	if ( hd->homunculusDB->gmaxHP <= hd->homunculusDB->gminHP ) 
+		growth_max_hp = hd->homunculusDB->gminHP ;
+	else
+		growth_max_hp = rand(hd->homunculusDB->gminHP, hd->homunculusDB->gmaxHP) ;
+	if ( hd->homunculusDB->gmaxSP <= hd->homunculusDB->gminSP ) 
+		growth_max_sp = hd->homunculusDB->gminSP ;
+	else
+		growth_max_sp = rand(hd->homunculusDB->gminSP, hd->homunculusDB->gmaxSP) ;
+	if ( hd->homunculusDB->gmaxSTR <= hd->homunculusDB->gminSTR ) 
+		growth_str = hd->homunculusDB->gminSTR ;
+	else
+		growth_str = rand(hd->homunculusDB->gminSTR, hd->homunculusDB->gmaxSTR) ;
+	if ( hd->homunculusDB->gmaxAGI <= hd->homunculusDB->gminAGI ) 
+		growth_agi = hd->homunculusDB->gminAGI ;
+	else
+		growth_agi = rand(hd->homunculusDB->gminAGI, hd->homunculusDB->gmaxAGI) ;
+	if ( hd->homunculusDB->gmaxVIT <= hd->homunculusDB->gminVIT ) 
+		growth_vit = hd->homunculusDB->gminVIT ;
+	else
+		growth_vit = rand(hd->homunculusDB->gminVIT, hd->homunculusDB->gmaxVIT) ;
+	if ( hd->homunculusDB->gmaxDEX <= hd->homunculusDB->gminDEX ) 
+		growth_dex = hd->homunculusDB->gminDEX ;
+	else
+		growth_dex = rand(hd->homunculusDB->gminDEX, hd->homunculusDB->gmaxDEX) ;
+	if ( hd->homunculusDB->gmaxINT <= hd->homunculusDB->gminINT ) 
+		growth_int = hd->homunculusDB->gminINT ;
+	else
+		growth_int = rand(hd->homunculusDB->gminINT, hd->homunculusDB->gmaxINT) ;
+	if ( hd->homunculusDB->gmaxLUK <= hd->homunculusDB->gminLUK ) 
+		growth_luk = hd->homunculusDB->gminLUK ;
+	else
+		growth_luk = rand(hd->homunculusDB->gminLUK, hd->homunculusDB->gmaxLUK) ;
+
+	hd->base_status.max_hp += growth_max_hp ;
+	hd->base_status.max_sp += growth_max_sp ;
+	hd->master->homunculus.max_hp = hd->base_status.max_hp ;
+	hd->master->homunculus.max_sp = hd->base_status.max_sp ;
+	hd->master->homunculus.str += growth_str ;
+	hd->master->homunculus.agi += growth_agi ;
+	hd->master->homunculus.vit += growth_vit ;
+	hd->master->homunculus.dex += growth_dex ;
+	hd->master->homunculus.int_ += growth_int ;
+	hd->master->homunculus.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/(float)10, growth_agi/(float)10, growth_vit/(float)10, growth_int/(float)10, growth_dex/(float)10, growth_luk/(float)10 ) ;
+			clif_disp_onlyself(hd->master,output,strlen(output));
+	}
+	
+	hd->base_status.str = (int) (hd->master->homunculus.str / 10) ;
+	hd->base_status.agi = (int) (hd->master->homunculus.agi / 10) ;
+	hd->base_status.vit = (int) (hd->master->homunculus.vit / 10) ;
+	hd->base_status.dex = (int) (hd->master->homunculus.dex / 10) ;
+	hd->base_status.int_ = (int) (hd->master->homunculus.int_ / 10) ;
+	hd->base_status.luk = (int) (hd->master->homunculus.luk / 10) ;
+			
+	memcpy(&hd->battle_status, &hd->base_status, sizeof(struct status_data)) ;
+	status_calc_homunculus(hd,1) ;
 
-	sd->hd->hskill[(skillnum-HM_SKILLBASE)%4].id=skillnum;
-	sd->hd->hskill[(skillnum-HM_SKILLBASE)%4].level+=1;
-	sd->hd->skillpts-=1;
+	status_percent_heal(&hd->bl, 100, 100);
+//	merc_save(hd) ;	//not necessary
+	
+	return 1 ;
+}
 
-	clif_homuninfo(sd);
-	clif_homunskillinfoblock(sd);
-	clif_skillup(sd, skillnum);
+int merc_hom_evolution(struct homun_data *hd)
+{
+	nullpo_retr(0, hd);
 
-	merc_save(sd->hd);
+	if(hd && hd->homunculusDB->evo_class)
+	{
+		hd->master->homunculus.class_ = hd->homunculusDB->evo_class;
+		hd->master->homunculus.vaporize = 1;
+		merc_stop_walking(hd, 1);
+		merc_stop_attack(hd);
+		merc_hom_delete(hd, 0) ;
+		merc_call_homunculus(hd->master);
+		clif_emotion(&hd->master->bl, 21) ;	//no1
+		clif_misceffect2(&hd->bl,568);
+		return 1 ;
+	} else {
+		clif_emotion(&hd->bl, 4) ;	//swt
+		return 0 ;
+	}
 }
 
-int merc_gainexp(struct homun_data *hd,int exp)
+int merc_hom_gainexp(struct homun_data *hd,int exp)
 {
-	hd->exp += exp;
+	if(hd->master->homunculus.vaporize)
+		return 1;
+
+	if( hd->exp_next == 0 ) {
+		hd->master->homunculus.exp = 0 ;
+		return 0;
+	}
+
+	hd->master->homunculus.exp += exp;
 
-	if(hd->exp < hd->exp_next)
+	if(hd->master->homunculus.exp < hd->exp_next) {
+		clif_hominfo(hd->master,0);
 		return 0;
-	  	//levelup
+	}
+
+ 	//levelup
 	do
 	{
-		hd->exp-=hd->exp_next;
-		hd->exp_next=hexptbl[hd->level];
-		hd->level++;
+		merc_hom_levelup(hd) ;
 	}
-	while(hd->exp > hd->exp_next);
+	while(hd->master->homunculus.exp > hd->exp_next && hd->exp_next != 0 );
 		
-	clif_misceffect(&hd->bl,0);
-	status_calc_homunculus(hd,0);
-	status_percent_heal(&hd->bl, 100, 100);
-	clif_homuninfo(hd->master);
+	if( hd->exp_next == 0 ) {
+		hd->master->homunculus.exp = 0 ;
+	}
+
+	status_calc_homunculus(hd,1);
+	clif_misceffect2(&hd->bl,568);
+	status_calc_homunculus(hd,1);
 	return 0;
 }
 
-void merc_heal(struct homun_data *hd,int hp,int sp)
+int merc_hom_heal(struct homun_data *hd,int hp,int sp)
 {
-	clif_homuninfo(hd->master);
+	nullpo_retr(0, hd);
+
+	if( hd->battle_status.max_hp < hd->battle_status.hp )
+		hd->battle_status.hp = hd->battle_status.max_hp;
+	else if (hd->battle_status.hp <= 0) {
+		hd->battle_status.hp = 1;
+	}
+
+	if( hd->battle_status.max_sp < hd->battle_status.sp )
+		hd->battle_status.sp = hd->battle_status.max_sp;
+	else if (hd->battle_status.sp < 0) {
+		hd->battle_status.sp = 0;
+	}
+
+	if ( 	(hd->battle_status.hp != hd->base_status.hp) ||
+	 			(hd->battle_status.sp != hd->base_status.sp) )
+	{
+		clif_hominfo(hd->master,0);
+	}
+	hd->master->homunculus.hp = hd->base_status.hp = hd->battle_status.hp ;
+	hd->master->homunculus.sp = hd->base_status.sp = hd->battle_status.sp ;
+
+	return 1;
 }
 
-#ifndef TXT_ONLY
-void merc_save(struct homun_data *hd)
+static unsigned int natural_heal_prev_tick,natural_heal_diff_tick;
+static void merc_natural_heal_hp(struct homun_data *hd)
 {
-	sprintf(tmp_sql, "UPDATE `homunculus` SET `class`='%d',`name`='%s',`level`='%d',`exp`='%lu',`hunger`='%d',`hp`='%u',`sp`='%u',`skill1lv`='%d',`skill2lv`='%d',`skill3lv`='%d',`skill4lv`='%d',`skillpts`='%d' WHERE `id` = '%d'", 
-		hd->class_,hd->name,hd->level,hd->exp,hd->hunger_rate,
-		hd->battle_status.hp,hd->battle_status.sp,
-		hd->hskill[0].level,hd->hskill[1].level,hd->hskill[2].level,hd->hskill[3].level,
-		hd->skillpts,hd->id);
- 	if(mysql_query(&mmysql_handle, tmp_sql)){
-		ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
-		ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+
+	nullpo_retv(hd);
+
+//	ShowDebug("merc_natural_heal_hp (1) : homunculus = %s | hd->ud.walktimer = %d |\n", hd->name, hd->ud.walktimer) ;
+	if(hd->ud.walktimer == -1) {
+		hd->inchealhptick += natural_heal_diff_tick;
+	}
+	else {
+		hd->inchealhptick = 0;
 		return;
 	}
+
+//	ShowDebug("merc_natural_heal_hp (2) : homunculus = %s | hd->regenhp = %d |\n", hd->name, hd->regenhp) ;
+	if (hd->battle_status.hp != hd->battle_status.max_hp) {
+		if ((unsigned int)status_heal(&hd->bl, hd->regenhp, 0, 1) < hd->regenhp)
+		{	//At full.
+			hd->inchealhptick = 0;
+			return;
+		}
+	}
+
+	return;
+}
+
+static void merc_natural_heal_sp(struct homun_data *hd)
+{
+
+	nullpo_retv(hd);
+
+//	ShowDebug("merc_natural_heal_sp (1) : homunculus = %s | hd->regensp = %d |\n", hd->name, hd->regensp) ;
+	if (hd->battle_status.sp != hd->battle_status.max_sp) {
+		if ((unsigned int)status_heal(&hd->bl, 0, hd->regensp, 1) < hd->regensp)
+		{	//At full.
+			hd->inchealsptick = 0;
+			return;
+		}
+	}
+
+	return;
+}
+
+static void merc_bleeding (struct homun_data *hd)
+{
+	int hp = 0, sp = 0;
+
+	if (hd->hp_loss_value > 0) {
+		hd->hp_loss_tick += natural_heal_diff_tick;
+		if (hd->hp_loss_tick >= hd->hp_loss_rate) {
+			do {
+				hp += hd->hp_loss_value;
+				hd->hp_loss_tick -= hd->hp_loss_rate;
+			} while (hd->hp_loss_tick >= hd->hp_loss_rate);
+			hd->hp_loss_tick = 0;
+		}
+	}
+	
+	if (hd->sp_loss_value > 0) {
+		hd->sp_loss_tick += natural_heal_diff_tick;
+		if (hd->sp_loss_tick >= hd->sp_loss_rate) {
+			do {
+				sp += hd->sp_loss_value;
+				hd->sp_loss_tick -= hd->sp_loss_rate;
+			} while (hd->sp_loss_tick >= hd->sp_loss_rate);
+			hd->sp_loss_tick = 0;
+		}
+	}
+
+	if (hp > 0 || sp > 0)
+		status_zap(&hd->bl, hp, sp);
+
+	return;
+}
+
+/*==========================================
+ * HP/SP natural heal
+ *------------------------------------------
+ */
+
+//static int merc_natural_heal_sub(struct homun_data *hd,va_list ap) {
+static int merc_natural_heal_sub(struct homun_data *hd,int tick) {
+//	int tick;
+
+	nullpo_retr(0, hd);
+//	tick = va_arg(ap,int);
+
+// -- moonsoul (if conditions below altered to disallow natural healing if under berserk status)
+	if (  hd && ( status_isdead(&hd->bl) ||
+		( ( hd->sc.count ) &&
+			( (hd->sc.data[SC_POISON].timer != -1 ) || ( hd->sc.data[SC_BLEEDING].timer != -1 ) )
+		) )
+	) { //Cannot heal neither natural or special.
+		hd->hp_sub = hd->inchealhptick = 0;
+		hd->sp_sub = hd->inchealsptick = 0;
+	} else {
+		if ( DIFF_TICK (tick, hd->canregen_tick)<0 ) {
+			hd->hp_sub = hd->inchealhptick = 0;
+			hd->sp_sub = hd->inchealsptick = 0;
+		} else { //natural heal
+			merc_natural_heal_hp(hd);
+			merc_natural_heal_sp(hd);
+			hd->canregen_tick = tick;
+		}
+	}
+	if (hd->hp_loss_value > 0 || hd->sp_loss_value > 0)
+		merc_bleeding(hd);
+	else
+		hd->hp_loss_tick = hd->sp_loss_tick = 0;
+
+	return 0;
+}
+
+/*==========================================
+ * orn
+ *------------------------------------------
+ */
+int merc_natural_heal(int tid,unsigned int tick,int id,int data)
+{
+	struct map_session_data *sd;
+
+	sd=map_id2sd(id);
+
+	nullpo_retr(0, sd);
+	
+	if(sd->homunculus.vaporize)
+		return 1;
+
+	if(sd && sd->hd) {
+		natural_heal_diff_tick = DIFF_TICK(tick,natural_heal_prev_tick);
+		merc_natural_heal_sub(sd->hd, tick);
+	
+		natural_heal_prev_tick = tick;
+		sd->hd->natural_heal_timer = add_timer(gettick()+battle_config.natural_healhp_interval, merc_natural_heal,sd->bl.id,0);
+	}
+	return 0;
+}
+
+#ifndef TXT_ONLY
+void merc_save(struct homun_data *hd)
+{
+	intif_homunculus_requestsave(hd->master->status.account_id, &hd->master->homunculus) ;
+
 }
 #else
 void merc_save(struct homun_data *hd)
@@ -170,151 +563,581 @@ void merc_save(struct homun_data *hd)
 }
 #endif
 
-static void merc_load_sub(struct homun_data *hd, struct map_session_data *sd)
+static int merc_calc_pos(struct homun_data *hd,int tx,int ty,int dir)	//[orn]
+{
+	int x,y,dx,dy;
+	int i,k;
+
+	nullpo_retr(0, hd);
+
+	hd->ud.to_x = tx;
+	hd->ud.to_y = ty;
+
+	if(dir < 0 || dir >= 8)
+	 return 1;
+	
+	dx = -dirx[dir]*2;
+	dy = -diry[dir]*2;
+	x = tx + dx;
+	y = ty + dy;
+	if(!unit_can_reach_pos(&hd->bl,x,y,0)) {
+		if(dx > 0) x--;
+		else if(dx < 0) x++;
+		if(dy > 0) y--;
+		else if(dy < 0) y++;
+		if(!unit_can_reach_pos(&hd->bl,x,y,0)) {
+			for(i=0;i<12;i++) {
+				k = rand(1, 8);
+//				k = rand()%8;
+				dx = -dirx[k]*2;
+				dy = -diry[k]*2;
+				x = tx + dx;
+				y = ty + dy;
+				if(unit_can_reach_pos(&hd->bl,x,y,0))
+					break;
+				else {
+					if(dx > 0) x--;
+					else if(dx < 0) x++;
+					if(dy > 0) y--;
+					else if(dy < 0) y++;
+					if(unit_can_reach_pos(&hd->bl,x,y,0))
+						break;
+				}
+			}
+			if(i>=12) {
+				x = tx;
+				y = ty;
+				if(!unit_can_reach_pos(&hd->bl,x,y,0))
+					return 1;
+			}
+		}
+	}
+	hd->ud.to_x = x;
+	hd->ud.to_y = y;
+	return 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:
+			merc_hom_food(sd, sd->hd);
+			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, k, emotion;
+
+	if(hd->master->homunculus.vaporize)
+		return 1 ;
+
+	k=hd->homunculusDB->foodID;
+	i=pc_search_inventory(sd,k);
+	if(i < 0) {
+		clif_hom_food(sd,k,0);
+		return 1;
+	}
+	pc_delitem(sd,i,1,0);
+
+	if ( hd->master->homunculus.hunger >= 91 ) {
+		hd->master->homunculus.intimacy -= 50 ;
+		emotion = 16 ;
+	} else if ( hd->master->homunculus.hunger >= 76 ) {
+		hd->master->homunculus.intimacy -= 30 ;
+		emotion = 19 ;
+	} else if ( hd->master->homunculus.hunger >= 26 ) {
+		hd->master->homunculus.intimacy += 80 ;
+		emotion = 2 ;
+	} else if ( hd->master->homunculus.hunger >= 11 ) {
+		hd->master->homunculus.intimacy += 100 ;
+		emotion = 2 ;
+	} else {
+		hd->master->homunculus.intimacy += 50 ;
+		emotion = 2 ;
+	}
+	if(hd->master->homunculus.intimacy > 100000)
+		hd->master->homunculus.intimacy = 100000;
+	if(hd->master->homunculus.intimacy < 0)
+		hd->master->homunculus.intimacy = 0 ;
+
+	emotion = 5 ; // Thanks 
+	hd->master->homunculus.hunger += 10;	//dunno increase value for each food
+	if(hd->master->homunculus.hunger > 100)
+		hd->master->homunculus.hunger = 100;
+
+	clif_emotion(&hd->bl,emotion) ;
+	clif_send_homdata(sd,SP_HUNGRY,sd->homunculus.hunger);
+	clif_send_homdata(sd,SP_INTIMATE,sd->homunculus.intimacy / 100);
+	clif_hom_food(sd,hd->homunculusDB->foodID,1);
+
+	return 0;
+}
+
+static int merc_hom_hungry(int tid,unsigned int tick,int id,int data)
+{
+	struct map_session_data *sd;
+	struct homun_data *hd;
+
+	sd=map_id2sd(id);
+	if(!sd)
+		return 1;
+
+	if(!sd->status.hom_id || !sd->hd)
+		return 1;
+
+	hd = sd->hd;
+	if(hd->hungry_timer != tid){
+		if(battle_config.error_log)
+			ShowError("merc_hom_hungry_timer %d != %d\n",hd->hungry_timer,tid);
+		return 0 ;
+	}
+	hd->master->homunculus.hunger-- ;
+	if(hd->master->homunculus.hunger >= 0 && hd->master->homunculus.hunger <= 10) {
+		clif_emotion(&hd->bl, 6) ;	//an
+	}
+	if(hd->master->homunculus.hunger == 25) {
+		clif_emotion(&hd->bl, 20) ;	//hmm
+	}
+	if(hd->master->homunculus.hunger == 75) {
+		clif_emotion(&hd->bl, 33) ;	//ok
+	}  
+	if(hd->master->homunculus.hunger < 0) {
+		hd->master->homunculus.hunger = 0;
+		hd->master->homunculus.intimacy -= 100 ;
+		clif_send_homdata(sd,SP_INTIMATE,sd->homunculus.intimacy / 100);
+		if ( hd->master->homunculus.intimacy <= 0 ) {
+			merc_stop_walking(hd, 1);
+			merc_stop_attack(hd);
+			clif_emotion(&hd->master->bl, 23) ;	//omg
+			merc_hom_delete(hd,1) ;
+		}
+		return 0 ;
+	} else {
+		clif_send_homdata(sd,SP_HUNGRY,sd->homunculus.hunger);
+	
+		hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); //simple Fix albator
+		return 1 ;
+	}
+
+
+}
+
+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 = -1;
+	}
+
+	return 1;
+}
+
+int merc_natural_heal_timer_delete(struct homun_data *hd)
+{
+	nullpo_retr(0, hd);
+	if(hd->natural_heal_timer != -1) {
+		delete_timer(hd->natural_heal_timer,merc_natural_heal);
+		hd->natural_heal_timer = -1;
+	}
+
+	return 1;
+}
+
+int search_homunculusDB_index(int key,int type)
+{
+	int i;
+
+	for(i=0;i<MAX_HOMUNCULUS_CLASS;i++) {
+		if(homunculus_db[i].class_ <= 0)
+			continue;
+		switch(type) {
+			case HOMUNCULUS_CLASS:
+				if(homunculus_db[i].class_ == key)
+					return i;
+				break;
+			case HOMUNCULUS_FOOD:
+				if(homunculus_db[i].foodID == key)
+					return i;
+				break;
+			default:
+				return -1;
+		}
+	}
+	return -1;
+}
+
+int merc_hom_data_init(struct map_session_data *sd)
 {
+	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(sd->homunculus.class_,HOMUNCULUS_CLASS);
+	if(i < 0) {
+		sd->status.hom_id = 0;
+		return 1;
+	}
+	sd->hd = hd = (struct homun_data *)aCalloc(1,sizeof(struct homun_data));
+	hd->homunculusDB = &homunculus_db[i];
+	merc_calc_pos(hd,sd->bl.x,sd->bl.y,sd->ud.dir);
+	hd->bl.x = hd->ud.to_x;
+	hd->bl.y = hd->ud.to_y;
+	hd->master = sd;
+
+	sd->status.hom_id = sd->homunculus.hom_id ;
+
 	hd->bl.m=sd->bl.m;
 	hd->bl.x=sd->bl.x;
-	hd->bl.y=sd->bl.y;
+	hd->bl.y=sd->bl.y - 1 ;
+	hd->bl.subtype = MONS;
 	hd->bl.type=BL_HOMUNCULUS;
 	hd->bl.id= npc_get_new_npc_id();
 	hd->bl.prev=NULL;
 	hd->bl.next=NULL;
+	hd->exp_next=hexptbl[hd->master->homunculus.level - 1];
+	hd->ud.attacktimer=-1;
+	hd->ud.attackabletime=gettick();
+	hd->target_id = 0 ;
+	hd->attackable = 1 ;
 
-	status_set_viewdata(&hd->bl, hd->class_);
+	for(i=0;i<MAX_STATUSCHANGE;i++) {
+		hd->sc.data[i].timer=-1;
+		hd->sc.data[i].val1 = hd->sc.data[i].val2 = hd->sc.data[i].val3 = hd->sc.data[i].val4 = 0;
+	}
+
+	hd->base_status.hp = hd->master->homunculus.hp ;
+	hd->base_status.max_hp = hd->master->homunculus.max_hp ;
+	hd->base_status.sp = hd->master->homunculus.sp ;
+	hd->base_status.max_sp = hd->master->homunculus.max_sp ;
+	hd->base_status.str = (int) (hd->master->homunculus.str / 10) ;
+	hd->base_status.agi = (int) (hd->master->homunculus.agi / 10) ;
+	hd->base_status.vit = (int) (hd->master->homunculus.vit / 10) ;
+	hd->base_status.int_ = (int) (hd->master->homunculus.int_ / 10) ;
+	hd->base_status.dex = (int) (hd->master->homunculus.dex / 10) ;
+	hd->base_status.luk = (int) (hd->master->homunculus.luk / 10) ;
+			
+	memcpy(&hd->battle_status, &hd->base_status, sizeof(struct status_data)) ;
+
+	status_set_viewdata(&hd->bl, hd->master->homunculus.class_);
 	status_change_init(&hd->bl);
+	hd->ud.dir = sd->ud.dir;
 	unit_dataset(&hd->bl);
-
+	
 	map_addiddb(&hd->bl);
-	status_calc_homunculus(hd,1);	//this function will have more sense later on
+	status_calc_homunculus(hd,1);
+	//timer
+		hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0);
+		natural_heal_prev_tick = gettick();
+		hd->natural_heal_timer = add_timer(gettick()+battle_config.natural_healhp_interval, merc_natural_heal,sd->bl.id,0);
+		
+	return 0;
 }
-#ifndef TXT_ONLY
-void merc_load(struct map_session_data *sd)
+// FIX call_homunculus [albator]
+int merc_call_homunculus(struct map_session_data *sd)
 {
-	struct homun_data *hd;
-	sd->hd=NULL;
-	
-	sprintf(tmp_sql, "SELECT `id`,`class`,`name`,`level`,`exp`,`hunger`,`hp`,`sp`,`skill1lv`,`skill2lv`,`skill3lv`,`skill4lv`,`skillpts` FROM `homunculus` WHERE `char_id` = '%d'", sd->char_id);
-	if(mysql_query(&mmysql_handle, tmp_sql)){
-		ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
-		ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
-		return;
+	int class_ = 0 ;
+	nullpo_retr(0, sd);
+
+	// call vaporized homunculus [albator]
+	if(sd->homunculus.vaporize == 1)
+	{
+		sd->homunculus.vaporize = 0;
+		merc_hom_data_init(sd);
+
+		if ( sd->homunculus.alive && sd->hd && sd->bl.prev != NULL) {
+			map_addblock(&sd->hd->bl);
+			clif_spawn(&sd->hd->bl);
+			clif_send_homdata(sd,SP_ACK,0);
+			clif_hominfo(sd,1);
+			clif_hominfo(sd,0); // send this x2. dunno why, but kRO does that [blackhole89]
+			clif_homskillinfoblock(sd);
+		}
+		// save
+		merc_save(sd->hd);
+		return 1;
+		
 	}
 
-	sql_res = mysql_store_result(&mmysql_handle);
-	if(!sql_res)
-		return;
+	if ( sd->status.hom_id ) {
+		return merc_hom_recv_data(sd->status.account_id, &sd->homunculus, 1 ) ;
+	} else {
+		class_ = 6000 + rand(1, 8) ;
+		return merc_create_homunculus(sd, class_) ;
+	}
+
+	
+}
+// Albator
+// Recv data of an homunculus after it loading
+int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag)
+{
+	struct map_session_data *sd ;
 
-	if(mysql_num_rows(sql_res) <= 0){
-		mysql_free_result(sql_res);
-		return;	//no homunculus for this char
+	sd = map_id2sd(account_id);
+	if(sd == NULL)
+		return 0;
+	
+	if(flag == 0) {
+		sd->status.hom_id = 0;
+		return 0;
 	}
-	sql_row = mysql_fetch_row(sql_res);
+	memcpy(&sd->homunculus, sh, sizeof(struct s_homunculus));
+
 	
-	//dummy code
-	hd=(struct homun_data *) aCalloc(1, sizeof(struct homun_data));
-	sd->hd=hd;		//pointer from master to homunculus
-	memset(hd,0,sizeof(struct homun_data));
-	hd->master=sd;	//pointer from homunculus to master
-	hd->id=atoi(sql_row[0]);
-	hd->class_=atoi(sql_row[1]);
-	hd->level=atoi(sql_row[3]);
-	hd->battle_status.hp=atoi(sql_row[6]);
-	hd->battle_status.sp=atoi(sql_row[7]);
-	hd->exp=atoi(sql_row[4]);
-	hd->hunger_rate=atoi(sql_row[5]);
-	hd->hskill[0].level=atoi(sql_row[8]);
-	hd->hskill[1].level=atoi(sql_row[9]);
-	hd->hskill[2].level=atoi(sql_row[10]);
-	hd->hskill[3].level=atoi(sql_row[11]);
-	hd->skillpts=atoi(sql_row[12]);
-	hd->exp_next=hexptbl[hd->level-1];
-	strncpy(hd->name,sql_row[2],NAME_LENGTH);
-	mysql_free_result(sql_res);
-	merc_load_sub(hd, sd);
-}
-#else 
-void merc_load(struct map_session_data *sd)
+	if ( flag == 2 ) {
+		sh->hp = 1 ; 
+		sd->homunculus.alive = 1 ;
+	}
+	if(sd->homunculus.alive && sh->vaporize!=1)
+	{
+		merc_hom_data_init(sd);
+		
+		if ( sd->hd && sd->bl.prev != NULL) {
+			map_addblock(&sd->hd->bl);
+			clif_spawn(&sd->hd->bl);
+			clif_hominfo(sd,1);
+			clif_hominfo(sd,0); // send this x2. dunno why, but kRO does that [blackhole89]
+			clif_homskillinfoblock(sd);
+			clif_hominfo(sd,0);
+			clif_send_homdata(sd,SP_ACK,0);
+		}
+	}
+
+	return 1;
+
+}
+
+int merc_create_homunculus(struct map_session_data *sd, int class_)
 {
-	struct homun_data *hd;
-	int id,charid,class_,level,exp,hunger,hp,sp;
-	char name[24];
-	FILE *fl=fopen("save/homunculus.txt","r");
-	sd->hd=NULL;
-
-	if(!fl) return; //Unable to open file.	
-	ShowInfo("Looking up Homunculus for %d...\n",sd->char_id);
-	do {
-		fscanf(fl,"%d,%d,%d,%s ,%d,%d,%d,%d,%d\n",&id,&charid,&class_,name,&level,&exp,&hunger,&hp,&sp);
-		ShowInfo("%d",charid);
-		if(charid==sd->char_id) break;
-	} while(charid!=0);
-	if (!charid)
-		return;	//none found
-	ShowInfo("found it!\n");
+	int i=0 ;
+
+	nullpo_retr(1, sd);
+
+	i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
+	if(i < 0) {
+		sd->status.hom_id = 0;
+		return 0;
+	}
+	memcpy(sd->homunculus.name, homunculus_db[i].name, NAME_LENGTH-1);
+
+	sd->homunculus.class_ = class_;
+	sd->homunculus.level=1;
+	sd->homunculus.intimacy = 21;
+	sd->homunculus.hunger = 32;
+	sd->homunculus.exp = 0;
+	sd->homunculus.rename_flag = 0;
+	sd->homunculus.skillpts = 0;
+	sd->homunculus.char_id = sd->status.char_id;
+	sd->homunculus.vaporize = 0; // albator
+	sd->homunculus.alive = 1 ;
 	
-	//dummy code
-	hd=(struct homun_data *) aCalloc(1, sizeof(struct homun_data));
-	sd->hd=hd;		//pointer from master to homunculus
-	memset(hd,0,sizeof(struct homun_data));
-	hd->master=sd;	//pointer from homunculus to master
-	hd->id=id;
-	hd->class_=class_;
-	hd->level=level;
-	hd->exp=exp;
-	hd->hunger_rate=hunger;
-	hd->battle_status.hp=hp;
-	hd->battle_status.sp=sp;
-	hd->exp_next=hexptbl[hd->level-1];
-	strncpy(hd->name,name,NAME_LENGTH);
-	merc_load_sub(hd, sd);
-}
-#endif	
-
-int merc_create_homunculus(struct map_session_data *sd,int id,int m,int x,int y)
-{
-/*	struct homun_data *hd;
-	//dummy code
-	hd=(struct homun_data *) aCalloc(1, sizeof(struct homun_data));
-	sd->hd=hd;		//pointer from master to homunculus
-	memset(hd,0,sizeof(struct homun_data));
-	hd->master=sd;	//pointer from homunculus to master
-	hd->class_=id;
-	hd->speed=0x96;
-	hd->level=1;
-	hd->bl.m=m;
-	hd->bl.x=hd->to_x=x;
-	hd->bl.y=hd->to_y=y;
-	hd->to_x+=2;
-	hd->bl.type=BL_HOMUNCULUS;
-	hd->bl.id= npc_get_new_npc_id();
-	hd->bl.prev=NULL;
-	hd->bl.next=NULL;
-	map_addiddb(&hd->bl);
-	hd->max_hp=500;
-	hd->hp=400;
-	hd->max_sp=300;
-	hd->sp=200;
-	hd->atk=15;
-	hd->matk=2;
-	hd->hit=3;
-	hd->crit=90;
-	hd->def=5;
-	hd->mdef=6;
-	hd->flee=7;
-	hd->exp=10;
-	hd->exp_next=100;
-	hd->hunger_rate=32;
-	hd->walktimer=-1;
-	memcpy(hd->name,"Homunculus\0",11);
-	merc_calc_stats(hd);
-	hd->attackabletime=0;
-
-	merc_save(hd);
-
-	clif_spawnhomun(hd);
-	clif_homunack(sd);
-	clif_homuninfo(sd);
-	clif_homuninfo(sd);*/ // send this x2. dunno why, but kRO does that [blackhole89]
+	sd->homunculus.hp = 10 ;
+	sd->homunculus.sp = 0 ;
+	sd->homunculus.max_hp = homunculus_db[i].basemaxHP ;
+	sd->homunculus.max_sp = homunculus_db[i].basemaxSP ;
+	sd->homunculus.str = homunculus_db[i].baseSTR ;
+	sd->homunculus.agi = homunculus_db[i].baseAGI ;
+	sd->homunculus.vit = homunculus_db[i].baseVIT;
+	sd->homunculus.int_ = homunculus_db[i].baseINT ;
+	sd->homunculus.dex = homunculus_db[i].baseDEX ;
+	sd->homunculus.luk = homunculus_db[i].baseLUK ;
+	sd->homunculus.str *= 10 ;
+	sd->homunculus.agi *= 10 ;
+	sd->homunculus.vit *= 10 ;
+	sd->homunculus.int_ *= 10 ;
+	sd->homunculus.dex *= 10 ;
+	sd->homunculus.luk *= 10 ;
+	
+	for(i=0;i<MAX_HOMUNSKILL;i++)
+		sd->homunculus.hskill[i].id = sd->homunculus.hskill[i].lv = sd->homunculus.hskill[i].flag = 0;
+
+	intif_homunculus_create(sd->status.account_id, &sd->homunculus); // request homunculus creation
+	
+	return 1;
+}
+
+int merc_hom_revive(struct map_session_data *sd, int per)
+{
+	nullpo_retr(0, sd);
+
+		sd->homunculus.alive = 1;
+		merc_hom_data_init(sd);
+
+		if ( sd->hd && sd->bl.prev != NULL) {
+			sd->homunculus.hp = sd->hd->base_status.hp = sd->hd->battle_status.hp = 1 ;
+			status_heal(&sd->hd->bl, sd->homunculus.max_hp*per/100, 0, 1) ;
+			map_addblock(&sd->hd->bl);
+			clif_spawn(&sd->hd->bl);
+			clif_send_homdata(sd,SP_ACK,0);
+			clif_hominfo(sd,1);
+			clif_hominfo(sd,0);
+			clif_homskillinfoblock(sd);
+			clif_specialeffect(&sd->hd->bl,77,AREA) ;	//resurrection angel
+		}
+
+	return 1 ;
+}
+
+int read_homunculusdb()
+{
+	FILE *fp;
+	char line[1024], *p;
+	int i,k,l; 
+	int j=0;
+	int c = 0 ;
+	int lines;
+	char *filename[]={"homunculus_db.txt","homunculus_db2.txt"};
+	char *str[36],*h,*nh;
+
+
+	j = 0;
+	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==NULL){
+			if(i>0)
+				continue;
+			ShowError("can't read %s\n",line);
+			return -1;
+		}
+		lines = 0;
+		while(fgets(line,sizeof(line)-1,fp) && j < MAX_HOMUNCULUS_CLASS){
+			
+			lines++;
+
+			if(line[0] == '/' && line[1] == '/')
+				continue;
+
+			for(k=0,h=line;k<36;k++){
+				if((nh=strchr(h,','))!=NULL){
+					str[k]=h;
+					*nh=0;
+					h=nh+1;
+				} else {
+					str[k]=h;
+					h+=strlen(h);
+				}
+			}
+
+			if(atoi(str[0]) < 6001 || atoi(str[0]) > 6099)
+				continue;
+		
+			//Class,Homunculus,HP,SP,ATK,MATK,HIT,CRI,DEF,MDEF,FLEE,ASPD,STR,AGI,VIT,INT,DEX,LUK
+			homunculus_db[j].class_ = atoi(str[0]);
+			memcpy(homunculus_db[j].name,str[1],NAME_LENGTH-1);
+			homunculus_db[j].basemaxHP = atoi(str[2]);
+			homunculus_db[j].basemaxSP = atoi(str[3]);
+			homunculus_db[j].baseSTR = atoi(str[4]);
+			homunculus_db[j].baseAGI = atoi(str[5]);
+			homunculus_db[j].baseVIT = atoi(str[6]);
+			homunculus_db[j].baseINT = atoi(str[7]);
+			homunculus_db[j].baseDEX = atoi(str[8]);
+			homunculus_db[j].baseLUK = atoi(str[9]);
+			homunculus_db[j].baseIntimacy = atoi(str[10]);
+			homunculus_db[j].baseHungry = atoi(str[11]);
+			homunculus_db[j].hungryDelay = atoi(str[12]);
+			homunculus_db[j].foodID = atoi(str[13]);
+			homunculus_db[j].gminHP = atoi(str[14]);
+			homunculus_db[j].gmaxHP = atoi(str[15]);
+			homunculus_db[j].gminSP = atoi(str[16]);
+			homunculus_db[j].gmaxSP = atoi(str[17]);
+			homunculus_db[j].gminSTR = atoi(str[18]);
+			homunculus_db[j].gmaxSTR = atoi(str[19]);
+			homunculus_db[j].gminAGI = atoi(str[20]);
+			homunculus_db[j].gmaxAGI = atoi(str[21]);
+			homunculus_db[j].gminVIT = atoi(str[22]);
+			homunculus_db[j].gmaxVIT = atoi(str[23]);
+			homunculus_db[j].gminINT = atoi(str[24]);
+			homunculus_db[j].gmaxINT = atoi(str[25]);
+			homunculus_db[j].gminDEX = atoi(str[26]);
+			homunculus_db[j].gmaxDEX = atoi(str[27]);
+			homunculus_db[j].gminLUK = atoi(str[28]);
+			homunculus_db[j].gmaxLUK = atoi(str[29]);
+			homunculus_db[j].evo_class = atoi(str[30]);
+			homunculus_db[j].baseASPD = atoi(str[31]);
+			homunculus_db[j].size = atoi(str[32]);
+			homunculus_db[j].race = atoi(str[33]);
+			homunculus_db[j].element = atoi(str[34]);
+			homunculus_db[j].accessID = atoi(str[35]);
+
+			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]);
+	}
+
+	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)-1, fp)){
+		char *split[50];
+		int f=0, m=3;
+		if(line[0]=='/' && line[1]=='/')
+			continue;
+		for(j=0,p=line;j<14 && p;j++){
+			split[j]=p;
+			p=strchr(p,',');
+			if(p) *p++=0;
+		}
+		if(j<13)
+			continue;
+		if (j == 14) {
+			f=1;	// MinJobLvl has been added
+			m++;
+		}
+		// check for bounds [celest]
+		c = atoi(split[0]) ;
+		l = c - 6001 ;
+		if ( l >= MAX_HOMUNCULUS_CLASS )
+			continue;
+		k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
+		for(j = 0; j < MAX_SKILL_TREE && hskill_tree[l][j].id && hskill_tree[l][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, l);
+			continue;
+		}
+		hskill_tree[l][j].id=k;
+		hskill_tree[l][j].max=atoi(split[2]);
+		if (f) hskill_tree[l][j].joblv=atoi(split[3]);
+
+		for(k=0;k<5;k++){
+			hskill_tree[l][j].need[k].id=atoi(split[k*2+m]);
+			hskill_tree[l][j].need[k].lv=atoi(split[k*2+m+1]);
+		}
+	}
+	fclose(fp);
+	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","homun_skill_tree.txt");
+
+
 	return 0;
 }
 

+ 72 - 7
src/map/mercenary.h

@@ -1,13 +1,78 @@
 // Homunculus and future Mercenary system code go here [Celest]
+// implemented by [orn]
+struct homunculus_db {
+	int class_ ;
+	char name[NAME_LENGTH];
+	int basemaxHP ;
+	int basemaxSP ;
+	int baseSTR ;
+	int baseAGI ;
+	int baseVIT ;
+	int baseINT ;
+	int baseDEX ;
+	int baseLUK ;
+	int foodID ;
+	int baseIntimacy ;
+	short baseHungry ;
+	long hungryDelay ;
+	int gminHP ;
+	int gmaxHP ;
+	int gminSP ;
+	int gmaxSP ;
+	int gminSTR ;
+	int gmaxSTR ;
+	int gminAGI ;
+	int gmaxAGI ;
+	int gminVIT ;
+	int gmaxVIT ;
+	int gminINT ;
+	int gmaxINT ;
+	int gminDEX ;
+	int gmaxDEX ;
+	int gminLUK ;
+	int gmaxLUK ;
+	int evo_class ;
+	int baseASPD ;
+	//short size ;
+	//short race ;
+	//short element ;
+	unsigned char element, race, size; // albator
+	int accessID ;
+};
+extern struct homunculus_db homuncumlus_db[MAX_HOMUNCULUS_CLASS];
+enum { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD };
+enum {
+	SP_ACK 	= 0x00,
+	SP_INTIMATE 	= 0x100,
+	SP_HUNGRY 		= 0x200
+};
 
 int do_init_merc (void);
+int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag); //albator
+void merc_load_sub(struct homun_data *hd, struct map_session_data *sd);
 void merc_load_exptables(void);
-char *merc_skill_get_name(int id);
+char *merc_hom_skill_get_name(int id);
 void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp);
-int merc_dead(struct homun_data *hd, struct block_list *src);
-void merc_skillup(struct map_session_data *sd,short skillnum);
-int merc_gainexp(struct homun_data *hd,int exp);
-void merc_heal(struct homun_data *hd,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 map_session_data *sd) ;
+int merc_hom_checkskill(struct map_session_data *sd,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) ;
+int merc_hom_heal(struct homun_data *hd,int hp,int sp);
+int merc_hom_delete(struct homun_data *hd, int flag) ;
+int merc_hom_revive(struct map_session_data *sd, int per);
 void merc_save(struct homun_data *hd);
-void merc_load(struct map_session_data *sd);
-int merc_create_homunculus(struct map_session_data *sd,int id,int m,int x,int y);
+int merc_call_homunculus(struct map_session_data *sd);
+int merc_create_homunculus(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_natural_heal_timer_delete(struct homun_data *hd);
+#define merc_checkoverhp(hd) (hd->battle_status.hp == hd->battle_status.max_hp)
+#define merc_checkoversp(hd) (hd->battle_status.sp == hd->battle_status.max_sp)
+#define merc_stop_walking(hd, type) { if((hd)->ud.walktimer != -1) unit_stop_walking(&(hd)->bl, type); }
+#define merc_stop_attack(hd) { if((hd)->ud.attacktimer != -1) unit_stop_attack(&(hd)->bl); hd->ud.target = 0; }
+int read_homunculusdb(void);

+ 53 - 18
src/map/mob.c

@@ -22,6 +22,7 @@
 #include "pc.h"
 #include "status.h"
 #include "mob.h"
+#include "mercenary.h"	//[orn]
 #include "guild.h"
 #include "itemdb.h"
 #include "skill.h"
@@ -761,6 +762,7 @@ int mob_target(struct mob_data *md,struct block_list *bl,int dist)
  */
 static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap)
 {
+	struct map_session_data *sd;
 	struct mob_data *md;
 	struct block_list **target;
 	int dist;
@@ -783,9 +785,15 @@ static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap)
 	switch (bl->type)
 	{
 	case BL_PC:
+	{
 		if (((TBL_PC*)bl)->state.gangsterparadise &&
 			!(status_get_mode(&md->bl)&MD_BOSS))
 			return 0; //Gangster paradise protection.
+		sd = (TBL_PC*)bl;	//[orn] monster target homunculus while hunting
+		if (sd->hd && sd->homunculus.alive && (distance_bl(&md->bl,  &sd->hd->bl ) < md->db->range2 ) )	//
+			return 0; //Gangster paradise protection.
+	}
+	case BL_HOMUNCULUS:	//[orn]
 	case BL_MOB:
 		if((dist=distance_bl(&md->bl, bl)) < md->db->range2 &&
 			((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) &&
@@ -824,6 +832,7 @@ static int mob_ai_sub_hard_changechase(struct block_list *bl,va_list ap)
 	switch (bl->type)
 	{
 	case BL_PC:
+	case BL_HOMUNCULUS:	//[orn]
 	case BL_MOB:
 		if(check_distance_bl(&md->bl, bl, md->status.rhw.range) &&
 			battle_check_range (&md->bl, bl, md->status.rhw.range)
@@ -1167,13 +1176,13 @@ static int mob_ai_sub_hard(struct block_list *bl,va_list ap)
 		(mode&MD_ANGRY && md->state.skillstate == MSS_FOLLOW)
 	) {
 		map_foreachinrange (mob_ai_sub_hard_activesearch, &md->bl,
-			view_range, md->special_state.ai?BL_CHAR:BL_PC, md, &tbl);
+			view_range, md->special_state.ai?BL_CHAR:BL_PC|BL_HOMUNCULUS, md, &tbl);	//[orn]
 		if(!tbl && mode&MD_ANGRY && !md->state.aggressive)
 			md->state.aggressive = 1; //Restore angry state when no targets are visible.
 	} else if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW)) {
 		search_size = view_range<md->status.rhw.range ? view_range:md->status.rhw.range;
 		map_foreachinrange (mob_ai_sub_hard_changechase, &md->bl,
-				search_size, (md->special_state.ai?BL_CHAR:BL_PC), md, &tbl);
+				search_size, (md->special_state.ai?BL_CHAR:BL_PC|BL_HOMUNCULUS), md, &tbl);	//[orn]
 	}
 
 	if (tbl)
@@ -1596,7 +1605,16 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage)
 		case BL_PC: 
 		{
 			struct map_session_data *sd = (TBL_PC*)src;
-			id = sd->status.char_id;
+//			id = sd->status.char_id;
+			id = sd->bl.id;	//[orn]
+			if(rand()%1000 < 1000/md->attacked_players)
+				md->attacked_id = src->id;
+			break;
+		}
+		case BL_HOMUNCULUS:	//[orn]
+		{
+			struct homun_data *hd = (TBL_HOMUNCULUS*)src;
+			id = hd->bl.id;
 			if(rand()%1000 < 1000/md->attacked_players)
 				md->attacked_id = src->id;
 			break;
@@ -1605,7 +1623,8 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage)
 		{
 			struct pet_data *pd = (TBL_PET*)src;
 			if (battle_config.pet_attack_exp_to_master) {
-				id = pd->msd->status.char_id;
+//				id = pd->msd->status.char_id;
+				id = pd->msd->bl.id;	//[orn]
 				damage=(damage*battle_config.pet_attack_exp_rate)/100; //Modify logged damage accordingly.
 			}
 			//Let mobs retaliate against the pet's master [Skotlex]
@@ -1618,7 +1637,8 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage)
 			struct mob_data* md2 = (TBL_MOB*)src;
 			if(md2->special_state.ai && md2->master_id) {
 				struct map_session_data* msd = map_id2sd(md2->master_id);
-				if (msd) id = msd->status.char_id;
+//				if (msd) id = msd->status.char_id;
+				if (msd) id = msd->bl.id;	//[orn]
 			}
 			if(rand()%1000 < 1000/md->attacked_players)
 			{	//Let players decide whether to retaliate versus the master or the mob. [Skotlex]
@@ -1670,8 +1690,9 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage)
 int mob_dead(struct mob_data *md, struct block_list *src, int type)
 {
 	struct status_data *status;
-	struct map_session_data *sd = NULL,*tmpsd[DAMAGELOG_SIZE],
+	struct map_session_data *sd = NULL,/**tmpsd[DAMAGELOG_SIZE],*/
 		*mvp_sd = NULL, *second_sd = NULL,*third_sd = NULL;
+	struct block_list *tmpbl[DAMAGELOG_SIZE] ;	//[orn]
 	
 	struct {
 		struct party_data *p;
@@ -1706,7 +1727,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 
 	map_freeblock_lock();
 
-	memset(tmpsd,0,sizeof(tmpsd));
+	memset(tmpbl,0,sizeof(tmpbl));
 	memset(pt,0,sizeof(pt));
 
 	if(src && src->type == BL_MOB)
@@ -1735,16 +1756,19 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 
 	for(temp=0,i=0,mvp_damage=0;i<DAMAGELOG_SIZE && md->dmglog[i].id;i++)
 	{
-		tmpsd[temp] = map_charid2sd(md->dmglog[i].id);
-		if(tmpsd[temp] == NULL)
+		tmpbl[temp] = (struct block_list*)map_id2bl(md->dmglog[i].id);
+		if(tmpbl[temp] == NULL)
 			continue;
-		if(tmpsd[temp]->bl.m != md->bl.m || pc_isdead(tmpsd[temp]))
+		if( (tmpbl[temp])->m != md->bl.m || status_isdead(tmpbl[temp]))
 			continue;
 
 		if(mvp_damage<(unsigned int)md->dmglog[i].dmg){
 			third_sd = second_sd;
 			second_sd = mvp_sd;
-			mvp_sd=tmpsd[temp];
+			if ( (tmpbl[temp])->type == BL_HOMUNCULUS ) {
+				mvp_sd = (struct map_session_data *) ((struct homun_data *)tmpbl[temp])->master ;
+			} else
+				mvp_sd=(struct map_session_data *)tmpbl[temp];
 			mvp_damage=md->dmglog[i].dmg;
 		}
 
@@ -1758,7 +1782,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 		(!map[md->bl.m].flag.nobaseexp || !map[md->bl.m].flag.nojobexp) //Gives Exp
 	) { //Experience calculation.
 
-	for(i=0;i<DAMAGELOG_SIZE && tmpsd[i];i++){
+	for(i=0;i<DAMAGELOG_SIZE && tmpbl[i];i++){
 		int flag=1,zeny=0;
 		unsigned int base_exp,job_exp;
 		double per; //Your share of the mob's exp
@@ -1835,7 +1859,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 				job_exp = 1;
 		}
  		
-		if((temp=tmpsd[i]->status.party_id)>0)
+		if( (tmpbl[i]->type == BL_PC) && (temp = ((struct map_session_data *)tmpbl[i])->status.party_id )>0 )	//only pc have party [orn]
 		{
 			int j;
 			for(j=0;j<pnum && pt[j].id!=temp;j++); //Locate party.
@@ -1866,11 +1890,22 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 				flag=0;
 			}
 		}
-		if(flag) {
-			if(base_exp || job_exp)
-				pc_gainexp(tmpsd[i], &md->bl, base_exp,job_exp);
-			if(zeny) // zeny from mobs [Valaris]
-				pc_getzeny(tmpsd[i], zeny);
+		if(flag) {	//homunculus aren't considered in party [orn]
+			switch( (tmpbl[i])->type ) {
+				case BL_PC:
+					if(base_exp || job_exp)
+						pc_gainexp((struct map_session_data *)tmpbl[i], &md->bl, base_exp,job_exp);
+					if(zeny) // zeny from mobs [Valaris]
+						pc_getzeny((struct map_session_data *)tmpbl[i], zeny);
+					break ;
+				case BL_HOMUNCULUS:
+					if(base_exp)
+						merc_hom_gainexp((struct homun_data *)tmpbl[i], base_exp);
+					if(zeny)	//homunculus give zeny to master
+						pc_getzeny((struct map_session_data *)((struct homun_data *)tmpbl[i])->master, zeny);
+					break ;
+
+			}
 		}
 	}
 	

+ 26 - 0
src/map/pc.c

@@ -24,6 +24,7 @@
 #include "npc.h"
 #include "mob.h"
 #include "pet.h"
+#include "mercenary.h"	//orn
 #include "itemdb.h"
 #include "script.h"
 #include "battle.h"
@@ -676,6 +677,10 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t
 	if (sd->status.pet_id > 0)
 		intif_request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id);
 
+	// Homunculus [albator]
+	if (sd->status.hom_id > 0)	
+			intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
+
 	// パ?ティ、ギルドデ?タの要求
 	if (sd->status.party_id > 0 && party_search(sd->status.party_id) == NULL)
 		party_request_info(sd->status.party_id);
@@ -3257,6 +3262,10 @@ int pc_setpos(struct map_session_data *sd,unsigned short mapindex,int x,int y,in
 					unit_remove_map(&sd->pd->bl, clrtype);
 					intif_save_petdata(sd->status.account_id,&sd->pet);
 				}
+				if(sd->status.hom_id > 0 && sd->hd) {	//orn
+					unit_remove_map(&sd->hd->bl, clrtype);
+					intif_homunculus_requestsave(sd->status.account_id, &sd->homunculus);
+				}
 				chrif_save(sd,2);
 				chrif_changemapserver(sd, mapindex, x, y, ip, (short)port);
 				return 0;
@@ -3289,6 +3298,8 @@ int pc_setpos(struct map_session_data *sd,unsigned short mapindex,int x,int y,in
 		unit_remove_map(&sd->bl, clrtype);
 		if(sd->status.pet_id > 0 && sd->pd)
 			unit_remove_map(&sd->pd->bl, clrtype);
+		if(sd->status.hom_id > 0 && sd->hd)	//orn
+			unit_remove_map(&sd->hd->bl, clrtype);
 		clif_changemap(sd,map[m].index,x,y); // [MouseJstr]
 	} else if(sd->state.auth)
 		//Tag player for rewarping after map-loading is done. [Skotlex]
@@ -3306,6 +3317,13 @@ int pc_setpos(struct map_session_data *sd,unsigned short mapindex,int x,int y,in
 		sd->pd->ud.dir = sd->ud.dir;
 	}
 
+	if(sd->status.hom_id > 0 && sd->hd ) {	//orn
+		sd->hd->bl.m = m;
+		sd->hd->bl.x = sd->hd->ud.to_x = x;
+		sd->hd->bl.y = sd->hd->ud.to_y = y;
+		sd->hd->ud.dir = sd->ud.dir;
+	}
+
 	return 0;
 }
 
@@ -4717,6 +4735,14 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 			pet_unlocktarget(sd->pd);
 	}
 
+	if(sd->status.hom_id > 0 && sd->hd)	//orn
+	{
+		merc_stop_walking(sd->hd, 1) ;
+		merc_stop_attack(sd->hd) ;
+		merc_hom_delete(sd->hd,0);
+	}
+			
+
 	// Leave duel if you die [LuzZza]
 	if(battle_config.duel_autoleave_when_die) {
 		if(sd->duel_group > 0)

+ 20 - 2
src/map/script.c

@@ -36,6 +36,7 @@
 #include "mob.h"
 #include "npc.h"
 #include "pet.h"
+#include "mercenary.h"	//[orn]
 #include "intif.h"
 #include "skill.h"
 #include "chat.h"
@@ -441,6 +442,7 @@ int buildin_getvariableofnpc(struct script_state *st);
 // [blackhole89] -->
 int buildin_warpportal(struct script_state *st);
 // <-- [blackhole89]
+int buildin_homunculus_evolution(struct script_state *st) ;	//[orn]
 void push_val(struct script_stack *stack,int type,int val);
 int run_func(struct script_state *st);
 
@@ -785,6 +787,7 @@ struct {
 	// [blackhole89] -->
 	{buildin_warpportal,"warpportal","iisii"},
 	// <--- [blackhole89]
+	{buildin_homunculus_evolution,"homevolution",""},	//[orn]
 	{NULL,NULL,NULL},
 };
 
@@ -6516,6 +6519,21 @@ int buildin_catchpet(struct script_state *st)
 	return 0;
 }
 
+/*==========================================
+ * [orn]
+ *------------------------------------------
+ */
+int buildin_homunculus_evolution(struct script_state *st)
+{
+	struct map_session_data *sd;
+	sd=script_rid2sd(st);
+	if ( sd->hd && sd->hd->homunculusDB->evo_class && sd->homunculus.intimacy > 91000 ) {
+		return merc_hom_evolution(sd->hd) ;
+	}
+	clif_emotion(&sd->hd->bl, 4) ;	//swt
+	return 0;
+}
+
 /*==========================================
  *Œg‘Ñ—‘›z‰»‹@Žg—p
  *------------------------------------------
@@ -10388,7 +10406,7 @@ int buildin_rid2name(struct script_state *st){
 				push_str(st->stack,C_CONSTSTR,((struct pet_data *)bl)->name);
 				break;
 			case BL_HOMUNCULUS:
-				push_str(st->stack,C_CONSTSTR,((struct homun_data *)bl)->name);
+				push_str(st->stack,C_CONSTSTR,((struct homun_data *)bl)->master->homunculus.name);
 				break;
 			default:
 				ShowError("buildin_rid2name: BL type unknown.\n");
@@ -10810,7 +10828,7 @@ int buildin_unittalk(struct script_state *st)
 				memcpy(message, ((TBL_NPC *)bl)->name, NAME_LENGTH);
 				break;
 			case BL_HOMUNCULUS:
-				memcpy(message, ((TBL_HOMUNCULUS *)bl)->name, NAME_LENGTH);
+				memcpy(message, ((TBL_HOMUNCULUS *)bl)->master->homunculus.name, NAME_LENGTH);
 				break;
 			case BL_PET:
 				memcpy(message, ((TBL_PET *)bl)->name, NAME_LENGTH);

+ 379 - 3
src/map/skill.c

@@ -20,6 +20,7 @@
 #include "pc.h"
 #include "status.h"
 #include "pet.h"
+#include "mercenary.h"	//[orn]
 #include "mob.h"
 #include "battle.h"
 #include "party.h"
@@ -706,6 +707,8 @@ const char*	skill_get_name( int id ){
 		return "UNKNOWN_SKILL";
 	if (id >= GD_SKILLBASE)
 		id = GD_SKILLRANGEMIN + id - GD_SKILLBASE;
+	if (id >= HM_SKILLBASE)	//[orn]
+		id = HM_SKILLRANGEMIN + id - HM_SKILLBASE;
 	if (id < 1 || id > MAX_SKILL_DB || skill_db[id].name==NULL)
 		return "UNKNOWN_SKILL"; //Can't use skill_chk because we return a string.
 	return skill_db[id].name; 
@@ -809,6 +812,8 @@ int skill_calc_heal (struct block_list *bl, int skill_lv)
 	if(bl->type == BL_PC && (skill = pc_checkskill((TBL_PC*)bl, HP_MEDITATIO)) > 0)
 		heal += heal * skill * 2 / 100;
 
+	if(bl->type == BL_HOMUNCULUS && (skill = merc_hom_checkskill( ((TBL_HOMUNCULUS*)bl)->master, HLIF_BRAIN)) > 0)	//[orn]
+		heal += heal * skill * 2 / 100;
 	return heal;
 }
 
@@ -846,6 +851,8 @@ int skillnotok (int skillid, struct map_session_data *sd)
 
 	if (i >= GD_SKILLBASE)
 		i = GD_SKILLRANGEMIN + i - GD_SKILLBASE;
+	if (i >= HM_SKILLBASE)	//[orn]
+		i = HM_SKILLRANGEMIN + i - HM_SKILLBASE;
 	
 	if (i > MAX_SKILL || i < 0)
 		return 1;
@@ -919,6 +926,49 @@ int skillnotok (int skillid, struct map_session_data *sd)
 	return (map[sd->bl.m].flag.noskill);
 }
 
+// [orn] - skill ok to cast? and when?	//homunculus
+int skillnotok_hom (int skillid, struct homun_data *hd)
+{	
+	int i = skillid;
+	nullpo_retr (1, hd);
+	//if (sd == 0)
+		//return 0; 
+		//return 1;
+	// I think it was meant to be "no skills allowed when not a valid sd"
+	
+	if (skillid >= GD_SKILLRANGEMIN && skillid <= GD_SKILLRANGEMAX)
+		return 1;
+
+	if (i >= GD_SKILLBASE)
+		i = GD_SKILLRANGEMIN + i - GD_SKILLBASE;
+	if (i >= HM_SKILLBASE)	//[orn]
+		i = HM_SKILLRANGEMIN + i - HM_SKILLBASE;
+	
+	if (i > MAX_SKILL || i < 0)
+		return 1;
+	
+	if (hd->blockskill[i] > 0)
+		return 1;
+
+	// Check skill restrictions [Celest]
+	if(!map_flag_vs(hd->bl.m) && skill_get_nocast (skillid) & 1)
+		return 1;
+	if(map[hd->bl.m].flag.pvp) {
+		if(!battle_config.pk_mode && skill_get_nocast (skillid) & 2)
+			return 1;
+		if(battle_config.pk_mode && skill_get_nocast (skillid) & 16)
+			return 1;
+	}
+	if(map_flag_gvg(hd->bl.m) && skill_get_nocast (skillid) & 4)
+		return 1;
+	if(agit_flag && skill_get_nocast (skillid) & 8)
+		return 1;
+	if(map[hd->bl.m].flag.restricted && map[hd->bl.m].zone && skill_get_nocast (skillid) & (8*map[hd->bl.m].zone))
+		return 1;
+
+	return (map[hd->bl.m].flag.noskill);
+}
+
 /* 繧ケ繧ュ繝ォ繝ヲ繝九ャ繝医�驟咲スョ諠��ア繧定ソ斐☆ */
 struct skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT];
 int firewall_unit_pos;
@@ -2263,6 +2313,99 @@ int skill_guildaura_sub (struct block_list *bl, va_list ap)
 	return 0;
 }
 
+/*==========================================
+ * [orn]
+ * Checks that you have the requirements for casting a skill for homunculus.
+ * Flag:
+ * &1: finished casting the skill (invoke hp/sp/item consumption)
+ * &2: picked menu entry (Warp Portal, Teleport and other menu based skills)
+ *------------------------------------------
+ */
+static int skill_check_condition_hom (struct homun_data *hd, int skill, int lv, int type)
+{
+	struct status_data *status;
+	struct status_change *sc;
+	int j,hp,sp,hp_rate,sp_rate,state,mhp ;
+
+	nullpo_retr(0, hd);
+
+	if (lv <= 0) return 0;
+
+	status = &hd->battle_status;
+	sc = &hd->sc;
+	if (!sc->count)
+		sc = NULL;
+	
+	// for the guild skills [celest]
+	if (skill >= HM_SKILLBASE)	//[orn]
+		j = HM_SKILLRANGEMIN + skill - HM_SKILLBASE;
+	else
+		j = skill;
+	if (j < 0 || j >= MAX_SKILL_DB)
+  		return 0;
+	//Code speedup, rather than using skill_get_* over and over again.
+	if (lv < 1 || lv > MAX_SKILL_LEVEL)
+		return 0;
+	hp = skill_db[j].hp[lv-1];
+	sp = skill_db[j].sp[lv-1];
+	hp_rate = skill_db[j].hp_rate[lv-1];
+	sp_rate = skill_db[j].sp_rate[lv-1];
+	state = skill_db[j].state;
+	mhp = skill_db[j].mhp[lv-1];
+	if(mhp > 0)
+		hp += (status->max_hp * mhp)/100;
+	if(hp_rate > 0)
+		hp += (status->hp * hp_rate)/100;
+	else
+		hp += (status->max_hp * (-hp_rate))/100;
+	if(sp_rate > 0)
+		sp += (status->sp * sp_rate)/100;
+	else
+		sp += (status->max_sp * (-sp_rate))/100;
+
+	switch(skill) { // Check for cost reductions due to skills & SCs
+		case HFLI_SBR44:
+			if(hd->master->homunculus.intimacy < 200)
+				return 0;
+			break;
+		case HVAN_EXPLOSION:
+			if(hd->master->homunculus.intimacy < battle_config.hvan_explosion_intimate)
+				return 0;
+			break;
+	}
+	if(!(type&2)){
+		if( hp>0 && status->hp <= (unsigned int)hp) {
+			clif_skill_fail(hd->master,skill,2,0);
+			return 0;
+		}
+		if( sp>0 && status->sp < (unsigned int)sp) {
+			clif_skill_fail(hd->master,skill,1,0);
+			return 0;
+		}
+	}
+
+	switch(state) {
+	case ST_MOVE_ENABLE:
+		//Check only on begin casting. [Skotlex]
+		if(!type && !unit_can_move(&hd->bl)) {
+			clif_skill_fail(hd->master,skill,0,0);
+			return 0;
+		}
+		break;
+	}
+
+	if(!(type&1))
+		return 1;
+
+	if(type&2)
+		return 1;
+
+	if(sp || hp)
+		status_zap(&hd->bl, hp, sp);
+
+	return 1;
+}
+
 /*=========================================================================
  * 遽�峇繧ケ繧ュ繝ォ菴ソ逕ィ蜃ヲ逅�ー丞�縺代%縺薙°繧�
  */
@@ -2454,6 +2597,7 @@ static int skill_reveal_trap (struct block_list *bl, va_list ap)
 int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag)
 {
 	struct map_session_data *sd = NULL, *tsd = NULL;
+	struct homun_data *hd = NULL ;	//[orn]
 	struct status_data *tstatus;
 	struct status_change *sc;
 
@@ -2472,6 +2616,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 		sd = (struct map_session_data *)src;
 	if (bl->type == BL_PC)
 		tsd = (struct map_session_data *)bl;
+	if (bl->type == BL_HOMUNCULUS)	//[orn]
+		hd = (struct homun_data *)bl;
 
 	if (status_isdead(src) || (src != bl && status_isdead(bl)))
 		return 1;
@@ -2568,6 +2714,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 	case TK_DOWNKICK:
 	case TK_COUNTER:
 	case ASC_BREAKER:
+	case HFLI_MOON:	//[orn]
+	case HFLI_SBR44:	//[orn]
 		skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
 		break;
 
@@ -2725,8 +2873,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 				skill_castend_damage_id);
 		}
 		break;
-
-
 	case SM_MAGNUM:
 		if(flag&1)
 			skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
@@ -2974,6 +3120,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 	case TF_THROWSTONE:
 	case NPC_SMOKING:
 	case NPC_SELFDESTRUCTION:
+	case HVAN_EXPLOSION:	//[orn]
 	case GS_FLING:
 	case NJ_ZENYNAGE:
 		skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag);
@@ -3112,6 +3259,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag)
 {
 	struct map_session_data *sd = NULL;
+	struct homun_data *hd = NULL;
 	struct map_session_data *dstsd = NULL;
 	struct status_data *sstatus, *tstatus;
 	struct status_change *tsc;
@@ -3129,6 +3277,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 
 	if (src->type == BL_PC) {
 		sd = (struct map_session_data *)src;
+	} else if (src->type == BL_HOMUNCULUS) {	//[orn]
+		hd = (struct homun_data *)src;
 	} else if (src->type == BL_MOB) {
 		md = (struct mob_data *)src;
 	}
@@ -3151,7 +3301,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 
 	//Check for undead skills that convert a no-damage skill into a damage one. [Skotlex]
 	switch (skillid) {
-		case AL_HEAL:
+		case HLIF_HEAL:	//[orn]
+			if ( !hd ) {
+			        clif_skill_fail(hd->master,skillid,0,0) ;
+			        break ;
+			}
+ 		case AL_HEAL:
 		case ALL_RESURRECTION:
 		case PR_ASPERSIO:
 			if (battle_check_undead(tstatus->race,tstatus->def_ele)) {
@@ -3184,6 +3339,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	map_freeblock_lock();
 	switch(skillid)
 	{
+	case HLIF_HEAL:	//[orn]
 	case AL_HEAL:				/* 繝偵�繝ォ */
 		{
 			int heal = skill_calc_heal(src, skilllv);
@@ -3971,6 +4127,19 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 			skill_castend_damage_id);
 		status_damage(src, src, sstatus->max_hp,0,0,1);
 		break;
+	case HVAN_EXPLOSION:	//[orn]
+		ShowDebug("skill_castend_nodamage_id : intimacy = %d\n", hd->master->homunculus.intimacy) ;	//ORN DEBUG
+		clif_skill_nodamage(src, src, skillid, -1, 1);
+		map_foreachinrange(skill_area_sub, bl,
+			skill_get_splash(skillid, skilllv), BL_CHAR,
+			src, skillid, skilllv, tick, flag|BCT_ENEMY,
+			skill_castend_damage_id);
+		if(hd){
+			hd->master->homunculus.intimacy = 200;
+			clif_send_homdata(hd->master,0x100,hd->master->homunculus.intimacy/100);
+		}
+		status_damage(src, src, sstatus->max_hp,0,0,1);
+		break;
 
 	/* 繝代�繝�ぅ繧ケ繧ュ繝ォ */
 	case AL_ANGELUS:		/* 繧ィ繝ウ繧ク繧ァ繝ゥ繧ケ */
@@ -5397,6 +5566,149 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 				pc_delspiritball(sd,1,0);
 		}
 		break;
+	
+	case AM_CALLHOMUN:	//[orn]
+	{
+		int i = 0;
+		if (sd && (sd->status.hom_id == 0 || sd->homunculus.vaporize == 1)) {
+			if (sd->status.hom_id == 0) {
+				i = pc_search_inventory(sd,7142);
+				if(i < 0) {
+					clif_skill_fail(sd,skillid,0,0);
+					break ;
+				}
+				pc_delitem(sd,i,1,0);
+			}
+			if (merc_call_homunculus(sd))
+				break;
+		}
+		
+		clif_skill_fail(sd,skillid,0,0);
+		break;
+	}
+	case AM_REST:	//[orn]
+	{
+		if (sd && sd->hd && ( sd->hd->battle_status.hp >= (sd->hd->battle_status.max_hp * 80 / 100 ) ) ) {
+			sd->homunculus.vaporize = 1;
+			merc_hom_delete(sd->hd, 0) ;
+		} else if ( sd ) 
+		{
+			clif_skill_fail(sd,skillid,0,0);
+		}
+		
+		break;
+	}
+	case AM_RESURRECTHOMUN:	//[orn]
+	{
+		if ( sd && sd->status.hom_id ) {
+			if( map_flag_gvg(bl->m) )
+			{	//No reviving in WoE grounds!
+				clif_skill_fail(sd,skillid,0,0);
+				break;
+			}
+			if ( sd->homunculus.alive == 0 ) {
+				int per = 10 * skilllv;
+	
+				if (merc_hom_revive(sd, per) )
+				{
+					clif_skill_nodamage(src,&sd->hd->bl,AM_RESURRECTHOMUN,skilllv,1);
+				} else {
+					clif_skill_fail(sd,skillid,0,0);
+				}
+			} else {	
+				clif_skill_fail(sd,skillid,0,0);
+			}
+
+		}
+		break;
+	}
+
+	case HAMI_CASTLE:	//[orn]
+		{
+			if(hd && rand()%100 < 20*skilllv)
+			{
+				int x,y;
+				struct walkpath_data wpd;
+				struct map_session_data *sd = hd->master;
+				if( path_search(&wpd,hd->bl.m,hd->bl.x,hd->bl.y,sd->bl.x,sd->bl.y,0) != 0 ) {
+					clif_skill_fail(sd,skillid,0,0);
+					break;
+				}
+
+				clif_skill_nodamage(&hd->bl,&sd->bl,skillid,skilllv,1);
+
+				x = hd->bl.x;
+				y = hd->bl.y;
+
+				unit_movepos(&hd->bl,sd->bl.x,sd->bl.y,0,0);
+				unit_movepos(&sd->bl,x,y,0,0);
+				clif_fixpos(&hd->bl) ;
+				clif_fixpos(&sd->bl) ;
+
+				map_foreachinarea(skill_chastle_mob_changetarget,hd->bl.m,
+						  hd->bl.x-AREA_SIZE,hd->bl.y-AREA_SIZE,
+						  hd->bl.x+AREA_SIZE,hd->bl.y+AREA_SIZE,
+						  BL_MOB,&hd->master->bl,&hd->bl);
+			}
+		}
+		break;
+	case HVAN_CHAOTIC:	//[orn]
+		{
+			if(hd){
+				//HOM,PC,MOB
+				struct block_list* heal_target=NULL;
+				int heal = skill_calc_heal( src, 1+rand()%skilllv );
+				static const int per[10][2]={{20,50},{50,60},{25,75},{60,64},{34,67},
+											 {34,67},{34,67},{34,67},{34,67},{34,67}};
+				int rnd = rand()%100;
+				if(rnd<per[skilllv-1][0])
+				{
+					heal_target = &hd->bl;
+				}else if(rnd<per[skilllv-1][1])
+				{
+					if(!status_isdead(&hd->master->bl))
+						heal_target = &hd->master->bl;
+					else
+						heal_target = &hd->bl;
+				}else{//MOB
+					heal_target = map_id2bl(hd->target_id);
+					if(heal_target==NULL)
+						heal_target = &hd->bl;
+				}
+				clif_skill_nodamage(src,heal_target,AL_HEAL,heal,1);
+				clif_skill_nodamage(src,heal_target,skillid,heal,1);
+				status_heal(heal_target, heal, 0, 0);
+				skill_blockmerc_start(hd, skillid, skill_get_time2(skillid,skilllv)) ;
+			}
+		}
+		break;
+	case HLIF_AVOID:	//[orn]
+	case HAMI_DEFENCE:	//[orn]
+		if ( hd ) {
+			clif_skill_nodamage(src,&hd->master->bl,skillid,skilllv,
+				sc_start(&hd->master->bl,type,100,skilllv,skill_get_time(skillid,skilllv))) ;
+		}
+	case HAMI_BLOODLUST:	//[orn]
+	case HFLI_FLEET:	//[orn]
+	case HFLI_SPEED:	//[orn]
+		if ( hd ) {
+			clif_skill_nodamage(src,bl,skillid,skilllv,
+				sc_start(&hd->bl,type,100,skilllv,skill_get_time(skillid,skilllv))) ;
+			skill_blockmerc_start(hd, skillid, skill_get_time2(skillid,skilllv)) ;
+		}
+		else
+				clif_skill_fail(hd->master,skillid,0,0);
+		break;
+	case HLIF_CHANGE:	//[orn]
+		if ( hd ) {
+			clif_skill_nodamage(src,bl,skillid,skilllv,
+				sc_start(&hd->bl,type,100,skilllv,skill_get_time(skillid,skilllv))) ;
+			status_heal(&hd->bl, hd->master->homunculus.max_hp, 0, 0);
+			skill_blockmerc_start(hd, skillid, skill_get_time2(skillid,skilllv)) ;
+		}
+		else
+				clif_skill_fail(hd->master,skillid,0,0);
+		break;
 
 	default:
 		ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skillid);
@@ -5421,6 +5733,7 @@ int skill_castend_id (int tid, unsigned int tick, int id, int data)
 {
 	struct block_list *target, *src = map_id2bl(id);
 	struct map_session_data* sd = NULL;
+	struct homun_data* hd = NULL;	//[orn]
 	struct mob_data* md = NULL;
 	struct unit_data* ud = unit_bl2ud(src);
 	struct status_change *sc;
@@ -5429,6 +5742,7 @@ int skill_castend_id (int tid, unsigned int tick, int id, int data)
 	nullpo_retr(0, ud);
 
 	BL_CAST( BL_PC,  src, sd);
+	BL_CAST( BL_HOMUNCULUS,  src, hd);	//[orn]
 	BL_CAST( BL_MOB, src, md);
 
 	if( src->prev == NULL ) {
@@ -5538,6 +5852,9 @@ int skill_castend_id (int tid, unsigned int tick, int id, int data)
 		if(sd && !skill_check_condition(sd,ud->skillid, ud->skilllv,1))		/* 菴ソ逕ィ譚。莉カ繝√ぉ繝�け */
 			break;
 			
+		if(hd && !skill_check_condition_hom(hd,ud->skillid, ud->skilllv,1))	//[orn]
+			break;
+			
 		if (ud->walktimer != -1 && ud->skillid != TK_RUN)
 			unit_stop_walking(src,1);
 		
@@ -5585,12 +5902,14 @@ int skill_castend_pos (int tid, unsigned int tick, int id, int data)
 	struct block_list* src = map_id2bl(id);
 	int maxcount;
 	struct map_session_data *sd = NULL;
+	struct homun_data *hd = NULL;	//[orn]
 	struct unit_data *ud = unit_bl2ud(src);
 	struct mob_data *md = NULL;
 
 	nullpo_retr(0, ud);
 
 	BL_CAST( BL_PC , src, sd);
+	BL_CAST( BL_HOMUNCULUS , src, hd);	//[orn]
 	BL_CAST( BL_MOB, src, md);
 
 	if( src->prev == NULL ) {
@@ -5651,6 +5970,9 @@ int skill_castend_pos (int tid, unsigned int tick, int id, int data)
 		if(sd && !skill_check_condition(sd,ud->skillid, ud->skilllv, 1))	/* 菴ソ逕ィ譚。莉カ繝√ぉ繝�け */
 			break;
 
+		if(hd && !skill_check_condition_hom(hd,ud->skillid, ud->skilllv, 1))	//[orn]
+			break;
+
 		if(md) {
 			md->last_thinktime=tick + (tid==-1?md->status.adelay:md->status.amotion);
 			if(md->skillidx >= 0) {
@@ -7586,6 +7908,8 @@ int skill_check_condition (struct map_session_data *sd, int skill, int lv, int t
 	// for the guild skills [celest]
 	if (skill >= GD_SKILLBASE)
 		j = GD_SKILLRANGEMIN + skill - GD_SKILLBASE;
+	else if (skill >= HM_SKILLBASE)	//[orn]
+		j = HM_SKILLRANGEMIN + skill - HM_SKILLBASE;
 	else
 		j = skill;
 	if (j < 0 || j >= MAX_SKILL_DB)
@@ -9034,6 +9358,23 @@ int skill_ganbatein (struct block_list *bl, va_list ap)
 	return 1;
 }
 
+/*==========================================
+ * キャスリングのターゲット変更
+ *------------------------------------------
+ */
+int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap)
+{
+	struct mob_data* md;
+	struct block_list *from_bl;
+	struct block_list *to_bl;
+	nullpo_retr(0, md = (struct mob_data*)bl);
+	nullpo_retr(0, from_bl = va_arg(ap,struct block_list *));
+	nullpo_retr(0, to_bl = va_arg(ap,struct block_list *));
+	if(md->target_id == from_bl->id)
+		md->target_id = to_bl->id;
+	return 0;
+}
+
 /*==========================================
  * 謖�ョ夂ッ�峇蜀�〒src縺ォ蟇セ縺励※譛牙柑縺ェ繧ソ繝シ繧イ繝�ヨ縺ョbl縺ョ謨ー繧呈焚縺医k(foreachinarea)
  *------------------------------------------
@@ -10303,6 +10644,8 @@ int skill_blockpc_start(struct map_session_data *sd, int skillid, int tick)
 
 	if (skillid >= GD_SKILLBASE)
 		skillid = GD_SKILLRANGEMIN + skillid - GD_SKILLBASE;
+	if (skillid >= HM_SKILLBASE)	//[orn]
+		skillid = HM_SKILLRANGEMIN + skillid - HM_SKILLBASE;
 	if (skillid < 1 || skillid > MAX_SKILL)
 		return -1;
 
@@ -10310,6 +10653,31 @@ int skill_blockpc_start(struct map_session_data *sd, int skillid, int tick)
 	return add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,skillid);
 }
 
+int skill_blockmerc_end (int tid, unsigned int tick, int id, int data)	//[orn]
+{
+	struct homun_data *hd = (TBL_HOMUNCULUS*) map_id2bl(id);
+	if (data <= 0 || data >= MAX_SKILL)
+		return 0;
+	if (hd) hd->blockskill[data] = 0;
+	
+	return 1;
+}
+
+int skill_blockmerc_start(struct homun_data *hd, int skillid, int tick)	//[orn]
+{
+	nullpo_retr (-1, hd);
+
+	if (skillid >= GD_SKILLBASE)
+		skillid = GD_SKILLRANGEMIN + skillid - GD_SKILLBASE;
+	if (skillid >= HM_SKILLBASE)	//[orn]
+		skillid = HM_SKILLRANGEMIN + skillid - HM_SKILLBASE;
+	if (skillid < 1 || skillid > MAX_SKILL)
+		return -1;
+
+	hd->blockskill[skillid] = 1;
+	return add_timer(gettick()+tick,skill_blockmerc_end,hd->bl.id,skillid);
+}
+
 
 /*----------------------------------------------------------------------------
  * 蛻晄悄蛹也ウサ
@@ -10641,6 +11009,8 @@ int skill_readdb (void)
 		}
 		if (i >= GD_SKILLBASE)
 			i = GD_SKILLRANGEMIN + i - GD_SKILLBASE;
+		if (i >= HM_SKILLBASE)	//[orn]
+			i = HM_SKILLRANGEMIN + i - HM_SKILLBASE;
 		if(i<=0 || i>MAX_SKILL_DB)
 			continue;
 		
@@ -10697,6 +11067,8 @@ int skill_readdb (void)
 		i=atoi(split[0]);
 		if (i >= GD_SKILLBASE)
 			i = GD_SKILLRANGEMIN + i - GD_SKILLBASE;
+		if (i >= HM_SKILLBASE)	//[orn]
+			i = HM_SKILLRANGEMIN + i - HM_SKILLBASE;
 		if(i<=0 || i>MAX_SKILL_DB)
 			continue;
 
@@ -10784,6 +11156,8 @@ int skill_readdb (void)
 		i=atoi(split[0]);
 		if (i >= GD_SKILLBASE)
 			i = GD_SKILLRANGEMIN + i - GD_SKILLBASE;
+		if (i >= HM_SKILLBASE)	//[orn]
+			i = HM_SKILLRANGEMIN + i - HM_SKILLBASE;
 		if(i<=0 || i>MAX_SKILL_DB)
 			continue;
 
@@ -10816,6 +11190,8 @@ int skill_readdb (void)
 		i=atoi(split[0]);
 		if (i >= GD_SKILLBASE)
 			i = GD_SKILLRANGEMIN + i - GD_SKILLBASE;
+		if (i >= HM_SKILLBASE)	//[orn]
+			i = HM_SKILLRANGEMIN + i - HM_SKILLBASE;
 		if(i<=0 || i>MAX_SKILL_DB)
 			continue;
 		skill_db[i].unit_id[0] = strtol(split[1],NULL,16);

+ 3 - 0
src/map/skill.h

@@ -237,6 +237,8 @@ int skill_check_cloaking(struct block_list *bl, struct status_change *sc);
 // ステ?タス異常
 int skill_enchant_elemental_end(struct block_list *bl, int type);
 int skillnotok(int skillid, struct map_session_data *sd);
+int skillnotok_hom (int skillid, struct homun_data *hd) ;	//[orn]
+int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap);	//[orn]
 
 // アイテム作成
 int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty);
@@ -250,6 +252,7 @@ int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int
 int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
 int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag);
 int skill_blockpc_start (struct map_session_data*,int,int);	// [celest]
+int skill_blockmerc_start (struct homun_data*,int,int);	//[orn]
 
 // スキル攻?一括?理
 int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc,

+ 306 - 104
src/map/status.c

@@ -54,13 +54,14 @@ int current_equip_item_index; //Contains inventory index of an equipped item. To
 int current_equip_card_id; //To prevent card-stacking (from jA) [Skotlex]
 //we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only
 //to avoid cards exploits
+void status_calc_bl_sub_hom(struct homun_data *hd, unsigned long flag);	//[orn]
 
 static void add_sc(int skill, int sc)
 {
 	int sk = skill;
 	if (sk > GD_SKILLBASE) sk = skill - GD_SKILLBASE + SC_GD_BASE;
 	else
-	if (sk > HM_SKILLBASE) sk = skill - HM_SKILLBASE + SC_HM_BASE;
+	if (sk >= HM_SKILLBASE) sk = skill - HM_SKILLBASE + SC_HM_BASE;
 	if (sk < 0 || sk >= MAX_SKILL) {
 		if (battle_config.error_log)
 			ShowError("add_sc: Unsupported skill id %d\n", skill);
@@ -374,6 +375,8 @@ void initChangeTables(void) {
 	set_sc(HLIF_CHANGE, SC_CHANGE, SI_BLANK, SCB_INT);
 	set_sc(HAMI_BLOODLUST, SC_BLOODLUST, SI_BLANK, SCB_BATK|SCB_WATK);
 	set_sc(HFLI_FLEET, SC_FLEET, SI_BLANK, SCB_ASPD|SCB_BATK|SCB_WATK);
+	set_sc(HFLI_SPEED, SC_SPEED, SI_BLANK, SCB_FLEE);	//[orn]
+	set_sc(HAMI_DEFENCE, SC_DEFENCE, SI_BLANK, SCB_DEF);	//[orn]
 
 	set_sc(GD_LEADERSHIP, SC_GUILDAURA, SI_GUILDAURA, SCB_STR|SCB_AGI|SCB_VIT|SCB_DEX);
 	set_sc(GD_BATTLEORDER, SC_BATTLEORDERS, SI_BATTLEORDERS, SCB_STR|SCB_INT|SCB_DEX);
@@ -452,7 +455,7 @@ int SkillStatusChangeTable(int skill)
 	int sk = skill;
 	if (sk > GD_SKILLBASE) sk = skill - GD_SKILLBASE + SC_GD_BASE;
 	else
-	if (sk > HM_SKILLBASE) sk = skill - HM_SKILLBASE + SC_HM_BASE;
+	if (sk >= HM_SKILLBASE) sk = skill - HM_SKILLBASE + SC_HM_BASE;
 	if (sk < 0 || sk >= MAX_SKILL) {
 		if (battle_config.error_log)
 			ShowError("add_sc: Unsupported skill id %d\n", skill);
@@ -501,8 +504,8 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
 	struct status_data *status;
 	struct status_change *sc;
 
-	if(sp && target->type != BL_PC)
-		sp = 0; //Only players get SP damage.
+	if(sp && target->type != BL_PC && target->type != BL_HOMUNCULUS)	//[orn]
+		sp = 0; //Only players and Homunculus get SP damage.
 	
 	if (hp < 0) { //Assume absorbed damage.
 		status_heal(target, -hp, 0, 1);
@@ -641,7 +644,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
 			flag = pc_dead((TBL_PC*)target,src);
 			break;
 		case BL_HOMUNCULUS:
-			flag = merc_dead((TBL_HOMUNCULUS*)target,src);
+			flag = merc_hom_dead((TBL_HOMUNCULUS*)target,src);
 			break;
 		default:	//Unhandled case, do nothing to object.
 			flag = 0;
@@ -738,7 +741,7 @@ int status_heal(struct block_list *bl,int hp,int sp, int flag)
 		pc_heal((TBL_PC*)bl,hp,sp,flag&2?1:0);
 		break;
 	case BL_HOMUNCULUS:
-		merc_heal((TBL_HOMUNCULUS*)bl,hp,sp);
+		merc_hom_heal((TBL_HOMUNCULUS*)bl,hp,sp);
 		break;
 	}
 	return hp+sp;
@@ -819,7 +822,9 @@ int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per
 			break;
 		case BL_PC:
 			pc_revive((TBL_PC*)bl, hp, sp);
-			break;
+//		case BL_HOMUNCULUS:	//[orn]
+//			merc_hom_revive((TBL_HOMUNCULUS*)bl, hp, sp);
+//			break;
 	}
 	return 1;
 }
@@ -1360,97 +1365,6 @@ int status_calc_pet(struct pet_data *pd, int first)
 	return 1;
 }	
 
-int status_calc_homunculus(struct homun_data *hd, int first)
-{
-	struct status_data *status = &hd->base_status;
-	int lv, i;
-	/* very proprietary */
-	lv=hd->level;
-	memset(status, 0, sizeof(struct status_data));
-	switch(hd->class_)
-	{
-	case 6001:	//LIF ~ int,dex,vit
-		status->str = 3+lv/7;
-		status->agi = 3+2*lv/5;
-		status->vit = 4+lv;
-		status->int_ = 4+3*lv/4;
-		status->dex = 4+2*lv/3;
-		status->luk = 3+lv/4;
-		for(i=8001;i<8005;++i)
-		{
-			hd->hskill[i-8001].id=i;
-			//hd->hskill[i-8001].level=1;
-		}
-		break;
-	case 6003:	//FILIR ~ str,agi,dex
-		status->str = 4+3*lv/4;
-		status->agi = 4+2*lv/3;
-		status->vit = 3+2*lv/5;
-		status->int_ = 3+lv/4;
-		status->dex = 4+lv;
-		status->luk = 3+lv/7;
-		for(i=8009;i<8013;++i)
-		{
-			hd->hskill[i-8009].id=i;
-			//hd->hskill[i-8009].level=1;
-		}
-		break;
-	case 6002:	//AMISTR ~ str,vit,luk
-		status->str = 4+lv;
-		status->agi = 3+lv/4;
-		status->vit = 3+3*lv/4;
-		status->int_ = 3+lv/10;
-		status->dex = 3+2*lv/5;
-		status->luk = 4+2*lv/3;
-		for(i=8005;i<8009;++i)
-		{
-			hd->hskill[i-8005].id=i;
-			//hd->hskill[i-8005].level=1;
-		}
-		break;
-	case 6004:	//VANILMIRTH ~ int,dex,luk
-		status->str = 3+lv/4;
-		status->agi = 3+lv/7;
-		status->vit = 3+2*lv/5;
-		status->int_ = 4+lv;
-		status->dex = 4+2*lv/3;
-		status->luk = 4+3*lv/4;
-		for(i=8013;i<8017;++i)
-		{
-			hd->hskill[i-8013].id=i;
-			//hd->hskill[i-8013].level=1;
-		}
-		break;
-	default:
-		if (battle_config.error_log)
-			ShowError("status_calc_homun: Unknown class %d\n", hd->class_);
-		memcpy(status, &dummy_status, sizeof(struct status_data));
-		break;
-	}
-	status->hp = 10; //Revive HP/SP?
-	status->sp = 0;
-	status->max_hp=500+lv*10+lv*lv;
-	status->max_sp=300+lv*11+lv*lv*90/100;
-	status->aspd_rate = 1000;
-	status->speed=0x96;
-	status->batk = status_base_atk(&hd->bl, status);
-	status_calc_misc(status, hd->level);
-
-	// hp recovery
-	hd->regenhp = 1 + (status->vit/5) + (status->max_hp/200);
-
-	// sp recovery
-	hd->regensp = 1 + (status->int_/6) + (status->max_sp/100);
-	if(status->int_ >= 120)
-		hd->regensp += ((status->int_-120)>>1) + 4;
-
-	status->amotion = 1800 - (1800 * status->agi / 250 + 1800 * status->dex / 1000);
-	status->amotion	-= 200;
-	status->dmotion=status->amotion;
-	status_calc_bl(&hd->bl, SCB_ALL);
-	return 1;
-}
-
 static unsigned int status_base_pc_maxhp(struct map_session_data* sd, struct status_data *status)
 {
 	unsigned int val;
@@ -2230,6 +2144,59 @@ int status_calc_pc(struct map_session_data* sd,int first)
 	return 0;
 }
 
+int status_calc_homunculus(struct homun_data *hd, int first)
+{
+	struct status_data b_status, *status;
+	memcpy(&b_status, &hd->base_status, sizeof(struct status_data));
+	status = &hd->base_status;
+
+	status->def_ele = b_status.def_ele = hd->homunculusDB->element ;	//[orn]
+	status->ele_lv = b_status.ele_lv = 1 ;	//[orn]
+	status->race = b_status.race = hd->homunculusDB->race ;	//[orn]
+	status->size = b_status.size = hd->homunculusDB->size ;	//[orn]
+	status->rhw.range = b_status.rhw.range = 1 + hd->homunculusDB->size ;	//[orn]
+	status->mode = b_status.mode = MD_CANMOVE|MD_CANATTACK|MD_ASSIST|MD_AGGRESSIVE|MD_CASTSENSOR;	//[orn]
+	status->speed = b_status.speed = DEFAULT_WALK_SPEED;
+	status->aspd_rate = b_status.aspd_rate = 1000;
+
+	merc_hom_calc_skilltree(hd->master);	//
+
+	status_cpy(&b_status, status);
+	status_calc_misc(status, hd->master->homunculus.level);
+	status_calc_bl(&hd->bl, SCB_ALL); //Status related changes.
+
+	if ( 	(b_status.str != status->str) ||
+				(b_status.agi != status->agi) ||
+				(b_status.vit != status->vit) ||
+				(b_status.int_ != status->int_) ||
+				(b_status.dex != status->dex) ||
+				(b_status.luk != status->luk) ||
+				(b_status.hit != status->hit) ||
+				(b_status.flee != status->flee) ||
+				(b_status.amotion != status->amotion) ||
+				(b_status.rhw.atk != status->rhw.atk) ||
+				(b_status.def != status->def) ||
+				(b_status.rhw.atk2 != status->rhw.atk2) ||
+				(b_status.def2 != status->def2) ||
+				(b_status.flee2 != status->flee2) ||
+				(b_status.cri != status->cri) ||
+				(b_status.matk_max != status->matk_max) ||
+				(b_status.matk_min != status->matk_min) ||
+				(b_status.mdef != status->mdef) ||
+				(b_status.mdef2 != status->mdef2) ||
+				(b_status.rhw.range != status->rhw.range) ||
+				(b_status.max_hp != status->max_hp) ||
+				(b_status.max_sp != status->max_sp) ||
+				(b_status.hp != status->hp) ||
+				(b_status.sp != status->sp)
+			)
+		{
+			clif_hominfo(hd->master,0) ;
+		}
+
+	return 1;
+}
+
 static unsigned short status_calc_str(struct block_list *,struct status_change *,int);
 static unsigned short status_calc_agi(struct block_list *,struct status_change *,int);
 static unsigned short status_calc_vit(struct block_list *,struct status_change *,int);
@@ -2552,12 +2519,210 @@ void status_calc_bl_sub_pc(struct map_session_data *sd, unsigned long flag)
 		clif_updatestatus(sd,SP_MAXSP);
 }
 
+//Calculates some attributes that depends on modified stats from status changes.
+void status_calc_bl_sub_hom(struct homun_data *hd, unsigned long flag)	//[orn]
+{
+	struct status_data *status = &hd->battle_status, *b_status = &hd->base_status;
+	int skill = 0;
+
+	if(flag&(SCB_MAXHP|SCB_VIT))
+	{
+		flag|=SCB_MAXHP; //Ensures client-side refresh
+		
+		// Apply relative modifiers from equipment
+		if(status->max_hp > (unsigned int)battle_config.max_hp)
+			status->max_hp = battle_config.max_hp;
+		else if(!status->max_hp)
+			status->max_hp = 1;
+
+		// hp recovery
+		hd->regenhp = 1 + (status->vit/5) + (status->max_hp/200);
+
+		if(hd->regenhp < 1) hd->regenhp = 1;
+
+		// Skill-related Adamantium Skin
+		if((skill=merc_hom_checkskill(hd->master,HAMI_SKIN)) > 0) {
+			status->max_hp = hd->master->homunculus.max_hp + skill * 2 * hd->master->homunculus.max_hp / 100 ;
+			hd->regenhp += skill * 5 * hd->regenhp / 100 ;
+		}
+
+		status->max_hp = status_calc_maxhp(&hd->bl, &hd->sc, status->max_hp);
+
+	}
+	if(flag&SCB_DEF)
+	{
+		status->def =	hd->master->homunculus.level / 10 + status->vit / 5 ;
+		if(hd->sc.data[SC_DEFENCE].timer != -1)
+			status->def += hd->sc.data[SC_DEFENCE].val2;
+		if((skill=merc_hom_checkskill(hd->master,HAMI_SKIN)) > 0) {
+			status->def +=	skill * 4 ;
+		}
+	}
+	if(flag&(SCB_MAXSP|SCB_INT))
+	{	
+		flag|=SCB_MAXSP;
+		
+		// Skill-related Instruction Change
+		if((skill = merc_hom_checkskill(hd->master,HVAN_INSTRUCT)) > 0) {
+			if ( skill == 5 ) {
+				status->int_ += 3 ;
+			} else if ( skill == 1 ) {
+				status->int_ += 1 ;
+			} else {
+				status->int_ += 2 ;
+			}
+			if ( skill > 3 ) {
+				status->str += 4 ;
+			} else if ( skill == 3 ) {
+				status->str += 3 ;
+			} else {
+				status->str += 1 ;
+			}
+		}
+
+		if((skill = merc_hom_checkskill(hd->master,HLIF_BRAIN)) > 0) {
+			status->max_sp = hd->master->homunculus.max_sp + skill * 2 * hd->master->homunculus.max_sp / 100 ;
+			hd->regensp += skill * 3 * hd->regensp / 100 ;
+			if ( skill == 5 ) {
+				status->max_sp *= 103 / 100 ;
+			} else if ( skill == 1 ) {
+				status->max_sp *= 101 / 100 ;
+			} else {
+				status->max_sp *= 102 / 100 ;
+			}
+		}
+
+		status->mdef =	hd->master->homunculus.level / 10 + status->int_ / 5 ;
+		status->max_sp = status_calc_maxsp(&hd->bl, &hd->sc, status->max_sp);
+		
+		if(status->max_sp > (unsigned int)battle_config.max_sp)
+			status->max_sp = battle_config.max_sp;
+		else if(!status->max_sp)
+			status->max_sp = 1;
+		
+		if(status->sp > status->max_sp) {
+			status->sp = status->max_sp;
+		}
+
+		// sp recovery
+		hd->regensp = 1 + (status->int_/6) + (status->max_sp/100);
+		if(status->int_ >= 120)
+			hd->regensp += ((status->int_-120)>>1) + 4;
+
+		if(hd->regensp < 1) hd->regensp = 1;
+
+	}
+
+	if(flag&(SCB_BATK|SCB_WATK)) {
+		status->rhw.atk = status->rhw.atk2 = status->str + ( status->str / 10 ) * ( status->str / 10 ) ;
+		status->rhw.atk += status->dex ;
+		if ( (status->str + hd->master->homunculus.level) > status->dex ) 
+			status->rhw.atk2 += status->str + hd->master->homunculus.level ;
+		else
+			status->rhw.atk2 += status->dex ;
+
+		if(hd->sc.data[SC_FLEET].timer!=-1)
+			status->rhw.atk2 += status->rhw.atk2 * hd->sc.data[SC_FLEET].val3/100;
+	}
+	
+	if(flag&SCB_MATK) {
+		status->matk_min = status->int_+(status->int_/7)*(status->int_/7);
+		status->matk_max = status->int_+(status->int_/5)*(status->int_/5);
+
+		status->matk_min = status_calc_matk(&hd->bl, &hd->sc, status->matk_min);
+		status->matk_max = status_calc_matk(&hd->bl, &hd->sc, status->matk_max);
+
+	}
+	
+	if(flag&SCB_HIT) {
+		if(status->hit < 1) status->hit = 1;
+	}
+
+	if(flag&SCB_FLEE) {
+		if(status->flee < 1) status->flee = 1;
+	}
+
+	if(flag&SCB_DEF2) {
+		if(status->def2 < 1) status->def2 = 1;
+	}
+
+	if(flag&SCB_MDEF2) {
+		if(status->mdef2 < 1) status->mdef2 = 1;
+	}
+
+	if(flag&SCB_SPEED) {
+		if(status->speed < battle_config.max_walk_speed)
+			status->speed = battle_config.max_walk_speed;
+	}
+	if(flag&(SCB_ASPD|SCB_AGI|SCB_DEX)) {
+		flag|=SCB_ASPD;
+		status->amotion = hd->homunculusDB->baseASPD - ((status->agi*4+status->dex)* hd->homunculusDB->baseASPD / 1000);
+
+		status->aspd_rate = status_calc_aspd_rate(&hd->bl, &hd->sc , b_status->aspd_rate);
+		if(status->aspd_rate != 1000)
+			status->amotion = status->amotion *status->aspd_rate/1000;
+
+		status->amotion = cap_value(status->amotion,battle_config.max_aspd,2000);
+
+		status->adelay = 2*status->amotion;
+	}
+	
+	if(flag&(SCB_AGI|SCB_DSPD)) {
+		//Even though people insist this is too slow, packet data reports this is the actual real equation.
+		skill = 800-status->agi*4;
+		status->dmotion = cap_value(skill, 400, 800);
+
+		if(battle_config.pc_damage_delay_rate != 100)
+			status->dmotion  = status->dmotion*battle_config.pc_damage_delay_rate/100;
+		status->dmotion = status_calc_dmotion(&hd->bl, &hd->sc, b_status->dmotion);
+	}
+
+	if(flag&SCB_CRI)
+	{
+		if(status->cri < 10) status->cri = 10;
+	}
+
+	if(flag&SCB_FLEE2) {
+		if(status->flee2 < 10) status->flee2 = 10;
+	}
+	if (flag == SCB_ALL)
+		return; //Refresh is done on invoking function (status_calc_hom)
+
+	if ( 	(flag&SCB_SPEED) ||
+				(flag&SCB_STR) ||
+				(flag&SCB_AGI) ||
+				(flag&SCB_VIT) ||
+				(flag&SCB_INT) ||
+				(flag&SCB_DEX) ||
+				(flag&SCB_LUK) ||
+				(flag&SCB_HIT) ||
+				(flag&SCB_FLEE) ||
+				(flag&SCB_ASPD) ||
+				(flag&(SCB_BATK|SCB_WATK)) ||
+				(flag&SCB_DEF) ||
+				(flag&SCB_WATK) ||
+				(flag&SCB_DEF2) ||
+				(flag&SCB_FLEE2) ||
+				(flag&SCB_CRI) ||
+				(flag&SCB_MATK) ||
+				(flag&SCB_MDEF) ||
+				(flag&SCB_MDEF2) ||
+				(flag&SCB_RANGE) ||
+				(flag&SCB_MAXHP) ||
+				(flag&SCB_MAXSP)
+		 )
+		{
+			clif_hominfo(hd->master,0);
+		}
+}
+
 void status_calc_bl(struct block_list *bl, unsigned long flag)
 {
 	struct status_data *b_status, *status;
 	struct status_change *sc;
 	int temp;
 	TBL_PC *sd;
+	TBL_HOMUNCULUS *hd;
 	b_status = status_get_base_status(bl);
 	status = status_get_status_data(bl);
 	sc = status_get_sc(bl);
@@ -2566,6 +2731,7 @@ void status_calc_bl(struct block_list *bl, unsigned long flag)
 		return;
 
 	BL_CAST(BL_PC,bl,sd);
+	BL_CAST(BL_HOMUNCULUS,bl,hd);
 
 	if(sd && flag&SCB_PC)
 	{	//Recalc everything.
@@ -2573,7 +2739,8 @@ void status_calc_bl(struct block_list *bl, unsigned long flag)
 		return;
 	}
 	
-	if(!sd && (!sc || !sc->count)) { //No difference.
+//	if(!sd && (!sc || !sc->count)) { //No difference.
+	if( (!sd && !hd ) && (!sc || !sc->count)) { //No difference.
 		status_cpy(status, b_status);
 		return;
 	}
@@ -2716,6 +2883,12 @@ void status_calc_bl(struct block_list *bl, unsigned long flag)
 		return;
 	}
 	
+	if(hd) {
+		//The remaining are handled quite different by homunculus, so use their own function.
+		status_calc_bl_sub_hom(hd, flag);
+		return;
+	}
+	
 	if(flag&SCB_MAXHP) {
 		status->max_hp = status_calc_maxhp(bl, sc, b_status->max_hp);
 		if (status->hp > status->max_hp) //FIXME: Should perhaps a status_zap should be issued?
@@ -2985,8 +3158,6 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan
 		batk += batk * 3;
 	if(sc->data[SC_BLOODLUST].timer!=-1)
 		batk += batk * sc->data[SC_BLOODLUST].val2/100;
-	if(sc->data[SC_FLEET].timer!=-1)
-		batk += batk * sc->data[SC_FLEET].val3/100;
 	if(sc->data[SC_JOINTBEAT].timer!=-1 && sc->data[SC_JOINTBEAT].val2==4)
 		batk -= batk * 25/100;
 	if(sc->data[SC_CURSE].timer!=-1)
@@ -3032,8 +3203,6 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan
 	}
 	if(sc->data[SC_BLOODLUST].timer!=-1)
 		watk += watk * sc->data[SC_BLOODLUST].val2/100;
-	if(sc->data[SC_FLEET].timer!=-1)
-		watk += watk * sc->data[SC_FLEET].val3/100;
 	if(sc->data[SC_CURSE].timer!=-1)
 		watk -= watk * 25/100;
 	if(sc->data[SC_STRIPWEAPON].timer!=-1)
@@ -3140,6 +3309,8 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change
 		flee += 30;
 	if(sc->data[SC_GATLINGFEVER].timer!=-1)
 		flee -= sc->data[SC_GATLINGFEVER].val1*5;
+	if(sc->data[SC_SPEED].timer!=-1)
+		flee += 10 + sc->data[SC_SPEED].val1 * 10 ;
 
 	return cap_value(flee,0,SHRT_MAX);
 }
@@ -3166,6 +3337,8 @@ static signed char status_calc_def(struct block_list *bl, struct status_change *
 		return 100;
 	if(sc->data[SC_SKA].timer != -1)
 		return sc->data[SC_SKA].val3;
+	if (sc->data[SC_DEFENCE].timer != -1)	//[orn]
+		def += sc->data[SC_DEFENCE].val2 ;
 	if(sc->data[SC_STEELBODY].timer!=-1)
 		return 90;
 	if(sc->data[SC_DRUMBATTLE].timer!=-1)
@@ -3586,6 +3759,8 @@ int status_get_class(struct block_list *bl)
 		return ((struct map_session_data *)bl)->status.class_;
 	if(bl->type==BL_PET)
 		return ((struct pet_data *)bl)->class_;
+	if(bl->type==BL_HOMUNCULUS)
+		return ((struct homun_data *)bl)->master->homunculus.class_;
 	return 0;
 }
 /*==========================================
@@ -3603,7 +3778,7 @@ int status_get_lv(struct block_list *bl)
 	if(bl->type==BL_PET)
 		return ((TBL_PET*)bl)->msd->pet.level;
 	if(bl->type==BL_HOMUNCULUS)
-		return ((TBL_HOMUNCULUS*)bl)->level;
+		return ((TBL_HOMUNCULUS*)bl)->master->homunculus.level;
 	return 1;
 }
 
@@ -3699,6 +3874,16 @@ int status_get_party_id(struct block_list *bl)
 		}
 		return 0; //No party.
 	}
+	if(bl->type==BL_HOMUNCULUS){	//[orn]
+		struct homun_data *hd=(struct homun_data *)bl;
+		if( hd->master->bl.id>0 )
+		{
+			if ( hd->master != NULL)
+				return hd->master->status.party_id;
+			return -1;
+		}
+		return 0; //No party.
+	}
 	if(bl->type==BL_SKILL)
 		return ((struct skill_unit *)bl)->group->party_id;
 	return 0;
@@ -3721,6 +3906,16 @@ int status_get_guild_id(struct block_list *bl)
 			return msd->status.guild_id; //Alchemist's mobs [Skotlex]
 		return 0; //No guild.
 	}
+	if(bl->type==BL_HOMUNCULUS){	//[orn]
+		struct homun_data *hd=(struct homun_data *)bl;
+		if( hd->master->bl.id>0 )
+		{
+			if ( hd->master != NULL)
+				return hd->master->status.guild_id;
+			return -1;
+		}
+		return 0; //No guild.
+	}
 	if (bl->type == BL_NPC && bl->subtype == SCRIPT)
 		return ((TBL_NPC*)bl)->u.scr.guild_id;
 	if(bl->type==BL_SKILL)
@@ -4133,6 +4328,7 @@ int status_get_sc_tick(struct block_list *bl, int type, int tick)
 int status_change_start(struct block_list *bl,int type,int rate,int val1,int val2,int val3,int val4,int tick,int flag)
 {
 	struct map_session_data *sd = NULL;
+	struct homun_data *hd = NULL;
 	struct status_change* sc;
 	struct status_data *status;
 	int opt_flag , calc_flag, undead_flag;
@@ -4149,6 +4345,9 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val
 		case BL_PC:
 			sd=(struct map_session_data *)bl;
 			break;
+		case BL_HOMUNCULUS:
+			hd=(struct homun_data *)bl;	//[orn]
+			break;
 		case BL_MOB:
 			if (((struct mob_data*)bl)->class_ == MOBID_EMPERIUM && type != SC_SAFETYWALL)
 				return 0; //Emperium can't be afflicted by status changes.
@@ -5188,6 +5387,9 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val
 		case SC_AVOID:
 			val2 = 10*val1; //Speed change rate.
 			break;
+		case SC_DEFENCE:
+			val2 = 2*val1; //Def bonus
+			break;
 		case SC_BLOODLUST:
 			val2 = 20+10*val1; //Atk rate change.
 			break;

+ 2 - 0
src/map/status.h

@@ -253,6 +253,8 @@ enum {
 	SC_CHANGE,
 	SC_BLOODLUST,
 	SC_FLEET,
+	SC_SPEED,	//[orn]
+	SC_DEFENCE,	//[orn]
 	SC_INCAGIRATE,
 	SC_INCDEXRATE,
 	SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex]

+ 23 - 1
src/map/unit.c

@@ -15,6 +15,7 @@
 #include "pc.h"
 #include "mob.h"
 #include "pet.h"
+#include "mercenary.h"	///[orn]
 #include "skill.h"
 #include "clif.h"
 #include "npc.h"
@@ -38,6 +39,7 @@ struct unit_data* unit_bl2ud(struct block_list *bl) {
 	if( bl->type == BL_MOB) return &((struct mob_data*)bl)->ud;
 	if( bl->type == BL_PET) return &((struct pet_data*)bl)->ud;
 	if( bl->type == BL_NPC) return &((struct npc_data*)bl)->ud;
+	if( bl->type == BL_HOMUNCULUS) return &((struct homun_data*)bl)->ud;	//[orn]
 	return NULL;
 }
 
@@ -100,6 +102,7 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data)
 	struct block_list       *bl;
 	struct map_session_data *sd = NULL;
 	struct mob_data         *md = NULL;
+	struct homun_data       *hd = NULL;	//[orn]
 	struct unit_data        *ud = NULL;
 
 	bl=map_id2bl(id);
@@ -109,6 +112,8 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data)
 		ud = &sd->ud;
 	} else if( BL_CAST( BL_MOB, bl, md ) ) {
 		ud = &md->ud;
+	} else if( BL_CAST( BL_HOMUNCULUS, bl, hd ) ) {	//[orn]
+		ud = &hd->ud;
 	} else
 		ud = unit_bl2ud(bl);
 	
@@ -262,6 +267,9 @@ int unit_walktoxy( struct block_list *bl, int x, int y, int easy) {
 
 	nullpo_retr(0, bl);
 	
+	if ( status_isdead(bl) )	//[orn]
+		return 0;
+
 	ud = unit_bl2ud(bl);
 	
 	if( ud == NULL) return 0;
@@ -707,6 +715,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int
 	struct status_data *tstatus;
 	struct status_change *sc;
 	struct map_session_data *sd = NULL;
+	struct homun_data *hd = NULL;	//[orn]
 	struct block_list * target = NULL;
 	unsigned int tick = gettick();
 	int temp;
@@ -717,6 +726,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int
 
 	if( BL_CAST( BL_PC,  src, sd ) ) {
 		ud = &sd->ud;
+	} else 	if( BL_CAST( BL_HOMUNCULUS,  src, hd ) ) {	//[orn]
+		ud = &hd->ud;
 	} else
 		ud = unit_bl2ud(src);
 
@@ -1184,6 +1195,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t
 	struct status_data *sstatus;
 	struct map_session_data *sd = NULL;
 	struct mob_data *md = NULL;
+	struct homun_data *hd = NULL;	//[orn]
 	int range;
 	
 	if((ud=unit_bl2ud(src))==NULL)
@@ -1195,6 +1207,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t
 	}
 	BL_CAST( BL_PC , src, sd);
 	BL_CAST( BL_MOB, src, md);
+	BL_CAST( BL_HOMUNCULUS, src, hd);	//[orn]
 	ud->attacktimer=-1;
 	target=map_id2bl(ud->target);
 
@@ -1704,6 +1717,16 @@ int unit_free(struct block_list *bl) {
 		}
 		if(mob_is_clone(md->class_))
 			mob_clone_delete(md->class_);
+	} else if( bl->type == BL_HOMUNCULUS ) {	//[orn]
+		struct homun_data *hd = (struct homun_data*)bl;
+		struct map_session_data *sd = hd->master;
+		merc_hom_hungry_timer_delete(hd);
+		merc_natural_heal_timer_delete(hd) ;
+		if (sd) {
+//			if(hd->intimacy > 0)
+//				intif_save_mercdata(sd->status.account_id,&sd->hom);
+			sd->hd = NULL;
+		}
 	}
 
 	skill_clear_unitgroup(bl);
@@ -1729,4 +1752,3 @@ int do_final_unit(void) {
 	// nothing to do
 	return 0;
 }
-

+ 6 - 0
vcproj-7.1/char-server_sql.vcproj

@@ -160,6 +160,9 @@
 			<File
 				RelativePath="..\src\char_sql\int_guild.c">
 			</File>
+			<File
+				RelativePath="..\src\char_sql\int_homun.c">
+			</File>
 			<File
 				RelativePath="..\src\char_sql\int_party.c">
 			</File>
@@ -242,6 +245,9 @@
 			<File
 				RelativePath="..\src\char_sql\int_guild.h">
 			</File>
+			<File
+				RelativePath="..\src\char_sql\int_homun.h">
+			</File>
 			<File
 				RelativePath="..\src\char_sql\int_party.h">
 			</File>