diff --git a/Changelog.txt b/Changelog.txt index 034689fd8..8b1d80e14 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -3914,3 +3914,236 @@ Added: 'H' shortcut for variables to get the value as hexadecimal. 13-10-2024, Jhobean - Added: @PetRelease trigger work like @petdesert and return 1 to prevent the pet from being released. + +07-11-2024, canerksk +- Fixed: If you are around an entity that you own and you are trying to open a bank next to a bank, the "bank" command is perceived as a pet command and the bank does not open. There is no problem when you type "bank" after mounting the mount, but "bank" does not work when you dismount. (Issue #1331) +25-10-2024, canerksk +- Added: Added more server (maria db) function + + [PLEVEL 7] + f_onserver_db_connect + f_onserver_db_query + f_onserver_db_execute + f_onserver_db_close + f_onserver_db_tick + + [FUNCTION f_onserver_db_connect] + // Called when the database is connected. + // Variables: + // Return: + // 1 --> Prevents the connection. + + [FUNCTION f_onserver_db_query] + // called when the database makes a query + // Variables: + // LOCAL.QUERY (R) = Query details (string) + // LOCAL.NUMROWS (R) = NUMROWS + // LOCAL.NUMCOLS (R) = NUMCOLS + // LOCAL.ISERROR (R) = Did the execute succeed or did it return an error? (1/0) + // Return: + // 1 --> Prevents the query. + + [FUNCTION f_onserver_db_execute] + // Called when the database performs an execute. + // Variables: + // LOCAL.EXECUTE (R) = Execute details (string) + // LOCAL.ISERROR (R) = Did the execute succeed or did it return an error? (1/0) + // Return: + // 1 --> Prevents the execute + + [FUNCTION f_onserver_db_close] + // Called when the database connection is closed. + + [FUNCTION f_onserver_db_tick] + // It is called when the database receives a tick (ping). + // Variables: + // ARGN1 (R/W) = how long will database be a tick? mysqle ping sending time. (Default: 1000ms) +26-10-2024, canerksk +- Added: New client function CLOSECONTAINER and CLOSEVENDORMENU + CLOSEVENDORMENU OR CLOSEVENDORMENU empty if empty, 14 squares will close the sales menu of all near shops. + If no value is entered, it closes the sales menu of all vendors in the surrounding area (14 squares). If a value is entered and this value is the vendor uid, it closes the sales menu of the vendor belonging to that serial number. + CLOSECONTAINER + Actually, there is already a package for this, but since dealing with the package takes a long time, such a command was needed. When CLOSECONTAINER is entered, that container is closed in the interface. + +25-10-2024, canerksk +- Added: Added NOREJOIN tag for corpses, If the NOREJOIN tag is one, the player cannot come back to life on that corpse. That is, the player comes back to life but not on the corpse. + This tag does not prevent the player from coming back to life, it just prevents them from respawning on that corpse. The player always comes back to life, but not on that corpse. +25-10-2024, canerksk +- Added: New trigger @HitReactive Reactive Armor plays a key role in the action mechanism and is currently under very fixed rules. It has been added as a new trigger with some variables to make it a bit more flexible. + @HitReactive + Local.Sound (r/w) =Sound ID, If it is blank or zero, no sound is produced. (Default: 01F1) + Local.EffectID (r/w) = Effect ID, If it is empty or zero, no effect will occur. (Default: 0374a) + Local.Damage (r/w) = This is the more1l, or PolyStr, value coming from the reactive armor spell to i_rune_reactive_armor. + LOCAL.RefDamage (r/w) = The amount of damage that will be reflected to this other party + LOCAL.RedDamage (r/w) = Amount to be deducted from damage received + LOCAL.ReactiveDamType (r/w) = Type of damage received (Default: DAMAGE_FIXED andDAMAGE_REACTIVE) + NOTE; + 1. If no RefDamage or RedDamage values ​​are entered, the system defaults to the Reactive Armor Effect value. + 2. No damage amount can be less than 1. +26-10-2024, canerksk +- Fixed: Memory OnTick link char if not, the skill will not fail. + If the memory owner is not present, skills do not fail when taking damage, but if the memory owner is alive, skills fail when taking damage. + +- Fixed: The entity was jumping frames in flooded pet commands (come, guard) + +25-10-2024, canerksk +- Added: @Logout trigger When NPCs (mounts) are logged out of the session in any way, i.e. mounted or minimised, they will log out (disconnect) but will not trigger. +25-10-2024, canerksk +- Added: Added more server function + + [PLEVEL 7] + f_onserver_mode + + f_onserver_resync_start + f_onserver_resync_restart + f_onserver_resync_failed + f_onserver_resync_success + f_onserver_resync_finish + + f_onserver_save_stage + f_onserver_save_force + f_onserver_save_try + + [FUNCTION f_onserver_mode] + // It is triggered when the server changes mode in any way, for example, saving, resyncing, loading, closing, etc. + // Variables: + // ARGN1 (R) = Server modes; + // RestockAll = 0 + // GarbageCollection = 1 + // Saving = 2 + // Running = 3 + // ResyncPause = 4 + // PreLoadingINI = 5 + // Loading = 6 + // ResyncLoad = 7 + // Exiting = 8 + + [FUNCTION f_onserver_resync_start] + // Triggered when resync starts. + // Variables: + // ARGN1 (R) = Resync Paused + // ARGN2 (R/W) = Broadcast message, 1(true) = with broadcast, 0(false) = without broadcast + + [FUNCTION f_onserver_resync_restart] + // Triggered when resync re-starts. (Only trig) + + [FUNCTION f_onserver_resync_failed] + // Triggered when resync failed. + // Variables: + // ARGN1 (R/W) = Broadcast message, 1(true) = with broadcast, 0(false) = without broadcast + + [FUNCTION f_onserver_resync_success] + // Triggered when resync success. + // Variables: + // ARGN1 (R/W) = Broadcast message, 1(true) = with broadcast, 0(false) = without broadcast + + [FUNCTION f_onserver_resync_finish] + // Triggered when resync finished. + // Variables: + // ARGN1 (R) = Is paused status + + [FUNCTION f_onserver_save_stage] + // Triggered when save mode changes + // Variables: + // ARGN1 (R) = save stage num + + [FUNCTION f_onserver_save_force] + // Triggered when save force mode + // Variables: + // ARGN1 (R) = save stage + // ARGN2 (R) = save stage num + + [FUNCTION f_onserver_save_try] + // Variables: + // ARGN1 (R) = Force Immediate + // ARGN2 (R) = Save Timer +25-10-2024, canerksk +- Added: New optionflag OF_PetBehaviorOwnerNeutral + If this setting is enabled, pets you own will appear to you with the original noto (old behavior). If this setting is not enabled, the assets you own will always appear natural to you. This setting only applies when the owning character is looking at the asset they own. It does not apply when someone else is looking at the asset you own. + +26-10-2024, canerksk +- Added: New OVERRIDE.MOVESTYLE tag and OF_NPCMovementOldStyle + Old style NPC walking has been added as an option. + If OVERRIDE.MOVESTYLE=1 is given to an entity, this indicates that the movement of that entity will be done using the old style calculation. + You can use OF_NPCMovementOldStyle to apply to all entities without needing a tag. +13-10-2024, canerksk + Event definitions in Sphere.ini have been made more extensive. + + // Events related to all NPCs (out of use) + //EventsPet=e_npc_generic_event + + // Events related to all NPCs + EventsNPC=e_npc_all + + // Events related to all animals (brain_animal without mountables) + EventsNPCAnimal=e_npc_animals + + // Events related to all monsters (brain_monster, brain_dragon, brain_berserk) + EventsNPCMonster=e_npc_monsters + + // Events related to all animals (brain_animal with mountable) + EventsNPCMountable=e_npc_mountables + + // Events related to all shopkeepers (brain_vendor, brain_stable, brain_healer) + EventsNPCShop=e_npc_shopkeepers + + // Events related to all char (player and staff) + EventsChar=e_char_all + + // Events related to all players (if the plevel is lower than 1) + EventsCharPlayer=e_char_players + + // Events related to all regions + //EventsRegion=er_region_all + + // Events related to all staff (if plevel is higher than 1) + EventsCharStaff=e_char_staffs + + // Events related to all players (out of use) + //EventsPlayer= + + // Events related to all items + EventsItem=ei_items + + // Events related to all weapons + EventsItemWeapon=ei_item_weapons +26-10-2024, canerksk +- Added: New @Deposit/@ItemDeposit trigger, + SRC/I = Char (Player) + ARGO = Gold + RETURN 1 = Prevents depositing gold into the bank. + It works just like DROPON_ITEM/ITEMDROPON_ITEM. + When using the virtual gold feature, there was no trigger triggered when physical gold was deposited into the bank, the Dropon_Item was not working because it was triggered lower down, and instead of moving the Dropon_Item up, a separate trigger was added to track the money on the server more easily from another location. + +26-10-2024, canerksk +- Added: Added new local variable in WOPSYSTEM LOCAL.WOPTalkMode and ini settings WOPTalkMode=10 + In some clients, different operations can be performed in return for this, for example, if this talkmode went from sphere as TALKMODE SPELL, the operation is performed according to this incoming talk mode according to the client versions. Here, the possibility of changing the talkmode according to the client version used is given. By default, TALKMODE_SPELL + Default talk mode: TALKMODE_SPELL = 10 + Sphere.ini; + WOPTalkMode=0/14 + + Trigger; + @SpellCast + Local.WOPTalkMode=0/14 + + TALKMODE_SAY = 0 // A character speaking. + TALKMODE_SYSTEM = 1 // Display as system prompt + TALKMODE_EMOTE = 2 // *smiles* at object (client shortcut: :+space) + TALKMODE_ITEM = 6 // text labeling an item. Preceeded by "You see" + TALKMODE_NOSCROLL = 7 // As a status msg. Does not scroll (as reported by the packet guides) + TALKMODE_WHISPER = 8 // Only those close can here. (client shortcut: ;+space) + TALKMODE_YELL = 9 // Can be heard 2 screens away. (client shortcut: !+space) + TALKMODE_SPELL = 10 // Used by spells + TALKMODE_GUILD = 13 // Used by guild chat (client shortcut: \) + TALKMODE_ALLIANCE = 14 // Used by alliance chat (client shortcut: shift+\) + +08-11-2024 +- Fixed: ITEMMEMORYEQUIP is not triggered in many default memories. + Previously, many memories did not have morex information, but now for some reason many memories have morex information and for this reason the trigger was not triggered in almost most of the memories, the morex query was removed. Also, the slang did not represent the memory item. +10-11-2024, canerksk +- Added: To get the character's final karma and fame titles, a lot of combined querying was required with the script, so the NOTOTITLE function was added, + .show NOTOTITLE outputs the character's final title according to gender and fame value. +-11-11-2024, canerksk +- Fixed: Hits, stam and mana increases were not disabled in the Eat trigger, now if the local values ​​are zero, hits, stam, mana are set to not increase. Those who want to increase can enter these local values. Also, anim and sound were added to the local variables. The trigger was not triggered after the values ​​changed, as a temporary solution, it was made to trigger after the values ​​changed, so now when you eat a meal, @Eat is triggered twice, once before the values ​​change and once after the values ​​change. +15-11-2024, canerksk +- Added: Registration of Packet 0x2D MobileAttributes Update packet diff --git a/src/common/CDataBase.cpp b/src/common/CDataBase.cpp index 352359d24..3e61d3610 100644 --- a/src/common/CDataBase.cpp +++ b/src/common/CDataBase.cpp @@ -62,7 +62,20 @@ bool CDataBase::Connect(const char *user, const char *password, const char *base return false; } - return (m_fConnected = true); + m_fConnected = true; + + CScriptTriggerArgs Args; + Args.m_VarsLocal.SetNum("VERSION", ver); + Args.m_VarsLocal.SetStrNew("USER", user); + Args.m_VarsLocal.SetStrNew("HOST", host); + Args.m_VarsLocal.SetNum("PORT", portnum); + Args.m_VarsLocal.SetNum("ISCONNECT", m_fConnected ? 1 : 0); + TRIGRET_TYPE tr = TRIGRET_RET_FALSE; + g_Serv.r_Call("f_onserver_db_connect", &g_Serv, &Args, nullptr, &tr); + if (tr == TRIGRET_RET_TRUE) + return false; + + return m_fConnected; } bool CDataBase::Connect() @@ -82,6 +95,9 @@ void CDataBase::Close() ADDTOCALLSTACK("CDataBase::Close"); SimpleThreadLock lock(m_connectionMutex); mysql_close(_myData); + + g_Serv.r_Call("f_onserver_db_close", &g_Serv, nullptr); + _myData = nullptr; m_fConnected = false; } @@ -153,6 +169,17 @@ bool CDataBase::query(const char *query, CVarDefMap & mapQueryResult) } ++rownum; } + + CScriptTriggerArgs Args; + Args.m_VarsLocal.SetStrNew("QUERY", query); + Args.m_VarsLocal.SetNum("NUMROWS", rownum); + Args.m_VarsLocal.SetNum("NUMCOLS", num_fields); + Args.m_VarsLocal.SetNum("ISERROR", myErr ? 1 : 0); + TRIGRET_TYPE tr = TRIGRET_RET_FALSE; + g_Serv.r_Call("f_onserver_db_query", &g_Serv, &Args, nullptr, &tr); + if (tr == TRIGRET_RET_TRUE) + return false; + mysql_free_result(m_res); return true; } @@ -185,6 +212,8 @@ bool CDataBase::exec(const char *query) return false; int result = 0; + MYSQL_RES *m_res = nullptr; + const char *myErr = nullptr; { // connection can only handle one query at a time, so we need to lock until we finish @@ -194,20 +223,34 @@ bool CDataBase::exec(const char *query) { // even though we don't want (or expect) any result data, we must retrieve // is anyway otherwise we will lose our connection to the server - MYSQL_RES* res = mysql_store_result(_myData); - if (res != nullptr) - mysql_free_result(res); - - return true; + m_res = mysql_store_result(_myData); + if (m_res == nullptr) + return false; } else { - const char *myErr = mysql_error(_myData); - g_Log.Event(LOGM_NOCONTEXT|LOGL_ERROR, "MariaDB query \"%s\" failed due to \"%s\"\n", - query, ( *myErr ? myErr : "unknown reason")); + myErr = mysql_error(_myData); } } + if (m_res != nullptr) + { + CScriptTriggerArgs Args; + Args.m_VarsLocal.SetStrNew("EXECUTE", query); + Args.m_VarsLocal.SetNum("ISERROR", myErr ? 1 : 0); + TRIGRET_TYPE tr = TRIGRET_RET_FALSE; + g_Serv.r_Call("f_onserver_db_execute", &g_Serv, &Args, nullptr, &tr); + if (tr == TRIGRET_RET_TRUE) + return false; + + mysql_free_result(m_res); + return true; + } + else + { + g_Log.Event(LOGM_NOCONTEXT | LOGL_ERROR, "MariaDB execute \"%s\" failed due to \"%s\"\n", query, (*myErr ? myErr : "unknown reason")); + } + if (( result == CR_SERVER_GONE_ERROR ) || ( result == CR_SERVER_LOST )) Close(); @@ -260,6 +303,11 @@ bool CDataBase::_OnTick() if ( !g_Cfg.m_fMySql ) // MariaDB is not supported return true; + CScriptTriggerArgs Args; + Args.m_iN1 = tickcnt; + g_Serv.r_Call("f_onserver_db_tick", &g_Serv, &Args); + tickcnt = (int)Args.m_iN1; + // do not ping sql server too heavily if ( ++tickcnt >= 1000 ) { diff --git a/src/common/sphereproto.h b/src/common/sphereproto.h index b9bbb908e..3d63f5804 100644 --- a/src/common/sphereproto.h +++ b/src/common/sphereproto.h @@ -46,6 +46,7 @@ enum XCMD_TYPE // XCMD_* messages are unique in both directions. XCMD_DropRejected = 0x28, XCMD_DropAccepted = 0x29, XCMD_DeathMenu = 0x2c, + XCMD_MobileAttributes = 0x2d, XCMD_ItemEquip = 0x2e, XCMD_Fight = 0x2f, // 0x30 diff --git a/src/game/CObjBase.h b/src/game/CObjBase.h index e34a42979..af502f5af 100644 --- a/src/game/CObjBase.h +++ b/src/game/CObjBase.h @@ -990,6 +990,7 @@ enum ITRIG_TYPE ITRIG_DELOBJ, // For t_spawn when obj is remove from list ITRIG_DELREDCANDLE, ITRIG_DELWHITECANDLE, + ITRIG_DEPOSIT, // IT_GOLD dropon bank deposit trigger ITRIG_DESTROY, //+I am nearly destroyed. ITRIG_DROPON_CHAR, // I have been dropped on this char. ITRIG_DROPON_GROUND, // I have been dropped on the ground here. @@ -1088,6 +1089,7 @@ enum CTRIG_TYPE : short CTRIG_HitIgnore, // I should ignore this target, just giving a record to scripts. CTRIG_HitMiss, // I just missed. CTRIG_HitParry, // I succesfully parried an hit. + CTRIG_HitReactive, // Reactive damage trigger CTRIG_HitTry, // I am trying to hit someone. starting swing. CTRIG_HouseDesignBegin, // Starting to customize. CTRIG_HouseDesignCommit, // I committed a new house design @@ -1106,6 +1108,7 @@ enum CTRIG_TYPE : short CTRIG_itemCreate, // Created one item. CTRIG_itemDamage, // Damaged one item. CTRIG_itemDCLICK, // I have dclicked item. + CTRIG_itemDeposit, // IT_GOLD dropon bank deposit trigger CTRIG_itemDestroy, // Item is nearly destroyed. CTRIG_itemDROPON_CHAR, // I have been dropped on this char. CTRIG_itemDROPON_GROUND, // I dropped an item on the ground. diff --git a/src/game/CServer.cpp b/src/game/CServer.cpp index 007c9890d..21765fb36 100644 --- a/src/game/CServer.cpp +++ b/src/game/CServer.cpp @@ -433,6 +433,10 @@ void CServer::SetServerMode( SERVMODE_TYPE mode ) { ADDTOCALLSTACK("CServer::SetServerMode"); m_iModeCode.store(mode, std::memory_order_release); + + CScriptTriggerArgs Args(mode); + g_Serv.r_Call("f_onserver_mode", &g_Serv, &Args); + #ifdef _WIN32 g_NTWindow.SetWindowTitle(); #endif @@ -2394,40 +2398,62 @@ void CServer::SetResyncPause(bool fPause, CTextConsole * pSrc, bool fMessage) if ( fPause ) { m_fResyncPause = true; - g_Log.Event(LOGL_EVENT, "%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_START)); + g_Log.Event(LOGL_EVENT, "%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_START)); // Only console log message + + CScriptTriggerArgs args; + args.m_iN1 = m_fResyncPause ? 1 : 0; + args.m_iN2 = fMessage ? 1 : 0; + g_Serv.r_Call("f_onserver_resync_start", &g_Serv, &args); + fMessage = args.m_iN2 == 1 ? true : false; if ( fMessage ) - CWorldComm::Broadcast(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_START)); + CWorldComm::Broadcast(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_START)); // World broadcast message else if ( pSrc && pSrc->GetChar() ) - pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_START)); + pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_START)); // Only character message g_Cfg.Unload(true); SetServerMode(SERVMODE_ResyncPause); } else { - g_Log.Event(LOGL_EVENT, "%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_RESTART)); + g_Log.Event(LOGL_EVENT, "%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_RESTART)); // Only console log message SetServerMode(SERVMODE_ResyncLoad); + g_Serv.r_Call("f_onserver_resync_restart", &g_Serv, nullptr); if ( !g_Cfg.Load(true) ) { - g_Log.EventError("%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_FAILED)); - if ( fMessage ) - CWorldComm::Broadcast(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_FAILED)); + g_Log.EventError("%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_FAILED)); // Only console log message + + CScriptTriggerArgs args; + args.m_iN1 = fMessage ? 1 : 0; + g_Serv.r_Call("f_onserver_resync_failed", &g_Serv, &args); + fMessage = args.m_iN1 == 1 ? true : false; + + if ( fMessage ) + CWorldComm::Broadcast(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_FAILED)); // World broadcast message else if ( pSrc && pSrc->GetChar() ) - pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_FAILED)); + pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_FAILED)); // Only character message } else { - g_Log.Event(LOGL_EVENT, "%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_SUCCESS)); + CScriptTriggerArgs args; + args.m_iN1 = fMessage ? 1 : 0; + g_Serv.r_Call("f_onserver_resync_success", &g_Serv, &args); + fMessage = args.m_iN1 == 1 ? true : false; + + g_Log.Event(LOGL_EVENT, "%s\n", g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_SUCCESS)); // Only console log message if ( fMessage ) - CWorldComm::Broadcast(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_SUCCESS)); + CWorldComm::Broadcast(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_SUCCESS)); // World broadcast message else if ( pSrc && pSrc->GetChar() ) - pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_SUCCESS)); + pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_SERVER_RESYNC_SUCCESS)); // Only character message } m_fResyncPause = false; + CScriptTriggerArgs args; + args.m_iN1 = m_fResyncPause ? 1 : 0; + g_Serv.r_Call("f_onserver_resync_finish", &g_Serv, &args); + g_World.SyncGameTime(); SetServerMode(SERVMODE_Run); diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 6beadd6bd..562ad7656 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -547,9 +547,16 @@ enum RC_TYPE RC_ERALIMITGEAR, // _iEraLimitGear RC_ERALIMITLOOT, // _iEraLimitLoot RC_ERALIMITPROPS, // _iEraLimitProps - RC_EVENTSITEM, // m_sEventsItem - RC_EVENTSPET, // m_sEventsPet - RC_EVENTSPLAYER, // m_sEventsPlayer + RC_EVENTSCHAR, // m_sEventsChar + RC_EVENTSCHARPLAYER, // m_sEventsCharPlayer + RC_EVENTSCHARSTAFF, // m_sEventsCharStaff + RC_EVENTSITEM, // m_sEventsItem + RC_EVENTSITEMWEAPON, // m_sEventsItemWeapon + RC_EVENTSNPC, // m_sEventsNpc + RC_EVENTSNPCANIMAL, // m_sEventsNpcAnimal + RC_EVENTSNPCMONSTER, // m_sEventsNPCMonster + RC_EVENTSNPCMOUNTABLE, // m_sEventsNPCMountable + RC_EVENTSNPCSHOP, // m_sEventsNPCShop RC_EVENTSREGION, // m_sEventsRegion RC_EXPERIENCEKOEFPVM, // m_iExperienceKoefPVM RC_EXPERIENCEKOEFPVP, // m_iExperienceKoefPVP @@ -732,6 +739,7 @@ enum RC_TYPE RC_WOPFONT, RC_WOPPLAYER, RC_WOPSTAFF, + RC_WOPTALKMODE, RC_WORLDSAVE, RC_ZEROPOINT, // m_sZeroPoint RC_QTY @@ -840,9 +848,16 @@ const CAssocReg CServerConfig::sm_szLoadKeys[RC_QTY + 1] { "ERALIMITGEAR", { ELEM_BYTE, static_castOFFSETOF(CServerConfig,_iEraLimitGear) }}, { "ERALIMITLOOT", { ELEM_BYTE, static_castOFFSETOF(CServerConfig,_iEraLimitLoot) }}, { "ERALIMITPROPS", { ELEM_BYTE, static_castOFFSETOF(CServerConfig,_iEraLimitProps) }}, + { "EVENTSCHAR", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsChar) } }, + { "EVENTSCHARPLAYER", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsCharPlayer) } }, + { "EVENTSCHARSTAFF", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsCharStaff) } }, { "EVENTSITEM", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sEventsItem) }}, - { "EVENTSPET", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sEventsPet) }}, - { "EVENTSPLAYER", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sEventsPlayer) }}, + { "EVENTSITEMWEAPON", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsItemWeapon) } }, + { "EVENTSNPC", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsNPC) } }, + { "EVENTSNPCANIMAL", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsNPCAnimal) } }, + { "EVENTSNPCMONSTER", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsNPCMonster) } }, + { "EVENTSNPCMOUNTABLE", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsNPCMountable) } }, + { "EVENTSNPCSHOP", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsNPCShop) } }, { "EVENTSREGION", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sEventsRegion) }}, { "EXPERIENCEKOEFPVM", { ELEM_INT, static_castOFFSETOF(CServerConfig,m_iExperienceKoefPVM) }}, { "EXPERIENCEKOEFPVP", { ELEM_INT, static_castOFFSETOF(CServerConfig,m_iExperienceKoefPVP) }}, @@ -1025,6 +1040,7 @@ const CAssocReg CServerConfig::sm_szLoadKeys[RC_QTY + 1] { "WOPFONT", { ELEM_INT, static_castOFFSETOF(CServerConfig,m_iWordsOfPowerFont) }}, { "WOPPLAYER", { ELEM_BOOL, static_castOFFSETOF(CServerConfig,m_fWordsOfPowerPlayer) }}, { "WOPSTAFF", { ELEM_BOOL, static_castOFFSETOF(CServerConfig,m_fWordsOfPowerStaff) }}, + { "WOPTALKMODE", { ELEM_INT, static_cast OFFSETOF(CServerConfig,m_iWordsOfPowerTalkMode) }}, { "WORLDSAVE", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sWorldBaseDir) }}, { "ZEROPOINT", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sZeroPoint) }}, { nullptr, { ELEM_VOID, 0, }} @@ -4815,7 +4831,10 @@ bool CServerConfig::Load( bool fResync ) pRegion->MakeRegionDefname(); } - // parse eventsitem + ///////////////////// + // parse eventsitem + ///////////////////// + // all items m_iEventsItemLink.clear(); if ( ! m_sEventsItem.IsEmpty() ) { @@ -4823,21 +4842,83 @@ bool CServerConfig::Load( bool fResync ) m_iEventsItemLink.r_LoadVal(script, RES_EVENTS); } - // parse eventspet - m_pEventsPetLink.clear(); - if ( ! m_sEventsPet.IsEmpty() ) - { - CScript script("EVENTSPET", m_sEventsPet); - m_pEventsPetLink.r_LoadVal(script, RES_EVENTS); - } + // all weapons + m_iEventsItemWeaponLink.clear(); + if (!m_sEventsItemWeapon.IsEmpty()) + { + CScript script("EVENTSITEMWEAPON", m_sEventsItemWeapon); + m_iEventsItemWeaponLink.r_LoadVal(script, RES_EVENTS); + } - // parse eventsplayer - m_pEventsPlayerLink.clear(); - if ( ! m_sEventsPlayer.IsEmpty() ) - { - CScript script("EVENTSPLAYER", m_sEventsPlayer); - m_pEventsPlayerLink.r_LoadVal(script, RES_EVENTS); - } + ///////////////////// + // parse eventsnpc + ///////////////////// + // all npcs + m_pEventsNPCLink.clear(); + if (!m_sEventsNPC.IsEmpty()) + { + CScript script("EVENTSNPC", m_sEventsNPC); + m_pEventsNPCLink.r_LoadVal(script, RES_EVENTS); + } + + // all animals + m_pEventsNPCAnimalLink.clear(); + if (!m_sEventsNPCAnimal.IsEmpty()) + { + CScript script("EVENTSNPCANIMAL", m_sEventsNPCAnimal); + m_pEventsNPCAnimalLink.r_LoadVal(script, RES_EVENTS); + } + // all monsters + m_pEventsNPCMonsterLink.clear(); + if (!m_sEventsNPCMonster.IsEmpty()) + { + CScript script("EVENTSNPCMONSTER", m_sEventsNPCMonster); + m_pEventsNPCMonsterLink.r_LoadVal(script, RES_EVENTS); + } + + // all mountables + m_pEventsNPCMountableLink.clear(); + if (!m_sEventsNPCMountable.IsEmpty()) + { + CScript script("EVENTSNPCMOUNTABLE", m_sEventsNPCMountable); + m_pEventsNPCMountableLink.r_LoadVal(script, RES_EVENTS); + } + + // all shopkeepers + m_pEventsNPCShopLink.clear(); + if (!m_sEventsNPCShop.IsEmpty()) + { + CScript script("EVENTSNPCSHOP", m_sEventsNPCShop); + m_pEventsNPCShopLink.r_LoadVal(script, RES_EVENTS); + } + + ///////////////////// + // parse eventschar + ///////////////////// + + // allchars (players or staffs) + m_pEventsCharLink.clear(); + if (!m_sEventsChar.IsEmpty()) + { + CScript script("EVENTSCHAR", m_sEventsChar); + m_pEventsCharLink.r_LoadVal(script, RES_EVENTS); + } + + // all players + m_pEventsCharPlayerLink.clear(); + if (!m_sEventsCharPlayer.IsEmpty()) + { + CScript script("EVENTSCHARPLAYER", m_sEventsCharPlayer); + m_pEventsCharPlayerLink.r_LoadVal(script, RES_EVENTS); + } + + // all staffs + m_pEventsCharStaffLink.clear(); + if (!m_sEventsCharStaff.IsEmpty()) + { + CScript script("EVENTSCHARSTAFF", m_sEventsCharStaff); + m_pEventsCharStaffLink.r_LoadVal(script, RES_EVENTS); + } // parse eventsregion m_pEventsRegionLink.clear(); diff --git a/src/game/CServerConfig.h b/src/game/CServerConfig.h index f47025fc2..b554f6b2b 100644 --- a/src/game/CServerConfig.h +++ b/src/game/CServerConfig.h @@ -87,7 +87,9 @@ enum OF_TYPE OF_OWNoDropCarriedItem = 0x0400000, // When overweighted, don't drop items on ground when moving them (or using BOUNCE) and checking if you can carry them. OF_AllowContainerInsideContainer = 0x0800000, //Allow containers inside other containers even if they are heavier than the container being inserted into. OF_VendorStockLimit = 0x01000000, // Limits how much of an item a vendor can buy using the value set in the TEMPLATE. Format: BUY=ID,AMOUNT - OF_EnableGuildAlignNotoriety = 0x02000000 // If enabled, guilds with the same alignment will see each other as enemy or ally. + OF_EnableGuildAlignNotoriety = 0x02000000, // If enabled, guilds with the same alignment will see each other as enemy or ally. + OF_PetBehaviorOwnerNeutral = 0x04000000, // Should my pets always appear natural to me? + OF_NPCMovementOldStyle = 0x06000000 // Required setting to make NPCs run like in the old version. }; /** @@ -303,6 +305,7 @@ extern class CServerConfig : public CResourceHolder int m_iWordsOfPowerFont; // Font used for Words Of Power. bool m_fWordsOfPowerPlayer; // Words of Power for players. bool m_fWordsOfPowerStaff; // Words of Power for staff. + TALKMODE_TYPE m_iWordsOfPowerTalkMode; // Walk mode used for Words Of Power. bool m_fEquippedCast; // Allow casting while equipped. bool m_fManaLossAbort; // Lose mana when spell casting aborted. bool m_fManaLossFail; // Lose mana when spell casting failed. @@ -433,17 +436,60 @@ extern class CServerConfig : public CResourceHolder CSString m_sDumpAccPackets; #endif - CSString m_sEventsPet; // Key to add Events to all pets. - CResourceRefArray m_pEventsPetLink; // EventsPet. - - CSString m_sEventsPlayer; // Key to add Events to all players. - CResourceRefArray m_pEventsPlayerLink; // EventsPlayer. - - CSString m_sEventsRegion; // Key to add Events to all regions. - CResourceRefArray m_pEventsRegionLink; // EventsRegion. - - CSString m_sEventsItem; // Key to add Events to all items. - CResourceRefArray m_iEventsItemLink; // EventsItem. + + ////////////////////// + // Npcs + ////////////////////// + // EVENTSNPC + CSString m_sEventsNPC; // Key to add Events to all npc. + CResourceRefArray m_pEventsNPCLink; // m_sEventsNPC. + + // EVENTSNPCANIMAL + CSString m_sEventsNPCAnimal; // Key to add Events to all animals + CResourceRefArray m_pEventsNPCAnimalLink; // m_sEventsNPCAnimal. + + // EVENTSNPCMONSTER + CSString m_sEventsNPCMonster; // Key to add Events to all monsters (brain_monster, brain_dragon, brain_berserk). + CResourceRefArray m_pEventsNPCMonsterLink; // m_sEventsNPCMonster. + + // EVENTSNPCMOUNTABLE + CSString m_sEventsNPCMountable; // Key to add Events to all mountables (brain_animal). + CResourceRefArray m_pEventsNPCMountableLink; // EventsNPCMountable. + + // EVENTSNPCSHOP + CSString m_sEventsNPCShop; // Key to add Events to all shopkeepers (brain_vendor). + CResourceRefArray m_pEventsNPCShopLink; // EventsNPCShop. + + ////////////////////// + // Chars + ////////////////////// + // EVENTSCHAR + CSString m_sEventsChar; // Key to add Events to all players and staff. + CResourceRefArray m_pEventsCharLink; // EventsChar. + + // EVENTSCHARSTAFF + CSString m_sEventsCharStaff; // Key to add Events to all players. + CResourceRefArray m_pEventsCharStaffLink; // EventsCharStaff. + + // EVENTSCHARPLAYER + CSString m_sEventsCharPlayer; // Key to add Events to all players. + CResourceRefArray m_pEventsCharPlayerLink; // EventsCharPlayer. + + ////////////////////// + // Regions + ////////////////////// + CSString m_sEventsRegion; // Key to add Events to all regions. + CResourceRefArray m_pEventsRegionLink; // EventsRegion. + + ////////////////////// + // Items + ////////////////////// + CSString m_sEventsItem; // Key to add Events to all items. + CResourceRefArray m_iEventsItemLink; // EventsItem. + + // Weapon + CSString m_sEventsItemWeapon; // Key to add Events to all items. + CResourceRefArray m_iEventsItemWeaponLink; //EventsItemWeapon. // Third Party Tools CSString m_sStripPath; // Strip Path for TNG and Axis. diff --git a/src/game/CWorld.cpp b/src/game/CWorld.cpp index df69a4ad9..216d17159 100644 --- a/src/game/CWorld.cpp +++ b/src/game/CWorld.cpp @@ -926,6 +926,10 @@ bool CWorld::SaveStage() // Save world state in stages. EXC_DEBUG_END; ++_iSaveStage; // to avoid loops, we need to skip the current operation in world save + + CScriptTriggerArgs SaveStageArgs(_iSaveStage); + g_Serv.r_Call("f_onserver_save_stage", &g_Serv, &SaveStageArgs); + return false; } @@ -992,6 +996,9 @@ bool CWorld::SaveForce() // Save world state fSuccess = false; } + CScriptTriggerArgs Args(fSave, _iSaveStage); + g_Serv.r_Call("f_onserver_save_force", &g_Serv, &Args); + g_Serv.SetServerMode(SERVMODE_Run); // Game is up and running return fSuccess; } @@ -1023,6 +1030,9 @@ bool CWorld::SaveTry( bool fForceImmediate ) // Save world state TIME_PROFILE_START; _iSaveTimer = llTicksStart; + CScriptTriggerArgs SaveTryArgs(fForceImmediate, _iSaveTimer); + g_Serv.r_Call("f_onserver_save_try", &g_Serv, &SaveTryArgs); + // Determine the save name based on the time. // exponentially degrade the saves over time. if ( ! OpenScriptBackup( m_FileData, g_Cfg.m_sWorldBaseDir, "data", m_iSaveCountID )) diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index c1b95127a..b49c40eb6 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -87,6 +87,7 @@ lpctstr const CChar::sm_szTrigName[CTRIG_QTY+1] = // static "@HitIgnore", // I'm going to avoid a target (attacker.n.ignore=1) , should I un-ignore him?. "@HitMiss", // I just missed. "@HitParry", // I succesfully parried an hit. + "@HitReactive", // Reactive damage trigger "@HitTry", // I am trying to hit someone. starting swing. "@HouseDesignBegin", // Starting to customize. "@HouseDesignCommit", // I committed a new house design. @@ -105,6 +106,7 @@ lpctstr const CChar::sm_szTrigName[CTRIG_QTY+1] = // static "@itemCreate", //? "@itemDamage", //? "@itemDCLICK", // I have dclicked item + "@itemDeposit", // IT_GOLD deposited virtualgold "@itemDestroy", //+I am nearly destroyed "@itemDropOn_Char", // I have been dropped on this char "@itemDropOn_Ground", // I dropped an item on the ground @@ -528,6 +530,14 @@ void CChar::SetDisconnected(CSector* pNewSector) GetClientActive()->GetNetState()->markReadClosed(); } + if (m_pNPC && !g_Serv.IsLoading()) + { + if (IsTrigUsed(TRIGGER_LOGOUT)) + { + OnTrigger(CTRIG_LogOut, this, nullptr); + } + } + if (m_pPlayer) { m_pPlayer->_iTimeLastDisconnectedMs = CWorldGameTime::GetCurrentTime().GetTimeRaw(); @@ -3309,7 +3319,26 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo else sVal = GetTradeTitle(); } - break; + break; + + case CHC_NOTOTITLE: + { + lpctstr pTitle = Noto_IsMurderer() ? g_Cfg.GetDefaultMsg(DEFMSG_TITLE_MURDERER) : + (IsStatFlag(STATF_CRIMINAL) ? g_Cfg.GetDefaultMsg(DEFMSG_TITLE_CRIMINAL) : + g_Cfg.GetNotoTitle(Noto_GetLevel(), Char_GetDef()->IsFemale())); + tchar *pTemp = Str_GetTemp(); + + snprintf(pTemp, Str_TempLength(), "%s%s%s%s", + (pTitle[0]) ? (Char_GetDef()->IsFemale() ? g_Cfg.GetDefaultMsg(DEFMSG_TITLE_ARTICLE_FEMALE) : g_Cfg.GetDefaultMsg(DEFMSG_TITLE_ARTICLE_MALE)) : "", + pTitle, + (pTitle[0]) ? " " : "", + Noto_GetFameTitle()); + + sVal = pTemp; + //sVal = Noto_GetTitle(); + } + break; + case CHC_EXP: sVal.FormatVal(m_exp); break; @@ -4035,6 +4064,11 @@ bool CChar::r_LoadVal( CScript & s ) case CHC_TITLE: m_sTitle = s.GetArgStr(); break; + + case CHC_NOTOTITLE: + // READ ONLY FOR NOW + break; + case CHC_EXP: m_exp = s.GetArgUVal(); ChangeExperience(); // auto-update level if applicable diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index b4c427f1a..aea0e1f34 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -280,11 +280,14 @@ void CChar::LayerAdd( CItem * pItem, LAYER_TYPE layer ) return; } - if (!pItem->IsTypeSpellable() && !pItem->m_itSpell.m_spell && !pItem->IsType(IT_WAND)) // can this item have a spell effect ? If so we do not send - { + //if (!pItem->IsTypeSpellable() && !pItem->m_itSpell.m_spell && !pItem->IsType(IT_WAND)) // can this item have a spell effect ? If so we do not send + // Since most of the memories came with a morex information by default, almost no memory was triggered. + if (!(pItem->IsTypeSpellable() || pItem->IsType(IT_WAND))) + { if ((IsTrigUsed(TRIGGER_MEMORYEQUIP)) || (IsTrigUsed(TRIGGER_ITEMMEMORYEQUIP))) { - CScriptTriggerArgs pArgs; + //CScriptTriggerArgs pArgs; + CScriptTriggerArgs pArgs(pItem); // added "argo" argument pArgs.m_iN1 = layer; if (pItem->OnTrigger(ITRIG_MemoryEquip, this, &pArgs) == TRIGRET_RET_TRUE) { @@ -3413,56 +3416,89 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick ) // OnEat() // Generating eating animation // also calling @Eat and setting food's level (along with other possible stats 'local.hits',etc?) -void CChar::EatAnim(CItem* pItem, ushort uiQty) +void CChar::EatAnim(CItem *pItem, ushort uiQty) { - ADDTOCALLSTACK("CChar::EatAnim"); + ADDTOCALLSTACK("CChar::EatAnim"); ASSERT(pItem); //Should never happen, but make sure item is valid. - static const SOUND_TYPE sm_EatSounds[] = { 0x03a, 0x03b, 0x03c }; - Sound(sm_EatSounds[g_Rand.GetVal(ARRAY_COUNT(sm_EatSounds))]); - - if ( !IsStatFlag(STATF_ONHORSE) ) - UpdateAnimate(ANIM_EAT); + static const SOUND_TYPE sm_EatSounds[] = { 0x03a, 0x03b, 0x03c }; EMOTEFLAGS_TYPE eFlag = (IsPlayer() ? EMOTEF_HIDE_EAT_PLAYER : EMOTEF_HIDE_EAT_NPC); if (!IsSetEmoteFlag(eFlag)) { - tchar* pszMsg = Str_GetTemp(); + tchar *pszMsg = Str_GetTemp(); snprintf(pszMsg, Str_TempLength(), g_Cfg.GetDefaultMsg(DEFMSG_MSG_EATSOME), pItem->GetName()); Emote(pszMsg); } - ushort uiHits = 0; - ushort uiMana = 0; - ushort uiStam = (ushort)( g_Rand.GetVal2(3, 6) + (uiQty / 5) ); - ushort uiFood = uiQty; - ushort uiStatsLimit = 0; - if (IsTrigUsed(TRIGGER_EAT)) - { - CScriptTriggerArgs Args(uiStatsLimit); - Args.m_VarsLocal.SetNumNew("Hits", uiHits); - Args.m_VarsLocal.SetNumNew("Mana", uiMana); - Args.m_VarsLocal.SetNumNew("Stam", uiStam); - Args.m_VarsLocal.SetNumNew("Food", uiFood); + SOUND_TYPE sound = sm_EatSounds[g_Rand.GetVal(ARRAY_COUNT(sm_EatSounds))]; + ANIM_TYPE animType = ANIM_EAT; + ushort uiHits = 0; + ushort uiMana = 0; + ushort uiStam = (ushort)(g_Rand.GetVal2(3, 6) + (uiQty / 5)); + ushort uiFood = uiQty; + ushort uiStatsLimit = 0; + CScriptTriggerArgs Args(uiStatsLimit); + if (IsTrigUsed(TRIGGER_EAT)) + { + Args.m_VarsLocal.SetNumNew("Hits", uiHits); + Args.m_VarsLocal.SetNumNew("Mana", uiMana); + Args.m_VarsLocal.SetNumNew("Stam", uiStam); + Args.m_VarsLocal.SetNumNew("Food", uiFood); + Args.m_VarsLocal.SetNumNew("Anim", animType); + Args.m_VarsLocal.SetNumNew("Sound", sound); Args.m_pO1 = pItem; - if ( OnTrigger(CTRIG_Eat, this, &Args) == TRIGRET_RET_TRUE ) - return; + if (OnTrigger(CTRIG_Eat, this, &Args) == TRIGRET_RET_TRUE) + return; + + uiHits = (ushort)(Args.m_VarsLocal.GetKeyNum("Hits")); + uiMana = (ushort)(Args.m_VarsLocal.GetKeyNum("Mana")); + uiStam = (ushort)(Args.m_VarsLocal.GetKeyNum("Stam")); + uiFood = (ushort)(Args.m_VarsLocal.GetKeyNum("Food")); + animType = static_cast(Args.m_VarsLocal.GetKeyNum("Anim")); + sound = (SOUND_TYPE)(Args.m_VarsLocal.GetKeyNum("Sound")); + uiStatsLimit = (ushort)(Args.m_iN1); + } - uiHits = (ushort)(Args.m_VarsLocal.GetKeyNum("Hits")) + Stat_GetVal(STAT_STR); - uiMana = (ushort)(Args.m_VarsLocal.GetKeyNum("Mana")) + Stat_GetVal(STAT_INT); - uiStam = (ushort)(Args.m_VarsLocal.GetKeyNum("Stam")) + Stat_GetVal(STAT_DEX); - uiFood = (ushort)(Args.m_VarsLocal.GetKeyNum("Food")) + Stat_GetVal(STAT_FOOD); - uiStatsLimit = (ushort)(Args.m_iN1); - } + Sound(sound); + + if (!IsStatFlag(STATF_ONHORSE)) + UpdateAnimate(animType); + + if (uiHits > 0) + { + uiHits += Stat_GetVal(STAT_STR); + UpdateStatVal(STAT_STR, uiHits, uiStatsLimit); + } + if (uiMana > 0) + { + uiMana += Stat_GetVal(STAT_INT); + UpdateStatVal(STAT_INT, uiMana, uiStatsLimit); + } + if (uiStam > 0) + { + uiStam += Stat_GetVal(STAT_DEX); + UpdateStatVal(STAT_DEX, uiStam, uiStatsLimit); + } + if (uiFood > 0) + { + uiFood += Stat_GetVal(STAT_FOOD); + UpdateStatVal(STAT_FOOD, uiFood, uiStatsLimit); + } + + // It didn't mean much since it wasn't triggered after the values ​​changed. + if (IsTrigUsed(TRIGGER_EAT)) + { + Args.m_VarsLocal.SetNumNew("Hits", uiHits); + Args.m_VarsLocal.SetNumNew("Mana", uiMana); + Args.m_VarsLocal.SetNumNew("Stam", uiStam); + Args.m_VarsLocal.SetNumNew("Food", uiFood); + Args.m_VarsLocal.SetNumNew("Anim", animType); + Args.m_VarsLocal.SetNumNew("Sound", sound); + Args.m_pO1 = pItem; + OnTrigger(CTRIG_Eat, this, &Args); + } - if ( uiHits ) - UpdateStatVal(STAT_STR, uiHits, uiStatsLimit); - if ( uiMana ) - UpdateStatVal(STAT_INT, uiMana, uiStatsLimit); - if ( uiStam ) - UpdateStatVal(STAT_DEX, uiStam, uiStatsLimit); - if ( uiFood ) - UpdateStatVal(STAT_FOOD, uiFood, uiStatsLimit); } // Some outside influence may be revealing us. @@ -5599,48 +5635,191 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript } } - // 5) EVENTSPET triggers for npcs - if (m_pNPC != nullptr) - { - EXC_SET_BLOCK("NPC triggers - EVENTSPET"); // EVENTSPET (constant events of NPCs set from sphere.ini) - for (size_t i = 0; i < g_Cfg.m_pEventsPetLink.size(); ++i) - { - CResourceLink * pLink = g_Cfg.m_pEventsPetLink[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) - continue; + // NPCs + if (m_pNPC != nullptr) + { + // 5) EVENTSNPC triggers for npcs + // All Npcs + EXC_SET_BLOCK("NPC triggers - EVENTSNPC"); // EVENTSNPC (constant events of NPCs set from sphere.ini) + for (size_t i = 0; i < g_Cfg.m_pEventsNPCLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; - CResourceLock s; - if (!pLink->ResourceLock(s)) - continue; + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; - executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); - if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) - goto stopandret; - } - } + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } - // 6) EVENTSPLAYER triggers for players - if ( m_pPlayer != nullptr ) - { - // EVENTSPLAYER triggers (constant events of players set from sphere.ini) - EXC_SET_BLOCK("chardef triggers - EVENTSPLAYER"); - for ( size_t i = 0; i < g_Cfg.m_pEventsPlayerLink.size(); ++i ) - { - CResourceLink *pLink = g_Cfg.m_pEventsPlayerLink[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) - continue; + ITEMID_TYPE memoryId = Horse_GetMountItemID(); - CResourceLock s; - if (!pLink->ResourceLock(s)) - continue; + // 6) EVENTSNPCANIMAL triggers for all animals npc (without mountables) + if (m_pNPC->m_Brain == NPCBRAIN_ANIMAL && memoryId <= ITEMID_NOTHING) + { + EXC_SET_BLOCK("NPC triggers - EVENTSNPCANIMAL"); + for (size_t i = 0; i < g_Cfg.m_pEventsNPCAnimalLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCAnimalLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } + } + + // 7) EVENTSNPCMOUNTABLE triggers for all mountables npc + if (memoryId > ITEMID_NOTHING) + { + EXC_SET_BLOCK("NPC triggers - EVENTSNPCMOUNTABLE"); + for (size_t i = 0; i < g_Cfg.m_pEventsNPCMountableLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCMountableLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } + } + + // 8) EVENTSNPCMONSTER triggers for all monsters npc + // Monsters + EXC_SET_BLOCK("NPC triggers - EVENTSNPCMONSTER"); + if (m_pNPC->m_Brain == NPCBRAIN_MONSTER || m_pNPC->m_Brain == NPCBRAIN_BERSERK || m_pNPC->m_Brain == NPCBRAIN_DRAGON) + { + for (size_t i = 0; i < g_Cfg.m_pEventsNPCMonsterLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCMonsterLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } + } + + // Shopkeepers + // 9) EVENTSNPCSHOP triggers for all shopkeepers + EXC_SET_BLOCK("NPC triggers - EVENTSNPCSHOP"); + if (m_pNPC->m_Brain == NPCBRAIN_VENDOR || m_pNPC->m_Brain == NPCBRAIN_STABLE || m_pNPC->m_Brain == NPCBRAIN_HEALER) + { + for (size_t i = 0; i < g_Cfg.m_pEventsNPCShopLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCShopLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } + } + + // Guards + /* + EXC_SET_BLOCK("NPC triggers - EVENTSNPCGUARD"); + if (m_pNPC->m_Brain == NPCBRAIN_GUARD) + { + } + */ + } + + // Chars + if (m_pPlayer != nullptr) + { + // 10) EVENTSCHAR triggers for chars (players or staffs) + EXC_SET_BLOCK("chardef triggers - EVENTSCHAR"); + for (size_t i = 0; i < g_Cfg.m_pEventsCharLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsCharLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } + + // 11) EVENTSCHARPLAYER triggers for playerss + if (GetPrivLevel() <= PLEVEL_Player) + { + // EVENTSCHARPLAYER triggers (constant events of players set from sphere.ini) + EXC_SET_BLOCK("chardef triggers - EVENTSCHARPLAYER"); + for (size_t i = 0; i < g_Cfg.m_pEventsCharPlayerLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsCharPlayerLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } + } + + // 12) EVENTSCHARSTAFF triggers for staffs + if (GetPrivLevel() >= PLEVEL_Counsel) + { + // EVENTSCHARSTAFF triggers (constant events of players set from sphere.ini) + EXC_SET_BLOCK("chardef triggers - EVENTSCHARSTAFF"); + for (size_t i = 0; i < g_Cfg.m_pEventsCharStaffLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsCharStaffLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } + } + } - executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); - if ( iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT ) - goto stopandret; - } - } } stopandret: diff --git a/src/game/chars/CCharFight.cpp b/src/game/chars/CCharFight.cpp index 3ce5c26ba..0a54a2c82 100644 --- a/src/game/chars/CCharFight.cpp +++ b/src/game/chars/CCharFight.cpp @@ -943,19 +943,59 @@ int CChar::OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uiType, int iDmgPhy { CItem* pReactive = LayerFind(LAYER_SPELL_Reactive); - if (pReactive) - { - int iReactiveDamage = (iDmg * pReactive->m_itSpell.m_PolyStr) / 100; - if (iReactiveDamage < 1) - { - iReactiveDamage = 1; - } - - iDmg -= iReactiveDamage; - pSrc->OnTakeDamage(iReactiveDamage, this, (DAMAGE_TYPE)(DAMAGE_FIXED | DAMAGE_REACTIVE), iDmgPhysical, iDmgFire, iDmgCold, iDmgPoison, iDmgEnergy,(SPELL_TYPE)pReactive->m_itSpell.m_spell); - pSrc->Sound(0x1F1); - pSrc->Effect(EFFECT_OBJ, ITEMID_FX_CURSE_EFFECT, this, 10, 16); - } + if (pReactive) + { + if (IsTrigUsed(TRIGGER_HITREACTIVE)) + { + int iReactiveDamage = (iDmg * pReactive->m_itSpell.m_PolyStr) / 100; + int iReactiveRefDam = iReactiveDamage; + int iReactiveRedDam = iReactiveDamage; + + SOUND_TYPE ReactiveSnd = 0x1F1; + ITEMID_TYPE ReactiveEffectID = ITEMID_FX_CURSE_EFFECT; + DAMAGE_TYPE ReactiveDamType = (DAMAGE_FIXED | DAMAGE_REACTIVE); + + CScriptTriggerArgs HitReactiveArgs; + HitReactiveArgs.m_VarsLocal.SetNum("Sound", ReactiveSnd); // SOUND + HitReactiveArgs.m_VarsLocal.SetNum("EffectID", ReactiveEffectID); // EFFECTID + HitReactiveArgs.m_VarsLocal.SetNum("Damage", iReactiveDamage); // DAM + HitReactiveArgs.m_VarsLocal.SetNum("RefDamage", iReactiveRefDam); // REFLECTED DAM + HitReactiveArgs.m_VarsLocal.SetNum("RedDamage", iReactiveRedDam); // REDUCED DAM + HitReactiveArgs.m_VarsLocal.SetNum("DamType", ReactiveDamType); // DAMTYPE + OnTrigger(CTRIG_HitReactive, pSrc, &HitReactiveArgs); + + ReactiveSnd = (SOUND_TYPE)HitReactiveArgs.m_VarsLocal.GetKeyNum("Sound"); // SOUND + ReactiveEffectID = (ITEMID_TYPE)HitReactiveArgs.m_VarsLocal.GetKeyNum("EffectID"); // EFFECTID + iReactiveDamage = (int)HitReactiveArgs.m_VarsLocal.GetKeyNum("Damage"); // DAM + iReactiveRefDam = (int)HitReactiveArgs.m_VarsLocal.GetKeyNum("RefDamage"); // REFLECTED DAM + iReactiveRedDam = (int)HitReactiveArgs.m_VarsLocal.GetKeyNum("RedDamage"); // REDUCED DAM + ReactiveDamType = (DAMAGE_TYPE)HitReactiveArgs.m_VarsLocal.GetKeyNum("ReactiveDamType"); // REDUCED DAM + + // should it be zero ? + //if (iReactiveDamage < 1) + // iReactiveDamage = 1; + + //if (iReactiveRedDam < 1) + // iReactiveRedDam = 1; + + //if (iReactiveRefDam < 1) + // iReactiveRefDam = 1; + + // reduce + if (iReactiveRedDam > 0 || iReactiveDamage > 0) + iDmg -= iReactiveRedDam ? iReactiveRedDam : iReactiveDamage; + + // reflect + if (iReactiveRefDam > 0 || iReactiveDamage > 0) + pSrc->OnTakeDamage(iReactiveRefDam ? iReactiveRefDam : iReactiveDamage, this, ReactiveDamType, iDmgPhysical, iDmgFire, iDmgCold, iDmgPoison, iDmgEnergy, (SPELL_TYPE)pReactive->m_itSpell.m_spell); + + if (ReactiveSnd) + pSrc->Sound(ReactiveSnd); + + if (ReactiveEffectID) + pSrc->Effect(EFFECT_OBJ, ReactiveEffectID, this, 10, 16); + } + } } } // Check if REFLECTPHYSICALDAM will reflect some damage back. @@ -981,7 +1021,7 @@ int CChar::OnTakeDamage( int iDmg, CChar * pSrc, DAMAGE_TYPE uiType, int iDmgPhy SoundChar(CRESND_GETHIT); UpdateStatVal( STAT_STR, -iDmg); if ( pSrc->IsClientActive() ) - pSrc->GetClientActive()->addHitsUpdate( this ); // always send updates to src + pSrc->GetClientActive()->addHitsUpdate( this, true ); // always send updates to src if ( IsAosFlagEnabled( FEATURE_AOS_DAMAGE ) ) { diff --git a/src/game/chars/CCharNPC.cpp b/src/game/chars/CCharNPC.cpp index 8f855ef37..b386804e1 100644 --- a/src/game/chars/CCharNPC.cpp +++ b/src/game/chars/CCharNPC.cpp @@ -323,20 +323,89 @@ void CChar::NPC_CreateTrigger() return; } - // 4) EVENTSPET triggers - for (size_t i = 0; i < g_Cfg.m_pEventsPetLink.size(); ++i) - { - CResourceLink * pLink = g_Cfg.m_pEventsPetLink[i].GetRef(); - if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) - continue; + // 4) EVENTSNPC triggers + for (size_t i = 0; i < g_Cfg.m_pEventsNPCLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, this, nullptr); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + return; + } - CResourceLock s; - if (!pLink->ResourceLock(s)) - continue; + // 5) EVENTSNPCANIMAL triggers + for (size_t i = 0; i < g_Cfg.m_pEventsNPCAnimalLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCAnimalLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, this, nullptr); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + return; + } + + // 6) EVENTSNPCMONSTER triggers + for (size_t i = 0; i < g_Cfg.m_pEventsNPCMonsterLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCMonsterLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, this, nullptr); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + return; + } + + // 7) EVENTSNPCMOUNTABLE triggers + for (size_t i = 0; i < g_Cfg.m_pEventsNPCMountableLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCMountableLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, this, nullptr); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + return; + } + + // 8) EVENTSNPCSHOP triggers + for (size_t i = 0; i < g_Cfg.m_pEventsNPCShopLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_pEventsNPCShopLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) + continue; + + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + + executedEvents.emplace(pLink); + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, this, nullptr); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + return; + } - executedEvents.emplace(pLink); - iRet = CScriptObj::OnTriggerScript(s, pszTrigName, this, nullptr); - if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) - return; - } } diff --git a/src/game/chars/CCharNPCAct.cpp b/src/game/chars/CCharNPCAct.cpp index 7499958f0..5e1599633 100644 --- a/src/game/chars/CCharNPCAct.cpp +++ b/src/game/chars/CCharNPCAct.cpp @@ -585,61 +585,101 @@ int CChar::NPC_WalkToPoint( bool fRun ) UpdateMove(ptOld); EXC_SET_BLOCK("Speed counting"); - // How fast can they move. - int64 iTickNext; - // TAG.OVERRIDE.MOVERATE - int64 tTick; - CVarDefCont * pVal = GetKey("OVERRIDE.MOVEDELAY", true); - if (pVal) + // How fast can they move. + int64 iTickNext; + CVarDefCont *pValMoveStyle = GetKey("OVERRIDE.MOVESTYLE", true); + + if (pValMoveStyle || IsSetOF(OF_NPCMovementOldStyle)) { - iTickNext = pVal->GetValNum(); // foot walking speed - if (IsStatFlag(STATF_ONHORSE | STATF_HOVERING)) // On Mount + if (fRun) { - if (IsStatFlag(STATF_FLY)) // Running - { - iTickNext /= 4; // 4 times faster when running while it's on a mount - } - else + if (IsStatFlag(STATF_PET)) // pets run a little faster. { - iTickNext /= 2; // 2 times faster when walking while it's on a mount + if (iDex < 75) + iDex = 75; } + iTickNext = MSECS_PER_SEC / 4 + g_Rand.GetValFast((100 - iDex) / 5) * MSECS_PER_SEC / 10; } else + iTickNext = MSECS_PER_SEC + g_Rand.GetValFast((100 - iDex) / 3) * MSECS_PER_SEC / 10; + + CVarDefCont *pValue = GetKey("OVERRIDE.MOVERATE", true); + if (pValue) { - if (IsStatFlag(STATF_FLY)) - { - iTickNext /= 2; // 2 times faster when running. - } + int64 tTick = pValue->GetValNum(); + if (tTick < 1) + tTick = 1; + iTickNext = (iTickNext * tTick) / 100; } + else + { + iTickNext = (iTickNext * pCharDef->m_iMoveRate) / 100; + } + if (iTickNext < 1) + iTickNext = 1; + + //_SetTimeout(iTickNext); + SetTimeout(iTickNext); } else { - CVarDefCont * pValue = GetKey("OVERRIDE.MOVERATE", true); - if (pValue) - tTick = pValue->GetValNum(); //Taking value from tag.override.moverate - else - tTick = pCharDef->m_iMoveRate; //no tag.override.moverate, we get default moverate (created from ini's one). - // END TAG.OVERRIDE.MOVERATE - if (fRun) + // TAG.OVERRIDE.MOVERATE + int64 tTick; + CVarDefCont *pVal = GetKey("OVERRIDE.MOVEDELAY", true); + if (pVal) { - if (IsStatFlag(STATF_PET)) // pets run a little faster. + iTickNext = pVal->GetValNum(); // foot walking speed + if (IsStatFlag(STATF_ONHORSE | STATF_HOVERING)) // On Mount { - if (iDex < 75) - iDex = 75; + if (IsStatFlag(STATF_FLY)) // Running + { + iTickNext /= 4; // 4 times faster when running while it's on a mount + } + else + { + iTickNext /= 2; // 2 times faster when walking while it's on a mount + } + } + else + { + if (IsStatFlag(STATF_FLY)) + { + iTickNext /= 2; // 2 times faster when running. + } } - iTickNext = MSECS_PER_SEC / 4 + g_Rand.GetValFast(int32(100 - (iDex*tTick) / 100) / 5) * MSECS_PER_SEC / 10; // TODO MSEC to TICK? custom timers for npc's movement? } else - iTickNext = MSECS_PER_SEC + g_Rand.GetValFast(int32(100 - (iDex*tTick) / 100) / 3) * MSECS_PER_SEC / 10; - } + { + CVarDefCont *pValue = GetKey("OVERRIDE.MOVERATE", true); + if (pValue) + tTick = pValue->GetValNum(); //Taking value from tag.override.moverate + else + tTick = pCharDef->m_iMoveRate; //no tag.override.moverate, we get default moverate (created from ini's one). + // END TAG.OVERRIDE.MOVERATE + if (fRun) + { + if (IsStatFlag(STATF_PET)) // pets run a little faster. + { + if (iDex < 75) + iDex = 75; + } + iTickNext = MSECS_PER_SEC / 4 + g_Rand.GetValFast(int32(100 - (iDex * tTick) / 100) / 5) * MSECS_PER_SEC / + 10; // TODO MSEC to TICK? custom timers for npc's movement? + } + else + iTickNext = MSECS_PER_SEC + g_Rand.GetValFast(int32(100 - (iDex * tTick) / 100) / 3) * MSECS_PER_SEC / 10; + } - if (iTickNext < MSECS_PER_TENTH) // Do not allow less than a tenth of second. This may be decreased in the future to allow more precise timers, at the cost of cpu. - iTickNext = MSECS_PER_TENTH; - else if (iTickNext > 5 * MSECS_PER_SEC) // neither more than 5 seconds. - iTickNext = 5 * MSECS_PER_SEC; + if (iTickNext < + MSECS_PER_TENTH) // Do not allow less than a tenth of second. This may be decreased in the future to allow more precise timers, at the cost of cpu. + iTickNext = MSECS_PER_TENTH; + else if (iTickNext > 5 * MSECS_PER_SEC) // neither more than 5 seconds. + iTickNext = 5 * MSECS_PER_SEC; - _SetTimeout(iTickNext); + _SetTimeout(iTickNext); + } + EXC_CATCH; return 1; } diff --git a/src/game/chars/CCharNPCPet.cpp b/src/game/chars/CCharNPCPet.cpp index 91e65ff6a..0b9ef724c 100644 --- a/src/game/chars/CCharNPCPet.cpp +++ b/src/game/chars/CCharNPCPet.cpp @@ -173,14 +173,20 @@ bool CChar::NPC_OnHearPetCmd( lpctstr pszCmd, CChar *pSrc, bool fAllPets ) break; case PC_GUARD_ME: - m_Act_UID = pSrc->GetUID(); - Skill_Start(NPCACT_GUARD_TARG); + if ((m_Act_UID != pSrc->GetUID()) || (Skill_GetActive() != NPCACT_GUARD_TARG)) // When you do all guard flood, the mount jumps one frame. + { + m_Act_UID = pSrc->GetUID(); + Skill_Start(NPCACT_GUARD_TARG); + } break; case PC_COME: case PC_FOLLOW_ME: - m_Act_UID = pSrc->GetUID(); - Skill_Start(NPCACT_FOLLOW_TARG); + if ((m_Act_UID != pSrc->GetUID()) || (Skill_GetActive() != NPCACT_FOLLOW_TARG)) // When you do all come flood, the mount jumps one frame. + { + m_Act_UID = pSrc->GetUID(); + Skill_Start(NPCACT_FOLLOW_TARG); + } break; case PC_FOLLOW: diff --git a/src/game/chars/CCharNotoriety.cpp b/src/game/chars/CCharNotoriety.cpp index c3880442b..389bba933 100644 --- a/src/game/chars/CCharNotoriety.cpp +++ b/src/game/chars/CCharNotoriety.cpp @@ -186,7 +186,7 @@ NOTO_TYPE CChar::Noto_CalcFlag(const CChar * pCharViewer, bool fAllowIncog, bool } } - if (NPC_IsOwnedBy(pCharViewer, false)) // All pets are neutral to their owners. + if (!IsSetOF(OF_PetBehaviorOwnerNeutral) && NPC_IsOwnedBy(pCharViewer, false)) // All pets are neutral to their owners. return NOTO_NEUTRAL; } diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index d68bf7da3..9ebca10ee 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -1982,13 +1982,23 @@ bool CChar::Spell_Equip_OnTick( CItem * pItem ) iDmgType = (DAMAGE_TYPE)(ResGetIndex((dword)Args.m_VarsLocal.GetKeyNum("DamageType"))); if (iDmgType > 0 && iEffect > 0) // This is necessary if we have a spell that is harmful but does no damage periodically. { - OnTakeDamage(iEffect, pItem->m_uidLink.CharFind(), iDmgType, - (iDmgType & (DAMAGE_HIT_BLUNT | DAMAGE_HIT_PIERCE | DAMAGE_HIT_SLASH)) ? 100 : 0, - (iDmgType & DAMAGE_FIRE) ? 100 : 0, - (iDmgType & DAMAGE_COLD) ? 100 : 0, - (iDmgType & DAMAGE_POISON) ? 100 : 0, - (iDmgType & DAMAGE_ENERGY) ? 100 : 0, - spell); + // + CChar *pLinkedChar = pItem->m_uidLink.CharFind(); + if (pLinkedChar == nullptr) + { + // pLinkedChar = this; // If it is not alive or deleted, should it be like it is self-damaging? Note: Under the OnTakeDamage() + Skill_Fail(); // If the memory does not belong to a creature, or if the creature is dead or deleted, skills do not take effect on damage taken. + } + + OnTakeDamage(iEffect, + pLinkedChar, + iDmgType, + (iDmgType & (DAMAGE_HIT_BLUNT | DAMAGE_HIT_PIERCE | DAMAGE_HIT_SLASH)) ? 100 : 0, + (iDmgType & DAMAGE_FIRE) ? 100 : 0, + (iDmgType & DAMAGE_COLD) ? 100 : 0, + (iDmgType & DAMAGE_POISON) ? 100 : 0, + (iDmgType & DAMAGE_ENERGY) ? 100 : 0, + spell); } } else if (pSpellDef->IsSpellType(SPELLFLAG_HEAL)) @@ -3463,6 +3473,11 @@ int CChar::Spell_CastStart() Args.m_VarsLocal.SetNum("WOP", fWOP); int64 WOPFont = g_Cfg.m_iWordsOfPowerFont; int64 WOPColor; + TALKMODE_TYPE WOPTalkMode = g_Cfg.m_iWordsOfPowerTalkMode ? g_Cfg.m_iWordsOfPowerTalkMode : TALKMODE_SPELL; + + if (WOPTalkMode < TALKMODE_SAY || WOPTalkMode >= TALKMODE_COMMAND) + WOPTalkMode = TALKMODE_SPELL; + if (g_Cfg.m_iWordsOfPowerColor > 0) WOPColor = g_Cfg.m_iWordsOfPowerColor; else if (m_SpeechHueOverride) @@ -3471,8 +3486,10 @@ int CChar::Spell_CastStart() WOPColor = m_pPlayer->m_SpeechHue; else WOPColor = HUE_TEXT_DEF; + Args.m_VarsLocal.SetNum("WOPColor", WOPColor, true); Args.m_VarsLocal.SetNum("WOPFont", WOPFont, true); + Args.m_VarsLocal.SetNum("WOPTalkMode", WOPTalkMode, true); if ( IsTrigUsed(TRIGGER_SPELLCAST) ) { @@ -3517,11 +3534,12 @@ int CChar::Spell_CastStart() { WOPColor = Args.m_VarsLocal.GetKeyNum("WOPColor"); WOPFont = Args.m_VarsLocal.GetKeyNum("WOPFont"); + WOPTalkMode = (TALKMODE_TYPE)Args.m_VarsLocal.GetKeyNum("WOPTalkMode"); // Correct talk mode for spells WOP is TALKMODE_SPELL, but sphere doesn't have any delay between spell casts this can allow WOP flood on screen. if ( pSpellDef->m_sRunes[0] == '.' ) { - Speak((pSpellDef->m_sRunes.GetBuffer()) + 1, (HUE_TYPE)WOPColor, TALKMODE_SPELL, (FONT_TYPE)WOPFont); + Speak((pSpellDef->m_sRunes.GetBuffer()) + 1, (HUE_TYPE)WOPColor, (TALKMODE_TYPE)WOPTalkMode, (FONT_TYPE)WOPFont); } else { @@ -3539,7 +3557,7 @@ int CChar::Spell_CastStart() if ( len > 0 ) { pszTemp[len] = 0; - Speak(pszTemp, (HUE_TYPE)WOPColor, TALKMODE_SPELL, (FONT_TYPE)WOPFont); + Speak(pszTemp, (HUE_TYPE)WOPColor, (TALKMODE_TYPE)WOPTalkMode, (FONT_TYPE)WOPFont); } } } diff --git a/src/game/clients/CClient.cpp b/src/game/clients/CClient.cpp index 7fc23ab34..ca1cdd76f 100644 --- a/src/game/clients/CClient.cpp +++ b/src/game/clients/CClient.cpp @@ -14,6 +14,7 @@ #include "../CWorld.h" #include "../CWorldGameTime.h" #include "../CWorldMap.h" +#include "../CWorldSearch.h" #include "../spheresvr.h" #include "../triggers.h" #include "CParty.h" @@ -1289,6 +1290,53 @@ bool CClient::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from } break; + case CV_CLOSECONTAINER: + { + const CItem *pItem = nullptr; + if (s.HasArgs()) + { + const CUID uid(s.GetArgDWVal()); + if (!uid.IsItem()) + return (false); + pItem = uid.ItemFind(); + } + if (pItem != nullptr && pItem->IsType(IT_CONTAINER)) + closeUIWindow(pItem, PacketCloseUIWindow::Container); + } + break; + + case CV_CLOSEVENDORMENU: + { + const CChar *pChar = m_pChar; + if (s.HasArgs()) + { + const CUID uid(s.GetArgDWVal()); + if (!uid.IsChar()) + return (false); + pChar = uid.CharFind(); + if (pChar) + addVendorClose(pChar); + } + else + { + auto AreaChars = CWorldSearchHolder::GetInstance(pChar->GetTopPoint(), UO_MAP_VIEW_SIGHT); + for (;;) + { + const CChar *pCharArea = AreaChars->GetChar(); + if (pCharArea == nullptr) + break; + if (pCharArea->m_pPlayer) + continue; + if (pCharArea == GetChar()) + continue; + if (!pCharArea->NPC_IsVendor()) + continue; + addVendorClose(pCharArea); + } + } + } + break; + case CV_CODEXOFWISDOM: { int64 piArgs[2]; diff --git a/src/game/clients/CClient.h b/src/game/clients/CClient.h index d96f64d1b..2a447db3a 100644 --- a/src/game/clients/CClient.h +++ b/src/game/clients/CClient.h @@ -519,9 +519,10 @@ class CClient : public CSObjListRec, public CScriptObj, public CChatChanMember, bool addBookOpen( CItem * pBook ) const; void addBookPage( const CItem * pBook, word wPage, word wCount ) const; void addStatusWindow( CObjBase * pObj, bool fRequested = false ); // Opens the status window - void addHitsUpdate( CChar * pChar ); - void addManaUpdate( CChar * pChar ); - void addStamUpdate( CChar * pChar ); + void addHitsUpdate(CChar *pChar, bool fFull = false); + void addManaUpdate(CChar *pChar, bool fFull = false); + void addStamUpdate(CChar *pChar, bool fFull = false); + void addAttributesUpdate(CChar *pChar); void addHealthBarUpdate( const CChar * pChar ) const; void addBondedStatus( const CChar * pChar, bool fIsDead ) const; void addSkillWindow(SKILL_TYPE skill, bool fFromInfo = false) const; // Opens the skills list diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index c4ecc42ff..fbb594520 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -360,6 +360,22 @@ void CClient::Event_Item_Drop( CUID uidItem, CPointMap pt, CUID uidOn, uchar gri // Convert physical gold into virtual gold when drop it on bankbox if ( pItem->IsType(IT_GOLD) && (g_Cfg.m_iFeatureTOL & FEATURE_TOL_VIRTUALGOLD) ) { + if (IsTrigUsed(TRIGGER_DEPOSIT) || IsTrigUsed(TRIGGER_ITEMDEPOSIT)) + { + CScriptTriggerArgs args(pItem); + TRIGRET_TYPE ttResult = pItem->OnTrigger(ITRIG_DEPOSIT, m_pChar, &args); + if (pItem->IsDeleted()) + { + SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_BVBOX_DEPOSITED_FAIL)); + return; + } + if (ttResult == TRIGRET_RET_TRUE) + { + SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_BVBOX_DEPOSITED_FAIL)); + Event_Item_Drop_Fail(pItem); + return; + } + } pChar->m_virtualGold += pItem->GetAmount(); SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_BVBOX_DEPOSITED), pItem->GetAmount()); addSound(pItem->GetDropSound(pObjOn)); @@ -1988,6 +2004,15 @@ void CClient::Event_Talk_Common(lpctstr pszText) // PC speech break; } */ + // NPC's with special key words ? + if (pChar->m_pNPC) + { + if (pChar->m_pNPC->m_Brain == NPCBRAIN_BANKER) + { + if (FindStrWord(pszText, "BANK") > 0) + break; + } + } } if ( !pChar ) diff --git a/src/game/clients/CClientMsg.cpp b/src/game/clients/CClientMsg.cpp index d2e6f6611..dc4c56a11 100644 --- a/src/game/clients/CClientMsg.cpp +++ b/src/game/clients/CClientMsg.cpp @@ -2109,8 +2109,8 @@ void CClient::addMapWaypoint(CObjBase *pObj, MAPWAYPOINT_TYPE type) const if (type) { // Classic clients only support MAPWAYPOINT_Remove and MAPWAYPOINT_Healer - if ((type != MAPWAYPOINT_Healer) && !GetNetState()->isClientKR() && !GetNetState()->isClientEnhanced()) - return; + //if ((type != MAPWAYPOINT_Healer) && !GetNetState()->isClientKR() && !GetNetState()->isClientEnhanced()) + //return; if (PacketWaypointAdd::CanSendTo(GetNetState())) new PacketWaypointAdd(this, pObj, type); @@ -2157,20 +2157,21 @@ void CClient::UpdateStats() { if ( m_fUpdateStats & SF_UPDATE_HITS ) { - addHitsUpdate( m_pChar); + addHitsUpdate(m_pChar, false); m_fUpdateStats &= ~SF_UPDATE_HITS; } if ( m_fUpdateStats & SF_UPDATE_MANA ) { - addManaUpdate( m_pChar ); + addManaUpdate(m_pChar, false); m_fUpdateStats &= ~SF_UPDATE_MANA; } if ( m_fUpdateStats & SF_UPDATE_STAM ) { - addStamUpdate( m_pChar ); + addStamUpdate(m_pChar, false); m_fUpdateStats &= ~SF_UPDATE_STAM; } + addAttributesUpdate(m_pChar); } } @@ -2197,17 +2198,20 @@ void CClient::addStatusWindow( CObjBase *pObj, bool fRequested ) // Opens the st } } -void CClient::addHitsUpdate( CChar *pChar ) +void CClient::addHitsUpdate(CChar *pChar, bool fFull) { - ADDTOCALLSTACK("CClient::addHitsUpdate"); - if ( !pChar ) - return; + ADDTOCALLSTACK("CClient::addHitsUpdate"); + if (!pChar) + return; PacketHealthUpdate cmd(pChar, pChar == m_pChar); cmd.send(this); + + if (fFull) + addAttributesUpdate(pChar); } -void CClient::addManaUpdate( CChar *pChar ) +void CClient::addManaUpdate(CChar *pChar, bool fFull) { ADDTOCALLSTACK("CClient::addManaUpdate"); if ( !pChar ) @@ -2216,14 +2220,20 @@ void CClient::addManaUpdate( CChar *pChar ) PacketManaUpdate cmd(pChar, true); cmd.send(this); + PacketMobileAttributes cmdAttr(pChar); + cmdAttr.send(this); + if ( pChar->m_pParty ) { PacketManaUpdate cmd2(pChar, false); pChar->m_pParty->AddStatsUpdate(pChar, &cmd2); } + + if (fFull) + addAttributesUpdate(pChar); } -void CClient::addStamUpdate( CChar *pChar ) +void CClient::addStamUpdate(CChar *pChar, bool fFull) { ADDTOCALLSTACK("CClient::addStamUpdate"); if ( !pChar ) @@ -2232,11 +2242,17 @@ void CClient::addStamUpdate( CChar *pChar ) PacketStaminaUpdate cmd(pChar, true); cmd.send(this); + PacketMobileAttributes cmdAttr(pChar); + cmdAttr.send(this); + if ( pChar->m_pParty ) { PacketStaminaUpdate cmd2(pChar, false); pChar->m_pParty->AddStatsUpdate(pChar, &cmd2); } + + if (fFull) + addAttributesUpdate(pChar); } void CClient::addHealthBarUpdate( const CChar * pChar ) const @@ -2252,6 +2268,16 @@ void CClient::addHealthBarUpdate( const CChar * pChar ) const new PacketHealthBarUpdate(this, pChar); } +void CClient::addAttributesUpdate(CChar *pChar) +{ + ADDTOCALLSTACK("CClient::addAttributesUpdate"); + if (!pChar) + return; + + PacketMobileAttributes cmd(pChar); + cmd.send(this); +} + void CClient::addBondedStatus( const CChar * pChar, bool bIsDead ) const { ADDTOCALLSTACK("CClient::addBondedStatus"); @@ -3106,18 +3132,21 @@ byte CClient::LogIn( CAccount * pAccount, CSString & sMsg ) return( PacketLoginError::MaxClients ); } + byte ErrorCode = PacketLoginError::Blocked; + // Do the scripts allow to login this account? pAccount->m_Last_IP.SetAddrIP(GetPeer().GetAddrIP()); CScriptTriggerArgs Args; Args.Init(pAccount->GetName()); Args.m_iN1 = GetConnectType(); + Args.m_iN2 = ErrorCode <= PacketLoginError::Invalid ? PacketLoginError::Blocked : ErrorCode; Args.m_pO1 = this; TRIGRET_TYPE tr = TRIGRET_RET_DEFAULT; g_Serv.r_Call("f_onaccount_login", &g_Serv, &Args, nullptr, &tr); if ( tr == TRIGRET_RET_TRUE ) { sMsg = g_Cfg.GetDefaultMsg( DEFMSG_MSG_ACC_DENIED ); - return (PacketLoginError::Blocked); + return ((byte)Args.m_iN2); } m_pAccount = pAccount; diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index fba785497..37f724f8a 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -62,6 +62,7 @@ lpctstr const CItem::sm_szTrigName[ITRIG_QTY+1] = // static "@DelObj", // For t_spawn when obj is remove from list "@DelRedCandle", "@DelWhiteCandle", + "@Deposit", // IT_GOLD deposited virtualgold "@Destroy", //+I am nearly destroyed "@DropOn_Char", // I have been dropped on this char "@DropOn_Ground", // I have been dropped on the ground here @@ -3769,6 +3770,24 @@ TRIGRET_TYPE CItem::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript goto stopandret; } + // Weapons + if (pItemDef->IsTypeWeapon(m_type)) + { + EXC_SET_BLOCK("Item triggers - EVENTSITEMWEAPON"); + for (size_t i = 0; i < g_Cfg.m_iEventsItemWeaponLink.size(); ++i) + { + CResourceLink *pLink = g_Cfg.m_iEventsItemWeaponLink[i].GetRef(); + if (!pLink || !pLink->HasTrigger(iAction)) + continue; + CResourceLock s; + if (!pLink->ResourceLock(s)) + continue; + iRet = CScriptObj::OnTriggerScript(s, pszTrigName, pSrc, pArgs); + if (iRet != TRIGRET_RET_FALSE && iRet != TRIGRET_RET_DEFAULT) + goto stopandret; + } + } + // 5) TYPEDEF EXC_SET_BLOCK("typedef"); { diff --git a/src/game/items/CItemCorpse.cpp b/src/game/items/CItemCorpse.cpp index 75c5b57c0..6b20cc6aa 100644 --- a/src/game/items/CItemCorpse.cpp +++ b/src/game/items/CItemCorpse.cpp @@ -150,6 +150,8 @@ CItemCorpse *CChar::FindMyCorpse( bool ignoreLOS, int iRadius ) const break; if ( !pItem->IsType(IT_CORPSE) ) continue; + if (pItem->m_TagDefs.GetKeyNum("NOREJOIN")) // OWNERS OF THE BODIES WITH THIS TAG SHOULD NOT STAND ON THE BODIES + continue; CItemCorpse *pCorpse = dynamic_cast(pItem); if ( !pCorpse || (pCorpse->m_uidLink != GetUID()) ) continue; diff --git a/src/game/spheresvr.cpp b/src/game/spheresvr.cpp index 6f7fdeb5a..8cd9e5354 100644 --- a/src/game/spheresvr.cpp +++ b/src/game/spheresvr.cpp @@ -20,6 +20,7 @@ #include "../common/CException.h" #include "../common/CExpression.h" #include "../common/CUOInstall.h" +#include "../common/CScriptTriggerArgs.h" #include "../common/sphereversion.h" #include "../network/CNetworkManager.h" #include "../network/PingServer.h" @@ -370,6 +371,12 @@ void Sphere_ExitServer() default: ptcReason = "Server shutdown complete"; break; } + CScriptTriggerArgs ExitArgs; + ExitArgs.m_VarsLocal.SetStrNew("Reason", ptcReason); + ExitArgs.m_VarsLocal.SetNum("Flag", iExitFlag); + g_Serv.r_Call("f_onserver_exit_later", &g_Serv, &ExitArgs); + + g_Log.Event(LOGM_INIT|LOGL_FATAL, "Server terminated: %s (code %d)\n", ptcReason, iExitFlag); #ifdef _WIN32 if (!g_Serv._fCloseNTWindowOnTerminate) diff --git a/src/network/send.cpp b/src/network/send.cpp index 50856ed42..ccc24323d 100644 --- a/src/network/send.cpp +++ b/src/network/send.cpp @@ -1030,6 +1030,29 @@ PacketDeathMenu::PacketDeathMenu(const CClient* target, Mode mode) : PacketSend( push(target); } +/*************************************************************************** + * + * + * Packet 0x2d : PacketMobileAttributes Sends all stat (NORMAL) + * + * + ***************************************************************************/ +PacketMobileAttributes::PacketMobileAttributes(const CChar *character) : PacketSend(XCMD_MobileAttributes, 17, PRI_NORMAL) +{ + ADDTOCALLSTACK("PacketMobileAttributes::PacketMobileAttributes"); + + writeInt32(character->GetUID()); + + writeInt16((word)(character->Stat_GetMaxAdjusted(STAT_STR))); + writeInt16((word)(character->Stat_GetVal(STAT_STR))); + + writeInt16((word)(character->Stat_GetMaxAdjusted(STAT_INT))); + writeInt16((word)(character->Stat_GetVal(STAT_INT))); + + writeInt16((word)(character->Stat_GetMaxAdjusted(STAT_DEX))); + writeInt16((word)(character->Stat_GetVal(STAT_DEX))); +} + /*************************************************************************** * diff --git a/src/network/send.h b/src/network/send.h index 52f5d41a2..4d7cb5d42 100644 --- a/src/network/send.h +++ b/src/network/send.h @@ -406,6 +406,19 @@ class PacketDeathMenu : public PacketSend PacketDeathMenu(const CClient* target, Mode mode); }; +/*************************************************************************** + * + * + * Packet 0x2D : PacketMobileAttributes Sends all stat (NORMAL) + * + * + ***************************************************************************/ +class PacketMobileAttributes : public PacketSend { +public: + PacketMobileAttributes(const CChar *character); +}; + + /*************************************************************************** * * @@ -1907,7 +1920,8 @@ class PacketWaypointAdd : public PacketSend virtual bool canSendTo(const CNetState *state) const { return CanSendTo(state); } static bool CanSendTo(const CNetState *state) { - return state->isClientVersionNumber(MINCLIVER_MAPWAYPOINT) || state->isClientKR() || state->isClientEnhanced(); + //return state->isClientVersionNumber(MINCLIVER_MAPWAYPOINT) || state->isClientKR() || state->isClientEnhanced(); + return state->isClientVersionNumber(MINCLIVER_NEWVERSIONING) || state->isClientKR() || state->isClientEnhanced(); } }; @@ -1926,7 +1940,8 @@ class PacketWaypointRemove : public PacketSend virtual bool canSendTo(const CNetState *state) const { return CanSendTo(state); } static bool CanSendTo(const CNetState *state) { - return state->isClientVersionNumber(MINCLIVER_MAPWAYPOINT) || state->isClientKR() || state->isClientEnhanced(); + //return state->isClientVersionNumber(MINCLIVER_MAPWAYPOINT) || state->isClientKR() || state->isClientEnhanced(); + return state->isClientVersionNumber(MINCLIVER_NEWVERSIONING) || state->isClientKR() || state->isClientEnhanced(); } }; diff --git a/src/sphere.ini b/src/sphere.ini index c0cea2587..349425241 100644 --- a/src/sphere.ini +++ b/src/sphere.ini @@ -660,17 +660,44 @@ SpeechSelf=spk_player // Speech block associated to pets SpeechPet=spk_pet +// Events related to all NPCs (out of use) +//EventsPet=e_npc_generic_event + // Events related to all NPCs -EventsPet=e_npc_generic_event //This is the default events for script pack +//EventsNPC=e_npc_all + +// Events related to all animals (brain_animal without mountables) +//EventsNPCAnimal=e_npc_animals + +// Events related to all monsters (brain_monster, brain_dragon, brain_berserk) +//EventsNPCMonster=e_npc_monsters + +// Events related to all animals (brain_animal with mountable) +//EventsNPCMountable=e_npc_mountables + +// Events related to all shopkeepers (brain_vendor, brain_stable, brain_healer) +//EventsNPCShop=e_npc_shopkeepers -// Events related to all players -EventsPlayer=e_player_generic_event,e_player_crafting_event //This is the default events for script pack +// Events related to all char (player and staff) +//EventsChar=e_char_all + +// Events related to all players (if the plevel is lower than 1) +//EventsCharPlayer=e_char_players // Events related to all regions -//EventsRegion=e_your_event +//EventsRegion=er_region_all + +// Events related to all staff (if plevel is higher than 1) +//EventsCharStaff=e_char_staffs + +// Events related to all players (out of use) +//EventsPlayer= // Events related to all items -//EventsItem=ei_your_event +//EventsItem=ei_items + +// Events related to all weapons +//EventsItemWeapon=ei_item_weapons // When player skills/stats goes this times more than skillclass allowed, drop // them to skillclass level. Setting this to 0 disables the action. @@ -857,6 +884,8 @@ Experimental=0 // OF_AllowContainerInsideContainer 00800000 // Allow containers inside other containers even if they are heavier than the container being inserted into. // OF_VendorStockLimit 01000000 // Limits how much of an item a vendor can buy using the value set in the TEMPLATE. Format: BUY=ID,AMOUNT // OF_EnableGuildAlignNotoriety 02000000 // If enabled, guilds with the same alignment will see each other as enemy or ally. +// OF_PetBehaviorOwnerNeutral 04000000 // Should my pets always appear natural to me? +// OF_NPCMovementOldStyle 06000000 // Required setting to make NPCs run like in the old version. OptionFlags=08|080|0200 // Area flags @@ -1006,6 +1035,19 @@ WOPStaff=0 // Words of power color (0 = inherit color from char SPEECHCOLOR value) //WOPColor=03b2 +// TALKMODE_SAY = 0 // A character speaking. +// TALKMODE_SYSTEM = 1 // Display as system prompt +// TALKMODE_EMOTE = 2 // *smiles* at object (client shortcut: :+space) +// TALKMODE_ITEM = 6 // text labeling an item. Preceeded by "You see" +// TALKMODE_NOSCROLL = 7 // As a status msg. Does not scroll (as reported by the packet guides) +// TALKMODE_WHISPER = 8 // Only those close can here. (client shortcut: ;+space) +// TALKMODE_YELL = 9 // Can be heard 2 screens away. (client shortcut: !+space) +// TALKMODE_SPELL = 10 // Used by spells +// TALKMODE_GUILD = 13 // Used by guild chat (client shortcut: \) +// TALKMODE_ALLIANCE = 14 // Used by alliance chat (client shortcut: shift+\) +// Words of power talk mode (Default: 10 = TALKMODE_SPELL) +WOPTalkMode=1 + // Mana lost when casting spell action fails or abort ManaLossAbort=0 // Lose Mana when the spell is aborted during spell casting. ManaLossFail=0 // Lose Mana when the spell is failed during the spell casting. diff --git a/src/tables/CChar_props.tbl b/src/tables/CChar_props.tbl index 17e05a718..f9e51f11e 100644 --- a/src/tables/CChar_props.tbl +++ b/src/tables/CChar_props.tbl @@ -79,6 +79,7 @@ ADD(MOVE, "MOVE") ADD(NAME, "NAME") ADD(NOTOGETFLAG, "NOTOGETFLAG") ADD(NOTOSAVE, "NOTOSAVE") +ADD(NOTOTITLE, "NOTOTITLE") ADD(NPC, "NPC") ADD(OBODY, "OBODY") ADD(ODEX, "ODEX") diff --git a/src/tables/CClient_functions.tbl b/src/tables/CClient_functions.tbl index 3ec981b58..66f87d7d6 100644 --- a/src/tables/CClient_functions.tbl +++ b/src/tables/CClient_functions.tbl @@ -16,9 +16,11 @@ ADD(CAST, "CAST") ADD(CHANGEFACE, "CHANGEFACE") ADD(CHARLIST, "CHARLIST") ADD(CLEARCTAGS, "CLEARCTAGS") +ADD(CLOSECONTAINER, "CLOSECONTAINER") ADD(CLOSEPAPERDOLL, "CLOSEPAPERDOLL") ADD(CLOSEPROFILE, "CLOSEPROFILE") ADD(CLOSESTATUS, "CLOSESTATUS") +ADD(CLOSEVENDORMENU,"CLOSEVENDORMENU") ADD(CODEXOFWISDOM, "CODEXOFWISDOM") ADD(CTAGLIST, "CTAGLIST") ADD(DYE, "DYE") diff --git a/src/tables/defmessages.tbl b/src/tables/defmessages.tbl index cf67059ce..48dff0fbf 100644 --- a/src/tables/defmessages.tbl +++ b/src/tables/defmessages.tbl @@ -53,6 +53,7 @@ MSG(AXIS_INFO_ERROR, "ERROR: Unable to retrieve Axis' database info") MSG(AXIS_NOT_PRIV, "This account does not exist or is not privileged to access Axis' database.") MSG(BEGGING_START, "You grovel at %s's feet") MSG(BVBOX_DEPOSITED, "%d gold was deposited in your account.") +MSG(BVBOX_DEPOSITED_FAIL, "Gold could not be deposited into your account") MSG(BVBOX_FULL_ITEMS, "Your bankbox can't hold more items.") MSG(BVBOX_FULL_WEIGHT, "Your bankbox can't hold more weight.") MSG(BVBOX_OPEN_OTHER, "%s has %d stones in %s %s") diff --git a/src/tables/triggers.tbl b/src/tables/triggers.tbl index a200f613a..71ad81eb5 100644 --- a/src/tables/triggers.tbl +++ b/src/tables/triggers.tbl @@ -47,6 +47,7 @@ ADD(DELMULTI) ADD(DELOBJ) ADD(DELREDCANDLE) ADD(DELWHITECANDLE) +ADD(DEPOSIT) ADD(DESTROY) ADD(DISMOUNT) ADD(DRINK) @@ -79,6 +80,7 @@ ADD(HITCHECK) ADD(HITIGNORE) ADD(HITMISS) ADD(HITPARRY) +ADD(HITREACTIVE) ADD(HITTRY) ADD(HOUSEDESIGNBEGIN) ADD(HOUSEDESIGNCOMMIT) @@ -95,6 +97,7 @@ ADD(ITEMCONTEXTMENUSELECT) ADD(ITEMCREATE) ADD(ITEMDAMAGE) ADD(ITEMDCLICK) +ADD(ITEMDEPOSIT) ADD(ITEMDESTROY) ADD(ITEMDROPON_CHAR) ADD(ITEMDROPON_GROUND)