Allow NPC view data modifications (#4385)

* Fixes #4289.
* Updated script commands setunitdata and getunitdata to support the modification of NPC view data.
* Converted mob_avail database to YAML.
Thanks to @Lemongrass3110, @4144, @exneval, @Balferian, @cahya1992 and @teededung!
Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
This commit is contained in:
Aleos 2019-12-22 10:27:43 -05:00 committed by GitHub
parent f2a1b6e784
commit b0119631a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 971 additions and 160 deletions

View File

@ -1,16 +0,0 @@
// Mob Availability and Alias Database
//
// Structure of Database:
// MobID,SpriteID{,Equipment}
//
// 01. MobID Mob ID to change.
// 02. SpriteID Mob ID which will be sent to the client instead of MobID.
// If 0, the mob becomes unavailable for use.
// 03. Equipment Item ID of pet equipment (must be available for pet counterpart, or this will cause problems).
//
// To disguise a mob as a player:
// MobID,SpriteID,Sex,Hair_Style,Hair_Color,Weapon,Shield,Head_Top,Head_Middle,Head_Bottom,Option,Dye_Color
//
// SpriteID is a job class value.
// Weapon and Shield uses Item ID, while Head uses View ID.

View File

@ -0,0 +1,123 @@
# This file is a part of rAthena.
# Copyright(C) 2019 rAthena Development Team
# https://rathena.org - https://github.com/rathena
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################
# Mob Availability and Alias Database
###########################################################################
#
# Mob Availability and Alias Settings
#
###########################################################################
# - Mob Mob to adjust.
# Sprite Sprite which will be sent to the client instead of Mob.
# Sex Sex (if Sprite is a player). (Default: Female)
# HairStyle Hair Style ID (if Sprite is a player). (Default: 0)
# HairColor Hair Color ID (if Sprite is a player). (Default: 0)
# ClothColor Cloth Color ID (if Sprite is a player). (Default: 0)
# Weapon Item name of weapon (if Sprite is a player). (Default: 0)
# Shield Item name of shield (if Sprite is a player). (Default: 0)
# HeadTop Item name of headgear (if Sprite is a player). (Default: 0)
# HeadMid Item name of headgear (if Sprite is a player). (Default: 0)
# HeadLow Item name of headgear (if Sprite is a player). (Default: 0)
# PetEquip Item name of pet equipment (if Mob is a valid pet). (Default: 0)
# Options: Set an option for an object. (Optional)
# <Option>: bool
###########################################################################
Header:
Type: MOB_AVAIL_DB
Version: 1
#Body:
# Examples
# - Mob: PORING
# Sprite: BAPHOMET
# - Mob: E_OBEAUNE
# Sprite: PORING
# PetEquip: Backpack
# Easter Event Monsters
# - Mob: MOROCC_3
# Sprite: DOPPELGANGER
# - Mob: MOROCC_4
# Sprite: ECLIPSE
# rAthena Dev Team
# Valaris
# - Mob: BOW_GUARDIAN_
# Sprite: JOB_ASSASSIN_CROSS
# Sex: Male
# HairStyle: 1
# HairColor: 1
# ClothColor: 1
# Weapon: Jamadhar
# HeadTop: Sahkkat
# HeadMid: Sunglasses
# HeadLow: Cigar
# Options:
# Falcon: true
# Valaris Worshiper
# - Mob: E_CONDOR
# Sprite: JOB_THIEF
# Sex: Male
# HairStyle: 1
# HairColor: 1
# ClothColor: 1
# Weapon: Gladius
# Shield: Guard
# HeadTop: Sahkkat
# HeadMid: Sunglasses
# HeadLow: Cigar
# MC Cameri
# - Mob: E_TREASURE1
# Sprite: JOB_CRUSADER
# Sex: Male
# HairStyle: 6
# HairColor: 6
# ClothColor: 3
# Weapon: Sword
# Shield: Shield
# Options:
# Riding: true
# Poki#3
# - Mob: E_TREASURE2
# Sprite: JOB_SNIPER
# Sex: Male
# HairStyle: 21
# Weapon: Bow_Of_Rudra
# HeadTop: Boy's_Cap
# HeadMid: Takius_Blindfold
# HeadLow: Centimental_Leaf
# Options:
# Falcon: true
# Sentry
# - Mob: BOMBPORING
# Sprite: KNIGHT_GUARDIAN
# iRO Halloween Event 2009
# - Mob: EP14_MORS_BOSSB
# Sprite: ZOMBIE
# - Mob: EP14_MORS_MOB1
# Sprite: GHOUL
# - Mob: EP14_MORS_MOB2
# Sprite: ZOMBIE_MASTER
# iRO Halloween Event 2009
# - Mob: EP14_3_DEATH_B_MOB2
# Sprite: WHISPER
# - Mob: EP14_3_DEATH_B_MOB3
# Sprite: DARK_LORD

View File

@ -1,44 +0,0 @@
// Mob Availability and Alias Database
//
// Structure of Database:
// MobID,SpriteID{,Equipment}
//
// 01. MobID Mob ID to change.
// 02. SpriteID Mob ID which will be sent to the client instead of MobID.
// If 0, the mob becomes unavailable for use.
// 03. Equipment Item ID of pet equipment (must be available for pet counterpart, or this will cause problems).
//
// To disguise a mob as a player:
// MobID,SpriteID,Sex,Hair_Style,Hair_Color,Weapon,Shield,Head_Top,Head_Middle,Head_Bottom,Option,Dye_Color
//
// SpriteID is a job class value.
// Weapon and Shield uses Item ID, while Head uses View ID.
// Option for carts only works if you compiled your server for a packet version before 2012-02-01
//1002,1039 // Poring - Baphomet
//1970,1002,10013 // Displays a Poring with a backpack
// Easter Event Monsters
//1920,1047,0
//1921,1093,0
// rAthena Dev Team
// Valaris
//1900,4013,1,1,1,1254,0,67,12,54,16,1
// Valaris Worshiper
//1901,6,1,1,1,1219,2101,67,12,54,0,1
// MC Cameri
//1902,14,1,6,6,1101,2105,0,0,0,32,3
// Poki#3
//1903,4012,1,21,0,1720,0,102,184,57,16,0
// Sentry
//1904,1286,0
// iRO Halloween Event 2008
//3000,1015,0
//3001,1036,0
//3002,1298,0
// iRO Halloween Event 2009
//3014,1179,0
//3015,1272,0

108
doc/mob_avail.txt Normal file
View File

@ -0,0 +1,108 @@
//===== rAthena Documentation ================================
//= rAthena Monster Availability Database Reference
//===== By: ==================================================
//= rAthena Dev Team
//===== Last Updated: ========================================
//= 20191213
//===== Description: =========================================
//= Explanation of the mob_avail.yml file and structure.
//============================================================
---------------------------------------
Mob: The AEGIS name of the monster.
---------------------------------------
Sprite: The name of the sprite the monster will be changed to.
This can be another mob, a player (prefixed with 'JOB_'), or an NPC. When using an NPC sprite,
the prefix is not required in the mob_avail database as the script engine will strip it.
Example:
- Mob: POPORING
Sprite: PORING # This will change the Poporing into a Poring.
- Mob: PORING
Sprite: JOB_STALKER # This will change the Poring into a Stalker.
- Mob: WOLF
Sprite: 4_M_BARBER # This will change the Wolf into the Barber NPC.
These constants can be found in src/map/script_constants.hpp.
---------------------------------------
Sex: The sex to be displayed if the Sprite is a player.
Valid types:
Female
Male
---------------------------------------
HairStyle: The hair style ID to be displayed if the Sprite is a player.
---------------------------------------
HairColor: The hair color ID to be displayed if the Sprite is a player.
---------------------------------------
ClothColor: The cloth color ID to be displayed if the Sprite is a player.
---------------------------------------
Weapon: The AEGIS name of the item to be displayed if the Sprite is a player.
---------------------------------------
Shield: The AEGIS name of the item to be displayed if the Sprite is a player.
---------------------------------------
HeadTop: The AEGIS name of the item to be displayed if the Sprite is a player.
---------------------------------------
HeadMid: The AEGIS name of the item to be displayed if the Sprite is a player.
---------------------------------------
HeadLow: The AEGIS name of the item to be displayed if the Sprite is a player.
---------------------------------------
PetEquip: The AEGIS name of the item to be displayed if the Mob is a valid pet.
---------------------------------------
Options: The view option to be applied to the Mob.
Valid types:
Sight
Cart1
Falcon
Riding
Cart2
Cart3
Cart4
Cart5
Orcish
Wedding
Ruwach
Flying
Xmas
Transform
Summer
Dragon1
Wug
WugRider
MadoGear
Dragon2
Dragon3
Dragon4
Dragon5
Hanbok
Oktoberfest
Summer2

View File

@ -7979,6 +7979,8 @@ Parameters (indexes) for monsters are:
UMOB_ADELAY
UMOB_DMOTION
UMOB_TARGETID
UMOB_ROBE
UMOB_BODY2
-----
@ -8152,7 +8154,6 @@ Parameter (indexes) for elementals are:
-----
Parameter (indexes) for NPCs are:
UNPC_DISPLAY
UNPC_LEVEL
UNPC_HP
UNPC_MAXHP
@ -8185,6 +8186,19 @@ Parameter (indexes) for NPCs are:
UNPC_AMOTION
UNPC_ADELAY
UNPC_DMOTION
UNPC_SEX
UNPC_CLASS
UNPC_HAIRSTYLE
UNPC_HAIRCOLOR
UNPC_HEADBOTTOM
UNPC_HEADMIDDLE
UNPC_HEADTOP
UNPC_CLOTHCOLOR
UNPC_SHIELD
UNPC_WEAPON
UNPC_ROBE
UNPC_BODY2
UNPC_DEADSIT
*Notes:
- *_SIZE: small (0); medium (1); large (2)
@ -8201,12 +8215,14 @@ Parameter (indexes) for NPCs are:
- *_AMOTION: see doc/mob_db.txt
- *_ADELAY: see doc/mob_db.txt
- *_DMOTION: see doc/mob_db.txt
- *_BODY2: enable (1) the alternate display, or disable (0)
- UMOB_AI: none (0); attack (1); marine sphere (2); flora (3); zanzou (4); legion (5); faw (6)
- UMOB_SCOPTION: see the 'Variables' section at the top of this document
- UMOB_SLAVECPYMSTRMD: make the slave copy the master's mode (1), or not (0)
- UNPC_PLUSALLSTAT: same as 'bAllStats'; increases/decreses all stats by given amount
- UNPC_PLUSALLSTAT: same as 'bAllStats'; increases/decreases all stats by given amount
- UNPC_DEADSIT: stand (0), dead (1), sit (2)
Example:
// Spawn some Porings and save the Game ID.

22
doc/yaml/db/mob_avail.yml Normal file
View File

@ -0,0 +1,22 @@
###########################################################################
# Mob Availability and Alias Database
###########################################################################
#
# Mob Availability and Alias Settings
#
###########################################################################
# - Mob Mob to adjust.
# Sprite Sprite which will be sent to the client instead of Mob.
# Sex Sex (if Sprite is a player). (Default: Female)
# HairStyle Hair Style ID (if Sprite is a player). (Default: 0)
# HairColor Hair Color ID (if Sprite is a player). (Default: 0)
# ClothColor Cloth Color ID (if Sprite is a player). (Default: 0)
# Weapon Item name of weapon (if Sprite is a player). (Default: 0)
# Shield Item name of shield (if Sprite is a player). (Default: 0)
# HeadTop Item name of headgear (if Sprite is a player). (Default: 0)
# HeadMid Item name of headgear (if Sprite is a player). (Default: 0)
# HeadLow Item name of headgear (if Sprite is a player). (Default: 0)
# PetEquip Item name of pet equipment (if Mob is a valid pet). (Default: 0)
# Options: Set an option for an object. (Optional)
# <Option>: bool
###########################################################################

View File

@ -283,7 +283,12 @@ static inline unsigned char clif_bl_type(struct block_list *bl) {
case BL_SKILL: return 0x3; //SKILL_TYPE
case BL_CHAT: return 0x4; //UNKNOWN_TYPE
case BL_MOB: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x5; //NPC_MOB_TYPE
case BL_NPC: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x6; //NPC_EVT_TYPE
case BL_NPC:
#if PACKETVER >= 20170726
return 0x6; //NPC_EVT_TYPE
#else
return (pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x6);
#endif
case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE
case BL_HOM: return 0x8; //NPC_HOM_TYPE
case BL_MER: return 0x9; //NPC_MERSOL_TYPE
@ -1144,7 +1149,7 @@ static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool
WBUFW(buf,53) = (sd ? sd->status.font : 0);
#endif
#if PACKETVER >= 20120221
if ( battle_config.monster_hp_bars_info && !map_getmapflag(bl->m, MF_HIDEMOBHPBAR) && bl->type == BL_MOB && (status_get_hp(bl) < status_get_max_hp(bl)) ) {
if ( battle_config.monster_hp_bars_info && bl->type == BL_MOB && !map_getmapflag(bl->m, MF_HIDEMOBHPBAR) && (status_get_hp(bl) < status_get_max_hp(bl)) ) {
WBUFL(buf,55) = status_get_max_hp(bl); // maxHP
WBUFL(buf,59) = status_get_hp(bl); // HP
} else {
@ -1447,7 +1452,7 @@ int clif_spawn(struct block_list *bl)
if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
return 0;
len = clif_set_unit_idle(bl, buf,true);
len = clif_set_unit_idle(bl, buf, (bl->type == BL_NPC && vd->dead_sit ? false : true));
clif_send(buf, len, bl, AREA_WOS);
if (disguised(bl))
clif_setdisguise(bl, buf, len);
@ -3606,7 +3611,7 @@ void clif_changelook(struct block_list *bl, int type, int val) {
#if PACKETVER < 20150513
return;
#else
if (val && sd->sc.option&OPTION_COSTUME)
if (val && sd && sd->sc.option&OPTION_COSTUME)
val = 0;
vd->body_style = val;
#endif
@ -3620,17 +3625,19 @@ void clif_changelook(struct block_list *bl, int type, int val) {
#if PACKETVER < 4
clif_sprite_change(bl, bl->id, type, val, 0, target);
#else
if(type == LOOK_WEAPON || type == LOOK_SHIELD) {
nullpo_retv(vd);
type = LOOK_WEAPON;
val = vd->weapon;
val2 = vd->shield;
}
if( disguised(bl) ) {
clif_sprite_change(bl, bl->id, type, val, val2, AREA_WOS);
clif_sprite_change(bl, -bl->id, type, val, val2, SELF);
if (bl->type != BL_NPC) {
if (type == LOOK_WEAPON || type == LOOK_SHIELD) {
type = LOOK_WEAPON;
val = (vd ? vd->weapon : 0);
val2 = (vd ? vd->shield : 0);
}
if (disguised(bl)) {
clif_sprite_change(bl, bl->id, type, val, val2, AREA_WOS);
clif_sprite_change(bl, -bl->id, type, val, val2, SELF);
} else
clif_sprite_change(bl, bl->id, type, val, val2, target);
} else
clif_sprite_change(bl, bl->id, type, val, val2, target);
unit_refresh(bl);
#endif
}

View File

@ -340,7 +340,7 @@
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\map_index.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\map_index.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mercenary_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mercenary_db.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mercenary_skill_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mercenary_skill_db.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mob_avail.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mob_avail.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mob_avail.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mob_avail.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mob_boss.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mob_boss.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mob_branch.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mob_branch.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\mob_chat_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\mob_chat_db.txt')" />

View File

@ -4348,55 +4348,308 @@ static int mob_read_sqldb(void)
return 0;
}
/*==========================================
* MOB display graphic change data reading
*------------------------------------------*/
static bool mob_readdb_mobavail(char* str[], int columns, int current)
{
int mob_id, sprite_id;
struct mob_db *db;
const std::string MobAvailDatabase::getDefaultLocation() {
return std::string(db_path) + "/" + DBIMPORT + "/mob_avail.yml";
}
mob_id = atoi(str[0]);
/**
* Reads and parses an entry from the mob_avail.
* @param node: YAML node containing the entry.
* @return count of successfully parsed rows
*/
uint64 MobAvailDatabase::parseBodyNode(const YAML::Node &node) {
std::string mob_name;
if( (db=mob_db(mob_id)) == NULL) // invalid class (probably undefined in db)
{
ShowWarning("mob_readdb_mobavail: Unknown mob id %d.\n", mob_id);
return false;
if (!this->asString(node, "Mob", mob_name))
return 0;
uint32 mob_id = mobdb_searchname(mob_name.c_str());
if (mob_id == 0) {
this->invalidWarning(node["Mob"], "Unknown mob %s.\n", mob_name.c_str());
return 0;
}
sprite_id = atoi(str[1]);
struct mob_db *mob = mob_db(mob_id);
memset(&db->vd, 0, sizeof(struct view_data));
db->vd.class_ = sprite_id;
if (this->nodeExists(node, "Sprite")) {
std::string sprite;
//Player sprites
if(pcdb_checkid(sprite_id) && columns==12) {
db->vd.sex=atoi(str[2]);
db->vd.hair_style=atoi(str[3]);
db->vd.hair_color=atoi(str[4]);
db->vd.weapon=atoi(str[5]);
db->vd.shield=atoi(str[6]);
db->vd.head_top=atoi(str[7]);
db->vd.head_mid=atoi(str[8]);
db->vd.head_bottom=atoi(str[9]);
db->option=atoi(str[10])&~(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE);
db->vd.cloth_color=atoi(str[11]); // Monster player dye option - Valaris
if (!this->asString(node, "Sprite", sprite))
return 0;
int constant;
if (script_get_constant(sprite.c_str(), &constant)) {
if (npcdb_checkid(constant) == 0 && pcdb_checkid(constant) == 0) {
this->invalidWarning(node["Sprite"], "Unknown sprite constant %s.\n", sprite.c_str());
return 0;
}
} else {
constant = mobdb_searchname(sprite.c_str());
if (constant == 0) {
this->invalidWarning(node["Sprite"], "Unknown mob sprite constant %s.\n", sprite.c_str());
return 0;
}
}
mob->vd.class_ = constant;
} else {
this->invalidWarning(node["Sprite"], "Sprite is missing.\n");
return 0;
}
if (this->nodeExists(node, "Sex")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["Sex"], "Sex is only applicable to Job sprites.\n");
return 0;
}
std::string sex;
if (!this->asString(node, "Sex", sex))
return 0;
std::string sex_constant = "SEX_" + sex;
int constant;
if (!script_get_constant(sex_constant.c_str(), &constant)) {
this->invalidWarning(node["Sex"], "Unknown sex constant %s.\n", sex.c_str());
return 0;
}
if (constant < SEX_FEMALE || constant > SEX_MALE) {
this->invalidWarning(node["Sex"], "Sex %s is not valid.\n", sex.c_str());
return 0;
}
mob->vd.sex = constant;
}
if (this->nodeExists(node, "HairStyle")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["HairStyle"], "HairStyle is only applicable to Job sprites.\n");
return 0;
}
uint16 hair_style;
if (!this->asUInt16(node, "HairStyle", hair_style))
return 0;
if (hair_style < MIN_HAIR_STYLE || hair_style > MAX_HAIR_STYLE) {
this->invalidWarning(node["HairStyle"], "HairStyle %d is out of range %d~%d. Setting to MIN_HAIR_STYLE.\n", hair_style, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
hair_style = MIN_HAIR_STYLE;
}
mob->vd.hair_style = hair_style;
}
if (this->nodeExists(node, "HairColor")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["HairColor"], "HairColor is only applicable to Job sprites.\n");
return 0;
}
uint16 hair_color;
if (!this->asUInt16(node, "HairColor", hair_color))
return 0;
if (hair_color < MIN_HAIR_COLOR || hair_color > MAX_HAIR_COLOR) {
this->invalidWarning(node["HairColor"], "HairColor %d is out of range %d~%d. Setting to MIN_HAIR_COLOR.\n", hair_color, MIN_HAIR_COLOR, MAX_HAIR_COLOR);
hair_color = MIN_HAIR_COLOR;
}
mob->vd.hair_color = hair_color;
}
if (this->nodeExists(node, "ClothColor")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["ClothColor"], "ClothColor is only applicable to Job sprites.\n");
return 0;
}
uint32 cloth_color;
if (!this->asUInt32(node, "ClothColor", cloth_color))
return 0;
if (cloth_color < MIN_CLOTH_COLOR || cloth_color > MAX_CLOTH_COLOR) {
this->invalidWarning(node["ClothColor"], "ClothColor %d is out of range %d~%d. Setting to MIN_CLOTH_CLOR.\n", cloth_color, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
cloth_color = MIN_CLOTH_COLOR;
}
mob->vd.cloth_color = cloth_color;
}
if (this->nodeExists(node, "Weapon")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["Weapon"], "Weapon is only applicable to Job sprites.\n");
return 0;
}
std::string weapon;
if (!this->asString(node, "Weapon", weapon))
return 0;
struct item_data *item = itemdb_searchname(weapon.c_str());
if (item == nullptr) {
this->invalidWarning(node["Weapon"], "Weapon %s is not a valid item.\n", weapon.c_str());
return 0;
}
mob->vd.weapon = item->nameid;
}
if (this->nodeExists(node, "Shield")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["Shield"], "Shield is only applicable to Job sprites.\n");
return 0;
}
std::string shield;
if (!this->asString(node, "Shield", shield))
return 0;
struct item_data *item = itemdb_searchname(shield.c_str());
if (item == nullptr) {
this->invalidWarning(node["Shield"], "Shield %s is not a valid item.\n", shield.c_str());
return 0;
}
mob->vd.shield = item->nameid;
}
if (this->nodeExists(node, "HeadTop")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["HeadTop"], "HeadTop is only applicable to Job sprites.\n");
return 0;
}
std::string head;
if (!this->asString(node, "HeadTop", head))
return 0;
struct item_data *item;
if ((item = itemdb_searchname(head.c_str())) == nullptr) {
this->invalidWarning(node["HeadTop"], "HeadTop %s is not a valid item.\n", head.c_str());
return 0;
}
mob->vd.head_top = item->look;
}
if (this->nodeExists(node, "HeadMid")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["HeadMid"], "HeadMid is only applicable to Job sprites.\n");
return 0;
}
std::string head;
if (!this->asString(node, "HeadMid", head))
return 0;
struct item_data *item = itemdb_searchname(head.c_str());
if (item == nullptr) {
this->invalidWarning(node["HeadMid"], "HeadMid %s is not a valid item.\n", head.c_str());
return 0;
}
mob->vd.head_mid = item->look;
}
if (this->nodeExists(node, "HeadLow")) {
if (pcdb_checkid(mob->vd.class_) == 0) {
this->invalidWarning(node["HeadLow"], "HeadLow is only applicable to Job sprites.\n");
return 0;
}
std::string head;
if (!this->asString(node, "HeadLow", head))
return 0;
struct item_data *item = itemdb_searchname(head.c_str());
if (item == nullptr) {
this->invalidWarning(node["HeadLow"], "HeadLow %s is not a valid item.\n", head.c_str());
return 0;
}
mob->vd.head_bottom = item->look;
}
if (this->nodeExists(node, "PetEquip")) {
std::shared_ptr<s_pet_db> pet_db_ptr = pet_db.find(mob->vd.class_);
if (pet_db_ptr == nullptr) {
this->invalidWarning(node["PetEquip"], "PetEquip is only applicable to defined pets.\n");
return 0;
}
std::string equipment;
if (!this->asString(node, "PetEquip", equipment))
return 0;
struct item_data *item = itemdb_searchname(equipment.c_str());
if (item == nullptr) {
this->invalidWarning(node["PetEquip"], "PetEquip %s is not a valid item.\n", equipment.c_str());
return 0;
}
mob->vd.head_bottom = item->nameid;
}
if (this->nodeExists(node, "Options")) {
for (const auto &optionNode : node["Options"]) {
std::string option = optionNode.first.as<std::string>();
std::string option_constant = "OPTION_" + option;
int constant;
if (!script_get_constant(option_constant.c_str(), &constant)) {
this->invalidWarning(optionNode, "Unknown option constant %s, skipping.\n", option.c_str());
continue;
}
bool active;
if (!this->asBool(node, option, active))
continue;
#ifdef NEW_CARTS
if( db->option & OPTION_CART ){
ShowWarning("mob_readdb_mobavail: You tried to use a cart for mob id %d. This does not work with setting an option anymore.\n", mob_id );
db->option &= ~OPTION_CART;
}
if (constant & OPTION_CART) {
this->invalidWarning(optionNode, "OPTION_CART was replace by SC_PUSH_CART, skipping.\n");
continue;
}
#endif
}
else if(columns==3)
db->vd.head_bottom=atoi(str[2]); // mob equipment [Valaris]
else if( columns != 2 )
return false;
return true;
if (active)
mob->option |= constant;
else
mob->option &= ~constant;
}
mob->option &= ~(OPTION_HIDE | OPTION_CLOAK | OPTION_INVISIBLE | OPTION_CHASEWALK); // Remove hiding types
}
return 1;
}
MobAvailDatabase mob_avail_db;
/*==========================================
* Reading of random monster data
* MobGroup,MobID,DummyName,Rate
@ -5282,7 +5535,6 @@ static void mob_load(void)
mob_readskilldb(dbsubpath2, silent);
}
sv_readdb(dbsubpath1, "mob_avail.txt", ',', 2, 12, -1, &mob_readdb_mobavail,silent);
sv_readdb(dbsubpath2, "mob_race2_db.txt", ',', 2, MAX_RACE2_MOBS, -1, &mob_readdb_race2, silent);
sv_readdb(dbsubpath1, "mob_item_ratio.txt", ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, &mob_readdb_itemratio, silent);
sv_readdb(dbsubpath1, "mob_chat_db.txt", '#', 3, 3, -1, &mob_parse_row_chatdb, silent);
@ -5299,6 +5551,8 @@ static void mob_load(void)
aFree(dbsubpath2);
}
mob_avail_db.load();
mob_drop_ratio_adjust();
mob_skill_db_set();
}
@ -5392,15 +5646,12 @@ static int mob_reload_sub( struct mob_data *md, va_list args ){
static int mob_reload_sub_npc( struct npc_data *nd, va_list args ){
// If the view data points to a mob
if( mobdb_checkid(nd->class_) ){
// Get the new view data from the mob database
nd->vd = mob_get_viewdata(nd->class_);
struct view_data *vd = mob_get_viewdata(nd->class_);
// If they are spawned right now
if( nd->bl.prev != NULL ){
// Respawn all NPCs on client side so that they are displayed correctly(if their view id changed)
clif_clearunit_area(&nd->bl, CLR_OUTSIGHT);
clif_spawn(&nd->bl);
}
if (vd) // Get the new view data from the mob database
memcpy(&nd->vd, vd, sizeof(struct view_data));
if (nd->bl.prev) // If they are spawned right now
unit_refresh(&nd->bl); // Respawn all NPCs on client side so that they are displayed correctly(if their view id changed)
}
return 0;

View File

@ -6,6 +6,7 @@
#include <vector>
#include "../common/database.hpp"
#include "../common/mmo.hpp" // struct item
#include "../common/timer.hpp"
@ -243,6 +244,17 @@ struct mob_data {
int tomb_nid;
};
class MobAvailDatabase : public YamlDatabase {
public:
MobAvailDatabase() : YamlDatabase("MOB_AVAIL_DB", 1) {
}
void clear() { };
const std::string getDefaultLocation();
uint64 parseBodyNode(const YAML::Node& node);
};
enum e_mob_skill_target {
MST_TARGET = 0,
MST_RANDOM, //Random Target!

View File

@ -118,8 +118,12 @@ struct script_event_s{
// Holds pointers to the commonly executed scripts for speedup. [Skotlex]
std::map<enum npce_event, std::vector<struct script_event_s>> script_event;
struct view_data* npc_get_viewdata(int class_)
{ //Returns the viewdata for normal npc classes.
/**
* Returns the viewdata for normal NPC classes.
* @param class_: NPC class ID
* @return viewdata or nullptr if the ID is invalid
*/
struct view_data* npc_get_viewdata(int class_) {
if( class_ == JT_INVISIBLE )
return &npc_viewdb[0];
if (npcdb_checkid(class_)){
@ -129,7 +133,7 @@ struct view_data* npc_get_viewdata(int class_)
return &npc_viewdb[class_];
}
}
return NULL;
return nullptr;
}
int npc_isnear_sub(struct block_list* bl, va_list args) {
@ -2556,17 +2560,18 @@ int npc_parseview(const char* w4, const char* start, const char* buffer, const c
* @return npc_data
*/
struct npc_data *npc_create_npc(int16 m, int16 x, int16 y){
struct npc_data *nd;
struct npc_data *nd = nullptr;
CREATE(nd, struct npc_data, 1);
nd->bl.id = npc_get_new_npc_id();
nd->bl.prev = nd->bl.next = NULL;
nd->bl.prev = nd->bl.next = nullptr;
nd->bl.m = m;
nd->bl.x = x;
nd->bl.y = y;
nd->sc_display = NULL;
nd->sc_display = nullptr;
nd->sc_display_count = 0;
nd->progressbar.timeout = 0;
nd->vd = npc_viewdb[0]; // Default to JT_INVISIBLE
return nd;
}
@ -3749,14 +3754,9 @@ void npc_setclass(struct npc_data* nd, short class_)
if( nd->class_ == class_ )
return;
struct map_data *mapdata = map_getmapdata(nd->bl.m);
if( mapdata->users )
clif_clearunit_area(&nd->bl, CLR_OUTSIGHT);// fade out
nd->class_ = class_;
status_set_viewdata(&nd->bl, class_);
if( mapdata->users )
clif_spawn(&nd->bl);// fade in
unit_refresh(&nd->bl);
}
// @commands (script based)

View File

@ -41,8 +41,8 @@ struct s_npc_buy_list {
struct npc_data {
struct block_list bl;
struct unit_data ud; //Because they need to be able to move....
struct view_data *vd;
struct unit_data ud; //Because they need to be able to move....
struct view_data vd;
struct status_change sc; //They can't have status changes, but.. they want the visual opt values.
struct npc_data *master_nd;
short class_,speed,instance_id;

View File

@ -16542,10 +16542,7 @@ BUILDIN_FUNC(setnpcdisplay)
if( class_ != JT_FAKENPC && nd->class_ != class_ )
npc_setclass(nd, class_);
else if( size != -1 )
{ // Required to update the visual size
clif_clearunit_area(&nd->bl, CLR_OUTSIGHT);
clif_spawn(&nd->bl);
}
unit_refresh(&nd->bl); // Required to update the visual size
script_pushint(st,0);
return SCRIPT_CMD_SUCCESS;
@ -17609,6 +17606,8 @@ BUILDIN_FUNC(getunitdata)
getunitdata_sub(UMOB_ADELAY, md->status.adelay);
getunitdata_sub(UMOB_DMOTION, md->status.dmotion);
getunitdata_sub(UMOB_TARGETID, md->target_id);
getunitdata_sub(UMOB_ROBE, md->vd->robe);
getunitdata_sub(UMOB_BODY2, md->vd->body_style);
break;
case BL_HOM:
@ -17797,7 +17796,6 @@ BUILDIN_FUNC(getunitdata)
ShowWarning("buildin_getunitdata: Error in finding object BL_NPC!\n");
return SCRIPT_CMD_FAILURE;
}
getunitdata_sub(UNPC_DISPLAY, nd->class_);
getunitdata_sub(UNPC_LEVEL, nd->level);
getunitdata_sub(UNPC_HP, nd->status.hp);
getunitdata_sub(UNPC_MAXHP, nd->status.max_hp);
@ -17830,6 +17828,19 @@ BUILDIN_FUNC(getunitdata)
getunitdata_sub(UNPC_AMOTION, nd->status.amotion);
getunitdata_sub(UNPC_ADELAY, nd->status.adelay);
getunitdata_sub(UNPC_DMOTION, nd->status.dmotion);
getunitdata_sub(UNPC_SEX, nd->vd.sex);
getunitdata_sub(UNPC_CLASS, nd->vd.class_);
getunitdata_sub(UNPC_HAIRSTYLE, nd->vd.hair_style);
getunitdata_sub(UNPC_HAIRCOLOR, nd->vd.hair_color);
getunitdata_sub(UNPC_HEADBOTTOM, nd->vd.head_bottom);
getunitdata_sub(UNPC_HEADMIDDLE, nd->vd.head_mid);
getunitdata_sub(UNPC_HEADTOP, nd->vd.head_top);
getunitdata_sub(UNPC_CLOTHCOLOR, nd->vd.cloth_color);
getunitdata_sub(UNPC_SHIELD, nd->vd.shield);
getunitdata_sub(UNPC_WEAPON, nd->vd.weapon);
getunitdata_sub(UNPC_ROBE, nd->vd.robe);
getunitdata_sub(UNPC_BODY2, nd->vd.body_style);
getunitdata_sub(UNPC_DEADSIT, nd->vd.dead_sit);
break;
default:
@ -17912,6 +17923,8 @@ BUILDIN_FUNC(setunitdata)
case UMOB_CLOTHCOLOR:
case UMOB_SHIELD:
case UMOB_WEAPON:
case UMOB_ROBE:
case UMOB_BODY2:
mob_set_dynamic_viewdata( md );
break;
}
@ -17929,8 +17942,8 @@ BUILDIN_FUNC(setunitdata)
case UMOB_MODE: md->base_status->mode = (enum e_mode)value; calc_status = true; break;
case UMOB_AI: md->special_state.ai = (enum mob_ai)value; break;
case UMOB_SCOPTION: md->sc.option = (unsigned short)value; break;
case UMOB_SEX: md->vd->sex = (char)value; clif_clearunit_area(bl, CLR_OUTSIGHT); clif_spawn(bl); break;
case UMOB_CLASS: status_set_viewdata(bl, (unsigned short)value); clif_clearunit_area(bl, CLR_OUTSIGHT); clif_spawn(bl); break;
case UMOB_SEX: md->vd->sex = (char)value; unit_refresh(bl); break;
case UMOB_CLASS: status_set_viewdata(bl, (unsigned short)value); unit_refresh(bl); break;
case UMOB_HAIRSTYLE: clif_changelook(bl, LOOK_HAIR, (unsigned short)value); break;
case UMOB_HAIRCOLOR: clif_changelook(bl, LOOK_HAIR_COLOR, (unsigned short)value); break;
case UMOB_HEADBOTTOM: clif_changelook(bl, LOOK_HEAD_BOTTOM, (unsigned short)value); break;
@ -17987,6 +18000,8 @@ BUILDIN_FUNC(setunitdata)
mob_target(md,target,0);
break;
}
case UMOB_ROBE: clif_changelook(bl, LOOK_ROBE, (unsigned short)value); break;
case UMOB_BODY2: clif_changelook(bl, LOOK_BODY2, (unsigned short)value); break;
default:
ShowError("buildin_setunitdata: Unknown data identifier %d for BL_MOB.\n", type);
return SCRIPT_CMD_FAILURE;
@ -18233,7 +18248,6 @@ BUILDIN_FUNC(setunitdata)
return SCRIPT_CMD_FAILURE;
}
switch (type) {
case UNPC_DISPLAY: status_set_viewdata(bl, (unsigned short)value); break;
case UNPC_LEVEL: nd->level = (unsigned int)value; break;
case UNPC_HP: nd->status.hp = (unsigned int)value; status_set_hp(bl, (unsigned int)value, 0); break;
case UNPC_MAXHP: nd->status.hp = nd->status.max_hp = (unsigned int)value; status_set_maxhp(bl, (unsigned int)value, 0); break;
@ -18265,6 +18279,19 @@ BUILDIN_FUNC(setunitdata)
case UNPC_AMOTION: nd->status.amotion = (short)value; break;
case UNPC_ADELAY: nd->status.adelay = (short)value; break;
case UNPC_DMOTION: nd->status.dmotion = (short)value; break;
case UNPC_SEX: nd->vd.sex = (char)value; unit_refresh(bl); break;
case UNPC_CLASS: npc_setclass(nd, (short)value); break;
case UNPC_HAIRSTYLE: clif_changelook(bl, LOOK_HAIR, (unsigned short)value); break;
case UNPC_HAIRCOLOR: clif_changelook(bl, LOOK_HAIR_COLOR, (unsigned short)value); break;
case UNPC_HEADBOTTOM: clif_changelook(bl, LOOK_HEAD_BOTTOM, (unsigned short)value); break;
case UNPC_HEADMIDDLE: clif_changelook(bl, LOOK_HEAD_MID, (unsigned short)value); break;
case UNPC_HEADTOP: clif_changelook(bl, LOOK_HEAD_TOP, (unsigned short)value); break;
case UNPC_CLOTHCOLOR: clif_changelook(bl, LOOK_CLOTHES_COLOR, (unsigned short)value); break;
case UNPC_SHIELD: clif_changelook(bl, LOOK_SHIELD, (unsigned short)value); break;
case UNPC_WEAPON: clif_changelook(bl, LOOK_WEAPON, (unsigned short)value); break;
case UNPC_ROBE: clif_changelook(bl, LOOK_ROBE, (unsigned short)value); break;
case UNPC_BODY2: clif_changelook(bl, LOOK_BODY2, (unsigned short)value); break;
case UNPC_DEADSIT: nd->vd.dead_sit = (char)value; unit_refresh(bl); break;
default:
ShowError("buildin_setunitdata: Unknown data identifier %d for BL_NPC.\n", type);
return SCRIPT_CMD_FAILURE;

View File

@ -476,6 +476,8 @@ enum unitdata_mobtypes {
UMOB_ADELAY,
UMOB_DMOTION,
UMOB_TARGETID,
UMOB_ROBE,
UMOB_BODY2,
};
enum unitdata_homuntypes {
@ -644,8 +646,7 @@ enum unitdata_elemtypes {
};
enum unitdata_npctypes {
UNPC_DISPLAY = 0,
UNPC_LEVEL,
UNPC_LEVEL = 0,
UNPC_HP,
UNPC_MAXHP,
UNPC_MAPID,
@ -677,6 +678,19 @@ enum unitdata_npctypes {
UNPC_AMOTION,
UNPC_ADELAY,
UNPC_DMOTION,
UNPC_SEX,
UNPC_CLASS,
UNPC_HAIRSTYLE,
UNPC_HAIRCOLOR,
UNPC_HEADBOTTOM,
UNPC_HEADMIDDLE,
UNPC_HEADTOP,
UNPC_CLOTHCOLOR,
UNPC_SHIELD,
UNPC_WEAPON,
UNPC_ROBE,
UNPC_BODY2,
UNPC_DEADSIT,
};
enum navigation_service {

View File

@ -3964,9 +3964,14 @@
export_constant(OPTION_SIGHT);
export_constant(OPTION_HIDE);
export_constant(OPTION_CLOAK);
export_constant(OPTION_CART1);
export_constant(OPTION_FALCON);
export_constant(OPTION_RIDING);
export_constant(OPTION_INVISIBLE);
export_constant(OPTION_CART2);
export_constant(OPTION_CART3);
export_constant(OPTION_CART4);
export_constant(OPTION_CART5);
export_constant(OPTION_ORCISH);
export_constant(OPTION_WEDDING);
export_constant(OPTION_RUWACH);
@ -3985,6 +3990,7 @@
export_constant(OPTION_DRAGON5);
export_constant(OPTION_HANBOK);
export_constant(OPTION_OKTOBERFEST);
export_constant(OPTION_SUMMER2);
/* status option compounds */
export_constant(OPTION_DRAGON);
@ -4059,6 +4065,8 @@
export_constant(UMOB_ADELAY);
export_constant(UMOB_DMOTION);
export_constant(UMOB_TARGETID);
export_constant(UMOB_ROBE);
export_constant(UMOB_BODY2);
/* unit control - homunculus */
export_constant(UHOM_SIZE);
@ -4222,7 +4230,7 @@
export_constant(UELE_TARGETID);
/* unit control - NPC */
export_constant(UNPC_DISPLAY);
export_deprecated_constant3("UNPC_DISPLAY", UNPC_CLASS, "UNPC_CLASS");
export_constant(UNPC_LEVEL);
export_constant(UNPC_HP);
export_constant(UNPC_MAXHP);
@ -4255,6 +4263,19 @@
export_constant(UNPC_AMOTION);
export_constant(UNPC_ADELAY);
export_constant(UNPC_DMOTION);
export_constant(UNPC_SEX);
export_constant(UNPC_CLASS);
export_constant(UNPC_HAIRSTYLE);
export_constant(UNPC_HAIRCOLOR);
export_constant(UNPC_HEADBOTTOM);
export_constant(UNPC_HEADMIDDLE);
export_constant(UNPC_HEADTOP);
export_constant(UNPC_CLOTHCOLOR);
export_constant(UNPC_SHIELD);
export_constant(UNPC_WEAPON);
export_constant(UNPC_ROBE);
export_constant(UNPC_BODY2);
export_constant(UNPC_DEADSIT);
export_constant(NAV_NONE);
export_constant(NAV_AIRSHIP_ONLY);
@ -4954,7 +4975,9 @@
/* NPC view ids */
// Special macro to strip the prefix 'JT_'
#define export_constant_npc(a) export_constant_offset(a,3)
#if !defined(export_constant_npc)
#define export_constant_npc(a) export_constant_offset(a,3)
#endif
export_constant_npc(JT_WARPNPC);
export_constant_npc(JT_1_ETC_01);

View File

@ -7797,7 +7797,7 @@ struct view_data* status_get_viewdata(struct block_list *bl)
case BL_PC: return &((TBL_PC*)bl)->vd;
case BL_MOB: return ((TBL_MOB*)bl)->vd;
case BL_PET: return &((TBL_PET*)bl)->vd;
case BL_NPC: return ((TBL_NPC*)bl)->vd;
case BL_NPC: return &((TBL_NPC*)bl)->vd;
case BL_HOM: return ((TBL_HOM*)bl)->vd;
case BL_MER: return ((TBL_MER*)bl)->vd;
case BL_ELEM: return ((TBL_ELEM*)bl)->vd;
@ -7861,10 +7861,10 @@ void status_set_viewdata(struct block_list *bl, int class_)
sd->vd.head_top = sd->status.head_top;
sd->vd.head_mid = sd->status.head_mid;
sd->vd.head_bottom = sd->status.head_bottom;
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.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.hair_style = cap_value(sd->status.hair, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
sd->vd.hair_color = cap_value(sd->status.hair_color, MIN_HAIR_COLOR, MAX_HAIR_COLOR);
sd->vd.cloth_color = cap_value(sd->status.clothes_color, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
sd->vd.body_style = cap_value(sd->status.body, MIN_BODY_STYLE, MAX_BODY_STYLE);
sd->vd.sex = sd->status.sex;
if (sd->vd.cloth_color) {
@ -7898,6 +7898,8 @@ void status_set_viewdata(struct block_list *bl, int class_)
mob_set_dynamic_viewdata( md );
md->vd->class_ = class_;
md->vd->hair_style = cap_value(md->vd->hair_style, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
md->vd->hair_color = cap_value(md->vd->hair_color, MIN_HAIR_COLOR, MAX_HAIR_COLOR);
}else
ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_);
}
@ -7923,15 +7925,18 @@ void status_set_viewdata(struct block_list *bl, int class_)
{
TBL_NPC* nd = (TBL_NPC*)bl;
if (vd)
nd->vd = vd;
else {
ShowError("status_set_viewdata (NPC): No view data for class %d\n", class_);
memcpy(&nd->vd, vd, sizeof(struct view_data));
else if (pcdb_checkid(class_)) {
memset(&nd->vd, 0, sizeof(struct view_data));
nd->vd.class_ = class_;
nd->vd.hair_style = cap_value(nd->vd.hair_style, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
} else {
if (bl->m >= 0)
ShowDebug("Source (NPC): %s at %s (%d,%d)\n", nd->name, map_mapid2mapname(bl->m), bl->x, bl->y);
else
ShowDebug("Source (NPC): %s (invisible/not on a map)\n", nd->name);
break;
}
break;
}
break;
case BL_HOM:

View File

@ -3134,6 +3134,26 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
return 1;
}
/**
* Refresh the area with a change in display of a unit.
* @bl: Object to update
*/
void unit_refresh(struct block_list *bl) {
nullpo_retv(bl);
if (bl->m < 0)
return;
struct map_data *mapdata = map_getmapdata(bl->m);
// Using CLR_TRICKDEAD because other flags show effects
// Probably need to use another flag or other way to refresh it
if (mapdata->users) {
clif_clearunit_area(bl, CLR_TRICKDEAD); // Fade out
clif_spawn(bl); // Fade in
}
}
/**
* Removes units of a master when the master is removed from map
* @param sd: Player

View File

@ -79,7 +79,7 @@ unsigned short
cloth_color,
body_style;
char sex;
unsigned dead_sit : 2;
unsigned dead_sit : 2; // 0: Standing, 1: Dead, 2: Sitting
};
/// Enum for unit_blown_immune
@ -161,6 +161,7 @@ void unit_dataset(struct block_list *bl);
// Remove unit
struct unit_data* unit_bl2ud(struct block_list *bl);
void unit_remove_map_pc(struct map_session_data *sd, clr_type clrtype);
void unit_refresh(struct block_list *bl);
void unit_free_pc(struct map_session_data *sd);
#define unit_remove_map(bl,clrtype) unit_remove_map_(bl,clrtype,__FILE__,__LINE__,__func__)
int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, int line, const char* func);

View File

@ -43,9 +43,9 @@
#include "../map/npc.hpp"
#include "../map/pc.hpp"
#include "../map/pet.hpp"
#include "../map/quest.hpp"
#include "../map/script.hpp"
#include "../map/storage.hpp"
#include "../map/quest.hpp"
using namespace rathena;
@ -73,9 +73,11 @@ static bool skill_parse_row_magicmushroomdb(char* split[], int column, int curre
static bool skill_parse_row_abradb(char* split[], int columns, int current);
static bool skill_parse_row_improvisedb(char* split[], int columns, int current);
static bool skill_parse_row_spellbookdb(char* split[], int columns, int current);
static bool mob_readdb_mobavail(char *str[], int columns, int current);
// Constants for conversion
std::unordered_map<uint16, std::string> aegis_itemnames;
std::unordered_map<uint16, uint16> aegis_itemviewid;
std::unordered_map<uint16, std::string> aegis_mobnames;
std::unordered_map<uint16, std::string> aegis_skillnames;
std::unordered_map<const char*, int32> constants;
@ -218,6 +220,7 @@ int do_init( int argc, char** argv ){
sv_readdb( path_db_import.c_str(), "skill_db.txt", ',', 18, 18, -1, parse_skill_constants, false );
// Load constants
#define export_constant_npc(a) export_constant(a)
#include "../map/script_constants.hpp"
std::vector<std::string> root_paths = {
@ -262,6 +265,12 @@ int do_init( int argc, char** argv ){
return 0;
}
if (!process("MOB_AVAIL_DB", 1, root_paths, "mob_avail", [](const std::string& path, const std::string& name_ext) -> bool {
return sv_readdb(path.c_str(), name_ext.c_str(), ',', 2, 12, -1, &mob_readdb_mobavail, false);
})) {
return 0;
}
// TODO: add implementations ;-)
return 0;
@ -419,6 +428,9 @@ static bool parse_item_constants( const char* path ){
aegis_itemnames[item_id] = std::string(name);
if (atoi(str[14]) & (EQP_HELM | EQP_COSTUME_HELM) && util::umap_find(aegis_itemviewid, (uint16)atoi(str[18])) == nullptr)
aegis_itemviewid[atoi(str[18])] = item_id;
count++;
}
@ -808,3 +820,233 @@ static bool skill_parse_row_spellbookdb(char* split[], int columns, int current)
return true;
}
// Copied and adjusted from mob.cpp
static bool mob_readdb_mobavail(char* str[], int columns, int current) {
uint16 mob_id = atoi(str[0]);
std::string *mob_name = util::umap_find(aegis_mobnames, mob_id);
if (mob_name == nullptr) {
ShowWarning("mob_avail reading: Invalid mob-class %hu, Mob not read.\n", mob_id);
return false;
}
body << YAML::Key << "Mob" << YAML::Value << *mob_name;
uint16 sprite_id = atoi(str[1]);
std::string *sprite_name = util::umap_find(aegis_mobnames, sprite_id);
if (sprite_name == nullptr) {
char *sprite = const_cast<char *>(constant_lookup(sprite_id, "JOB_"));
if (sprite == nullptr) {
sprite = const_cast<char *>(constant_lookup(sprite_id, "JT_"));
if (sprite == nullptr) {
ShowError("Sprite name %s is not known.\n", sprite);
return false;
}
sprite += 3; // Strip JT_ here because the script engine doesn't send this prefix for NPC.
body << YAML::Key << "Sprite" << YAML::Value << *sprite;
} else
body << YAML::Key << "Sprite" << YAML::Value << *sprite;
} else
body << YAML::Key << "Sprite" << YAML::Value << *sprite_name;
if (columns == 12) {
body << YAML::Key << "Sex" << YAML::Value << (atoi(str[2]) ? "Male" : "Female");
if (atoi(str[3]) != 0)
body << YAML::Key << "HairStyle" << YAML::Value << atoi(str[3]);
if (atoi(str[4]) != 0)
body << YAML::Key << "HairColor" << YAML::Value << atoi(str[4]);
if (atoi(str[11]) != 0)
body << YAML::Key << "ClothColor" << YAML::Value << atoi(str[11]);
if (atoi(str[5]) != 0) {
uint16 weapon_item_id = atoi(str[5]);
std::string *weapon_item_name = util::umap_find(aegis_itemnames, weapon_item_id);
if (weapon_item_name == nullptr) {
ShowError("Item name for item ID %hu (weapon) is not known.\n", weapon_item_id);
return false;
}
body << YAML::Key << "Weapon" << YAML::Value << *weapon_item_name;
}
if (atoi(str[6]) != 0) {
uint16 shield_item_id = atoi(str[6]);
std::string *shield_item_name = util::umap_find(aegis_itemnames, shield_item_id);
if (shield_item_name == nullptr) {
ShowError("Item name for item ID %hu (shield) is not known.\n", shield_item_id);
return false;
}
body << YAML::Key << "Shield" << YAML::Value << *shield_item_name;
}
if (atoi(str[7]) != 0) {
uint16 *headtop_item_id = util::umap_find(aegis_itemviewid, (uint16)atoi(str[7]));
if (headtop_item_id == nullptr) {
ShowError("Item ID for view ID %hu (head top) is not known.\n", atoi(str[7]));
return false;
}
std::string *headtop_item_name = util::umap_find(aegis_itemnames, *headtop_item_id);
if (headtop_item_name == nullptr) {
ShowError("Item name for item ID %hu (head top) is not known.\n", *headtop_item_id);
return false;
}
body << YAML::Key << "HeadTop" << YAML::Value << *headtop_item_name;
}
if (atoi(str[8]) != 0) {
uint16 *headmid_item_id = util::umap_find(aegis_itemviewid, (uint16)atoi(str[8]));
if (headmid_item_id == nullptr) {
ShowError("Item ID for view ID %hu (head mid) is not known.\n", atoi(str[8]));
return false;
}
std::string *headmid_item_name = util::umap_find(aegis_itemnames, *headmid_item_id);
if (headmid_item_name == nullptr) {
ShowError("Item name for item ID %hu (head mid) is not known.\n", *headmid_item_id);
return false;
}
body << YAML::Key << "HeadMid" << YAML::Value << *headmid_item_name;
}
if (atoi(str[9]) != 0) {
uint16 *headlow_item_id = util::umap_find(aegis_itemviewid, (uint16)atoi(str[9]));
if (headlow_item_id == nullptr) {
ShowError("Item ID for view ID %hu (head low) is not known.\n", atoi(str[9]));
return false;
}
std::string *headlow_item_name = util::umap_find(aegis_itemnames, *headlow_item_id);
if (headlow_item_name == nullptr) {
ShowError("Item name for item ID %hu (head low) is not known.\n", *headlow_item_id);
return false;
}
body << YAML::Key << "HeadLow" << YAML::Value << *headlow_item_name;
}
if (atoi(str[10]) != 0) {
uint32 options = atoi(str[10]);
body << YAML::Key << "Options";
body << YAML::BeginMap;
while (options > OPTION_NOTHING && options <= OPTION_SUMMER2) {
if (options & OPTION_SIGHT) {
body << YAML::Key << "Sight" << YAML::Value << "true";
options &= ~OPTION_SIGHT;
} else if (options & OPTION_CART1) {
body << YAML::Key << "Cart1" << YAML::Value << "true";
options &= ~OPTION_CART1;
} else if (options & OPTION_FALCON) {
body << YAML::Key << "Falcon" << YAML::Value << "true";
options &= ~OPTION_FALCON;
} else if (options & OPTION_RIDING) {
body << YAML::Key << "Riding" << YAML::Value << "true";
options &= ~OPTION_RIDING;
} else if (options & OPTION_CART2) {
body << YAML::Key << "Cart2" << YAML::Value << "true";
options &= ~OPTION_CART2;
} else if (options & OPTION_CART3) {
body << YAML::Key << "Cart2" << YAML::Value << "true";
options &= ~OPTION_CART3;
} else if (options & OPTION_CART4) {
body << YAML::Key << "Cart4" << YAML::Value << "true";
options &= ~OPTION_CART4;
} else if (options & OPTION_CART5) {
body << YAML::Key << "Cart5" << YAML::Value << "true";
options &= ~OPTION_CART5;
} else if (options & OPTION_ORCISH) {
body << YAML::Key << "Orcish" << YAML::Value << "true";
options &= ~OPTION_ORCISH;
} else if (options & OPTION_WEDDING) {
body << YAML::Key << "Wedding" << YAML::Value << "true";
options &= ~OPTION_WEDDING;
} else if (options & OPTION_RUWACH) {
body << YAML::Key << "Ruwach" << YAML::Value << "true";
options &= ~OPTION_RUWACH;
} else if (options & OPTION_FLYING) {
body << YAML::Key << "Flying" << YAML::Value << "true";
options &= ~OPTION_FLYING;
} else if (options & OPTION_XMAS) {
body << YAML::Key << "Xmas" << YAML::Value << "true";
options &= ~OPTION_XMAS;
} else if (options & OPTION_TRANSFORM) {
body << YAML::Key << "Transform" << YAML::Value << "true";
options &= ~OPTION_TRANSFORM;
} else if (options & OPTION_SUMMER) {
body << YAML::Key << "Summer" << YAML::Value << "true";
options &= ~OPTION_SUMMER;
} else if (options & OPTION_DRAGON1) {
body << YAML::Key << "Dragon1" << YAML::Value << "true";
options &= ~OPTION_DRAGON1;
} else if (options & OPTION_WUG) {
body << YAML::Key << "Wug" << YAML::Value << "true";
options &= ~OPTION_WUG;
} else if (options & OPTION_WUGRIDER) {
body << YAML::Key << "WugRider" << YAML::Value << "true";
options &= ~OPTION_WUGRIDER;
} else if (options & OPTION_MADOGEAR) {
body << YAML::Key << "MadoGear" << YAML::Value << "true";
options &= ~OPTION_MADOGEAR;
} else if (options & OPTION_DRAGON2) {
body << YAML::Key << "Dragon2" << YAML::Value << "true";
options &= ~OPTION_DRAGON2;
} else if (options & OPTION_DRAGON3) {
body << YAML::Key << "Dragon3" << YAML::Value << "true";
options &= ~OPTION_DRAGON3;
} else if (options & OPTION_DRAGON4) {
body << YAML::Key << "Dragon4" << YAML::Value << "true";
options &= ~OPTION_DRAGON4;
} else if (options & OPTION_DRAGON5) {
body << YAML::Key << "Dragon5" << YAML::Value << "true";
options &= ~OPTION_DRAGON5;
} else if (options & OPTION_HANBOK) {
body << YAML::Key << "Hanbok" << YAML::Value << "true";
options &= ~OPTION_HANBOK;
} else if (options & OPTION_OKTOBERFEST) {
body << YAML::Key << "Oktoberfest" << YAML::Value << "true";
options &= ~OPTION_OKTOBERFEST;
} else if (options & OPTION_SUMMER2) {
body << YAML::Key << "Summer2" << YAML::Value << "true";
options &= ~OPTION_SUMMER2;
}
}
body << YAML::EndMap;
}
} else if (columns == 3) {
if (atoi(str[5]) != 0) {
uint16 peteq_item_id = atoi(str[5]);
std::string *peteq_item_name = util::umap_find(aegis_itemnames, peteq_item_id);
if (peteq_item_name == nullptr) {
ShowError("Item name for item ID %hu (pet equip) is not known.\n", peteq_item_id);
return false;
}
body << YAML::Key << "PetEquip" << YAML::Value << *peteq_item_name;
}
}
body << YAML::EndMap;
return true;
}