AmProsius / gothic-1-community-patch

Gothic 1 Community Patch
Other
48 stars 4 forks source link

Individual NPC_Attitudes get lost after a reload #123

Open i5sue5 opened 3 years ago

i5sue5 commented 3 years ago

Describe the bug There are several changes of NPC_Attitudes towards the hero throughout the game. After a reload, the individual NPC_Attitudes are overwritten by the general GIL_Attitudes.

Expected behavior NPCs should keep their individual attitudes.

szapp commented 3 years ago

Does this happen by the program or are the attitudes reset by script? E.g. is a function like B_InitGuildAttitudes responsible?

pawbuj1981 commented 3 years ago

@szapp I remember you have supported me to fix that issue some time ago. You have created extra aivars to fix that issue - I will try to recover that code and paste it here.

pawbuj1981 commented 3 years ago

Here is the code

/* 
 * Iterate over all NPCs and set their AI variables according to the attitudes
 */
func void FixAttitudes_Save() {
    if (!Hlp_IsValidNpc(hero)) {
        return;
    };

    var int list; list = MEM_World.voblist_npcs;
    var zCListSort l;
    while(list);
        l = _^(list);
        list = l.next;

        if (l.data) {
            var C_Npc slf; slf = _^(l.data);
            if (Hlp_IsValidNpc(slf)) && (!Npc_IsPlayer(slf)) {
                slf.aivar[AIV_PERMATT] = Npc_GetPermAttitude(slf, hero);
                slf.aivar[AIV_TEMPATT] = Npc_GetAttitude(slf,     hero);
            };
        };
    end;

    // From now on we can restore the attitudes from the AI variables
    FixAttitudes_Saved = TRUE;
};

/*
 * Iterate over all NPCs and set the attitudes according to their AI variables
 */
func void FixAttitudes_Restore() {
    // Do not restore the attitudes if they have not been saved yet
    if (!FixAttitudes_Saved) {
        return;
    };

    var int list; list = MEM_World.voblist_npcs;
    var zCListSort l;
    while(list);
        l = _^(list);
        list = l.next;

        if (l.data) {
            var C_Npc slf; slf = _^(l.data);
            if (Hlp_IsValidNpc(slf)) && (!Npc_IsPlayer(slf)) {
                Npc_SetAttitude(slf,     slf.aivar[AIV_PERMATT]);
                Npc_SetTempAttitude(slf, slf.aivar[AIV_TEMPATT]);
            };
        };
    end;
};

/*
 * Initialization: Call this function from Init_Global
 */
func void FixAttitudes_Init() {
    MEM_InitAll();
    //Print("Działa fix attitude");
    // Restore the attitudes when loading
    FixAttitudes_Restore();

    // Save the attitudes every frame
    FF_ApplyOnce(FixAttitudes_Save);
}; */
szapp commented 3 years ago

@pawbuj1981 Thanks. unfortunately that approach won’t work here, because we cannot use AI variables. You cannot add new AI variables in a script patch. (I think I had also mentioned this before). Here, we want to fix the problem at its origin. The code I had written previously only fixes the symptoms.

szapp commented 3 years ago

@AmProsius are you aware of any mod that has fixed this issue? I haven’t looked at this yet and I wonder whether the bug originates in the scripts or engine.

AmProsius commented 3 years ago

I know that the Definitive Edition fixed this behavior, but I didn't get the scripts from Issues yet.

szapp commented 3 years ago

I know that the Definitive Edition fixed this behavior, but I didn't get the scripts from Issues yet.

I checked with DecDat, and in DE the attitudes are just reinstated individually for each NPC based on prior set variables.

szapp commented 3 years ago

For each NPC their attitudes are written and read from savegames. However, the individual attitudes of all NPC are overwritten in oCGame::InitNpcAttitudes through Wld_ExchangeGuildAttitudes called from B_InitGuildAttitudes at every loading. Although the attitudes are actually stored in the savegame, they are reset to their defaults every time.

https://github.com/AmProsius/gothic-1-community-patch/blob/d12c08ee231d85881a5a624dd948843188f0512c/scriptbase/_work/Data/Scripts/Content/Story/Startup.d#L2266-L2278

B_InitGuildAttitudes and B_InitMonsterAttitudes are essential when entering any world the first time to properly initialize the attitudes of all monsters and humans the first time. Afterwards, the attitudes would actually be maintained by themselves through saving and loading. Nevertheless, the guild attitudes (not the NPC attitudes!) would still have to be set - for any new NPC being spawned later.

The actual issue here is oCGame::InitNpcAttitudes within Wld_ExchangeGuildAttitudes: It overwrites the attitudes of all existing NPCs. This is only desired if the human guild attitude table was actually exchanged, i.e. before and after chapter 4, see GIL_ATTITUDES_FMTAKEN. However, which guild table is loaded is not stored anywhere.

Meddling with the content of Wld_ExchangeGuildAttitudes or when it is called is not a good idea, because if the human guild attitude table is exchanged in another world, the attitudes need to be updated when finally entering the correct world. Furthermore, a mod might have different circumstances in which the function is called.

The solution to the problem would be to somehow check if the human guild attitude table has in fact changed and only update the attitudes of all NPCs in that case. An easy way might be to compare all current guild attitudes to the entries of the new table. ~But that does not work, because the guild attitudes are initially empty when loading.~ UPDATE: The guild table is actually saved and loaded properly. The only time it is empty (human guilds excluded) is on first creation, i.e. new game.

I can't think of a way to do this yet.


On a related note. This does not explain the friendly/neutral (?) attitudes of skeletons (and other monsters?) when saving and loading, see #238. I don't know if/how exactly this is related.