Open pawbuj1981 opened 3 years ago
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.
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;
};
Я дополню пост оригинальными (недекомпилированными функциями).
Из 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;
};
The essential gist of it is this:
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:
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!
What's with LEVER
s? Is someone guarding a lever anyway?
What's with
LEVER
s? 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.
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).
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?
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).
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.
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
Additional context The bug can be easily reproduce at OC gates.