gothic-1-community-patch icon indicating copy to clipboard operation
gothic-1-community-patch copied to clipboard

NPCs draw weapon on dead monsters

Open AmProsius opened this issue 4 years ago • 9 comments

NPCs sometimes draw their weapon even if the monster is already dead.

AmProsius avatar Dec 30 '20 15:12 AmProsius

https://github.com/AmProsius/gothic-1-community-patch/blob/2775fbc70322594ed1d00c96717143f0c2af5af7/scriptbase/_work/Data/Scripts/Content/AI/ZS_Human/ZS_AssessMonster.d#L36-L40

changed to

	PrintGlobals			(PD_ZS_CHECK);

	if C_NpcIsDown(other)
	{
		PrintDebugNpc		(PD_ZS_CHECK, "...Monster kampfunfähig!");
		return;
	};

	//######## Ist NSC eine WACHE oder BOSS ? ########

AmProsius avatar Feb 16 '21 22:02 AmProsius

The provided fix might not fully work, because the NPC would remain in that AI state and continue to the state loop after the return. An AI_ContinueRoutine(self) before the return would work. UPDATE: I think it wouldn't the NPC would re-detect the monster every few seconds and restart their daily routine every time. Not an option.

Conceptually, I would like to stick to the mentioned assumptions in the comment block above the function, and not even let the state be started in the first place. That would require to find out which of the callers cause the bug: https://github.com/AmProsius/gothic-1-community-patch/blob/2775fbc70322594ed1d00c96717143f0c2af5af7/scriptbase/_work/Data/Scripts/Content/AI/ZS_Human/ZS_AssessMonster.d#L4-L15

szapp avatar Mar 18 '21 22:03 szapp

@AmProsius, I think I am gonna need an example case where that happens, in order to work on it.

There are so many callers (notes to myself):

  • [x] B_AssessEnemy
  • [x] B_AssessFighter
  • [ ] B_AssessFightSound <-- Intercept call to AI_StartState, check for ZS_AssessMonster and return if monster is down
  • [ ] B_AssessWarn <-- Intercept call to AI_StartState, check for ZS_AssessMonster and return if monster is down
  • [x] B_ObserveIntruder
  • [ ] B_AssessEnterRoom <-- Intercept call to AI_StartState, check for ZS_AssessMonster and return if monster is down
  • [ ] ZS_AssessDefeat <-- Intercept call to AI_StartState, check for ZS_AssessMonster and return if monster is down
  • [ ] ZS_AssessMurder <-- Intercept call to AI_StartState, check for ZS_AssessMonster and return if monster is down
  • [x] ZS_AssessMonster <-- should be safe
  • [x] ZS_MCHunting <-- should be safe

szapp avatar Mar 29 '21 17:03 szapp

In Gothic Mod Fix, this function is rewritten like this:

func void ZS_AssessMonster()
{
	C_ZSInit();
	
	// Идентификатор "other" не инициализирован (загрузка сохранения) -> выход.
	if(!Hlp_IsValidNpc(other))
	{
		AI_ContinueRoutine(self);
		return;
	};
	
	// К моменту старта состояния враг уже мёртв -> выход.
	if(C_NpcIsDown(other))
	{
		AI_ContinueRoutine(self);
		return;
	};
	
	Npc_SendPassivePerc(self,PERC_ASSESSWARN,other,self);
	
	// Остановка, быстрый поворот к врагу.
	B_FullStop(self);
	B_WhirlAround(self,other);
	
	// Непись является представителем стражи или боссом -> старт состояния атаки.
	if(C_NpcIsGuard(self) || C_NpcIsGuardArcher(self) || C_NpcIsBoss(self))
	{
		B_Say_Monster(self,other);
		B_SetAttackReason(self,AR_GuildEnemy);
		Npc_SetTarget(self,other);
		AI_StartState(self,ZS_Attack,0,"");
		return;
	};
	
	// Непись слабее врага -> старт состояния бегства.
	if(C_AmIWeaker(self,other))
	{
		Npc_SetTarget(self,other);
		db_say(self,NULL,"ShitWhatAMonster");
		AI_StartState(self,ZS_Flee,0,"");
		return;
	};
	
	// Непись является партнёром по группе -> старт состояния атаки.
	if(self.aivar[AIV_PartyMember])
	{
		B_Say_Monster(self,other);
		B_SetAttackReason(self,AR_GuildEnemy);
		Npc_SetTarget(self,other);
		AI_StartState(self,ZS_Attack,0,"");
		return;
	};
	
	// Активация восприятий.
	Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
	Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_AssessDamage);
	Npc_PercEnable(self,PERC_ASSESSTALK,B_RefuseTalk);
	Npc_PercEnable(self,PERC_ASSESSSURPRISE,B_AssessSurprise);
	
	// !!!!! Другие восприятия в состоянии????? 
	
	// Извлечение оружия.
	B_DrawWeapon(self,other);
	
	// Время ожидания.
	self.aivar[AIV_StateTime] = Hlp_Random(100)%3 + 3;
};

func int ZS_AssessMonster_Loop()
{
	//B_PrintAIInfo("ZS_AssessMonster_Loop",5,8,1);
	
	// Выход, если враг уже мёртв.
	if(C_NpcIsDown(other))
	{
		return LOOP_END;
	};
	
	// Выход, если расстояние до врага превышает 18м.
	if(Npc_GetDistToNpc(self,other) > HAI_DIST_ABORT_ASSESS_MONSTER)
	{
		return LOOP_END;
	};
	
	// Непись находится в режиме дальнего боя или магического боя -> старт состояния атаки.
	if(Npc_IsInFightMode(self,FMODE_FAR) || Npc_IsInFightMode(self,FMODE_MAGIC))
	{
		Npc_SetTarget(self,other);
		B_SetAttackReason(self,AR_GuildEnemy);
		AI_StartState(self,ZS_Attack,0,"");
	};
	
	// Расстояние до врага стало меньше 10м или время ожидание превысило отведённое значение -> старт состояния атаки.
	if((Npc_GetDistToNpc(self,other) < HAI_DIST_ATTACK_MONSTER) || (Npc_GetStateTime(self) > self.aivar[AIV_StateTime]))
	{
		Npc_SetTarget(self,other);
		B_Say_Monster(self,other);
		B_SetAttackReason(self,AR_GuildEnemy);
		AI_StartState(self,ZS_Attack,0,"");
	};
	
	B_SmartTurnToNpc(self,other);
	B_SelectWeapon(self,other);
	AI_Wait(self,0.5);
	return LOOP_CONTINUE;
};

func void ZS_AssessMonster_End()
{
	AI_StopLookAt(self);
	B_RemoveWeapon(self);
	self.aivar[AIV_StateTime] = 0;
};

N1kX94 avatar Mar 30 '21 16:03 N1kX94

I think then it should suffice to add a check for C_NpcIsDown(other) in both ZS_AssessMonster and ZS_AssessMonster_Loop to resolve the specific bug discussed here.

szapp avatar Mar 31 '21 09:03 szapp

The test is not functional yet. I cannot reproduce the problem, and need an example of how to trigger the bug.

szapp avatar Mar 31 '21 09:03 szapp

This bug still has to be validated. Whoever does this, when doing so, please provide instruction on how to reproduce the bug, such that the test can be adjusted.

szapp avatar Apr 05 '21 13:04 szapp

Moved to v1.2.0, because of unclear circumstances of the bug.

szapp avatar Apr 17 '21 16:04 szapp

What's left to do here is to clarify the circumstances that cause this bug. I could not reproduce it. As I never experienced it I don't know when it happens. Someone else will have to provide that information. I will have to remove myself from being assigned.

szapp avatar Jul 18 '21 11:07 szapp