浏览代码

Add support body style implementation
* This feature require the client support before 2015-05-13 or newer
Make sure you data&lub support for this

note : read info to https://rathena.org/board/topic/104205-2015-client-support/

Special Thanks you to Rytech, @aleos89

Napster 9 年之前
父节点
当前提交
d70b1734c8

+ 7 - 0
conf/battle/client.conf

@@ -72,6 +72,8 @@ min_hair_color: 0
 max_hair_color: 8
 max_hair_color: 8
 min_cloth_color: 0 
 min_cloth_color: 0 
 max_cloth_color: 4
 max_cloth_color: 4
+min_body_style: 0
+max_body_style: 1
 
 
 // When set to yes, the damage field in packets sent from woe maps will be set
 // When set to yes, the damage field in packets sent from woe maps will be set
 // to -1, making it impossible for GMs, Bots and Hexed clients to know the
 // to -1, making it impossible for GMs, Bots and Hexed clients to know the
@@ -120,6 +122,11 @@ wedding_modifydisplay: no
 // Save Clothes color. (This will degrade performance) (Note 1)
 // Save Clothes color. (This will degrade performance) (Note 1)
 save_clothcolor: yes
 save_clothcolor: yes
 
 
+// Save body styles. (Note 1)
+// Note: Don't turn this on unless you know what your doing.
+// Sprites are not released officially.
+save_body_style: no
+
 // Do not display cloth colors for the wedding class?
 // Do not display cloth colors for the wedding class?
 // Note: Both save_clothcolor and wedding_modifydisplay have to be enabled
 // Note: Both save_clothcolor and wedding_modifydisplay have to be enabled
 // for this option to take effect. Set this to yes if your cloth palettes 
 // for this option to take effect. Set this to yes if your cloth palettes 

+ 6 - 2
conf/msg_conf/map_msg.conf

@@ -777,7 +777,11 @@
 737: '%s' (%d) cannot be cloned, limit (%d).
 737: '%s' (%d) cannot be cloned, limit (%d).
 738: Clone '%s' is done.
 738: Clone '%s' is done.
 
 
-//739-899 free
+// @bodystyle
+739: Please enter a body style (usage: @bodystyle <body ID: %d-%d>).
+740: This job has no alternate body styles.
+
+//741-899 free
 
 
 //------------------------------------
 //------------------------------------
 // More atcommands message
 // More atcommands message
@@ -1112,7 +1116,7 @@
 
 
 // @changelook
 // @changelook
 1177: Usage: @changelook {<position>} <view id>
 1177: Usage: @changelook {<position>} <view id>
-1178: Position: 1-Top 2-Middle 3-Bottom 4-Weapon 5-Shield 6-Shoes 7-Robe
+1178: Position: 1-Top 2-Middle 3-Bottom 4-Weapon 5-Shield 6-Shoes 7-Robe 8-Body
 
 
 // @autotrade
 // @autotrade
 1179: Autotrade is not allowed on this map.
 1179: Autotrade is not allowed on this map.

+ 22 - 0
doc/script_commands.txt

@@ -8998,5 +8998,27 @@ The command returns 1 upon success, and these values upon failure:
 
 
 ---------------------------------------
 ---------------------------------------
 
 
+*opendressroom({<char_id>});
+
+This will open the Dress Room window on the client connected to the invoking character.
+
+    mes "Close this window to open the Dress Room window.";
+    close2;
+    opendressroom;
+    end;
+
+---------------------------------------
+
+*closedressroom({<char_id>});
+
+This will close an open Dress Room window on the client connected to the invoking character.
+
+    mes "Close this window to close the Dress Room window.";
+    close2;
+    closedressroom;
+    end;
+
+---------------------------------------
+
 Whew.
 Whew.
 That's about all of them.
 That's about all of them.

+ 2 - 1
sql-files/main.sql

@@ -172,6 +172,7 @@ CREATE TABLE IF NOT EXISTS `char` (
   `hair` tinyint(4) unsigned NOT NULL default '0',
   `hair` tinyint(4) unsigned NOT NULL default '0',
   `hair_color` smallint(5) unsigned NOT NULL default '0',
   `hair_color` smallint(5) unsigned NOT NULL default '0',
   `clothes_color` smallint(5) unsigned NOT NULL default '0',
   `clothes_color` smallint(5) unsigned NOT NULL default '0',
+  `body` smallint(5) unsigned NOT NULL default '0',
   `weapon` smallint(6) unsigned NOT NULL default '0',
   `weapon` smallint(6) unsigned NOT NULL default '0',
   `shield` smallint(6) unsigned NOT NULL default '0',
   `shield` smallint(6) unsigned NOT NULL default '0',
   `head_top` smallint(6) unsigned NOT NULL default '0',
   `head_top` smallint(6) unsigned NOT NULL default '0',
@@ -197,7 +198,7 @@ CREATE TABLE IF NOT EXISTS `char` (
   `font` tinyint(3) unsigned NOT NULL default '0',
   `font` tinyint(3) unsigned NOT NULL default '0',
   `uniqueitem_counter` int(11) unsigned NOT NULL default '0',
   `uniqueitem_counter` int(11) unsigned NOT NULL default '0',
   `sex` ENUM('M','F','U') NOT NULL default 'U',
   `sex` ENUM('M','F','U') NOT NULL default 'U',
-  `hotkey_rowshift` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+  `hotkey_rowshift` tinyint(3) unsigned NOT NULL default '0',
   PRIMARY KEY  (`char_id`),
   PRIMARY KEY  (`char_id`),
   UNIQUE KEY `name_key` (`name`),
   UNIQUE KEY `name_key` (`name`),
   KEY `account_id` (`account_id`),
   KEY `account_id` (`account_id`),

+ 1 - 0
sql-files/upgrades/upgrade_20160112.sql

@@ -0,0 +1 @@
+ALTER TABLE `char` ADD `body`smallint(5) unsigned NOT NULL default '0' AFTER  `clothes_color`;

+ 51 - 49
src/char/char.c

@@ -351,7 +351,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){
 			mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
 			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, p->rename,
 			mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename,
 			(unsigned long)p->delete_date, // FIXME: platform-dependent size
 			(unsigned long)p->delete_date, // FIXME: platform-dependent size
-			p->robe,p->character_moves,p->font, p->uniqueitem_counter,
+			p->robe, p->character_moves, p->font, p->uniqueitem_counter,
 			p->hotkey_rowshift,
 			p->hotkey_rowshift,
 			p->account_id, p->char_id) )
 			p->account_id, p->char_id) )
 		{
 		{
@@ -364,7 +364,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){
 	//Values that will seldom change (to speed up saving)
 	//Values that will seldom change (to speed up saving)
 	if (
 	if (
 		(p->hair != cp->hair) || (p->hair_color != cp->hair_color) || (p->clothes_color != cp->clothes_color) ||
 		(p->hair != cp->hair) || (p->hair_color != cp->hair_color) || (p->clothes_color != cp->clothes_color) ||
-		(p->class_ != cp->class_) ||
+		(p->body != cp->body) || (p->class_ != cp->class_) ||
 		(p->partner_id != cp->partner_id) || (p->father != cp->father) ||
 		(p->partner_id != cp->partner_id) || (p->father != cp->father) ||
 		(p->mother != cp->mother) || (p->child != cp->child) ||
 		(p->mother != cp->mother) || (p->child != cp->child) ||
  		(p->karma != cp->karma) || (p->manner != cp->manner) ||
  		(p->karma != cp->karma) || (p->manner != cp->manner) ||
@@ -372,12 +372,12 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){
 	)
 	)
 	{
 	{
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `class`='%d',"
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `class`='%d',"
-			"`hair`='%d',`hair_color`='%d',`clothes_color`='%d',"
+			"`hair`='%d', `hair_color`='%d', `clothes_color`='%d', `body`='%d',"
 			"`partner_id`='%u', `father`='%u', `mother`='%u', `child`='%u',"
 			"`partner_id`='%u', `father`='%u', `mother`='%u', `child`='%u',"
 			"`karma`='%d',`manner`='%d', `fame`='%d'"
 			"`karma`='%d',`manner`='%d', `fame`='%d'"
 			" WHERE  `account_id`='%d' AND `char_id` = '%d'",
 			" WHERE  `account_id`='%d' AND `char_id` = '%d'",
 			schema_config.char_db, p->class_,
 			schema_config.char_db, p->class_,
-			p->hair, p->hair_color, p->clothes_color,
+			p->hair, p->hair_color, p->clothes_color, p->body,
 			p->partner_id, p->father, p->mother, p->child,
 			p->partner_id, p->father, p->mother, p->child,
 			p->karma, p->manner, p->fame,
 			p->karma, p->manner, p->fame,
 			p->account_id, p->char_id) )
 			p->account_id, p->char_id) )
@@ -921,7 +921,7 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) {
 		"`char_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
 		"`char_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`,"
 		"`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`,"
 		"`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`,"
 		"`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`,"
-		"`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`,"
+		"`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`,"
 		"`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`"
 		"`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`"
 		" FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", schema_config.char_db, sd->account_id, MAX_CHARS)
 		" FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", schema_config.char_db, sd->account_id, MAX_CHARS)
 	||	SQL_ERROR == SqlStmt_Execute(stmt)
 	||	SQL_ERROR == SqlStmt_Execute(stmt)
@@ -952,21 +952,22 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) {
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 24, SQLDT_SHORT,  &p.hair, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 24, SQLDT_SHORT,  &p.hair, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 25, SQLDT_SHORT,  &p.hair_color, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 25, SQLDT_SHORT,  &p.hair_color, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 26, SQLDT_SHORT,  &p.clothes_color, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 26, SQLDT_SHORT,  &p.clothes_color, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 27, SQLDT_SHORT,  &p.weapon, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 28, SQLDT_SHORT,  &p.shield, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 29, SQLDT_SHORT,  &p.head_top, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT,  &p.head_mid, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT,  &p.head_bottom, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 32, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 33, SQLDT_SHORT,	&p.rename, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 34, SQLDT_UINT32, &p.delete_date, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 35, SQLDT_SHORT,  &p.robe, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 36, SQLDT_UINT,   &p.character_moves, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_LONG,   &p.unban_time, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_UCHAR,  &p.font, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_UINT,   &p.uniqueitem_counter, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 40, SQLDT_ENUM,   &sex, sizeof(sex), NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 41, SQLDT_UCHAR,   &p.hotkey_rowshift, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 27, SQLDT_SHORT,  &p.body, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 28, SQLDT_SHORT,  &p.weapon, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 29, SQLDT_SHORT,  &p.shield, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT,  &p.head_top, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT,  &p.head_mid, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 32, SQLDT_SHORT,  &p.head_bottom, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 33, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 34, SQLDT_SHORT,	&p.rename, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 35, SQLDT_UINT32, &p.delete_date, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 36, SQLDT_SHORT,  &p.robe, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_UINT,   &p.character_moves, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_LONG,   &p.unban_time, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_UCHAR,  &p.font, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 40, SQLDT_UINT,   &p.uniqueitem_counter, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 41, SQLDT_ENUM,   &sex, sizeof(sex), NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 42, SQLDT_UCHAR,   &p.hotkey_rowshift, 0, NULL, NULL)
 	)
 	)
 	{
 	{
 		SqlStmt_ShowDebug(stmt);
 		SqlStmt_ShowDebug(stmt);
@@ -1030,7 +1031,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 		"`char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
 		"`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`,"
 		"`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`,`homun_id`,`elemental_id`,`hair`,"
 		"`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`,"
-		"`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`,"
+		"`hair_color`,`clothes_color`,`body`,`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`,`rename`,`delete_date`,`robe`, `moves`,"
 		"`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`, `moves`,"
 		"`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`"
 		"`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`"
 		" FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db)
 		" FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db)
@@ -1069,31 +1070,32 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT,  &p->hair, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT,  &p->hair, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT,  &p->hair_color, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT,  &p->hair_color, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 32, SQLDT_SHORT,  &p->clothes_color, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 32, SQLDT_SHORT,  &p->clothes_color, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 33, SQLDT_SHORT,  &p->weapon, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 34, SQLDT_SHORT,  &p->shield, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 35, SQLDT_SHORT,  &p->head_top, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 36, SQLDT_SHORT,  &p->head_mid, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_SHORT,  &p->head_bottom, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_SHORT,  &p->last_point.x, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 40, SQLDT_SHORT,  &p->last_point.y, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 41, SQLDT_STRING, &save_map, sizeof(save_map), NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 42, SQLDT_SHORT,  &p->save_point.x, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 43, SQLDT_SHORT,  &p->save_point.y, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 44, SQLDT_UINT32,    &p->partner_id, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 45, SQLDT_UINT32,    &p->father, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 46, SQLDT_UINT32,    &p->mother, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 47, SQLDT_UINT32,    &p->child, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 48, SQLDT_INT,    &p->fame, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 49, SQLDT_SHORT,  &p->rename, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 50, SQLDT_UINT32, &p->delete_date, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 51, SQLDT_SHORT,  &p->robe, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 52, SQLDT_UINT32, &p->character_moves, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 53, SQLDT_LONG,   &p->unban_time, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 54, SQLDT_UCHAR,  &p->font, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 55, SQLDT_UINT,   &p->uniqueitem_counter, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 56, SQLDT_ENUM,   &sex, sizeof(sex), NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 57, SQLDT_UCHAR,  &p->hotkey_rowshift, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 33, SQLDT_SHORT,  &p->body, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 34, SQLDT_SHORT,  &p->weapon, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 35, SQLDT_SHORT,  &p->shield, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 36, SQLDT_SHORT,  &p->head_top, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_SHORT,  &p->head_mid, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_SHORT,  &p->head_bottom, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 40, SQLDT_SHORT,  &p->last_point.x, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 41, SQLDT_SHORT,  &p->last_point.y, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 42, SQLDT_STRING, &save_map, sizeof(save_map), NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 43, SQLDT_SHORT,  &p->save_point.x, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 44, SQLDT_SHORT,  &p->save_point.y, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 45, SQLDT_UINT32,    &p->partner_id, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 46, SQLDT_UINT32,    &p->father, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 47, SQLDT_UINT32,    &p->mother, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 48, SQLDT_UINT32,    &p->child, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 49, SQLDT_INT,    &p->fame, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 50, SQLDT_SHORT,  &p->rename, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 51, SQLDT_UINT32, &p->delete_date, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 52, SQLDT_SHORT,  &p->robe, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 53, SQLDT_UINT32, &p->character_moves, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 54, SQLDT_LONG,   &p->unban_time, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 55, SQLDT_UCHAR,  &p->font, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 56, SQLDT_UINT,   &p->uniqueitem_counter, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 57, SQLDT_ENUM,   &sex, sizeof(sex), NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 58, SQLDT_UCHAR,  &p->hotkey_rowshift, 0, NULL, NULL)
 	)
 	)
 	{
 	{
 		SqlStmt_ShowDebug(stmt);
 		SqlStmt_ShowDebug(stmt);
@@ -1733,12 +1735,12 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p)
 	WBUFW(buf,48) = min(p->max_sp, INT16_MAX);
 	WBUFW(buf,48) = min(p->max_sp, INT16_MAX);
 	WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed;
 	WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed;
 	WBUFW(buf,52) = p->class_;
 	WBUFW(buf,52) = p->class_;
+	WBUFW(buf,54) = p->hair;
+
 #if PACKETVER >= 20141022
 #if PACKETVER >= 20141022
-	WBUFL(buf,54) = p->hair;
+	WBUFW(buf,56) = p->body;
 	offset+=2;
 	offset+=2;
 	buf = WBUFP(buffer,offset);
 	buf = WBUFP(buffer,offset);
-#else
-	WBUFW(buf,54) = p->hair;
 #endif
 #endif
 
 
 	//When the weapon is sent and your option is riding, the client crashes on login!?
 	//When the weapon is sent and your option is riding, the client crashes on login!?

+ 2 - 2
src/common/mmo.h

@@ -21,7 +21,7 @@
 // see conf/battle/client.conf for other version
 // see conf/battle/client.conf for other version
 
 
 #ifndef PACKETVER
 #ifndef PACKETVER
-	#define PACKETVER 20130807
+	#define PACKETVER 20150513
 	//#define PACKETVER 20120410
 	//#define PACKETVER 20120410
 #endif
 #endif
 
 
@@ -390,7 +390,7 @@ struct mmo_charstatus {
 	unsigned int option;
 	unsigned int option;
 	short manner; // Defines how many minutes a char will be muted, each negative point is equivalent to a minute.
 	short manner; // Defines how many minutes a char will be muted, each negative point is equivalent to a minute.
 	unsigned char karma;
 	unsigned char karma;
-	short hair,hair_color,clothes_color;
+	short hair,hair_color,clothes_color,body;
 	int party_id,guild_id,pet_id,hom_id,mer_id,ele_id;
 	int party_id,guild_id,pet_id,hom_id,mer_id,ele_id;
 	int fame;
 	int fame;
 
 

+ 37 - 3
src/map/atcommand.c

@@ -1727,6 +1727,39 @@ ACMD_FUNC(model)
 	return 0;
 	return 0;
 }
 }
 
 
+/*==========================================
+ * @bodystyle [Rytech]
+ *------------------------------------------*/
+ACMD_FUNC(bodystyle)
+{
+	int body_style = 0;
+	nullpo_retr(-1, sd);
+
+	memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+	// Limit body styles to certain jobs since not all of them are released yet.
+	if (!((sd->class_&MAPID_THIRDMASK) == MAPID_GUILLOTINE_CROSS || (sd->class_&MAPID_THIRDMASK) == MAPID_GENETIC)) {
+		clif_displaymessage(fd, msg_txt(sd,770));	// This job has no alternate body styles.
+		return -1;
+	}
+
+	if (!message || !*message || sscanf(message, "%d", &body_style) < 1) {
+		sprintf(atcmd_output, msg_txt(sd,739), MIN_BODY_STYLE, MAX_BODY_STYLE);		// Please enter a body style (usage: @bodystyle <body ID: %d-%d>).
+		clif_displaymessage(fd, atcmd_output);
+		return -1;
+	}
+
+	if (body_style >= MIN_BODY_STYLE && body_style <= MAX_BODY_STYLE) {
+			pc_changelook(sd, LOOK_BODY2, body_style);
+			clif_displaymessage(fd, msg_txt(sd,36)); // Appearence changed.
+	} else {
+		clif_displaymessage(fd, msg_txt(sd,37)); // An invalid number was specified.
+		return -1;
+	}
+
+	return 0;
+}
+
 /*==========================================
 /*==========================================
  * @dye && @ccolor
  * @dye && @ccolor
  *------------------------------------------*/
  *------------------------------------------*/
@@ -5736,14 +5769,14 @@ ACMD_FUNC(divorce)
 ACMD_FUNC(changelook)
 ACMD_FUNC(changelook)
 {
 {
 	int i, j = 0, k = 0;
 	int i, j = 0, k = 0;
-	int pos[7] = { LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HEAD_BOTTOM,LOOK_WEAPON,LOOK_SHIELD,LOOK_SHOES,LOOK_ROBE };
+	int pos[8] = { LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HEAD_BOTTOM,LOOK_WEAPON,LOOK_SHIELD,LOOK_SHOES,LOOK_ROBE, LOOK_BODY2 };
 
 
 	if((i = sscanf(message, "%11d %11d", &j, &k)) < 1) {
 	if((i = sscanf(message, "%11d %11d", &j, &k)) < 1) {
 		clif_displaymessage(fd, msg_txt(sd,1177)); // Usage: @changelook {<position>} <view id>
 		clif_displaymessage(fd, msg_txt(sd,1177)); // Usage: @changelook {<position>} <view id>
-		clif_displaymessage(fd, msg_txt(sd,1178)); // Position: 1-Top 2-Middle 3-Bottom 4-Weapon 5-Shield 6-Shoes 7-Robe
+		clif_displaymessage(fd, msg_txt(sd,1178)); // Position: 1-Top 2-Middle 3-Bottom 4-Weapon 5-Shield 6-Shoes 7-Robe 8-Body
 		return -1;
 		return -1;
 	} else if ( i == 2 ) {
 	} else if ( i == 2 ) {
-		if (j < 1 || j > 7)
+		if (j < 1 || j > 8)
 			j = 1;
 			j = 1;
 		j = pos[j - 1];
 		j = pos[j - 1];
 	} else if( i == 1 ) {	// position not defined, use HEAD_TOP as default
 	} else if( i == 1 ) {	// position not defined, use HEAD_TOP as default
@@ -10036,6 +10069,7 @@ void atcommand_basecommands(void) {
 		ACMD_DEF(costume),
 		ACMD_DEF(costume),
 		ACMD_DEF(cloneequip),
 		ACMD_DEF(cloneequip),
 		ACMD_DEF(clonestat),
 		ACMD_DEF(clonestat),
+		ACMD_DEF(bodystyle),
 	};
 	};
 	AtCommandInfo* atcommand;
 	AtCommandInfo* atcommand;
 	int i;
 	int i;

+ 3 - 0
src/map/battle.c

@@ -8171,6 +8171,9 @@ static const struct _battle_data {
 	{ "monster_loot_search_type",           &battle_config.monster_loot_search_type,        1,      0,      1,              },
 	{ "monster_loot_search_type",           &battle_config.monster_loot_search_type,        1,      0,      1,              },
 	{ "feature.roulette",                   &battle_config.feature_roulette,                1,      0,      1,              },
 	{ "feature.roulette",                   &battle_config.feature_roulette,                1,      0,      1,              },
 	{ "monster_hp_bars_info",                &battle_config.monster_hp_bars_info,             1,      0,      1,              },
 	{ "monster_hp_bars_info",                &battle_config.monster_hp_bars_info,             1,      0,      1,              },
+	{ "min_body_style",                     &battle_config.min_body_style,                  0,      0,      SHRT_MAX,       },
+	{ "max_body_style",                     &battle_config.max_body_style,                  4,      0,      SHRT_MAX,       },
+	{ "save_body_style",                    &battle_config.save_body_style,                 0,      0,      1,              },
 };
 };
 
 
 #ifndef STATS_OPT_OUT
 #ifndef STATS_OPT_OUT

+ 5 - 0
src/map/battle.h

@@ -126,6 +126,8 @@ bool is_infinite_defense(struct block_list *target, int flag);
 #define MAX_HAIR_COLOR battle_config.max_hair_color
 #define MAX_HAIR_COLOR battle_config.max_hair_color
 #define MIN_CLOTH_COLOR battle_config.min_cloth_color
 #define MIN_CLOTH_COLOR battle_config.min_cloth_color
 #define MAX_CLOTH_COLOR battle_config.max_cloth_color
 #define MAX_CLOTH_COLOR battle_config.max_cloth_color
+#define MIN_BODY_STYLE battle_config.min_body_style
+#define MAX_BODY_STYLE battle_config.max_body_style
 
 
 extern struct Battle_Config
 extern struct Battle_Config
 {
 {
@@ -592,6 +594,9 @@ extern struct Battle_Config
 	int monster_loot_search_type;
 	int monster_loot_search_type;
 	int feature_roulette;
 	int feature_roulette;
 	int monster_hp_bars_info;
 	int monster_hp_bars_info;
+	int min_body_style;
+	int max_body_style;
+	int save_body_style;
 } battle_config;
 } battle_config;
 
 
 void do_init_battle(void);
 void do_init_battle(void);

+ 41 - 6
src/map/clif.c

@@ -1123,7 +1123,7 @@ static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool
 	WBUFB(buf,63) = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0;		// isBoss
 	WBUFB(buf,63) = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0;		// isBoss
 #endif
 #endif
 #if PACKETVER >= 20150513
 #if PACKETVER >= 20150513
-	WBUFW(buf,64) = 0;		// body
+	WBUFW(buf,64) = vd->body_style;		// body
 	offset+= 2;
 	offset+= 2;
 	buf = WBUFP(buffer,offset);
 	buf = WBUFP(buffer,offset);
 #endif
 #endif
@@ -1266,7 +1266,7 @@ static int clif_set_unit_walking(struct block_list* bl, struct unit_data* ud, un
 	WBUFB(buf,70) = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0;		// isBoss
 	WBUFB(buf,70) = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0;		// isBoss
 #endif
 #endif
 #if PACKETVER >= 20150513
 #if PACKETVER >= 20150513
-	WBUFW(buf,71) = 0;		// body
+	WBUFW(buf,71) = vd->body_style;		// body
 	offset+= 2;
 	offset+= 2;
 	buf = WBUFP(buffer,offset);
 	buf = WBUFP(buffer,offset);
 #endif
 #endif
@@ -1423,6 +1423,8 @@ int clif_spawn(struct block_list *bl)
 
 
 	if (vd->cloth_color)
 	if (vd->cloth_color)
 		clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
 		clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
+	if (vd->body_style)
+		clif_refreshlook(bl,bl->id,LOOK_BODY2,vd->body_style,AREA_WOS);
 
 
 	switch (bl->type)
 	switch (bl->type)
 	{
 	{
@@ -1696,6 +1698,8 @@ static void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_
 
 
 	if(vd->cloth_color)
 	if(vd->cloth_color)
 		clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
 		clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
+	if(vd->body_style)
+		clif_refreshlook(bl,bl->id,LOOK_BODY2,vd->body_style,AREA_WOS);
 
 
 	switch(bl->type) {
 	switch(bl->type) {
 	case BL_PC:
 	case BL_PC:
@@ -3400,6 +3404,11 @@ void clif_changelook(struct block_list *bl,int type,int val)
 					vd->cloth_color = 0;
 					vd->cloth_color = 0;
 				if (sd->sc.option&OPTION_OKTOBERFEST && battle_config.oktoberfest_ignorepalette)
 				if (sd->sc.option&OPTION_OKTOBERFEST && battle_config.oktoberfest_ignorepalette)
 					vd->cloth_color = 0;
 					vd->cloth_color = 0;
+				if (vd->body_style && (
+ 					sd->sc.option&OPTION_WEDDING || sd->sc.option&OPTION_XMAS ||
+ 					sd->sc.option&OPTION_SUMMER || sd->sc.option&OPTION_HANBOK ||
+ 					sd->sc.option&OPTION_OKTOBERFEST))
+ 					vd->body_style = 0;
 			}
 			}
 		break;
 		break;
 		case LOOK_HAIR:
 		case LOOK_HAIR:
@@ -3455,6 +3464,18 @@ void clif_changelook(struct block_list *bl,int type,int val)
 			return;
 			return;
 #else
 #else
 			vd->robe = val;
 			vd->robe = val;
+#endif
+		break;
+		case LOOK_BODY2:
+#if PACKETVER < 20150513
+			return;
+#else
+			if (val && (
+ 				sd->sc.option&OPTION_WEDDING || sd->sc.option&OPTION_XMAS ||
+ 				sd->sc.option&OPTION_SUMMER || sd->sc.option&OPTION_HANBOK ||
+ 				sd->sc.option&OPTION_OKTOBERFEST))
+ 				val = 0;
+ 			vd->body_style = val;
 #endif
 #endif
 		break;
 		break;
 	}
 	}
@@ -4534,6 +4555,9 @@ void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl)
 
 
 	if (vd->cloth_color)
 	if (vd->cloth_color)
 		clif_refreshlook(&sd->bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,SELF);
 		clif_refreshlook(&sd->bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,SELF);
+	if (vd->body_style)
+		clif_refreshlook(&sd->bl,bl->id,LOOK_BODY2,vd->body_style,SELF);
+
 	switch (bl->type)
 	switch (bl->type)
 	{
 	{
 	case BL_PC:
 	case BL_PC:
@@ -9165,6 +9189,8 @@ void clif_refresh(struct map_session_data *sd)
 		clif_spiritcharm_single(sd->fd, sd);
 		clif_spiritcharm_single(sd->fd, sd);
 	if (sd->vd.cloth_color)
 	if (sd->vd.cloth_color)
 		clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
 		clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
+	if (sd->vd.body_style)
+		clif_refreshlook(&sd->bl,sd->bl.id,LOOK_BODY2,sd->vd.body_style,SELF);
 	if(hom_is_active(sd->hd))
 	if(hom_is_active(sd->hd))
 		clif_send_homdata(sd,SP_ACK,0);
 		clif_send_homdata(sd,SP_ACK,0);
 	if( sd->md ) {
 	if( sd->md ) {
@@ -10027,6 +10053,9 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 
 
 	if(sd->vd.cloth_color)
 	if(sd->vd.cloth_color)
 		clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
 		clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
+	if(sd->vd.body_style)
+		clif_refreshlook(&sd->bl,sd->bl.id,LOOK_BODY2,sd->vd.body_style,SELF);
+
 	// item
 	// item
 	clif_inventorylist(sd);  // inventory list first, otherwise deleted items in pc_checkitem show up as 'unknown item'
 	clif_inventorylist(sd);  // inventory list first, otherwise deleted items in pc_checkitem show up as 'unknown item'
 	pc_checkitem(sd);
 	pc_checkitem(sd);
@@ -18597,16 +18626,22 @@ void clif_broadcast_obtain_special_item(const char *char_name, unsigned short na
 	clif_send(buf, WBUFW(buf, 2), NULL, ALL_CLIENT);
 	clif_send(buf, WBUFW(buf, 2), NULL, ALL_CLIENT);
 }
 }
 
 
-void clif_dressing_room(struct map_session_data *sd, int view) {
-	int fd;
+/// Show body view windows (ZC_DRESSROOM_OPEN).
+/// 0A02 <view>.W
+/// Value <flag> has the following effects:
+/// 0: Close an open Dress Room window.
+/// 1: Open a Dress Room window.
+void clif_dressing_room(struct map_session_data *sd, int flag) {
+#if PACKETVER >= 20150513
+	int fd = sd->fd;
 
 
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
-	fd = sd->fd;
 	WFIFOHEAD(fd, packet_len(0xa02));
 	WFIFOHEAD(fd, packet_len(0xa02));
 	WFIFOW(fd,0) = 0xa02;
 	WFIFOW(fd,0) = 0xa02;
-	WFIFOW(fd,2) = view;
+	WFIFOW(fd,2) = flag;
 	WFIFOSET(fd, packet_len(0xa02));
 	WFIFOSET(fd, packet_len(0xa02));
+#endif
 }
 }
 
 
 /// Parsing a request from the client item identify oneclick (CZ_REQ_ONECLICK_ITEMIDENTIFY).
 /// Parsing a request from the client item identify oneclick (CZ_REQ_ONECLICK_ITEMIDENTIFY).

+ 1 - 1
src/map/clif.h

@@ -968,7 +968,7 @@ void clif_merge_item_open(struct map_session_data *sd);
 
 
 void clif_broadcast_obtain_special_item(const char *char_name, unsigned short nameid, unsigned short container, enum BROADCASTING_SPECIAL_ITEM_OBTAIN type, const char *srcname);
 void clif_broadcast_obtain_special_item(const char *char_name, unsigned short nameid, unsigned short container, enum BROADCASTING_SPECIAL_ITEM_OBTAIN type, const char *srcname);
 
 
-void clif_dressing_room(struct map_session_data *sd, int view);
+void clif_dressing_room(struct map_session_data *sd, int flag);
 void clif_SelectCart(struct map_session_data *sd);
 void clif_SelectCart(struct map_session_data *sd);
 
 
 #endif /* _CLIF_H_ */
 #endif /* _CLIF_H_ */

+ 2 - 1
src/map/map.h

@@ -533,7 +533,8 @@ enum _look {
 	LOOK_BODY,			//Purpose Unknown. Doesen't appear to do anything.
 	LOOK_BODY,			//Purpose Unknown. Doesen't appear to do anything.
 	LOOK_RESET_COSTUMES,//Makes all headgear sprites on player vanish when activated.
 	LOOK_RESET_COSTUMES,//Makes all headgear sprites on player vanish when activated.
 	LOOK_ROBE,
 	LOOK_ROBE,
-	LOOK_FLOOR
+	// LOOK_FLOOR,	// TODO : fix me!! offcial use this ?
+	LOOK_BODY2
 };
 };
 
 
 // used by map_setcell()
 // used by map_setcell()

+ 54 - 37
src/map/pc.c

@@ -616,12 +616,16 @@ bool pc_can_give_bounded_items(struct map_session_data *sd)
  * Prepares character for saving.
  * Prepares character for saving.
  * @param sd
  * @param sd
  *------------------------------------------*/
  *------------------------------------------*/
-void pc_makesavestatus(struct map_session_data *sd)
-{
+void pc_makesavestatus(struct map_session_data *sd) {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
 	if(!battle_config.save_clothcolor)
 	if(!battle_config.save_clothcolor)
-		sd->status.clothes_color=0;
+		sd->status.clothes_color = 0;
+
+	// Since this is currently not officially released,
+	// its best to have a forced option to not save body styles.
+	if(!battle_config.save_body_style)
+		sd->status.body = 0;
 
 
 	//Only copy the Cart/Peco/Falcon options, the rest are handled via
 	//Only copy the Cart/Peco/Falcon options, the rest are handled via
 	//status change load/saving. [Skotlex]
 	//status change load/saving. [Skotlex]
@@ -632,7 +636,7 @@ void pc_makesavestatus(struct map_session_data *sd)
 #endif
 #endif
 	if (sd->sc.data[SC_JAILED]) { //When Jailed, do not move last point.
 	if (sd->sc.data[SC_JAILED]) { //When Jailed, do not move last point.
 		if(pc_isdead(sd)){
 		if(pc_isdead(sd)){
-			pc_setrestartvalue(sd,0);
+			pc_setrestartvalue(sd, 0);
 		} else {
 		} else {
 			sd->status.hp = sd->battle_status.hp;
 			sd->status.hp = sd->battle_status.hp;
 			sd->status.sp = sd->battle_status.sp;
 			sd->status.sp = sd->battle_status.sp;
@@ -643,8 +647,8 @@ void pc_makesavestatus(struct map_session_data *sd)
 		return;
 		return;
 	}
 	}
 
 
-	if(pc_isdead(sd)){
-		pc_setrestartvalue(sd,0);
+	if(pc_isdead(sd)) {
+		pc_setrestartvalue(sd, 0);
 		memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point));
 		memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point));
 	} else {
 	} else {
 		sd->status.hp = sd->battle_status.hp;
 		sd->status.hp = sd->battle_status.hp;
@@ -654,7 +658,7 @@ void pc_makesavestatus(struct map_session_data *sd)
 		sd->status.last_point.y = sd->bl.y;
 		sd->status.last_point.y = sd->bl.y;
 	}
 	}
 
 
-	if(map[sd->bl.m].flag.nosave){
+	if(map[sd->bl.m].flag.nosave) {
 		struct map_data *m=&map[sd->bl.m];
 		struct map_data *m=&map[sd->bl.m];
 		if(m->save.map)
 		if(m->save.map)
 			memcpy(&sd->status.last_point,&m->save,sizeof(sd->status.last_point));
 			memcpy(&sd->status.last_point,&m->save,sizeof(sd->status.last_point));
@@ -666,20 +670,19 @@ void pc_makesavestatus(struct map_session_data *sd)
 /*==========================================
 /*==========================================
  * Off init ? Connection?
  * Off init ? Connection?
  *------------------------------------------*/
  *------------------------------------------*/
-void pc_setnewpc(struct map_session_data *sd, uint32 account_id, uint32 char_id, int login_id1, unsigned int client_tick, int sex, int fd)
-{
+void pc_setnewpc(struct map_session_data *sd, uint32 account_id, uint32 char_id, int login_id1, unsigned int client_tick, int sex, int fd) {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
-	sd->bl.id        = account_id;
-	sd->status.account_id   = account_id;
-	sd->status.char_id      = char_id;
-	sd->status.sex   = sex;
-	sd->login_id1    = login_id1;
-	sd->login_id2    = 0; // at this point, we can not know the value :(
-	sd->client_tick  = client_tick;
+	sd->bl.id = account_id;
+	sd->status.account_id = account_id;
+	sd->status.char_id = char_id;
+	sd->status.sex = sex;
+	sd->login_id1 = login_id1;
+	sd->login_id2 = 0; // at this point, we can not know the value :(
+	sd->client_tick = client_tick;
 	sd->state.active = 0; //to be set to 1 after player is fully authed and loaded.
 	sd->state.active = 0; //to be set to 1 after player is fully authed and loaded.
-	sd->bl.type      = BL_PC;
-	sd->canlog_tick  = gettick();
+	sd->bl.type = BL_PC;
+	sd->canlog_tick = gettick();
 	//Required to prevent homunculus copuing a base speed of 0.
 	//Required to prevent homunculus copuing a base speed of 0.
 	sd->battle_status.speed = sd->base_status.speed = DEFAULT_WALK_SPEED;
 	sd->battle_status.speed = sd->base_status.speed = DEFAULT_WALK_SPEED;
 }
 }
@@ -1120,6 +1123,7 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
 	sd->status.hair = cap_value(sd->status.hair,MIN_HAIR_STYLE,MAX_HAIR_STYLE);
 	sd->status.hair = cap_value(sd->status.hair,MIN_HAIR_STYLE,MAX_HAIR_STYLE);
 	sd->status.hair_color = cap_value(sd->status.hair_color,MIN_HAIR_COLOR,MAX_HAIR_COLOR);
 	sd->status.hair_color = cap_value(sd->status.hair_color,MIN_HAIR_COLOR,MAX_HAIR_COLOR);
 	sd->status.clothes_color = cap_value(sd->status.clothes_color,MIN_CLOTH_COLOR,MAX_CLOTH_COLOR);
 	sd->status.clothes_color = cap_value(sd->status.clothes_color,MIN_CLOTH_COLOR,MAX_CLOTH_COLOR);
+	sd->status.body = cap_value(sd->status.body,MIN_BODY_STYLE,MAX_BODY_STYLE);
 
 
 	//Initializations to null/0 unneeded since map_session_data was filled with 0 upon allocation.
 	//Initializations to null/0 unneeded since map_session_data was filled with 0 upon allocation.
 	if(!sd->status.hp) pc_setdead(sd);
 	if(!sd->status.hp) pc_setdead(sd);
@@ -8204,6 +8208,11 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper)
 		pc_resetfeel(sd);
 		pc_resetfeel(sd);
 	}
 	}
 
 
+	// Reset body style to 0 before changing job to avoid
+	// errors since not every job has a alternate outfit.
+	sd->status.body = 0;
+	clif_changelook(&sd->bl,LOOK_BODY2,0);
+
 	sd->status.class_ = job;
 	sd->status.class_ = job;
 	fame_flag = pc_famerank(sd->status.char_id,sd->class_&MAPID_UPPERMASK);
 	fame_flag = pc_famerank(sd->status.char_id,sd->class_&MAPID_UPPERMASK);
 	sd->class_ = (unsigned short)b_class;
 	sd->class_ = (unsigned short)b_class;
@@ -8239,7 +8248,10 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper)
 	clif_changelook(&sd->bl,LOOK_BASE,sd->vd.class_); // move sprite update to prevent client crashes with incompatible equipment [Valaris]
 	clif_changelook(&sd->bl,LOOK_BASE,sd->vd.class_); // move sprite update to prevent client crashes with incompatible equipment [Valaris]
 	if(sd->vd.cloth_color)
 	if(sd->vd.cloth_color)
 		clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
 		clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
-
+	/*
+	if(sd->vd.body_style)
+		clif_changelook(&sd->bl,LOOK_BODY2,sd->vd.body_style);
+	*/
 	//Update skill tree.
 	//Update skill tree.
 	pc_calc_skilltree(sd);
 	pc_calc_skilltree(sd);
 	clif_skillinfoblock(sd);
 	clif_skillinfoblock(sd);
@@ -8325,59 +8337,62 @@ void pc_equiplookall(struct map_session_data *sd)
 /*==========================================
 /*==========================================
  * Tell client player sd has change look (hair,equip...)
  * Tell client player sd has change look (hair,equip...)
  *------------------------------------------*/
  *------------------------------------------*/
-void pc_changelook(struct map_session_data *sd,int type,int val)
-{
+void pc_changelook(struct map_session_data *sd,int type,int val) {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
-	switch(type){
+	switch(type) {
 	case LOOK_HAIR:	//Use the battle_config limits! [Skotlex]
 	case LOOK_HAIR:	//Use the battle_config limits! [Skotlex]
 		val = cap_value(val, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
 		val = cap_value(val, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
 
 
-		if (sd->status.hair != val)
-		{
-			sd->status.hair=val;
+		if (sd->status.hair != val) {
+			sd->status.hair = val;
 			if (sd->status.guild_id) //Update Guild Window. [Skotlex]
 			if (sd->status.guild_id) //Update Guild Window. [Skotlex]
-				intif_guild_change_memberinfo(sd->status.guild_id,sd->status.account_id,sd->status.char_id,
-				GMI_HAIR,&sd->status.hair,sizeof(sd->status.hair));
+				intif_guild_change_memberinfo(sd->status.guild_id, sd->status.account_id, sd->status.char_id,
+				GMI_HAIR, &sd->status.hair, sizeof(sd->status.hair));
 		}
 		}
 		break;
 		break;
 	case LOOK_WEAPON:
 	case LOOK_WEAPON:
-		sd->status.weapon=val;
+		sd->status.weapon = val;
 		break;
 		break;
 	case LOOK_HEAD_BOTTOM:
 	case LOOK_HEAD_BOTTOM:
-		sd->status.head_bottom=val;
+		sd->status.head_bottom = val;
 		break;
 		break;
 	case LOOK_HEAD_TOP:
 	case LOOK_HEAD_TOP:
-		sd->status.head_top=val;
+		sd->status.head_top = val;
 		break;
 		break;
 	case LOOK_HEAD_MID:
 	case LOOK_HEAD_MID:
-		sd->status.head_mid=val;
+		sd->status.head_mid = val;
 		break;
 		break;
 	case LOOK_HAIR_COLOR:	//Use the battle_config limits! [Skotlex]
 	case LOOK_HAIR_COLOR:	//Use the battle_config limits! [Skotlex]
 		val = cap_value(val, MIN_HAIR_COLOR, MAX_HAIR_COLOR);
 		val = cap_value(val, MIN_HAIR_COLOR, MAX_HAIR_COLOR);
 
 
 		if (sd->status.hair_color != val) {
 		if (sd->status.hair_color != val) {
-			sd->status.hair_color=val;
+			sd->status.hair_color = val;
 			if (sd->status.guild_id) //Update Guild Window. [Skotlex]
 			if (sd->status.guild_id) //Update Guild Window. [Skotlex]
-				intif_guild_change_memberinfo(sd->status.guild_id,sd->status.account_id,sd->status.char_id,
-				GMI_HAIR_COLOR,&sd->status.hair_color,sizeof(sd->status.hair_color));
+				intif_guild_change_memberinfo(sd->status.guild_id, sd->status.account_id, sd->status.char_id,
+				GMI_HAIR_COLOR, &sd->status.hair_color, sizeof(sd->status.hair_color));
 		}
 		}
 		break;
 		break;
 	case LOOK_CLOTHES_COLOR:	//Use the battle_config limits! [Skotlex]
 	case LOOK_CLOTHES_COLOR:	//Use the battle_config limits! [Skotlex]
 		val = cap_value(val, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
 		val = cap_value(val, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
 
 
-		sd->status.clothes_color=val;
+		sd->status.clothes_color = val;
 		break;
 		break;
 	case LOOK_SHIELD:
 	case LOOK_SHIELD:
-		sd->status.shield=val;
+		sd->status.shield = val;
 		break;
 		break;
 	case LOOK_SHOES:
 	case LOOK_SHOES:
 		break;
 		break;
 	case LOOK_ROBE:
 	case LOOK_ROBE:
 		sd->status.robe = val;
 		sd->status.robe = val;
 		break;
 		break;
+	case LOOK_BODY2:
+		val = cap_value(val, MIN_BODY_STYLE, MAX_BODY_STYLE);
+
+		sd->status.body = val;
+		break;
 	}
 	}
-	clif_changelook(&sd->bl,type,val);
+	clif_changelook(&sd->bl, type, val);
 }
 }
 
 
 /*==========================================
 /*==========================================
@@ -8470,6 +8485,8 @@ void pc_setoption(struct map_session_data *sd,int type)
 	clif_changelook(&sd->bl,LOOK_BASE,new_look);
 	clif_changelook(&sd->bl,LOOK_BASE,new_look);
 	if (sd->vd.cloth_color)
 	if (sd->vd.cloth_color)
 		clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
 		clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
+	if( sd->vd.body_style )
+		clif_changelook(&sd->bl,LOOK_BODY2,sd->vd.body_style);
 	clif_skillinfoblock(sd); // Skill list needs to be updated after base change.
 	clif_skillinfoblock(sd); // Skill list needs to be updated after base change.
 }
 }
 
 

+ 43 - 0
src/map/script.c

@@ -11207,6 +11207,8 @@ BUILDIN_FUNC(changebase)
 		clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
 		clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
 		if (sd->vd.cloth_color)
 		if (sd->vd.cloth_color)
 			clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
 			clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
+		if (sd->vd.body_style)
+			clif_changelook(&sd->bl,LOOK_BODY2,sd->vd.body_style);
 		clif_skillinfoblock(sd);
 		clif_skillinfoblock(sd);
 	}
 	}
 	return SCRIPT_CMD_SUCCESS;
 	return SCRIPT_CMD_SUCCESS;
@@ -14335,6 +14337,7 @@ BUILDIN_FUNC(getlook)
 		case LOOK_SHIELD:   	val=sd->status.shield; break; //8
 		case LOOK_SHIELD:   	val=sd->status.shield; break; //8
 		case LOOK_SHOES:    	break; //9
 		case LOOK_SHOES:    	break; //9
 		case LOOK_ROBE:     	val=sd->status.robe; break; //12
 		case LOOK_ROBE:     	val=sd->status.robe; break; //12
+		case LOOK_BODY2:		val=sd->status.body; break; //13
 	}
 	}
 
 
 	script_pushint(st,val);
 	script_pushint(st,val);
@@ -21020,6 +21023,44 @@ BUILDIN_FUNC(setquestinfo_job) {
 	return SCRIPT_CMD_SUCCESS;
 	return SCRIPT_CMD_SUCCESS;
 }
 }
 
 
+/**
+ * opendressroom({<char_id>});
+ */
+BUILDIN_FUNC(opendressroom)
+{
+#if PACKETVER >= 20150513
+    TBL_PC* sd;
+
+    if (!script_charid2sd(2, sd))
+        return SCRIPT_CMD_FAILURE;
+
+    clif_dressing_room(sd, 1);
+
+    return SCRIPT_CMD_SUCCESS;
+#else
+    return SCRIPT_CMD_FAILURE;
+#endif
+}
+
+/**
+ * closedressroom({<char_id>});
+ */
+BUILDIN_FUNC(closedressroom)
+{
+#if PACKETVER >= 20150513
+    TBL_PC* sd;
+
+    if (!script_charid2sd(2, sd))
+        return SCRIPT_CMD_FAILURE;
+
+    clif_dressing_room(sd, 0);
+
+    return SCRIPT_CMD_SUCCESS;
+#else
+    return SCRIPT_CMD_FAILURE;
+#endif
+}
+
 #include "../custom/script.inc"
 #include "../custom/script.inc"
 
 
 // declarations that were supposed to be exported from npc_chat.c
 // declarations that were supposed to be exported from npc_chat.c
@@ -21585,6 +21626,8 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(setquestinfo_level,"iii"),
 	BUILDIN_DEF(setquestinfo_level,"iii"),
 	BUILDIN_DEF(setquestinfo_req,"iii*"),
 	BUILDIN_DEF(setquestinfo_req,"iii*"),
 	BUILDIN_DEF(setquestinfo_job,"ii*"),
 	BUILDIN_DEF(setquestinfo_job,"ii*"),
+	BUILDIN_DEF(opendressroom,"?"),
+	BUILDIN_DEF(closedressroom,"?"),
 
 
 #include "../custom/script_def.inc"
 #include "../custom/script_def.inc"
 
 

+ 1 - 0
src/map/script_constants.h

@@ -689,6 +689,7 @@
 	export_constant(LOOK_BODY);
 	export_constant(LOOK_BODY);
 	export_constant(LOOK_RESET_COSTUMES);
 	export_constant(LOOK_RESET_COSTUMES);
 	export_constant(LOOK_ROBE);
 	export_constant(LOOK_ROBE);
+	export_constant(LOOK_BODY2);
 	// TODO: check why we didnt have floor and enable it
 	// TODO: check why we didnt have floor and enable it
 	//export_constant(LOOK_FLOOR);
 	//export_constant(LOOK_FLOOR);
 
 

+ 25 - 0
src/map/status.c

@@ -7177,6 +7177,7 @@ void status_set_viewdata(struct block_list *bl, int class_)
 				sd->vd.hair_style = cap_value(sd->status.hair,0,battle_config.max_hair_style);
 				sd->vd.hair_style = cap_value(sd->status.hair,0,battle_config.max_hair_style);
 				sd->vd.hair_color = cap_value(sd->status.hair_color,0,battle_config.max_hair_color);
 				sd->vd.hair_color = cap_value(sd->status.hair_color,0,battle_config.max_hair_color);
 				sd->vd.cloth_color = cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color);
 				sd->vd.cloth_color = cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color);
+				sd->vd.body_style = cap_value(sd->status.body,0,battle_config.max_body_style);
 				sd->vd.sex = sd->status.sex;
 				sd->vd.sex = sd->status.sex;
 
 
 				if (sd->vd.cloth_color) {
 				if (sd->vd.cloth_color) {
@@ -7191,6 +7192,11 @@ void status_set_viewdata(struct block_list *bl, int class_)
 					if(sd->sc.option&OPTION_OKTOBERFEST && battle_config.oktoberfest_ignorepalette)
 					if(sd->sc.option&OPTION_OKTOBERFEST && battle_config.oktoberfest_ignorepalette)
 						sd->vd.cloth_color = 0;
 						sd->vd.cloth_color = 0;
 				}
 				}
+				if ( sd->vd.body_style && (
+ 					sd->sc.option&OPTION_WEDDING || sd->sc.option&OPTION_XMAS ||
+ 					sd->sc.option&OPTION_SUMMER || sd->sc.option&OPTION_HANBOK ||
+ 					sd->sc.option&OPTION_OKTOBERFEST))
+ 					sd->vd.body_style = 0;
 			} else if (vd)
 			} else if (vd)
 				memcpy(&sd->vd, vd, sizeof(struct view_data));
 				memcpy(&sd->vd, vd, sizeof(struct view_data));
 			else
 			else
@@ -10305,6 +10311,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 				clif_changelook(bl,LOOK_WEAPON,0);
 				clif_changelook(bl,LOOK_WEAPON,0);
 				clif_changelook(bl,LOOK_SHIELD,0);
 				clif_changelook(bl,LOOK_SHIELD,0);
 				clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
 				clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
+				clif_changelook(bl,LOOK_BODY2,0);
 				break;
 				break;
 		}
 		}
 
 
@@ -10653,6 +10660,16 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		calc_flag&=~SCB_DYE;
 		calc_flag&=~SCB_DYE;
 	}
 	}
 
 
+	/*if (calc_flag&SCB_BODY)// Might be needed in the future. [Rytech]
+	{	//Reset body style
+		if (vd && vd->body_style)
+		{
+			val4 = vd->body_style;
+			clif_changelook(bl,LOOK_BODY2,0);
+		}
+		calc_flag&=~SCB_BODY;
+	}*/
+
 	if(!(flag&SCSTART_NOICON) && !(flag&SCSTART_LOADED && StatusDisplayType[type]))
 	if(!(flag&SCSTART_NOICON) && !(flag&SCSTART_LOADED && StatusDisplayType[type]))
 		clif_status_change(bl,StatusIconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0);
 		clif_status_change(bl,StatusIconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0);
 
 
@@ -11632,6 +11649,13 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 		calc_flag&=~SCB_DYE;
 		calc_flag&=~SCB_DYE;
 	}
 	}
 
 
+	/*if (calc_flag&SCB_BODY)// Might be needed in the future. [Rytech]
+	{	//Restore body style
+		if (vd && !vd->body_style && sce->val4)
+			clif_changelook(bl,LOOK_BODY2,sce->val4);
+		calc_flag&=~SCB_BODY;
+	}*/
+
 	// On Aegis, when turning off a status change, first goes the sc packet, then the option packet.
 	// On Aegis, when turning off a status change, first goes the sc packet, then the option packet.
 	clif_status_change(bl,StatusIconChangeTable[type],0,0,0,0,0);
 	clif_status_change(bl,StatusIconChangeTable[type],0,0,0,0,0);
 
 
@@ -11645,6 +11669,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 			clif_changelook(bl,LOOK_WEAPON,sd->vd.weapon);
 			clif_changelook(bl,LOOK_WEAPON,sd->vd.weapon);
 			clif_changelook(bl,LOOK_SHIELD,sd->vd.shield);
 			clif_changelook(bl,LOOK_SHIELD,sd->vd.shield);
 			clif_changelook(bl,LOOK_CLOTHES_COLOR,cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color));
 			clif_changelook(bl,LOOK_CLOTHES_COLOR,cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color));
+			clif_changelook(bl,LOOK_BODY2,cap_value(sd->status.body,0,battle_config.max_body_style));
 		}
 		}
 	}
 	}
 	if (calc_flag)
 	if (calc_flag)

+ 2 - 1
src/map/unit.h

@@ -74,7 +74,8 @@ struct view_data {
 		head_bottom,
 		head_bottom,
 		hair_style,
 		hair_style,
 		hair_color,
 		hair_color,
-		cloth_color;
+		cloth_color,
+		body_style;
 	char sex;
 	char sex;
 	unsigned dead_sit : 2;
 	unsigned dead_sit : 2;
 };
 };