From 6960982a22bbdca7e51e46865cb188a37a7e8468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caner=20K=C4=B1l=C4=B1=C3=A7o=C4=9Flu?= Date: Sat, 26 Oct 2024 13:40:40 +0300 Subject: [PATCH 1/3] Event definitions in Sphere.ini have been made more extensive. EventsPet = removed EventsNPC EventsNPCAnimal EventsNPCMonster EventsNPCMountable EventsNPCShop EventsChar EventsCharPlayer EventsCharStaff EventsPlayer: removed EventsItem EventsItemWeapon Note: All events were tested one by one and they work without any problems. --- Changelog.txt | 42 +++++++ src/game/CServerConfig.cpp | 119 ++++++++++++++++---- src/game/CServerConfig.h | 65 +++++++++-- src/game/chars/CCharAct.cpp | 217 ++++++++++++++++++++++++++++++------ src/game/chars/CCharNPC.cpp | 97 +++++++++++++--- src/game/items/CItem.cpp | 18 +++ 6 files changed, 476 insertions(+), 82 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 034689fd8..bc1971242 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -3914,3 +3914,45 @@ 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. + +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 \ No newline at end of file diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 6beadd6bd..959af8ab7 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 @@ -840,9 +847,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) }}, @@ -4815,7 +4829,10 @@ bool CServerConfig::Load( bool fResync ) pRegion->MakeRegionDefname(); } - // parse eventsitem + ///////////////////// + // parse eventsitem + ///////////////////// + // all items m_iEventsItemLink.clear(); if ( ! m_sEventsItem.IsEmpty() ) { @@ -4823,21 +4840,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..cad9802e5 100644 --- a/src/game/CServerConfig.h +++ b/src/game/CServerConfig.h @@ -433,17 +433,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/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index b4c427f1a..af1b978aa 100644 --- a/src/game/chars/CCharAct.cpp +++ b/src/game/chars/CCharAct.cpp @@ -5599,48 +5599,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/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/items/CItem.cpp b/src/game/items/CItem.cpp index fba785497..dce5fada5 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -3769,6 +3769,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"); { From 313681697830234d77992d86f35f554c767b33f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caner=20K=C4=B1l=C4=B1=C3=A7o=C4=9Flu?= Date: Sat, 26 Oct 2024 13:42:38 +0300 Subject: [PATCH 2/3] sphere.ini file updated --- src/sphere.ini | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/sphere.ini b/src/sphere.ini index c0cea2587..104d0b7cb 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. From 9906dd133c4fd69edb0fbbbdc03e36f0cdcc3264 Mon Sep 17 00:00:00 2001 From: Jhobean <51728381+Jhobean@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:58:57 -0500 Subject: [PATCH 3/3] Change "Char" word to "client" and more explicit changelog (#1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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. Tested. * The entity was jumping frames in flooded pet commands (come, guard) * 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, * bank hear fix (#1332) 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) tested. * Added NOREJOIN tag for corpses (#1318) * Added TAG.NOREJOIN tag for corpses If the NOREJOIN tag is 1, 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. * Fixed MacOS compilation error Caused by changes in mariadb-c-connector homebrew packaging. * RANGE is complete for skills with distance query All skills that have a distance query have been adjusted to take the RANGE values ​​of that skill. * Add support for delay explosion spell * Add T_FLETCHING type to open bowcraft menu * t_multi_addon redeed fix (#1314) * New trigger @HitReactive (#1317) * New trigger @HitReactive - 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. * Local variable name update * changelog update --------- Co-authored-by: cbnolok * Mount Ceiling Check Fix (#1340) Checked if the char is onhorse or you can't pass under some roofs even on foot (Issue #1329) * Chat system crash fix (Empty Chatname) * Added new local variable in WOPSYSTEM LOCAL.WOPTalkMode and ini settings WOPTalkMode (#1325) * Added new local variable in WOPSYSTEM LOCAL.WOPTalkMode and ini settings WOPTalkMode 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 Tested. * initialize the new member variable in the cserverconfig --------- Co-authored-by: cbnolok * Change "Char" word to "client" and more explicit changelog --------- Co-authored-by: Caner Kılıçoğlu Co-authored-by: cbnolok Co-authored-by: Raydie <50953519+raydienull@users.noreply.github.com> Co-authored-by: Gladie Co-authored-by: DavideRei <118212274+DavideRei@users.noreply.github.com> --- Changelog.txt | 98 ++++++++++++--- .../include/OSX-AppleClang_common.inc.cmake | 2 + src/game/CObjBase.h | 1 + src/game/CServerConfig.cpp | 43 ++++--- src/game/CServerConfig.h | 21 ++-- src/game/chars/CChar.cpp | 5 +- src/game/chars/CCharAct.cpp | 37 +++--- src/game/chars/CCharFight.cpp | 66 ++++++++-- src/game/chars/CCharNPCPet.cpp | 14 ++- src/game/chars/CCharSkill.cpp | 119 ++++++++++++++---- src/game/chars/CCharSpell.cpp | 73 ++++++++--- src/game/chars/CCharStatus.cpp | 2 +- src/game/clients/CChatChanMember.cpp | 2 +- src/game/clients/CClientEvent.cpp | 9 ++ src/game/clients/CClientUse.cpp | 11 +- src/game/items/CItemCorpse.cpp | 2 + src/game/items/CItemMulti.cpp | 26 +++- src/game/items/item_types.h | 4 +- src/game/uo_files/uofiles_enums.h | 5 +- src/sphere.ini | 45 ++++--- src/tables/triggers.tbl | 1 + 21 files changed, 430 insertions(+), 156 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index bc1971242..2fe36ce80 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -3915,12 +3915,83 @@ 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. -13-10-2024, canerksk - Event definitions in Sphere.ini have been made more extensive. +27-11-2024, canerksk +- Added: All skills that have a distance query have been adjusted to take the RANGE values of that skill. + All skills to be used in RANGE; + BLACKSMITHING + MINING // It was added before. + FISHING // It was added before. + LUMBERJACKING // It was added before. + PEACEMAKING + ENTICEMENT + PROVOCATION + COOKING + TAMING + THROWING + SNOOPING + STEALING + Also added BREATH.MAXDIST to Breath. If this property is not given, the default is 14 distance. +- 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. +- 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) + +27-11-2024, Gladie +- Fixed: Redeeding t_multi_addon outside of housing system. Before it was only looking for owner of the multi, but in our case we need to know uid of the redeeding char. + +2-12-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). +- 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. + +02-12-2024, raydie +- Added: Add LAYER_SPELL_Explosion for use delayed explosion spell. Now if set Duration to spell explosion, these duration is the delay to execute the explosion (From UOGuide, explosion spell: 2 second delay between targetting and explosion). + +3-12-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.ReflectDamage (r/w) = The amount of damage that will be reflected to this other party + LOCAL.ReduceDamage (r/w) = Amount to be deducted from damage received + LOCAL.DamageType (r/w) = Type of damage received (Default: DAMAGE_FIXED andDAMAGE_REACTIVE) + NOTE; + 1. If no ReflectDamage or ReduceDamage values are entered, the system defaults to the Reactive Armor Effect value. + 2. No damage amount can be less than 1. + +4-12-2024, canerksk +- Fixed: Sphere crash troubleshooting if a player has no CHATNAME value and remove a channel. + +6-12-2024, canerksk +- Added: Added LOCAL.WOPTalkMode in @SpellCast trigger and ini setting WOPTalkMode. + 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 - // Events related to all NPCs (out of use) - //EventsPet=e_npc_generic_event + 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+\) + +13-10-2024, canerksk +[Sphere.ini] +- Removed: (Action require by admin and must update this) + EventsPet=e_npc_generic_event + EventsPlayer=e_player_generic_event +- Added: Event definitions have been made more extensive. // Events related to all NPCs EventsNPC=e_npc_all @@ -3936,23 +4007,20 @@ Added: 'H' shortcut for variables to get the value as hexadecimal. // 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 clients (player and staff) + EventsClient=e_client_all + + // Events related to all staff (if plevel is higher than 1) + EventsClientStaff=e_client_staffs // Events related to all players (if the plevel is lower than 1) - EventsCharPlayer=e_char_players + EventsClientPlayer=e_client_players // Events related to all regions - //EventsRegion=er_region_all + //EventsRegion=e_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 \ No newline at end of file + EventsItemWeapon=ei_item_weapons diff --git a/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake b/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake index 699b50881..efc752811 100644 --- a/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake +++ b/cmake/toolchains/include/OSX-AppleClang_common.inc.cmake @@ -17,6 +17,8 @@ function(toolchain_exe_stuff_common) HINT "/usr/local/opt/mariadb-connector-c/lib/mariadb" "/opt/homebrew/var/mariadb-connector-c/lib/mariadb" + "/opt/homebrew/opt/mariadb-connector-c/lib/mariadb" + "/opt/homebrew/opt/mariadb-connector-c/lib" "/opt/homebrew/lib/mariadb" ) message(STATUS "Library ${lib_name}: ${lib_${lib_name}_with_path}") diff --git a/src/game/CObjBase.h b/src/game/CObjBase.h index e34a42979..857db7cdd 100644 --- a/src/game/CObjBase.h +++ b/src/game/CObjBase.h @@ -1088,6 +1088,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 diff --git a/src/game/CServerConfig.cpp b/src/game/CServerConfig.cpp index 959af8ab7..3128e1e17 100644 --- a/src/game/CServerConfig.cpp +++ b/src/game/CServerConfig.cpp @@ -77,6 +77,7 @@ CServerConfig::CServerConfig() m_iWordsOfPowerFont = FONT_NORMAL; m_fWordsOfPowerPlayer = true; m_fWordsOfPowerStaff = false; + m_iWordsOfPowerTalkMode = TALKMODE_SPELL; m_fEquippedCast = true; m_iMagicUnlockDoor = 900; m_iSpellTimeout = 0; @@ -547,9 +548,9 @@ enum RC_TYPE RC_ERALIMITGEAR, // _iEraLimitGear RC_ERALIMITLOOT, // _iEraLimitLoot RC_ERALIMITPROPS, // _iEraLimitProps - RC_EVENTSCHAR, // m_sEventsChar - RC_EVENTSCHARPLAYER, // m_sEventsCharPlayer - RC_EVENTSCHARSTAFF, // m_sEventsCharStaff + RC_EVENTSCLIENT, // m_sEventsClient + RC_EVENTSCLIENTPLAYER, // m_sEventsClientPlayer + RC_EVENTSCLIENTSTAFF, // m_sEventsClientStaff RC_EVENTSITEM, // m_sEventsItem RC_EVENTSITEMWEAPON, // m_sEventsItemWeapon RC_EVENTSNPC, // m_sEventsNpc @@ -739,6 +740,7 @@ enum RC_TYPE RC_WOPFONT, RC_WOPPLAYER, RC_WOPSTAFF, + RC_WOPTALKMODE, RC_WORLDSAVE, RC_ZEROPOINT, // m_sZeroPoint RC_QTY @@ -847,9 +849,9 @@ 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) } }, + { "EVENTSCLIENT", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsClient) } }, + { "EVENTSCLIENTPLAYER", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsClientPlayer) } }, + { "EVENTSCLIENTSTAFF", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsClientStaff) } }, { "EVENTSITEM", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sEventsItem) }}, { "EVENTSITEMWEAPON", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsItemWeapon) } }, { "EVENTSNPC", { ELEM_CSTRING, static_cast OFFSETOF(CServerConfig, m_sEventsNPC) } }, @@ -1039,6 +1041,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_castOFFSETOF(CServerConfig,m_iWordsOfPowerTalkMode) }}, { "WORLDSAVE", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sWorldBaseDir) }}, { "ZEROPOINT", { ELEM_CSTRING, static_castOFFSETOF(CServerConfig,m_sZeroPoint) }}, { nullptr, { ELEM_VOID, 0, }} @@ -4891,31 +4894,31 @@ bool CServerConfig::Load( bool fResync ) } ///////////////////// - // parse eventschar + // parse eventsClient ///////////////////// - // allchars (players or staffs) - m_pEventsCharLink.clear(); - if (!m_sEventsChar.IsEmpty()) + // allClient (players or staffs) + m_pEventsClientLink.clear(); + if (!m_sEventsClient.IsEmpty()) { - CScript script("EVENTSCHAR", m_sEventsChar); - m_pEventsCharLink.r_LoadVal(script, RES_EVENTS); + CScript script("EVENTSCLIENT", m_sEventsClient); + m_pEventsClientLink.r_LoadVal(script, RES_EVENTS); } // all players - m_pEventsCharPlayerLink.clear(); - if (!m_sEventsCharPlayer.IsEmpty()) + m_pEventsClientPlayerLink.clear(); + if (!m_sEventsClientPlayer.IsEmpty()) { - CScript script("EVENTSCHARPLAYER", m_sEventsCharPlayer); - m_pEventsCharPlayerLink.r_LoadVal(script, RES_EVENTS); + CScript script("EVENTSCLIENTPLAYER", m_sEventsClientPlayer); + m_pEventsClientPlayerLink.r_LoadVal(script, RES_EVENTS); } // all staffs - m_pEventsCharStaffLink.clear(); - if (!m_sEventsCharStaff.IsEmpty()) + m_pEventsClientStaffLink.clear(); + if (!m_sEventsClientStaff.IsEmpty()) { - CScript script("EVENTSCHARSTAFF", m_sEventsCharStaff); - m_pEventsCharStaffLink.r_LoadVal(script, RES_EVENTS); + CScript script("EVENTSCLIENTSTAFF", m_sEventsClientStaff); + m_pEventsClientStaffLink.r_LoadVal(script, RES_EVENTS); } // parse eventsregion diff --git a/src/game/CServerConfig.h b/src/game/CServerConfig.h index cad9802e5..5a31d9578 100644 --- a/src/game/CServerConfig.h +++ b/src/game/CServerConfig.h @@ -303,6 +303,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. @@ -458,19 +459,19 @@ extern class CServerConfig : public CResourceHolder CResourceRefArray m_pEventsNPCShopLink; // EventsNPCShop. ////////////////////// - // Chars + // Clients ////////////////////// - // EVENTSCHAR - CSString m_sEventsChar; // Key to add Events to all players and staff. - CResourceRefArray m_pEventsCharLink; // EventsChar. + // EVENTSCLIENT + CSString m_sEventsClient; // Key to add Events to all players and staff. + CResourceRefArray m_pEventsClientLink; // EventsClient. - // EVENTSCHARSTAFF - CSString m_sEventsCharStaff; // Key to add Events to all players. - CResourceRefArray m_pEventsCharStaffLink; // EventsCharStaff. + // EVENTSCLIENTSTAFF + CSString m_sEventsClientStaff; // Key to add Events to all players. + CResourceRefArray m_pEventsClientStaffLink; // EventsClientStaff. - // EVENTSCHARPLAYER - CSString m_sEventsCharPlayer; // Key to add Events to all players. - CResourceRefArray m_pEventsCharPlayerLink; // EventsCharPlayer. + // EVENTSCLIENTPLAYER + CSString m_sEventsClientPlayer; // Key to add Events to all players. + CResourceRefArray m_pEventsClientPlayerLink; // EventsClientPlayer. ////////////////////// // Regions diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index c1b95127a..2504cbed8 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. @@ -2510,7 +2511,7 @@ bool CChar::r_WriteVal( lpctstr ptcKey, CSString & sVal, CTextConsole * pSrc, bo } case CHC_BREATH: { - if( !strnicmp(ptcKey, "BREATH.DAM", 10) ) + if (!strnicmp(ptcKey, "BREATH.MAXDIST", 14) || !strnicmp(ptcKey, "BREATH.DAM", 10)) { CVarDefCont * pVar = GetDefKey(ptcKey, true); sVal.FormatLLVal(pVar ? pVar->GetValNum() : 0); @@ -3808,7 +3809,7 @@ bool CChar::r_LoadVal( CScript & s ) break; case CHC_BREATH: { - if ( !strnicmp(ptcKey, "BREATH.DAM", 10) || !strnicmp(ptcKey, "BREATH.HUE", 10) || !strnicmp(ptcKey, "BREATH.ANIM", 11) || !strnicmp(ptcKey, "BREATH.TYPE", 11) || !strnicmp(ptcKey, "BREATH.DAMTYPE", 14)) + if ( !strnicmp(ptcKey, "BREATH.MAXDIST", 14) || !strnicmp(ptcKey, "BREATH.DAM", 10) || !strnicmp(ptcKey, "BREATH.HUE", 10) || !strnicmp(ptcKey, "BREATH.ANIM", 11) || !strnicmp(ptcKey, "BREATH.TYPE", 11) || !strnicmp(ptcKey, "BREATH.DAMTYPE", 14)) { SetDefNum(s.GetKey(), s.GetArgLLVal()); return true; diff --git a/src/game/chars/CCharAct.cpp b/src/game/chars/CCharAct.cpp index af1b978aa..2ba4b1bc7 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) { @@ -5721,11 +5724,11 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript // 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) + // 10) EVENTSCLIENT triggers for chars (players or staffs) + EXC_SET_BLOCK("chardef triggers - EVENTSCLIENT"); + for (size_t i = 0; i < g_Cfg.m_pEventsClientLink.size(); ++i) { - CResourceLink *pLink = g_Cfg.m_pEventsCharLink[i].GetRef(); + CResourceLink *pLink = g_Cfg.m_pEventsClientLink[i].GetRef(); if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) continue; @@ -5739,14 +5742,14 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript goto stopandret; } - // 11) EVENTSCHARPLAYER triggers for playerss + // 11) EVENTSCLIENTPLAYER 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) + // EVENTSCLIENTPLAYER triggers (constant events of players set from sphere.ini) + EXC_SET_BLOCK("chardef triggers - EVENTSCLIENTPLAYER"); + for (size_t i = 0; i < g_Cfg.m_pEventsClientPlayerLink.size(); ++i) { - CResourceLink *pLink = g_Cfg.m_pEventsCharPlayerLink[i].GetRef(); + CResourceLink *pLink = g_Cfg.m_pEventsClientPlayerLink[i].GetRef(); if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) continue; @@ -5761,14 +5764,14 @@ TRIGRET_TYPE CChar::OnTrigger( lpctstr pszTrigName, CTextConsole * pSrc, CScript } } - // 12) EVENTSCHARSTAFF triggers for staffs + // 12) EVENTSCLIENTSTAFF 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) + // EVENTSCLIENTSTAFF triggers (constant events of players set from sphere.ini) + EXC_SET_BLOCK("chardef triggers - EVENTSCLIENTSTAFF"); + for (size_t i = 0; i < g_Cfg.m_pEventsClientStaffLink.size(); ++i) { - CResourceLink *pLink = g_Cfg.m_pEventsCharStaffLink[i].GetRef(); + CResourceLink *pLink = g_Cfg.m_pEventsClientStaffLink[i].GetRef(); if (!pLink || !pLink->HasTrigger(iAction) || (executedEvents.find(pLink) != executedEvents.end())) continue; diff --git a/src/game/chars/CCharFight.cpp b/src/game/chars/CCharFight.cpp index 3ce5c26ba..182d96b14 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) + { + 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); + + if (IsTrigUsed(TRIGGER_HITREACTIVE)) + { + CScriptTriggerArgs HitReactiveArgs; + HitReactiveArgs.m_VarsLocal.SetNum("Sound", ReactiveSnd); // SOUND + HitReactiveArgs.m_VarsLocal.SetNum("EffectID", ReactiveEffectID); // EFFECTID + HitReactiveArgs.m_VarsLocal.SetNum("Damage", iReactiveDamage); // DAMAGE VALUE + HitReactiveArgs.m_VarsLocal.SetNum("ReflectDamage", iReactiveRefDam); // REFLECTED DAM + HitReactiveArgs.m_VarsLocal.SetNum("ReduceDamage", iReactiveRedDam); // REDUCED DAM + HitReactiveArgs.m_VarsLocal.SetNum("DamageType", ReactiveDamType); // DAMAGE TYPE + 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"); // DAMAGE VALUE + iReactiveRefDam = (int)HitReactiveArgs.m_VarsLocal.GetKeyNum("ReflectDamage"); // REFLECTED DAMAGE VALUE + iReactiveRedDam = (int)HitReactiveArgs.m_VarsLocal.GetKeyNum("ReduceDamage"); // REDUCED DAMAGE VALUE + ReactiveDamType = (DAMAGE_TYPE)HitReactiveArgs.m_VarsLocal.GetKeyNum("DamageType"); // DAMAGE TYPE + // 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. 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/CCharSkill.cpp b/src/game/chars/CCharSkill.cpp index fe11e036f..4a344e817 100644 --- a/src/game/chars/CCharSkill.cpp +++ b/src/game/chars/CCharSkill.cpp @@ -1078,10 +1078,21 @@ bool CChar::Skill_Mining_Smelt( CItem * pItemOre, CItem * pItemTarg ) return true; } - if ( pItemTarg != nullptr && pItemTarg->IsTopLevel() && pItemTarg->IsType( IT_FORGE )) - m_Act_p = pItemTarg->GetTopPoint(); - else - m_Act_p = CWorldMap::FindItemTypeNearby( GetTopPoint(), IT_FORGE, 3, false ); + if (pItemTarg != nullptr && pItemTarg->IsTopLevel() && pItemTarg->IsType(IT_FORGE)) + { + m_Act_p = pItemTarg->GetTopPoint(); + } + else + { + CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_BLACKSMITHING); + int iMaxRange = pSkillDef->m_Range; + if (!iMaxRange) + { + g_Log.EventError("Blacksmith skill doesn't have a value for RANGE, defaulting to 3\n"); + iMaxRange = 3; + } + m_Act_p = CWorldMap::FindItemTypeNearby(GetTopPoint(), IT_FORGE, iMaxRange, false); + } if ( !m_Act_p.IsValidPoint() || !CanTouch(m_Act_p)) { @@ -1405,7 +1416,7 @@ int CChar::Skill_Mining( SKTRIG_TYPE stage ) const CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_MINING); const int iTargRange = GetTopPoint().GetDist(m_Act_p); int iMaxRange = pSkillDef->m_Range; - if ( !iMaxRange ) + if (!iMaxRange) { g_Log.EventError("Mining skill doesn't have a value for RANGE, defaulting to 2\n"); iMaxRange = 2; @@ -1501,7 +1512,7 @@ int CChar::Skill_Fishing( SKTRIG_TYPE stage ) CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_FISHING); int iTargRange = GetTopPoint().GetDist(m_Act_p); int iMaxRange = pSkillDef->m_Range; - if ( !iMaxRange ) + if (!iMaxRange) { g_Log.EventError("Fishing skill doesn't have a value for RANGE, defaulting to 4\n"); iMaxRange = 4; @@ -1603,7 +1614,7 @@ int CChar::Skill_Lumberjack( SKTRIG_TYPE stage ) CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_LUMBERJACKING); int iTargRange = GetTopPoint().GetDist(m_Act_p); int iMaxRange = pSkillDef->m_Range; - if ( !pSkillDef->m_Range ) + if (!iMaxRange) { g_Log.EventError("Lumberjacking skill doesn't have a value for RANGE, defaulting to 2\n"); iMaxRange = 2; @@ -1825,7 +1836,15 @@ int CChar::Skill_Peacemaking( SKTRIG_TYPE stage ) { int peace = Skill_GetAdjusted(SKILL_PEACEMAKING); int iRadius = ( peace / 100 ) + 2; // 2..12 - auto Area = CWorldSearchHolder::GetInstance(GetTopPoint(), iRadius); + + CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_PEACEMAKING); + int iMaxRadius = pSkillDef->m_Range; + if (!iMaxRadius) + { + //g_Log.EventError("Peacemaking skill doesn't have a value for RANGE, defaulting to (Peacemaking skill level / 100 + 2) \n"); + iMaxRadius = iRadius; + } + auto Area = CWorldSearchHolder::GetInstance(GetTopPoint(), iMaxRadius); for (;;) { CChar *pChar = Area->GetChar(); @@ -1951,9 +1970,15 @@ int CChar::Skill_Enticement( SKTRIG_TYPE stage ) SysMessagef("%s %s.", pChar->GetName(), g_Cfg.GetDefaultMsg(DEFMSG_ENTICEMENT_BATTLE)); return -SKTRIG_ABORT; } - + CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_ENTICEMENT); + int iMaxRange = pSkillDef->m_Range; + if (!iMaxRange) + { + //g_Log.EventError("Enticement skill doesn't have a value for RANGE, defaulting 3\n"); + iMaxRange = 3; + } pChar->m_Act_p = GetTopPoint(); - pChar->NPC_WalkToPoint( ( pChar->m_Act_p.GetDist(pChar->GetTopPoint()) > 3) ); + pChar->NPC_WalkToPoint((pChar->m_Act_p.GetDist(pChar->GetTopPoint()) > iMaxRange)); return 0; } @@ -1969,6 +1994,9 @@ int CChar::Skill_Provocation(SKTRIG_TYPE stage) // m_Act_Prv_UID = provoke this person // m_Act_UID = against this person. + CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_PROVOCATION); + int iMaxRange = pSkillDef->m_Range; + if ( stage == SKTRIG_ABORT ) return -SKTRIG_ABORT; @@ -2070,8 +2098,14 @@ int CChar::Skill_Provocation(SKTRIG_TYPE stage) pCharProv->Memory_AddObjTypes(this, MEMORY_AGGREIVED|MEMORY_IRRITATEDBY); + if (!iMaxRange) + { + //g_Log.EventError("Provocation skill doesn't have a value for RANGE, defaulting to UO_MAP_VIEW_SIGHT(14) \n"); + iMaxRange = UO_MAP_VIEW_SIGHT; + } + // If out of range we might get attacked ourself. - if ( (pCharProv->GetTopDist3D(pCharTarg) > UO_MAP_VIEW_SIGHT) || (pCharProv->GetTopDist3D(this) > UO_MAP_VIEW_SIGHT) ) + if ((pCharProv->GetTopDist3D(pCharTarg) > iMaxRange) || (pCharProv->GetTopDist3D(this) > iMaxRange)) { // Check that only "evil" monsters attack provoker back if ( pCharProv->Noto_IsEvil() ) @@ -2183,7 +2217,13 @@ int CChar::Skill_Cooking( SKTRIG_TYPE stage ) // m_Act_p = the heat source // m_Act_UID = the skill tool - int iMaxDist = 3; + CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_COOKING); + int iMaxDist = pSkillDef->m_Range; + if (!iMaxDist) + { + g_Log.EventError("Cooking skill doesn't have a value for RANGE, defaulting to 3\n"); + iMaxDist = 3; + } if ( stage == SKTRIG_START ) { @@ -2239,10 +2279,13 @@ int CChar::Skill_Taming( SKTRIG_TYPE stage ) } CSkillDef* pSkillDef = g_Cfg.GetSkillDef(SKILL_TAMING); - if (pSkillDef->m_Range <= 0) - pSkillDef->m_Range = 10; - - if ( GetTopDist3D(pChar) > pSkillDef->m_Range) + int iMaxRange = pSkillDef->m_Range; + if (!iMaxRange) + { + g_Log.EventError("Taming skill doesn't have a value for RANGE, defaulting to 10\n"); + iMaxRange = 10; + } + if (GetTopDist3D(pChar) > iMaxRange) { SysMessageDefault( DEFMSG_TAMING_REACH ); return -SKTRIG_QTY; @@ -3237,8 +3280,16 @@ int CChar::Skill_Act_Breath( SKTRIG_TYPE stage ) return -SKTRIG_QTY; const CPointMap& pntMe = GetTopPoint(); - if ( pntMe.GetDist( m_Act_p ) > UO_MAP_VIEW_SIGHT ) - m_Act_p.StepLinePath( pntMe, UO_MAP_VIEW_SIGHT ); + + int iMaxDist = (int)(GetDefNum("BREATH.MAXDIST", true)); + if (!iMaxDist) + { + //g_Log.EventError("Breath skill doesn't have a value for RANGE, defaulting to UO_MAP_VIEW_SIGHT(14) \n"); + iMaxDist = UO_MAP_VIEW_SIGHT; + } + + if (pntMe.GetDist(m_Act_p) > iMaxDist) + m_Act_p.StepLinePath(pntMe, iMaxDist); int iDamage = (int)(GetDefNum("BREATH.DAM", true)); @@ -3319,8 +3370,17 @@ int CChar::Skill_Act_Throwing( SKTRIG_TYPE stage ) return -SKTRIG_QTY; const CPointMap pntMe(GetTopPoint()); - if ( pntMe.GetDist( m_Act_p ) > UO_MAP_VIEW_SIGHT ) - m_Act_p.StepLinePath( pntMe, UO_MAP_VIEW_SIGHT ); + + CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_THROWING); + int iMaxRange = pSkillDef->m_Range; + if (!iMaxRange) + { + //g_Log.EventError("Throwing skill doesn't have a value for RANGE, defaulting to UO_MAP_VIEW_SIGHT(14) \n"); + iMaxRange = UO_MAP_VIEW_SIGHT; + } + + if (pntMe.GetDist(m_Act_p) > iMaxRange) + m_Act_p.StepLinePath(pntMe, iMaxRange); SoundChar( CRESND_GETHIT ); @@ -4055,7 +4115,15 @@ int CChar::Skill_Snooping(SKTRIG_TYPE stage) if (!IsTakeCrime(pCont, &pCharMark) || pCharMark == nullptr) return 0; // Not a crime really. - if (GetTopDist3D(pCharMark) > 1) + CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_SNOOPING); + int iMaxRange = pSkillDef->m_Range; + if (!iMaxRange) + { + g_Log.EventError("Snooping skill doesn't have a value for RANGE, defaulting to 1\n"); + iMaxRange = 1; + } + + if (GetTopDist3D(pCharMark) > iMaxRange) { SysMessageDefault(DEFMSG_SNOOPING_REACH); return (-SKTRIG_QTY); @@ -4207,7 +4275,14 @@ int CChar::Skill_Stealing(SKTRIG_TYPE stage) bool fGround = false; if (pCharMark != nullptr) { - if (GetTopDist3D(pCharMark) > 2) + CSkillDef *pSkillDef = g_Cfg.GetSkillDef(SKILL_STEALING); + int iMaxRange = pSkillDef->m_Range; + if (!iMaxRange) + { + g_Log.EventError("Stealing skill doesn't have a value for RANGE, defaulting to 2\n"); + iMaxRange = 2; + } + if (GetTopDist3D(pCharMark) > iMaxRange) { SysMessageDefault(DEFMSG_STEALING_MARK); return -SKTRIG_QTY; diff --git a/src/game/chars/CCharSpell.cpp b/src/game/chars/CCharSpell.cpp index d68bf7da3..1a487493d 100644 --- a/src/game/chars/CCharSpell.cpp +++ b/src/game/chars/CCharSpell.cpp @@ -1894,6 +1894,14 @@ bool CChar::Spell_Equip_OnTick( CItem * pItem ) break; } + case SPELL_Explosion: + { + iEffect = iLevel; + iDmgType = DAMAGE_MAGIC | DAMAGE_FIRE; + Effect(EFFECT_OBJ, ITEMID_FX_EXPLODE_3, pItem->m_uidLink.CharFind(), 10, 16); + } + break; + case SPELL_Strangle: { /* @@ -1982,13 +1990,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)) @@ -2037,6 +2055,10 @@ CItem * CChar::Spell_Effect_Create( SPELL_TYPE spell, LAYER_TYPE layer, int iEff if ( layer == LAYER_SPELL_STATS && spell != pSpellPrev->m_itSpell.m_spell && IsSetMagicFlags(MAGICF_STACKSTATS) ) continue; + // If spell is explosion and there's already an explosion timer, dont remove it + if ( spell == SPELL_Explosion && layer == LAYER_SPELL_Explosion ) + continue; + pSpellPrev->Delete(); break; } @@ -2045,15 +2067,16 @@ CItem * CChar::Spell_Effect_Create( SPELL_TYPE spell, LAYER_TYPE layer, int iEff CItem *pSpell = CItem::CreateBase(pSpellDef ? pSpellDef->m_idSpell : ITEMID_RHAND_POINT_NW); ASSERT(pSpell); - switch ( layer ) - { - case LAYER_FLAG_Criminal: pSpell->SetName("Criminal Timer"); break; - case LAYER_FLAG_PotionUsed: pSpell->SetName("Potion Cooldown"); break; - case LAYER_FLAG_Drunk: pSpell->SetName("Drunk Effect"); break; - case LAYER_FLAG_Hallucination: pSpell->SetName("Hallucination Effect"); break; - case LAYER_FLAG_Murders: pSpell->SetName("Murder Decay"); break; - default: break; - } + switch ( layer ) + { + case LAYER_FLAG_Criminal: pSpell->SetName("Criminal Timer"); break; + case LAYER_FLAG_PotionUsed: pSpell->SetName("Potion Cooldown"); break; + case LAYER_FLAG_Drunk: pSpell->SetName("Drunk Effect"); break; + case LAYER_FLAG_Hallucination: pSpell->SetName("Hallucination Effect"); break; + case LAYER_FLAG_Murders: pSpell->SetName("Murder Decay"); break; + case LAYER_SPELL_Explosion: pSpell->SetName("Explosion Timer"); break; + default: break; + } g_World.m_uidNew = pSpell->GetUID(); pSpell->SetAttr(pSpellDef ? ATTR_NEWBIE|ATTR_MAGIC : ATTR_NEWBIE); @@ -3463,6 +3486,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 +3499,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 +3547,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 +3570,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); } } } @@ -3901,6 +3932,12 @@ bool CChar::OnSpellEffect( SPELL_TYPE spell, CChar * pCharSrc, int iSkillLevel, } break; + case SPELL_Explosion: + // if not a potion and have duration, create effect + if (!fPotion && iDuration > 0) + Spell_Effect_Create( SPELL_Explosion, LAYER_SPELL_Explosion, iEffect, iDuration, pCharSrc ); + break; + case SPELL_Invis: Spell_Effect_Create( spell, fPotion ? LAYER_FLAG_Potion : LAYER_SPELL_Invis, iEffect, iDuration, pCharSrc ); break; diff --git a/src/game/chars/CCharStatus.cpp b/src/game/chars/CCharStatus.cpp index 32c20165e..f6cff4e63 100644 --- a/src/game/chars/CCharStatus.cpp +++ b/src/game/chars/CCharStatus.cpp @@ -1996,7 +1996,7 @@ bool CChar::CanStandAt(CPointMap *ptDest, const CRegion* pArea, uint64 uiMyMovem if (!fPathfinding && (g_Cfg.m_iDebugFlags & DEBUGF_WALK)) g_Log.EventWarn("GetHeightMount() %hhu, block.m_Top.m_z %hhd, ptDest.m_z %hhd.\n", uiMyHeight, blockingState->m_Top.m_z, ptDest->m_z); - if ((uiMyHeight + ptDest->m_z >= blockingState->m_Top.m_z) && g_Cfg.m_iMountHeight && !IsPriv(PRIV_GM) && !IsPriv(PRIV_ALLMOVE)) + if (IsStatFlag(STATF_ONHORSE) && (uiMyHeight + ptDest->m_z >= blockingState->m_Top.m_z) && g_Cfg.m_iMountHeight && !IsPriv(PRIV_GM) && !IsPriv(PRIV_ALLMOVE)) { if (!fPathfinding) SysMessageDefault(DEFMSG_MSG_MOUNT_CEILING); diff --git a/src/game/clients/CChatChanMember.cpp b/src/game/clients/CChatChanMember.cpp index edc41a909..0904d5e95 100644 --- a/src/game/clients/CChatChanMember.cpp +++ b/src/game/clients/CChatChanMember.cpp @@ -197,7 +197,7 @@ lpctstr CChatChanMember::GetChatName() const ADDTOCALLSTACK("CChatChanMember::GetChatName"); const CClient *pClient = GetClientActive(); - if (pClient) + if (pClient && pClient->GetAccount() && !pClient->GetAccount()->m_sChatName.IsEmpty()) return(pClient->GetAccount()->m_sChatName); return ""; } diff --git a/src/game/clients/CClientEvent.cpp b/src/game/clients/CClientEvent.cpp index c4ecc42ff..491ec1962 100644 --- a/src/game/clients/CClientEvent.cpp +++ b/src/game/clients/CClientEvent.cpp @@ -1988,6 +1988,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/CClientUse.cpp b/src/game/clients/CClientUse.cpp index 2b13591ab..2e536abee 100644 --- a/src/game/clients/CClientUse.cpp +++ b/src/game/clients/CClientUse.cpp @@ -158,6 +158,7 @@ bool CClient::Cmd_Use_Item( CItem *pItem, bool fTestTouch, bool fScript ) case IT_SHAFT: case IT_FEATHER: + case IT_FLETCHING: return Skill_Menu(SKILL_BOWCRAFT, "sm_bolts", pItem->GetID()); case IT_FISH_POLE: // Just be near water ? @@ -185,7 +186,7 @@ bool CClient::Cmd_Use_Item( CItem *pItem, bool fTestTouch, bool fScript ) } else // I have the key but i need to use it to unlock the container. { - SysMessageDefault(DEFMSG_LOCK_HAS_KEY); + SysMessageDefault(DEFMSG_LOCK_HAS_KEY); return false; } break; @@ -194,7 +195,7 @@ bool CClient::Cmd_Use_Item( CItem *pItem, bool fTestTouch, bool fScript ) SysMessageDefault(DEFMSG_ITEMUSE_LOCKED); if ( !m_pChar->GetPackSafe()->ContentFindKeyFor(pItem) ) // I don't have the hold key { - + SysMessageDefault(DEFMSG_LOCK_HOLD_NO_KEY); if ( !IsPriv(PRIV_GM) ) return false; @@ -225,7 +226,7 @@ bool CClient::Cmd_Use_Item( CItem *pItem, bool fTestTouch, bool fScript ) if ( m_pChar->CheckCorpseCrime(pCorpseItem, true, true) ) SysMessageDefault(DEFMSG_LOOT_CRIMINAL_ACT); } - + return true; } @@ -849,7 +850,7 @@ int CClient::Cmd_Skill_Menu_Build( const CResourceID& rid, int iSelect, CMenuIte { if ( (iSelect < -1) && (iShowCount >= 1) ) // just a test. so we are done. return 1; - + CMenuItem miTest; if ( !miTest.ParseLine(s.GetArgRaw(), nullptr, m_pChar) ) { @@ -1150,7 +1151,7 @@ bool CClient::Cmd_Skill_Tracking( uint track_sel, bool fExec ) When the Tracking skill starts and the Effect property is defined on the Tracking skill use it instead of the hardcoded formula for the maximum distance. */ - + if (m_pChar->m_Act_Effect >= 0) m_pChar->m_atTracking.m_dwDistMax = (dword)m_pChar->m_Act_Effect; else //This is default Sphere maximum tracking distance. diff --git a/src/game/items/CItemCorpse.cpp b/src/game/items/CItemCorpse.cpp index 75c5b57c0..95b837923 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")) // The owner should not rejoin this body even if resurrected on top of it. + continue; CItemCorpse *pCorpse = dynamic_cast(pItem); if ( !pCorpse || (pCorpse->m_uidLink != GetUID()) ) continue; diff --git a/src/game/items/CItemMulti.cpp b/src/game/items/CItemMulti.cpp index 01cf07eca..64753406c 100644 --- a/src/game/items/CItemMulti.cpp +++ b/src/game/items/CItemMulti.cpp @@ -1174,7 +1174,7 @@ int16 CItemMulti::GetMultiCount() const return _iMultiCount; } -void CItemMulti::Redeed(bool fDisplayMsg, bool fMoveToBank, CUID uidChar) +void CItemMulti::Redeed(bool fDisplayMsg, bool fMoveToBank, CUID uidRedeedingChar) { ADDTOCALLSTACK("CItemMulti::Redeed"); if (GetKeyNum("REMOVED") > 0) // Just don't pass from here again, to avoid duplicated deeds. @@ -1209,7 +1209,7 @@ void CItemMulti::Redeed(bool fDisplayMsg, bool fMoveToBank, CUID uidChar) args.m_iN3 = fMoveToBank; // Transfer the Moving Crate to the owner's bank. if (IsTrigUsed(TRIGGER_REDEED)) { - tRet = OnTrigger(ITRIG_Redeed, uidChar.CharFind(), &args); + tRet = OnTrigger(ITRIG_Redeed, uidRedeedingChar.CharFind(), &args); if (args.m_iN2 == 0) { fMoveToBank = false; @@ -1230,7 +1230,8 @@ void CItemMulti::Redeed(bool fDisplayMsg, bool fMoveToBank, CUID uidChar) } CChar* pOwner = GetOwner().CharFind(); - if (!pOwner || !pOwner->m_pPlayer) + CChar* pChar = uidRedeedingChar.CharFind(); + if ((!pChar || !pChar->m_pPlayer) && (!pOwner || !pOwner->m_pPlayer)) { return; } @@ -1256,11 +1257,26 @@ void CItemMulti::Redeed(bool fDisplayMsg, bool fMoveToBank, CUID uidChar) } if (fMoveToBank) { - pOwner->GetBank(LAYER_BANKBOX)->ContentAdd(pDeed); + if (pOwner) + { + pOwner->GetBank(LAYER_BANKBOX)->ContentAdd(pDeed); + } + else + { + pChar->GetBank(LAYER_BANKBOX)->ContentAdd(pDeed); + } + } else { - pOwner->ItemBounce(pDeed, fDisplayMsg); + if (pOwner) + { + pOwner->ItemBounce(pDeed, fDisplayMsg); + } + else + { + pChar->ItemBounce(pDeed, fDisplayMsg); + } } } SetKeyNum("REMOVED", 1); diff --git a/src/game/items/item_types.h b/src/game/items/item_types.h index 64000b465..438e0569c 100644 --- a/src/game/items/item_types.h +++ b/src/game/items/item_types.h @@ -173,7 +173,7 @@ enum IT_TYPE : int32_t // double click type action. IT_TRAIN_DUMMY, // 156 IT_TRAIN_PICKPOCKET, // 157 IT_BEDROLL, // 158 - IT_UNUSED_159, // 159 + IT_FLETCHING, // 159 IT_HIDE, // 160 = hides are cured to make leather. IT_CLOTH_BOLT, // 161 = must be cut up to make cloth squares. IT_BOARD, // 162 = logs are plained into decent lumber @@ -214,7 +214,7 @@ enum IT_TYPE : int32_t // double click type action. IT_PILOT, // 197 = ship's pilot (PacketWheelMove) IT_ROPE, // 198 = t_rope (working like t_ship_plank but without id changes) IT_WEAPON_WHIP, // 199 - + // New SphereX hardcoded types starting from 300 IT_SPAWN_CHAMPION = 300,// 300 = t_spawn_champion IT_MULTI_ADDON, // 301 = t_multi_addon diff --git a/src/game/uo_files/uofiles_enums.h b/src/game/uo_files/uofiles_enums.h index 2653cd8d6..fe233958c 100644 --- a/src/game/uo_files/uofiles_enums.h +++ b/src/game/uo_files/uofiles_enums.h @@ -654,9 +654,10 @@ enum LAYER_TYPE : unsigned char // defined by UO. Only one item can be in a slot //Individual Spell Layers LAYER_SPELL_Mana_Drain, + LAYER_SPELL_Explosion, - LAYER_STORAGE, //80 New Storage layer, can equip t_container and t_container_locked. - LAYER_STABLE, //81 New stable layer, now stabled pets will be stored in this layer of the player instead of npc's itself. + LAYER_STORAGE, // New Storage layer, can equip t_container and t_container_locked. + LAYER_STABLE, // New stable layer, now stabled pets will be stored in this layer of the player instead of npc's itself. LAYER_QTY }; diff --git a/src/sphere.ini b/src/sphere.ini index 104d0b7cb..8f204c771 100644 --- a/src/sphere.ini +++ b/src/sphere.ini @@ -660,42 +660,36 @@ 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 //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 clients (player and staff) +EventsClient=e_client_all,e_player_generic_event -// Events related to all char (player and staff) -//EventsChar=e_char_all - +// Events related to all staff (if plevel is higher than 1) +//EventsClientStaff=e_client_staffs + // Events related to all players (if the plevel is lower than 1) -//EventsCharPlayer=e_char_players +//EventsClientPlayer=e_client_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= +//EventsRegion=e_region_all // Events related to all items //EventsItem=ei_items - + // Events related to all weapons //EventsItemWeapon=ei_item_weapons @@ -1033,6 +1027,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/triggers.tbl b/src/tables/triggers.tbl index a200f613a..53e82c442 100644 --- a/src/tables/triggers.tbl +++ b/src/tables/triggers.tbl @@ -79,6 +79,7 @@ ADD(HITCHECK) ADD(HITIGNORE) ADD(HITMISS) ADD(HITPARRY) +ADD(HITREACTIVE) ADD(HITTRY) ADD(HOUSEDESIGNBEGIN) ADD(HOUSEDESIGNCOMMIT)