Browse Source

LUA support for triggering a door and overriding via spawnscripts vs code 'always accept open door'

Fix #248
Image 3 years ago
parent
commit
567117054d

+ 59 - 1
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -4389,7 +4389,7 @@ int EQ2Emu_lua_UseWidget(lua_State* state) {
 	if (lua_interface) {
 		widget = lua_interface->GetSpawn(state);
 		if (widget && widget->IsWidget())
-			((Widget*)widget)->HandleUse(NULL, "");
+			((Widget*)widget)->HandleUse(nullptr, "");
 	}
 
 	return 0;
@@ -11575,4 +11575,62 @@ int EQ2Emu_lua_AddPlayerMailByCharID(lua_State* state) {
 	Client::CreateMail(char_id, fromName, subjectName, mailBody, mailType, copper, silver, gold, platinum, item_id, stack_size, time_sent, expire_time);
 	lua_interface->SetBooleanValue(state, true);
 	return 1;
+}
+
+int EQ2Emu_lua_OpenDoor(lua_State* state) {
+	Spawn* widget;
+
+	if (lua_interface) {
+		widget = lua_interface->GetSpawn(state);
+		bool disable_open_sound = lua_interface->GetBooleanValue(state, 2);
+
+		lua_interface->ResetFunctionStack(state);
+
+		if (widget && widget->IsWidget())
+		{
+			((Widget*)widget)->OpenDoor();
+
+			if(!disable_open_sound && ((Widget*)widget)->IsOpen() && ((Widget*)widget)->GetOpenSound())
+				widget->GetZone()->PlaySoundFile(0, ((Widget*)widget)->GetOpenSound(), ((Widget*)widget)->GetX(), ((Widget*)widget)->GetY(), ((Widget*)widget)->GetZ());
+		}
+		else
+			lua_interface->LogError("%s: LUA OpenDoor command error: spawn is not valid, either does not exist or is not a widget", lua_interface->GetScriptName(state));
+	}
+
+	return 0;
+}
+
+int EQ2Emu_lua_CloseDoor(lua_State* state) {
+	Spawn* widget;
+
+	if (lua_interface) {
+		widget = lua_interface->GetSpawn(state);
+		bool disable_close_sound = lua_interface->GetBooleanValue(state, 2);
+		
+		lua_interface->ResetFunctionStack(state);
+
+		if (widget && widget->IsWidget())
+		{
+			((Widget*)widget)->CloseDoor();
+
+			if(!disable_close_sound && !((Widget*)widget)->IsOpen() && ((Widget*)widget)->GetCloseSound())
+				widget->GetZone()->PlaySoundFile(0, ((Widget*)widget)->GetCloseSound(), ((Widget*)widget)->GetX(), ((Widget*)widget)->GetY(), ((Widget*)widget)->GetZ());
+		}
+		else
+			lua_interface->LogError("%s: LUA CloseDoor command error: spawn is not valid, either does not exist or is not a widget", lua_interface->GetScriptName(state));
+	}
+
+	return 0;
+}
+
+int EQ2Emu_lua_IsOpen(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* widget = lua_interface->GetSpawn(state);
+	if (widget && widget->IsWidget())
+	{
+		lua_interface->SetBooleanValue(state, ((Widget*)widget)->IsOpen());
+		return 1;
+	}
+	return 0;
 }

+ 4 - 0
EQ2/source/WorldServer/LuaFunctions.h

@@ -548,4 +548,8 @@ int EQ2Emu_lua_SetCharSheetChanged(lua_State* state);
 
 int EQ2Emu_lua_AddPlayerMail(lua_State* state);
 int EQ2Emu_lua_AddPlayerMailByCharID(lua_State* state);
+
+int EQ2Emu_lua_OpenDoor(lua_State* state);
+int EQ2Emu_lua_CloseDoor(lua_State* state);
+int EQ2Emu_lua_IsOpen(lua_State* state);
 #endif

+ 18 - 3
EQ2/source/WorldServer/LuaInterface.cpp

@@ -1262,6 +1262,10 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	
 	lua_register(state, "AddPlayerMail", EQ2Emu_lua_AddPlayerMail);
 	lua_register(state, "AddPlayerMailByCharID", EQ2Emu_lua_AddPlayerMailByCharID);
+	
+	lua_register(state, "OpenDoor", EQ2Emu_lua_OpenDoor);
+	lua_register(state, "CloseDoor", EQ2Emu_lua_CloseDoor);
+	lua_register(state, "IsOpen", EQ2Emu_lua_IsOpen);
 }
 
 void LuaInterface::LogError(const char* error, ...)  {
@@ -1970,10 +1974,14 @@ bool LuaInterface::RunItemScript(string script_name, const char* function_name,
 }
 
 
-bool LuaInterface::RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn, const char* message) {
+bool LuaInterface::RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn, const char* message, bool is_door_open) {
 	if(!npc || spawn_scripts_reloading)
 		return false;
 
+	bool isUseDoorFunction = false;
+	if(!strcmp(function_name,"usedoor"))
+		isUseDoorFunction = true;
+		
 	lua_State* state = GetSpawnScript(script_name.c_str(), true, true);
 	if(state){
 		Mutex* mutex = GetSpawnScriptMutex(script_name.c_str());
@@ -1993,11 +2001,18 @@ bool LuaInterface::RunSpawnScript(string script_name, const char* function_name,
 		}
 		SetSpawnValue(state, npc);
 		int8 num_parms = 1;
-		if(spawn){
+		// we always send spawn, even if null (nil) when its 'usedoor' function
+		if(spawn || isUseDoorFunction){
 			SetSpawnValue(state, spawn);
 			num_parms++;
 		}
-		if(message){
+
+		// usedoor function always passes just npc, spawn and is_door_open
+		if(isUseDoorFunction){
+			SetBooleanValue(state, is_door_open);
+			num_parms++;
+		}
+		else if(message){
 			SetStringValue(state, message);
 			num_parms++;
 		}

+ 1 - 1
EQ2/source/WorldServer/LuaInterface.h

@@ -248,7 +248,7 @@ public:
 	void			RemoveSpawnScript(const char* name);
 	bool			RunItemScript(string script_name, const char* function_name, Item* item, Spawn* spawn = 0);
 	bool			CallItemScript(lua_State* state, int8 num_parameters);
-	bool			RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn = 0, const char* message = 0);
+	bool			RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn = 0, const char* message = 0, bool is_door_open = false);
 	bool			CallSpawnScript(lua_State* state, int8 num_parameters);
 	bool			RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, int32 int32_arg1 = 0, const char* str_arg1 = 0, Spawn* spawn_arg1 = 0, int32 int32_arg2 = 0, const char* str_arg2 = 0, Spawn* spawn_arg2 = 0);
 	bool			CallZoneScript(lua_State* state, int8 num_parameters);

+ 32 - 17
EQ2/source/WorldServer/Widget.cpp

@@ -24,10 +24,12 @@
 #include "World.h"
 #include "../common/Log.h"
 #include "ClientPacketFunctions.h"
+#include "LuaInterface.h"
 
 extern World world;
 extern ConfigReader configReader;
 extern MasterSpellList master_spell_list;
+extern LuaInterface* lua_interface;
 
 Widget::Widget(){
 	widget_id = 0;
@@ -201,7 +203,9 @@ void Widget::SetCloseY(float val){
 	close_y = val;
 }
 bool Widget::IsOpen(){
-	return is_open;
+	std::lock_guard<std::mutex> lk(MWidgetMutex);
+	bool widget_open = is_open;
+	return widget_open;
 }
 int8 Widget::GetWidgetType(){
 	return widget_type;
@@ -244,10 +248,11 @@ void Widget::HandleTimerUpdate(){
 	if(widget_type == WIDGET_TYPE_LIFT)
 		return; //This Widget is a lift, return.
 	else if (widget_type == WIDGET_TYPE_DOOR && is_open)
-		HandleUse(0, "");
+		HandleUse(nullptr, "");
 }
 
 void Widget::OpenDoor(){
+	std::lock_guard<std::mutex> lk(MWidgetMutex);
 	if(GetOpenHeading() >= 0)
 		SetHeading(GetOpenHeading());
 	float openX = GetOpenX();
@@ -282,6 +287,7 @@ void Widget::OpenDoor(){
 }
 
 void Widget::CloseDoor(){
+	std::lock_guard<std::mutex> lk(MWidgetMutex);
 	if(GetClosedHeading() > 0)
 		SetHeading(GetClosedHeading());
 	else if(GetOpenHeading() >= 0)
@@ -316,20 +322,29 @@ void Widget::CloseDoor(){
 	GetZone()->SendSpawnChanges(this);
 }
 
-void Widget::ProcessUse(){
+void Widget::ProcessUse(Spawn* caller){
 	if(widget_type == WIDGET_TYPE_LIFT && GetZone()->HasWidgetTimer(this)) //this door is a lift and in use, wait until it gets to the 
 		return;
-	if(is_open) //close
-		CloseDoor();
-	else //open
-		OpenDoor();
-	if(is_open){
-		if(GetOpenSound())
-			GetZone()->PlaySoundFile(0, GetOpenSound(), widget_x, widget_y, widget_z);
+	if (GetZone()->CallSpawnScript(this, SPAWN_SCRIPT_USEDOOR, caller, "", is_open)) {
+		// handled in lua, nothing to do here!
+	}
+	else
+	{
+		bool wasOpen = IsOpen();
+			if(wasOpen) //close
+			CloseDoor();
+		else //open
+			OpenDoor();
+
+		bool isOpen = IsOpen();
+		if(isOpen){
+			if(GetOpenSound())
+				GetZone()->PlaySoundFile(0, GetOpenSound(), widget_x, widget_y, widget_z);
+		}
+		else	
+			if(GetCloseSound())
+				GetZone()->PlaySoundFile(0, GetCloseSound(), widget_x, widget_y, widget_z);
 	}
-	else	
-		if(GetCloseSound())
-			GetZone()->PlaySoundFile(0, GetCloseSound(), widget_x, widget_y, widget_z);
 }
 
 void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){
@@ -353,7 +368,7 @@ void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){
 		client->SetTemporaryTransportID(0);
 		GetZone()->GetTransporters(&destinations, client, GetTransporterID());
 	}
-	if (destinations.size())
+	if (destinations.size() && client)
 		client->ProcessTeleport(this, &destinations, GetTransporterID());
 	else if (overrideWidgetType == WIDGET_TYPE_DOOR || overrideWidgetType == WIDGET_TYPE_LIFT){
 		Widget* widget = this;
@@ -369,7 +384,7 @@ void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){
 		}
 		if (linked_spawn){
 			widget = linked_spawn;
-			ProcessUse(); //fire the first door, then fire the linked door below
+			ProcessUse(client ? client->GetPlayer() : nullptr); //fire the first door, then fire the linked door below
 		}
 		else if (action_spawn) {
 			widget = action_spawn;
@@ -380,9 +395,9 @@ void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){
 			}
 
 			if (widget->linked_spawn)
-				widget->linked_spawn->ProcessUse();
+				widget->linked_spawn->ProcessUse(client ? client->GetPlayer() : nullptr);
 		}
-		widget->ProcessUse();
+		widget->ProcessUse(client ? client->GetPlayer() : nullptr);
 	}
 	else if (client && m_houseID > 0 && strncasecmp("access", command.c_str(), 6) == 0) {
 		// Used a door to enter a house

+ 3 - 2
EQ2/source/WorldServer/Widget.h

@@ -22,6 +22,7 @@
 #include "Spawn.h"
 #include "client.h"
 #include <string.h>
+#include <mutex>
 
 using namespace std;
 #define WIDGET_TYPE_GENERIC	0
@@ -80,7 +81,7 @@ public:
 	void	SetCloseSound(const char* name);
 	void	SetOpenDuration(int16 val);
 	int16	GetOpenDuration();
-	void	ProcessUse();
+	void	ProcessUse(Spawn* caller=nullptr);
 	void	SetHouseID(int32 val) { m_houseID = val; }
 	int32	GetHouseID() { return m_houseID; }
 
@@ -101,7 +102,6 @@ public:
 
 		return string("Generic");
 	}
-
 private:
 	int8	widget_type;
 	bool	include_location;
@@ -128,5 +128,6 @@ private:
 	float	close_x;
 	float	close_z;
 	bool	multi_floor_lift;
+	std::mutex MWidgetMutex;
 };
 #endif

+ 5 - 1
EQ2/source/WorldServer/zoneserver.cpp

@@ -2575,7 +2575,7 @@ void ZoneServer::AddSpawnGroupLocation(int32 group_id, int32 location_id, int32
 	MSpawnGroupAssociation.releasewritelock(__FUNCTION__, __LINE__);
 }
 
-bool ZoneServer::CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn, const char* message){
+bool ZoneServer::CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn, const char* message, bool is_door_open){
 
 	LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
 	if(!npc)
@@ -2698,6 +2698,10 @@ bool ZoneServer::CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn, const char
 				lua_interface->RunSpawnScript(script, "prespawn", npc);
 				break;
 			}
+			case SPAWN_SCRIPT_USEDOOR: {
+				lua_interface->RunSpawnScript(script, "usedoor", npc, spawn, "", is_door_open);
+				break;
+			}
 			default:
 			{
 				result = false;

+ 2 - 1
EQ2/source/WorldServer/zoneserver.h

@@ -87,6 +87,7 @@ class Bot;
 #define SPAWN_SCRIPT_GROUP_DEAD			17
 #define SPAWN_SCRIPT_HEAR_SAY			18
 #define SPAWN_SCRIPT_PRESPAWN			19
+#define SPAWN_SCRIPT_USEDOOR			20
 
 #define SPAWN_CONDITIONAL_NONE			0
 #define SPAWN_CONDITIONAL_DAY			1
@@ -309,7 +310,7 @@ public:
 	
 	EQ2Packet* GetZoneInfoPacket(Client* client);
 	Spawn*	FindSpawn(Player* searcher, const char* name);
-	bool	CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn = 0, const char* message = 0);
+	bool	CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn = 0, const char* message = 0, bool is_door_open = false);
 	void	SendSpawnVisualState(Spawn* spawn, int16 type);
 	void	SendSpellFailedPacket(Client* client, int16 error);
 	void	SendInterruptPacket(Spawn* interrupted, LuaSpell* spell);