Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NPCs guarding gate switches don't reset the switch during their state loop #195

Open
pawbuj1981 opened this issue Mar 19, 2021 · 9 comments
Labels
compatibility: easy This issue is easy to make compatible. impl: hook script func This issue requires hooking script functions. provided fix This issue has a fix provided in the comments. type: session fix The fix for this issues is persistent across a session. validation: required This issue needs validation from one of the validators.

Comments

@pawbuj1981
Copy link

pawbuj1981 commented Mar 19, 2021

Describe the bug
A clear and concise description of what the bug is.
NPC don't use gateswitches after fight !
Expected behavior
A clear and concise description of what you expected to happen.

Steps to reproduce the issue

  1. Close the gate using switch !
  2. Guard will defeat a hero.
  3. Guard will not use the switch for proper state again !
  4. You have to start dialogue and then after the guard will close/open the gate according to the routine .

Additional context
The bug can be easily reproduce at OC gates.

@pawbuj1981 pawbuj1981 added the validation: required This issue needs validation from one of the validators. label Mar 19, 2021
@pawbuj1981 pawbuj1981 changed the title NPCc don't return gateswitch to the proper state if they defeat hero NPCc don't return to use gateswitch to the proper state if they defeat hero Mar 19, 2021
@pawbuj1981
Copy link
Author

pawbuj1981 commented Mar 19, 2021

That bug was already fixed by Gothic Mod FIx RU , the ZS routine code is below which may be useful , however there are defined funcions which are not present in vanilla scripts.

func void ZS_GuardWheelOpen()
{
PrintDebugNpc (PD_TA_FRAME,"ZS_GuardWheelOpen");
GuardPerception ();
AI_StandUp (self);
AI_SetWalkmode (self,NPC_WALK); // Walkmode für den Zustand
AI_GotoWP (self, self.wp); // Gehe zum Tagesablaufstart
if (Wld_GetMobState (self, "VWHEEL") == 1) // Tor geschlossen?
{
PrintDebugNpc (PD_TA_CHECK,"...Tor geschlossen!");
AI_UseMob (self, "VWHEEL", 0); // ...dann wieder aufmachen!
AI_UseMob (self, "VWHEEL", -1); //und vom Mobsi abmelden
AI_AlignToWP (self);
};
};
func int ZS_GuardWheelOpen_Loop()
{
PrintDebugNpc (PD_TA_LOOP,"ZS_GuardWheelOpen_Loop");
if (Npc_GetDistToWP(self,self.wp)>200)
{
AI_SetWalkmode (self,NPC_RUN);
AI_GotoWP (self, self.wp);
return LOOP_CONTINUE;
}
else if (Npc_GetDistToNpc(self,hero) > HAI_DIST_GUARDPASSAGE_ATTENTION)
{
AI_AlignToWP (self);
};
AI_Wait (self,1);
return LOOP_CONTINUE;
};
func void ZS_GuardWheelOpen_End ()
{
PrintDebugNpc (PD_TA_FRAME,"ZS_GuardWheelOpen_End");
};

changed to

func void ZS_GuardWheelOpen()
{
	b_checkpc(self);//new func 
	GuardPerception();
	Npc_PercDisable(self,PERC_OBSERVEINTRUDER);
	Npc_PercDisable(self,PERC_MOVENPC);
	AI_Standup(self);
	AI_StopLookAt(self);
	AI_RemoveWeapon(self);
	self.aivar[AIV_ITEMSTATUS] = FALSE;
	self.aivar[AIV_FOUNDPERSON] = FALSE;
	self.aivar[32] = NOTINPOS;
};

func int ZS_GuardWheelOpen_Loop()
{
	if(self.aivar[32] == ISINPOS)
	{
		if(guardcheckgatestate(self) == 1)//new cond
		{
			AI_Wait(self,1);
			self.aivar[32] = NOTINPOS;
			return LOOP_CONTINUE;
		};
		if(!self.aivar[AIV_FOUNDPERSON])
		{
			if(Npc_GetDistToNpc(self,hero) < 500)
			{
				if(Npc_CanSeeNpc(self,hero) || ((Npc_GetDistToNpc(self,hero) < 200) && !C_BodyStateContains(hero,BS_SNEAK)))
				{
					B_SmartTurnToNpc(self,hero);
					self.aivar[AIV_FOUNDPERSON] = TRUE;
					return LOOP_CONTINUE;
				};
			};
			if(Npc_GetStateTime(self) > self.aivar[21])
			{
				if(self.aivar[AIV_ITEMFREQ] == 1)
				{
					AI_PlayAni(self,"R_SCRATCHLSHOULDER");
				}
				else if(self.aivar[AIV_ITEMFREQ] == 3)
				{
					AI_PlayAni(self,"R_SCRATCHEGG");
				}
				else if(self.aivar[AIV_ITEMFREQ] == 5)
				{
					AI_PlayAni(self,"R_LEGSHAKE");
				}
				else if(self.aivar[AIV_ITEMFREQ] == 7)
				{
					AI_PlayAni(self,"R_SCRATCHHEAD");
				}
				else if(self.aivar[AIV_ITEMFREQ] == 9)
				{
					AI_PlayAni(self,"R_SCRATCHRSHOULDER");
				};
				self.aivar[AIV_ITEMFREQ] = Hlp_Random(100) % 10;
				self.aivar[21] = (Hlp_Random(100) % 5) + 5;
				Npc_SetStateTime(self,0);
			};
			AI_Wait(self,1);
			return LOOP_CONTINUE;
		};
		if(Npc_GetDistToNpc(self,hero) > 600)
		{
			self.aivar[AIV_FOUNDPERSON] = FALSE;
			self.aivar[32] = NOTINPOS;
			return LOOP_CONTINUE;
		};
		B_SmartTurnToNpc(self,hero);
		AI_Wait(self,1);
		return LOOP_CONTINUE;
	};
	if(Npc_GetDistToWP(self,self.wp) > 300)
	{
		AI_StopLookAt(self);
		AI_SetWalkMode(self,NPC_WALK);
		AI_GotoWP(self,self.wp);
		return LOOP_CONTINUE;
	};
	if(guardcheckgatestate(self) == 1)
	{
		AI_StopLookAt(self);
		AI_SetWalkMode(self,NPC_WALK);
		if(Wld_IsMobAvailable(self,"VWHEEL"))
		{
			if(Wld_GetMobState(self,"VWHEEL") == 0)
			{
				AI_UseMob(self,"VWHEEL",1);
			};
			AI_Wait(self,0.5);
			AI_UseMob(self,"VWHEEL",0);
			AI_UseMob(self,"VWHEEL",-1);
		}
		else if(Wld_IsMobAvailable(self,"LEVER"))
		{
			if(Wld_GetMobState(self,"LEVER") == 0)
			{
				AI_UseMob(self,"LEVER",1);
			};
			AI_Wait(self,0.5);
			AI_UseMob(self,"LEVER",0);
			AI_UseMob(self,"LEVER",-1);
		};
		AI_GotoWP(self,self.wp);
	};
	AI_StopLookAt(self);
	if(Npc_IsOnFP(self,"VWHEEL"))
	{
		AI_SetWalkMode(self,NPC_WALK);
		AI_GotoFP(self,"VWHEEL");
		AI_AlignToFP(self);
	}
	else if(Wld_IsFPAvailable(self,"VWHEEL"))
	{
		AI_SetWalkMode(self,NPC_WALK);
		AI_GotoFP(self,"VWHEEL");
		AI_AlignToFP(self);
	}
	else
	{
		AI_AlignToWP(self);
	};
	self.aivar[32] = ISINPOS;
	self.aivar[AIV_ITEMFREQ] = Hlp_Random(100) % 10;
	self.aivar[21] = (Hlp_Random(100) % 5) + 5;
	Npc_SetStateTime(self,0);
	return LOOP_CONTINUE;
};

func void ZS_GuardWheelOpen_End()
{
	AI_StopLookAt(self);
	self.aivar[32] = 0;
	self.aivar[AIV_FOUNDPERSON] = 0;
	self.aivar[21] = 0;
	self.aivar[AIV_ITEMSTATUS] = 0;
	self.aivar[AIV_ITEMFREQ] = 0;
};

@N1kX94
Copy link

N1kX94 commented Mar 29, 2021

Я дополню пост оригинальными (недекомпилированными функциями).

Из Gothic Mod Fix
Также некоторые возможности в цикле сделаны через циклический триггер в самом патче, также добавлены и задействованы некоторые неиспользуемые aivar, которые как мы знаем, записываются в сохранение к каждому NPC.

// Новый вариант 5.
func void ZS_GuardWheelOpen()
{
	B_CheckPC(self); // функция для проверки прохода через порталы
	GuardPerception();
	
	Npc_PercDisable(self,PERC_OBSERVEINTRUDER);
	Npc_PercDisable(self,PERC_MOVENPC);
	
	AI_Standup(self);
	AI_StopLookAt(self);
	AI_RemoveWeapon(self);
	
	self.aivar[AIV_ItemStatus] = FALSE;
	self.aivar[AIV_FoundPerson] = FALSE;
	self.aivar[AIV_TAPosition] = NOTINPOS;
};

func int ZS_GuardWheelOpen_Loop()
{
	// На позиции.
	if(self.aivar[AIV_TAPosition] == ISINPOS)
	{
		// Контроль состояния ворот. Ворота закрыты -> выход из режима "на позиции" для открытия ворот.
		if(GuardCheckGateState(self) == 1)
		{
			AI_Wait(self,1);
			self.aivar[AIV_TAPosition] = NOTINPOS;
			return LOOP_CONTINUE;
		};
		
		// Непись не видит ГГ.
		if(!self.aivar[AIV_FoundPerson])
		{
			// Расстояние между неписем и ГГ меньше 5м.
			if(Npc_GetDistToNpc(self,hero) < 500)
			{
				// Непись может видеть ГГ или расстояние меньше 2м и ГГ не крадётся -> поворот к ГГ, переход в режим "вижу ГГ".
				if(Npc_CanSeeNpc(self,hero) || ((Npc_GetDistToNpc(self,hero) < 200) && !C_BodyStateContains(hero,BS_SNEAK)))
				{
					B_SmartTurnToNpc(self,hero);
					self.aivar[AIV_FoundPerson] = TRUE;
					return LOOP_CONTINUE;
				};
			};
			
			// Проигрывание рандомных анимаций.
			if(Npc_GetStateTime(self) > self.aivar[AIV_StateTime])
			{
				if(self.aivar[AIV_ItemFreq] == 1)
				{
					AI_PlayAni(self,"R_SCRATCHLSHOULDER");
				}
				else if(self.aivar[AIV_ItemFreq] == 3)
				{
					AI_PlayAni(self,"R_SCRATCHEGG");
				}
				else if(self.aivar[AIV_ItemFreq] == 5)
				{
					AI_PlayAni(self,"R_LEGSHAKE");
				}
				else if(self.aivar[AIV_ItemFreq] == 7)
				{
					AI_PlayAni(self,"R_SCRATCHHEAD");
				}
				else if(self.aivar[AIV_ItemFreq] == 9)
				{
					AI_PlayAni(self,"R_SCRATCHRSHOULDER");
				};
				
				self.aivar[AIV_ItemFreq] = Hlp_Random(100)%10;
				self.aivar[AIV_StateTime] = Hlp_Random(100)%5 + 5;
				Npc_SetStateTime(self,0);
			};
			
			AI_Wait(self,1);
			return LOOP_CONTINUE;
		};
		
		// Непись видит ГГ, но расстояние между ним и ГГ больше 6м -> выход из состояния "на позиции" для занятия позиции.
		if(Npc_GetDistToNpc(self,hero) > 600)
		{
			self.aivar[AIV_FoundPerson] = FALSE;
			self.aivar[AIV_TAPosition] = NOTINPOS;
			return LOOP_CONTINUE;
		};
		
		// Поворот к ГГ, ожидание.
		B_SmartTurnToNpc(self,hero);
		AI_Wait(self,1);
		return LOOP_CONTINUE;
	};
	
	// Расстояние до точки выполнения распорядка превышает 3м -> путь к своему вейпоинту.
	if(Npc_GetDistToWP(self,self.wp) > 300)
	{
		AI_StopLookAt(self);
		AI_SetWalkMode(self,NPC_WALK);
		AI_GotoWP(self,self.wp);
		return LOOP_CONTINUE;
	};
	
	// Ворота закрыты -> открытие ворот.
	if(GuardCheckGateState(self) == 1) 
	{
		AI_StopLookAt(self);
		AI_SetWalkMode(self,NPC_WALK);
		
		// Поблизости от непися находится лебёдка -> взаимодействие с лебёдкой.
		if(Wld_IsMobAvailable(self,"VWHEEL"))
		{
			if(Wld_GetMobState(self,"VWHEEL") == 0)
			{
				AI_UseMob(self,"VWHEEL",1);
			};
			AI_Wait(self,0.5);
			AI_UseMob(self,"VWHEEL",0);
			AI_UseMob(self,"VWHEEL",-1);
		}
		
		// Поблизости от непися находится рычаг -> взаимодействие с рычагом.
		else if(Wld_IsMobAvailable(self,"LEVER"))
		{
			if(Wld_GetMobState(self,"LEVER") == 0)
			{
				AI_UseMob(self,"LEVER",1);
			};
			AI_Wait(self,0.5);
			AI_UseMob(self,"LEVER",0);
			AI_UseMob(self,"LEVER",-1);
		};
		
		// Путь к своему вейпоинту.
		AI_GotoWP(self,self.wp);
	};
	
	// Занятие позиции, переход в режим "на позиции".
	AI_StopLookAt(self);
	if(Npc_IsOnFP(self,"VWHEEL"))
	{
		AI_SetWalkMode(self,NPC_WALK);
		AI_GotoFP(self,"VWHEEL");
		AI_AlignToFP(self);
	}
	else if(Wld_IsFPAvailable(self,"VWHEEL"))
	{
		AI_SetWalkMode(self,NPC_WALK);
		AI_GotoFP(self,"VWHEEL");
		AI_AlignToFP(self);
	}
	else
	{
		AI_AlignToWP(self);
	};
	
	self.aivar[AIV_TAPosition] = ISINPOS;
	self.aivar[AIV_ItemFreq] = Hlp_Random(100)%10;
	self.aivar[AIV_StateTime] = Hlp_Random(100)%5 + 5;
	Npc_SetStateTime(self,0);
	
	return LOOP_CONTINUE;
};

func void ZS_GuardWheelOpen_End()
{
	AI_StopLookAt(self);
	self.aivar[AIV_TAPosition] = 0;
	self.aivar[AIV_FoundPerson] = 0;
	self.aivar[AIV_StateTime] = 0;
	self.aivar[AIV_ItemStatus] = 0;
	self.aivar[AIV_ItemFreq] = 0;
};
func void B_CheckPC(var C_Npc slf)
{
	var C_Npc portalowner;
	//var int portalguild;
	portalowner = Wld_GetPlayerPortalOwner();
	portalguild = Wld_GetPlayerPortalGuild();
	B_ResetFaceExpression(slf);
	
	//////////////////////////
	// Попытки обойти баг с двойной экипировкой стрелкового оружия после выхода из спящего режима.
	/*if(Npc_GetDistToNpc(slf,hero) > 3500)
	{
		//PrintScreen("UnequipRangedWeapon",-1,50,"FONT_OLD_10_WHITE.TGA",3);
		PrintScreen(slf.name[0],-1,50,"FONT_OLD_10_WHITE.TGA",3);
		EquippedWeapon = Npc_GetEquippedRangedWeapon(slf);
		if(Hlp_IsValidItem(EquippedWeapon))
		{
			PrintScreen(EquippedWeapon.name,-1,65,"FONT_OLD_10_WHITE.TGA",5);
			//AI_UnequipWeapons(slf);
			
			EquippedRangedWeapon = Hlp_GetInstanceID(EquippedWeapon);
			if(EquippedWeapon.flags & ITEM_MULTI)
			{
				amount = Npc_HasItems(slf,EquippedRangedWeapon);
				Npc_RemoveInvItems(slf,EquippedRangedWeapon,amount);
				CreateInvItems(slf,EquippedRangedWeapon,amount);
				//amount = Npc_HasItems(slf,EquippedRangedWeapon);
			}
			else
			{
				Npc_RemoveInvItem(slf,EquippedRangedWeapon);
				CreateInvItem(slf,EquippedRangedWeapon);
			};
			
			//AI_EquipBestMeleeWeapon(slf);
			AI_EquipBestRangedWeapon(slf);
		};
	};*/
	/*if(slf.aivar[AIV_LASTDISTTOWP] == -9999)
	{
		PrintScreen("EquipBestWeapons",-1,53,"FONT_OLD_10_WHITE.TGA",3);
		AI_EquipBestMeleeWeapon(slf);
		AI_EquipBestRangedWeapon(slf);
		slf.aivar[AIV_LASTDISTTOWP] = 0;
	}
	else if(Npc_GetDistToNpc(slf,hero) > 3500)
	{
		if(Npc_HasEquippedRangedWeapon(slf))
		{
			PrintScreen("UnequipWeapons",-1,50,"FONT_OLD_10_WHITE.TGA",3);
			AI_UnequipWeapons(slf);
			AI_UnequipWeapons(slf);
			slf.aivar[AIV_LASTDISTTOWP] = -9999;
			AI_ContinueRoutine(slf);
			return;
		};
	};*/
	//////////////////////////
	
	// Сброс флага "Бой на арене".
	if(slf.aivar[AIV_ArenaFight] == AF_AFTER)
	{
		slf.aivar[AIV_ArenaFight] = AF_NONE;
	};
	
	// Свидетель может видеть ГГ.
	if(Npc_CanSeeNpcFreeLOS(slf,hero))
	{
		// Выход, если свидетель был очарован заклинанием "Шарм" или он настроен враждебно по отношению к ГГ.
		if(Npc_RefuseTalk(slf) || (Npc_GetAttitude(slf,hero) == ATT_HOSTILE))
		{
			return;
		};
		
		// ГГ пребывает в своём облике.
		if(C_NpcIsHuman(hero))
		{
			// Свидетель является собственником помещения, где находится ГГ или гильдия свидетеля имеет дружественные отношения с гильдией-собственником помещения.
			if((slf == portalowner) || (Wld_GetGuildAttitude(slf.guild,portalguild) == ATT_FRIENDLY))
			{
				// Помещение принадлежит Баронам.
				if(portalguild == GIL_EBR)
				{
					// Свидетель стражник или Барон, ГГ - не маг Огня и не стражник -> старт состояния реакции на проникновение в чужое помещение.
					if((hero.guild != GIL_GRD) && (hero.guild != GIL_KDF) && ((slf.guild == GIL_GRD) || (slf.guild == GIL_EBR)))
					{
						AI_StartState(slf,ZS_ClearRoom,0,"");
						return;
					};
				};
				
				// ГГ не должен находиться в помещении -> старт состояния реакции на проникновение в чужое помещение.
				if((Wld_GetGuildAttitude(hero.guild,portalguild) != ATT_FRIENDLY) && (slf.npcType != npctype_friend) && (Npc_GetAttitude(slf,hero) != ATT_FRIENDLY) && (portalguild != GIL_NONE))
				{
					AI_StartState(slf,ZS_ClearRoom,0,"");
					return;
				};
			};
		}
		
		// ГГ превратился в опасного монстра.
		else if(C_NpcIsDangerousMonster(slf,hero))
		{
			// Дистанция меньше 15м - старт состояния реакции на монстров.
			if(Npc_GetDistToNpc(slf,hero) < HAI_DIST_ASSESS_MONSTER)
			{
				B_FullStop(slf);
				Npc_SetTarget(slf,hero);
				Npc_GetTarget(slf);
				AI_StartState(slf,ZS_AssessMonster,0,"");
				return;
			};
		};
	};
};
// Функция определения состояния ворот, открываемых/закрываемых лебёдкой. Вариант 3.
func int GuardCheckGateState(var C_Npc slf)
{
	// Ворота к месту обмена.
	if(Hlp_StrCmp(slf.wp,"OW_PATH_1_16_C"))
	{
		//slf.aivar[AIV_ItemStatus] = TRUE;
		slf.name[4] = "INTRO_GATE";
		
		// Охранник лебёдки находится в состоянии ZS_GuardWheelClosed.
		if(Npc_IsInSTate(slf,ZS_GuardWheelClosed))
		{
			Npc_PerceiveAll(slf);
			
			// Диего находится рядом с воротами -> переход в состояние временного открытия ворот.
			if(Wld_DetectNpc(slf,PC_Thief,NOFUNC,-1))
			{
				if(Npc_GetDistToWP(other,"INTRO_GATE") < 1000)
				{
					Npc_SetTarget(slf,other);
					Npc_GetTarget(slf);
					Npc_ClearAIQueue(slf);
					AI_StartState(slf,ZS_GuardWheelOpenAndWait,1,"");
					return GATE_01_STATE;
				};
			};
			
			// ГГ находится рядом с воротами.
			if(Npc_GetDistToWP(hero,"INTRO_GATE") < 450)
			{
				// Непись готов открыть ворота перед ГГ -> переход в состояние временного открытия ворот.
				if(C_WantToOpenGate(slf,hero))
				{
					Npc_ClearAIQueue(slf);
					AI_StartState(slf,ZS_GuardWheelOpenAndWait,1,"");
					return GATE_01_STATE;
				};
			};
		};
		
		return GATE_01_STATE;
	}
	
	// Главные ворота СЛ.
	else if(Hlp_StrCmp(slf.wp,"OCC_MAINGATE_VWHEEL"))
	{
		return GATE_02_STATE;
	}
	
	// Южные ворота СЛ.
	else if(Hlp_StrCmp(slf.wp,"OCR_NORTHGATE_VWHEEL"))
	{
		return GATE_03_STATE;
	}
	
	// Ворота замка СЛ.
	else if(Hlp_StrCmp(slf.wp,"OCC_GATE_VWHEEL"))
	{
		return GATE_04_STATE;
	}
	
	// Внешние ворота НЛ.
	else if(Hlp_StrCmp(slf.wp,"OW_PATH_067_WHEEL"))
	{
		//slf.aivar[AIV_ItemStatus] = TRUE;
		slf.name[4] = "NC_GATE";
		
		// Охранник лебёдки находится в состоянии ZS_GuardWheelClosed.
		if(Npc_IsInSTate(slf,ZS_GuardWheelClosed))
		{
			// ГГ находится рядом с воротами.
			if(Npc_GetDistToWP(hero,"NC_GATE") < 400)
			{
				// Непись готов открыть ворота перед ГГ -> переход в состояние временного открытия ворот.
				if(C_WantToOpenGate(slf,hero))
				{
					AI_OutputSVM_Overlay(hero,NULL,"$SC_HEYTURNAROUND");	//Эй, ты!
					Npc_ClearAIQueue(slf);
					AI_StartState(slf,ZS_GuardWheelOpenAndWait,1,"");
					return GATE_01_STATE;
				};
			};
		};
		
		return GATE_05_STATE;
	}
	
	// Внутренние ворота НЛ.
	else if(Hlp_StrCmp(slf.wp,"NC_MAINGATE_VWHEEL"))
	{
		return GATE_06_STATE;
	}
	
	// Ворота СШ.
	else if(Hlp_StrCmp(slf.wp,"OW_OM_ENTRANCE02_WHEEL_USE"))
	{
		return GATE_07_STATE;
	}
	
	// Ворота подземелья в замке СЛ.
	else if(Hlp_StrCmp(slf.wp,"OCC_MERCS_DOWNSTAIRS_3"))
	{
		return GATE_08_STATE;
	};
	
	return -1;
};

@szapp szapp added this to NPC state in Fix templates Mar 31, 2021
@szapp szapp changed the title NPCc don't return to use gateswitch to the proper state if they defeat hero NPCs guarding gate switches don't reset the switch during their state loop Apr 22, 2021
@szapp
Copy link
Collaborator

szapp commented May 13, 2021

The essential gist of it is this:

func int ZS_GuardWheelOpen_Loop()
{
PrintDebugNpc (PD_TA_LOOP,"ZS_GuardWheelOpen_Loop");
if (Npc_GetDistToWP(self,self.wp)>200)
{
AI_SetWalkmode (self,NPC_RUN);
AI_GotoWP (self, self.wp);
return LOOP_CONTINUE;
}
else if (Npc_GetDistToNpc(self,hero) > HAI_DIST_GUARDPASSAGE_ATTENTION)
{
AI_AlignToWP (self);
};
AI_Wait (self,1);
return LOOP_CONTINUE;
};

changed to

func int ZS_GuardWheelOpen_Loop()
{
     if (Wld_GetMobState (self,  "VWHEEL") == 1) { 
         AI_UseMob       (self,  "VWHEEL", 0);
         AI_UseMob       (self,  "VWHEEL", -1);
         AI_AlignToWP    (self); 
     }; 

    PrintDebugNpc       (PD_TA_LOOP,"ZS_GuardWheelOpen_Loop");

    if (Npc_GetDistToWP(self,self.wp)>200)
    {
        AI_SetWalkmode  (self,NPC_RUN);
        AI_GotoWP       (self, self.wp);
        return          LOOP_CONTINUE;
    }
    else if (Npc_GetDistToNpc(self,hero) > HAI_DIST_GUARDPASSAGE_ATTENTION)
    {
        AI_AlignToWP    (self);
    };

    AI_Wait             (self,1);
    return              LOOP_CONTINUE;
};

The same will have to be done for the daily routine to guard a closed switch:

func int ZS_GuardWheelClosed_Loop()
{
PrintDebugNpc (PD_TA_LOOP,"ZS_GuardWheelClosed_Loop");
if (Npc_GetDistToWP(self,self.wp)>200)
{
AI_SetWalkmode (self,NPC_RUN);
AI_GotoWP (self, self.wp);
return LOOP_CONTINUE;
}
else if (Npc_GetDistToNpc(self,hero) > HAI_DIST_GUARDPASSAGE_ATTENTION)
{
AI_AlignToWP (self);
};
AI_Wait (self,1);
return LOOP_CONTINUE;
};

changed to

func int ZS_GuardWheelClosed_Loop()
{
    if (Wld_GetMobState (self,  "VWHEEL") == 0) {
        AI_UseMob       (self,  "VWHEEL", 1);
        AI_UseMob       (self,  "VWHEEL", -1);
        AI_AlignToWP    (self);
    };

    PrintDebugNpc       (PD_TA_LOOP,"ZS_GuardWheelClosed_Loop");

    if (Npc_GetDistToWP(self,self.wp)>200)
    {
        AI_SetWalkmode  (self,NPC_RUN);
        AI_GotoWP       (self, self.wp);
        return          LOOP_CONTINUE;
    }
    else if (Npc_GetDistToNpc(self,hero) > HAI_DIST_GUARDPASSAGE_ATTENTION)
    {
        AI_AlignToWP    (self);
    };

    AI_Wait             (self,1);
    return              LOOP_CONTINUE;
};

This can be realized by hooking before the function ZS_GuardWheelOpen_Loop and ZS_GuardWheelClosed_Loop and adding the respective lines before continuing into the original function. Whether that works without problems would have to be checked.

This fix will probably require a manual test (just teleport to a gate that is guarded closed and then to one that is guarded open). An important part is to test this fix with mods that have already addressed that issue!

@szapp szapp added compatibility: easy This issue is easy to make compatible. impl: hook script func This issue requires hooking script functions. provided fix This issue has a fix provided in the comments. type: session fix The fix for this issues is persistent across a session. labels May 13, 2021
@AmProsius
Copy link
Owner

What's with LEVERs? Is someone guarding a lever anyway?

@szapp
Copy link
Collaborator

szapp commented May 14, 2021

What's with LEVERs? Is someone guarding a lever anyway?

It seems there is no such AI state for levers, only for winches. All the scripts posted before go much beyond the actual fix.

@pawbuj1981
Copy link
Author

pawbuj1981 commented Nov 24, 2021

As far as I know there is only one mod adressed to this issue : "Gothic Mod Fix" and it is only one mod I would not play together with G1CP. Is possible that G1CP will not run with GMF automatically ? (settings in INI could be changed).

@AmProsius
Copy link
Owner

As far as I know there is only one mod adressed to this issue : "Gothic Mod Fix" and it is only one mod I would not play together with G1CP. Is possible that G1CP will not run with GMF automatically ? (settings in INI could be changed).

Why wouldn’t you play the Gothic Mod Fix with the G1CP?

@pawbuj1981
Copy link
Author

GMF has fixed stucked gates already. Playing together with G1CP this bug occurs again. I have tested it. I would exclude this one fix for GMF (if it possible to exclude some fixes for some mods/patches).

@szapp
Copy link
Collaborator

szapp commented Dec 5, 2021

GMF has fixed stucked gates already. Playing together with G1CP this bug occurs again. I have tested it. I would exclude this one fix for GMF (if it possible to exclude some fixes for some mods/patches).

As in medicine, it may be wiser to treat the cause instead of the symptoms ;)

What I am saying is, it would be better to find out why there might be a conflict, and make it compatible in the G1CP. Disabling fixes in conjunction with certain mods/patches seems like a cheap, unsatisfying (and never ending) way.

I haven’t looked at this specific issue here, but from the top of my head I don’t recall where the G1CP does something about stuck gates. How do you come to the conclusion that the G1CP undoes the fix? Did you test this systematically?

PS: It seems like this potential bug report is not at all related to this here issue ticket. It should be discussed in the correct ticket #193 instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compatibility: easy This issue is easy to make compatible. impl: hook script func This issue requires hooking script functions. provided fix This issue has a fix provided in the comments. type: session fix The fix for this issues is persistent across a session. validation: required This issue needs validation from one of the validators.
Projects
Fix templates
Modify NPC state
Development

No branches or pull requests

4 participants