[Patch]PickLockHelper - Nie wieder Links Rechts Rechts ...
Kennt ihr das: Ihr seid gerade am Schlösser knacken und vergisst ständig was ihr schon getestet habt?
Ich habe DIE Lösung für diejenigen, welche den Papierverbrauch beim spielen auf 0 reduziert!
Ich stelle hiermit vor: PickLockHelper
Imaginärer Spieler: Ja aber Kirides, ich will nicht lesen ich will sehen!
Kein Problem - Schau dir diesen Spoiler an und bereite dich auf das vor was dich erwartet:
Spoiler:(zum lesen bitte Text markieren)
[Bild: Ingame_Gothic2.jpg] Der grüne Text ist die aktuell eingegebene Abfolge, der graue Text ist die letzte, weiteste Abfolge vor dem letzten abbrechen/"nochmal von vorne"
Here is the part of the code used in my mod. It makes 4 diff. colors of chest (due to locked by key or picklock, full or empty), Also requires skill to open (G1), giving required key name and disable allowing to open chest with torch in hand. If u predict new updates it can be used. Also reset key kombo after pick lock is being broken (the master skill of picklocking is more playable then!)
Code:
func void containerValueLoop(var int node) { var zCListSort l; l = _^(node);
if (Hlp_Is_oCItem(l.data)) {
var oCItem itm; itm = _^(l.data);
containerValue += itm.value * itm.amount; // Add value of current item
};
};
func void check_keyname3()
{
var oCNpc her; her = Hlp_GetNpc(hero);
if (!Hlp_Is_oCMobLockable(her.focus_vob)) {
return;
};
var oCMobLockable Chest2; Chest2 = _^(her.focus_vob);
if (Hlp_StrCmp(Chest2.keyInstance, "")) {
return;
};
var int symbPtr; symbPtr = MEM_GetParserSymbol(Chest2.keyInstance);
if (!symbPtr) {
return;
};
symb = _^(symbPtr);
if (!Hlp_Is_oCItem(symb.offset)) {
return;
};
key = _^(symb.offset);
text = ConcatStrings ("Użyłeś ",key.name);
PrintScreen(text,-1,14,"FONT_OLD_10_WHITE.TGA",2);
};
var int random2;
func void keymissing_say2()
{
random2 = Hlp_Random (100);
if (random > 66)
{
AI_OutputSVM(hero,hero,"$NeedKey");
};
if (random2 > 33)&&(random2 < 67)
{
AI_OutputSVM(hero,hero,"$keymissing");
};
if (random2 < 34)
{
AI_OutputSVM(hero,hero,"$neveropen");
};
};
func int NEED_CHEST_KEY()//many thanks for Fawkes
{
var oCNpc her; her = Hlp_GetNpc(hero);
var oCMobLockable Chest2; Chest2 = _^(her.focus_vob);
var oCMobContainer chest; chest = _^(her.focus_vob);
if (Hlp_Is_oCMobLockable(her.focus_vob))
{
symbID = MEM_GetSymbolIndex (Chest2.keyInstance);
if (symbID > 0) && (symbID < currSymbolTableLength)
{
if (NPC_HasItems (hero, symbID))
{
//text = ConcatStrings ("Użyłeś ",key.name);Print (text);
if (locked == true)
{
//containerValue = 0;
List_ForFS(chest.containList_next, containerValueLoop); // Iterate over container content
PrintScreen(ConcatStrings("Wartość przedmiotów w skrzyni ",IntToString(containerValue)),-1,10,"FONT_OLD_10_WHITE.TGA",3); // The total value is now in containerValue
check_keyname3();
Snd_Play ("Door_unlock");
};
return true;
}
else if (Str_len (Chest2.keyInstance) > 0)&&
(!Npc_HasItems(hero, symbID))
{
//
AI_PlayAni(hero,"T_DONTKNOW");
keymissing_say2();
text = ConcatStrings ("Wymagany klucz ",Chest2.keyInstance);
PrintScreen(text,-1,10,"FONT_OLD_10_WHITE.TGA",3);
if (STR_Len(Chest2.pickLockStr) > 0)
{
//text = ConcatStrings ("Mozesz próbować otwozyć wytrychem!");
PrintScreen("Mozesz próbować otwozyć wytrychem!",-1,15,"FONT_OLD_10_WHITE.TGA",3);
};
return true;
};
};
};
};
func void printchestxp ()//
{
if (chest_exp > 0) //(C_BodyStateContains (hero, BS_INVENTORY ))
{
Snd_Play ("Door_unlock");
//Snd_Play ("_STR_SOUND_PICKLOCK_UNLOCK");
//chest_count = chest_count + 1;
var string msg;var string chest_combo;
msg = NAME_XPGained;
msg = ConcatStrings(msg, IntToString(chest_exp));
//chest_combo = ConcatStrings("Otworzyłeś zamek ( ",IntToString(chest_count + 1));
chest_combo = ConcatStrings("Otworzyłeś zamek ( ",IntToString(totCharCount));
//PrintS_Ext(ConcatStrings ("Zamek został otwarty.",""),RGBA(0,255,255,120));
AI_PrintScreen_Ext (ConcatStrings(chest_combo," kombinacji )"), -1, 45, _STR_FONT_ONSCREEN, _TIME_MESSAGE_PICKLOCK);
//chest_exp = chest_count * 4;
chest_count = 0;
hero_room = false;
if ((STR_ToInt(MEM_GetModOpt("UPDATE_PACK", "ChestExp_enabled"))))
{
AI_PrintScreen_Ext (msg, -1,_YPOS_MESSAGE_XPGAINED,"font_old_10_white.tga",_TIME_MESSAGE_XPGAINED);
hero.exp = hero.exp + chest_exp;
chest_count = 0;
chest_exp = 0;
//check_keyname3();
PrintScreen(ConcatStrings("Wartość przedmiotów w skrzyni ",IntToString(containerValue)),-1,10,"FONT_OLD_10_WHITE.TGA",3); // The total value is now in containerValue
};
};
chest_count = 0;
chest_exp = 0;
};
/*
var oCMobContainer container; container = _^(ptr); // Container we are interested in
containerValue = 0; // Reset total value
List_ForFS(container.containList_next, containerValueLoop); // Iterate over container content
Print(ConcatStrings("Total value of chest content: ", // The total value is now in containerValue
IntToString(containerValue)));
*/
func void addConditionFuncToChests(var int node) {
var zCListSort list; list = _^(node);
var int ptr; ptr = list.data; // Ikarus says this is of type zCVob but that's just a typo
if (Hlp_Is_oCMobContainer(ptr)) {
var oCMobContainer chest; chest = _^(ptr);
chest._oCMobInter_conditionFunc = "CHEST_COND"; // You probably need to use all caps
};
};
var int u;
func int NEED_SKILL()
{
if(Npc_GetTalentSkill(hero, NPC_TALENT_PICKLOCK) > 0)
&& (Npc_HasItems(hero,ItKeLockpick) > 0)
{
return TRUE;
}
else if (Npc_GetTalentSkill(hero, NPC_TALENT_PICKLOCK) == 0)
{
//PrintScreen("Brak umiejętności (Otwieranie zamków)",-1,10,"FONT_OLD_10_WHITE.TGA",3);
AI_PlayAni(hero,"T_DONTKNOW");
//AI_OutputSVM(hero,hero,"$NoPickLockTalent");
hero.name ="";
B_Say(self,self,"$NoPickLockTalent");
//B_SayOverlay(hero,NULL,"$NoPickLockTalent");
return FALSE;
}
else if (Npc_HasItems(hero,ItKeLockpick) == 0)
//&&(InfoManager_HasFinished())
{
//PrintScreen("Brak umiejętności (Otwieranie zamków)",-1,10,"FONT_OLD_10_WHITE.TGA",3);
AI_PlayAni(hero,"T_DONTKNOW");
u = Hlp_Random (16);
if (u<5)
{
hero.name ="";
B_SayOverlay(hero,NULL,"$NoMorePicks");
//AI_OutputSVM(hero,hero,"$NoMorePicks"); return FALSE;
}
else if (u >= 5)&&(u < 9)
{
hero.name ="";
B_SayOverlay(hero,NULL,"$picklockmissing");
//AI_OutputSVM(hero,hero,"$picklockmissing");return FALSE;
}
else if (u >= 9)&&(u < 12)
{
hero.name ="";
B_SayOverlay(hero,NULL,"$picklockorkeymissing");
//AI_OutputSVM(hero,hero,"$picklockorkeymissing");return FALSE;
}
else if (u >= 12)&&(u < 16)
{
hero.name ="";
B_SayOverlay(hero,NULL,"$NoMorePicks2");
//AI_OutputSVM(hero,hero,"$NoMorePicks2");return FALSE;
};
};
};
func int REMOVE_TORCH()
{
if (NPC_UsesTorch(hero))
{
PrintScreen("Wyrzuć pochodnię z ręki!",-1,10,"FONT_OLD_10_WHITE.TGA",3);
AI_PlayAni(hero,"T_DONTKNOW");
return FALSE;
}
else
{
// PrintScreen("ok",-1,10,"FONT_OLD_10_WHITE.TGA",3);
return true;
};
};
/*
var zCPar_Symbol symb;
var int symbID;
var C_Item key;
var int symbPtr;
var string text;
var string name;
*/
Func void ResetCombo() {
var oCNpc her; her = Hlp_GetNpc(hero);
var oCMobLockable mob; mob = _^(her.focus_vob);
var string combo; combo = "LRLRLRLRLRLR"; // Random words will be drawn from here to assign new combo
var string newCombo;
while (STR_Len(newCombo) != STR_Len(mob.pickLockStr)); // It doesn't change length combo, but only content
var int rand; rand = r_MinMax(0, STR_Len(combo)-1);
newCombo = ConcatStrings(newCombo, STR_SubStr(combo, rand, 1)); // Draw random words
end;
MEM_WriteString(_@s(mob.pickLockStr), newCombo); // Assign new combo
newCombo = ""; // Reset
};
const int zCView_SetFontColor_offset = 7339392; //6FFD80 thiscall(zCColor)
var int zamkniety;
var int chest_value;
func void Status_Chest2()
{
var oCNpc her; her = Hlp_GetNpc(hero);
var oCMobContainer chest; chest = _^(her.focus_vob);
var oCMobDoor Door; Door = _^(her.focus_vob);
var oCMobLockable chest2;chest2 = _^(her.focus_vob);
//var oCItem quest_item;quest_item = _^(her.focus_vob);
//var oCMobInter patelnia; patelnia = _^(her.focus_vob);//_oCMobInter_conditionFunc
var int col; var int ptr;
if !(Hlp_Is_oCMobContainer(her.focus_vob) || Hlp_Is_oCMobDoor(her.focus_vob)){ return; };
if !her.focus_vob { return; };
if Hlp_Is_oCMobContainer(her.focus_vob)
{
if (chest._oCMobLockable_bitfield & oCMobLockable_bitfield_locked)
{
locked = true;
//chest._oCMobLockable_bitfield = oCMobLockable_bitfield_autoOpen;//otwiera chest !
//************************************************************************************************
if (Hlp_Is_oCMobLockable(her.focus_vob))
&& (STR_Len(chest2.pickLockStr) > 0)
&& (Str_len (chest2.keyInstance) == 0)
&& (locked == true)
&& (!NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "NEED_SKILL"; // You probably need to use all caps
totCharCount = STR_Len (chest2.pickLockStr);
col = RGBAToZColor(255,180,0,255); //Orange
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
}
else if (Hlp_Is_oCMobLockable(her.focus_vob))
&& (Str_len (chest2.keyInstance) > 0)
&& (locked == true)
&& (!NPC_UsesTorch(hero))
{
col = RGBAToZColor(255,0,0,255); //red
Chest._oCMobInter_conditionFunc = "NEED_CHEST_KEY"; // You probably need to use all caps
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
//Print("skrzynia zamknięta!");
};
//**************************************************************************************************
}
else
{
col = RGBAToZColor(255,180,0,255); //Orange
if (locked == 1)
{
locked = 0;
//Print("skrzynia otwarta!");
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
};
};
MEM_AssignContentInst (Chest,her.focus_vob);
//MEM_AssignInst (Chest,her.focus_vob);
if (chest.containList_next > 0)
&& (locked == 0)//jeżeli zawartośc skrzyni jest większa od 0
{
//col = RGBAToZColor(255,180,0,255); //Orange
col = RGBAToZColor(128,255,255,255); // niebieski partymember
containerValue = 0;
List_ForFS(chest.containList_next, containerValueLoop); // Iterate over container content
//PrintScreen(ConcatStrings("Wartość przedmiotów w skrzyni ",IntToString(containerValue)),-1,10,"FONT_OLD_10_WHITE.TGA",3); // The total value is now in containerValue
printchestxp ();
//ocena wartości skrzyni by mudfreak
//var oCMobContainer container; container= _^(ptr); // Container we are interested in
//containerValue = 0; // Reset total value
//List_ForFS(chest.containList_next, containerValueLoop); // Iterate over container content
//PrintScreen(ConcatStrings("Wartość przedmiotów w skrzyni ",IntToString(containerValue)),-1,10,"FONT_OLD_10_WHITE.TGA",3); // The total value is now in containerValue
//Print(ConcatStrings("Total value of chest content: ",IntToString(containerValue))); // The total value is now in containerValue
//*
//
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
}
else if (chest.containList_next == 0)
&& (locked == 0)//jeżeli zawartośc skrzyni jest pusta
{
col = RGBAToZColor(255,255,255,255); //white
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
};
}
else if Hlp_Is_oCMobDoor(her.focus_vob)
{
if (Door._oCMobLockable_bitfield & oCMobLockable_bitfield_locked)
{
//chest._oCMobLockable_bitfield = oCMobLockable_bitfield_autoOpen;
col = RGBAToZColor(255,0,0,255); //red
locked = 1;
//Print("drzwi zamknięta!");
Door._oCMobInter_conditionFunc = "NEED_DOOR_KEY"; // You probably need to use all caps
}
else
{
col = RGBAToZColor(255,255,255,255);
if (zamkniety == 1)
{
//Print("drzwi otwarta!");
locked = 0;
};
};
};
ptr = MEM_Alloc(4);
MEM_WriteInt(ptr, col);
CALL_IntParam(ptr);
CALL__thiscall(MEM_ReadInt(screen_offset), zCView_SetFontColor_offset);
MEM_Free(ptr);
};
//ItMi_Stuff_Gearwheel_01 Focus_2 do 5
var int find;
func void Status_Item()
{
var oCNpc her; her = Hlp_GetNpc(hero);
var oCItem quest_item;quest_item = _^(her.focus_vob);
//var oCMobInter patelnia; patelnia = _^(her.focus_vob);//_oCMobInter_conditionFunc
var int col; var int ptr;
if !(Hlp_Is_oCItem(her.focus_vob) ){ return; };
if !her.focus_vob { return; };
if Hlp_Is_oCItem(her.focus_vob)&&
((Hlp_GetInstanceID(quest_item) == Focus_2)
||(Hlp_GetInstanceID(quest_item) == Focus_3)
||(Hlp_GetInstanceID(quest_item) == Focus_4)
||(Hlp_GetInstanceID(quest_item) == Focus_5)
||(Hlp_GetInstanceID(quest_item) == Focus_1)
||(Hlp_GetInstanceID(quest_item) == Mythrilklinge)
||(Hlp_GetInstanceID(quest_item) == ItMi_Stuff_Gearwheel_01)
||(Hlp_GetInstanceID(quest_item) == theriddle1)
||(Hlp_GetInstanceID(quest_item) == theriddle2)
||(Hlp_GetInstanceID(quest_item) == theriddle3)
||(Hlp_GetInstanceID(quest_item) == theriddle4)
||(Hlp_GetInstanceID(quest_item) == theriddle5)
||(Hlp_GetInstanceID(quest_item) == theriddle6)
||(Hlp_GetInstanceID(quest_item) == OrcMedicine))
{
//Print("item ok!");
col = RGBAToZColor(128,255,255,255); // niebieski partymemebr
//AI_OutputSVM_Overlay(hero,hero,"$IMPOSSIBLE");
if (find == false){
hero.name ="";
B_SayOverlay(hero,NULL,"$findsomething");
//AI_OutputSVM(hero,hero,"$findsomething");
find = true;
};
}
else
{
col = RGBAToZColor(255,255,255,255);
find = false;
};
ptr = MEM_Alloc(4);
MEM_WriteInt(ptr, col);
CALL_IntParam(ptr);
CALL__thiscall(MEM_ReadInt(screen_offset), zCView_SetFontColor_offset);
MEM_Free(ptr);
};
func void ChangeVobFunc(var string VobName,var string Func)
{
var int arrPtr;
var zCArray arr;
arrPtr = MEM_SearchAllVobsByName (VobName);
MEM_AssignInst (arr, arrPtr);
var int i; i = 0; MEM_InitLabels();
var int loop; loop = MEM_StackPos.position;
if (i <= arr.numInArray)
{
var oCMobInter Vob;
MEM_AssignInst (Vob, MEM_ReadIntArray (arr.array, i));
//PrintScreen(Vob.onStateFuncName, 15+i,50+i+i,"FONT_DEFAULT.tga",1); // To jest niepotrzebne
Vob.onStateFuncName = Func;
i += 1;
MEM_StackPos.position = loop;
};
};
//Funkcje dodać obojętnie gdzie.
//a np:
//ChangeVobFunc("ORE_GROUND","OREABIT");
//ChangeVobFunc("OC_MOB_PAN","GRILLABIT");
//najlepiej dodać do startupu do init swiata.
func int chestlocked2_cf()
{
if(Npc_IsPlayer(self) && C_NpcIsHuman(self))
{
if(!Npc_HasItems(hero,ItKeLockpick))
{
hero.aivar[AIV_TALKBEFOREATTACK] = 96;
};
if((Npc_GetTalentSkill(hero,NPC_TALENT_PICKLOCK) > 0) || !PICKLOCK_MODE)
{
return TRUE;
};
//PrintScreen("ß íĺ óěĺţ âńęđűâŕňü çŕěęč îňěű÷ęŕěč.",-1,10,"FONT_OLD_10_WHITE.TGA",3);
if(!Npc_RefuseTalk(hero))
{
AI_PlayAni(hero,"T_DONTKNOW");
hero.name ="";
B_SayOverlay(hero,NULL,"$IMPOSSIBLE");
//AI_OutputSVM_Overlay(hero,hero,"$IMPOSSIBLE");
Npc_SetRefuseTalk(hero,4);
}
else
{
AI_PlayAni(hero,"T_DONTKNOW");
AI_OutputSVM_Overlay(hero,hero,"$DOESNTWORK2");
};
};
return FALSE;
};
/*
func int IsInventoryOpen() {
const int oCItemContainer__IsOpen_G1 = 6721328; //0x668F30
const int oCItemContainer__IsOpen_G2 = 7377408; //0x709200
var oCNpc her; her = Hlp_GetNpc(hero);
var int inv; inv = _@(her.inventory2_vtbl);
const int call = 0;
if (CALL_Begin(call)) {
CALL_PutRetValTo(_@(ret));
CALL__thiscall(_@(inv), MEMINT_SwitchG1G2(oCItemContainer__IsOpen_G1, oCItemContainer__IsOpen_G2));
call = CALL_End();
};
var int ret;
return +ret;
};
Func void ResetCombo() {
var oCNpc her; her = Hlp_GetNpc(hero);
var oCMobLockable mob; mob = _^(her.focus_vob);
var string combo; combo = "LRLRLRLRLRLR"; // Random words will be drawn from here to assign new combo
var string newCombo;
while (STR_Len(newCombo) != STR_Len(mob.pickLockStr)); // It doesn't change length combo, but only content
var int rand; rand = r_MinMax(0, STR_Len(combo)-1);
newCombo = ConcatStrings(newCombo, STR_SubStr(combo, rand, 1)); // Draw random words
end;
MEM_WriteString(_@s(mob.pickLockStr), newCombo); // Assign new combo
newCombo = ""; // Reset
};
*/
func void Status_Chest()
{
var oCNpc her; her = Hlp_GetNpc(hero);
var oCMobContainer chest; chest = _^(her.focus_vob);
var oCMobDoor Door; Door = _^(her.focus_vob);
var oCMobLockable chest2;chest2 = _^(her.focus_vob);
//var oCItem quest_item;quest_item = _^(her.focus_vob);
//var oCMobInter patelnia; patelnia = _^(her.focus_vob);//_oCMobInter_conditionFunc
var int col; var int ptr;
if !(Hlp_Is_oCMobContainer(her.focus_vob) || Hlp_Is_oCMobDoor(her.focus_vob)){ return; };
if !her.focus_vob { return; };
if Hlp_Is_oCMobContainer(her.focus_vob)
{
if (chest._oCMobLockable_bitfield & oCMobLockable_bitfield_locked)
{
zamkniety = 1;
//chest._oCMobLockable_bitfield = oCMobLockable_bitfield_autoOpen;//otwiera chest !
col = RGBAToZColor(255,0,0,255); //czerwony
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
//************************************************************************************************
if (Hlp_Is_oCMobLockable(her.focus_vob))
&& (STR_Len(chest2.pickLockStr) > 0)
&& (zamkniety == 1)
&& (!NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "NEED_SKILL"; // You probably need to use all caps
totCharCount = STR_Len (chest2.pickLockStr);
//Print("skrzynia zamknięta!");
};
//**************************************************************************************************
}
else
{
col = RGBAToZColor(255,180,0,255); //Orange
if (zamkniety == 1)
{
zamkniety = 0;
//Print("skrzynia otwarta!");
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
};
};
MEM_AssignContentInst (Chest,her.focus_vob);
//MEM_AssignInst (Chest,her.focus_vob);
if (chest.containList_next > 0)
&& (zamkniety == 0)//jeżeli zawartośc skrzyni jest większa od 0
{
col = RGBAToZColor(255,180,0,255); //Orange
//chest_value = ConcatStrings ("Wartość skrzyni ",(InttoString (chest.containList_next)));
//PrintS (InttoString (chest_value);
printchestxp ();
//ocena wartości skrzyni by mudfreak
// var oCMobContainer container; container= _^(ptr); // Container we are interested in
//containerValue = 0; // Reset total value
List_ForFS(chest.containList_next, containerValueLoop); // Iterate over container content
//PrintScreen((ConcatStrings("Wyrzuć pochodnię z ręki!",-1,10,"FONT_OLD_10_WHITE.TGA",3);
PrintScreen(ConcatStrings("Wartość przedmiotów w skrzyni ",IntToString(containerValue)),-1,10,"FONT_OLD_10_WHITE.TGA",2); // The total value is now in containerValue
//*
check_keyname3();
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
}
else if (chest.containList_next == 0)
&& (zamkniety == 0)//jeżeli zawartośc skrzyni jest pusta
{
col = RGBAToZColor(255,255,255,255); //white
if (NPC_UsesTorch(hero))
{
chest2._oCMobInter_conditionFunc = "REMOVE_TORCH"; // You probably need to use all caps
};
};
}
else if Hlp_Is_oCMobDoor(her.focus_vob)
{
if (Door._oCMobLockable_bitfield & oCMobLockable_bitfield_locked)
{
//chest._oCMobLockable_bitfield = oCMobLockable_bitfield_autoOpen;
col = RGBAToZColor(255,0,0,255); //red
zamkniety = 1;
//Print("drzwi zamknięta!");
Door._oCMobInter_conditionFunc = "NEED_DOOR_KEY"; // You probably need to use all caps
}
else
{
col = RGBAToZColor(255,255,255,255);
if (zamkniety == 1)
{
//Print("drzwi otwarta!");
zamkniety = 0;
};
};
};
ptr = MEM_Alloc(4);
MEM_WriteInt(ptr, col);
CALL_IntParam(ptr);
CALL__thiscall(MEM_ReadInt(screen_offset), zCView_SetFontColor_offset);
MEM_Free(ptr);
};
Die Idee ist geil aber ich habe seitdem ich das erste Mal Gothic II gespielt habe einen schönen DIN A4 Zettel beidseitig beschrieben mit fast allen Truhen im Spiel, damit ich nicht jedesmal neu schreiben muss!
Ich kenne keine Furcht, denn ich bin die Inkarnation der Furcht.
Fragen zu Returning 2.0 oder alternativem Balacing?
Fast alle Antworten gibt es entweder im Startpost oder als bereits gestellte Frage in diesem Thread
Auf die Idee einer Liste kam ich zwar (und man findet die Codes ja auch online), aber ich finde es ein bisschen witzlos. Damit kommt man halt mit einem einzigen Dietrich durchs Spiel, was 'ne Art Cheaten ist und Teile des Gameplays/Balancings aushebelt. Dagegen argumentieren könnte man aber, dass das System an sich nicht so toll ist, weil ein Schloss zu öffnen eigentlich eine Skillfrage sein sollte, so dass man lange mit einem Dietrich durchkommt, wenn man sich besonders geschickt anstellt. Das stupide links/rechts try/fail/repeat/success find ich eher blöd.
Deshalb halte ich den PickLockHelper für eine Art Behelf, der das System weniger nervig macht, aber wirklich cool ist es damit noch nicht. Ich schlage vor, sich da mal zusammen Gedanken zu machen.
Das könnte man auch sehr gut als neues Talent einführen!
Entweder als erlernbares (1x z.B. 10LP und man merkt sich beliebig langer Kombinationen) oder verbesserbares (1-2LPs und um so längere Kombinationen kann sich der Held merken).
Das könnte man auch sehr gut als neues Talent einführen!
Entweder als erlernbares (1x z.B. 10LP und man merkt sich beliebig langer Kombinationen) oder verbesserbares (1-2LPs und um so längere Kombinationen kann sich der Held merken).
Da der Quellcode frei verfügbar ist kann ein Mod-Ersteller dies gerne einbauen. (nicht vergessen die "IncompatibleNinjaPatches" entsprechend zu hinterlegen)
Dieser Patch wird das nicht beinhalten, da dies ein zu tiefer Eingriff in das Spiel ist.
Da der Quellcode frei verfügbar ist kann ein Mod-Ersteller dies gerne einbauen. (nicht vergessen die "IncompatibleNinjaPatches" entsprechend zu hinterlegen)
Dieser Patch wird das nicht beinhalten, da dies ein zu tiefer Eingriff in das Spiel ist.
Ich denke ich werde das in meine Mod einbauen.
Es war auch eher nur als Anregung für andere Modder gedacht. Nicht als Vorschlag für deinen Patch.
Btw danke für die Bereitstellung! Es ist schön, dass Ninja so gut genutzt wird!
I added this mod as a flavor/optional modification/supplemental for YAUP in the installation guide (link comes from here; no hardlinking either), because having a picklocking assistance is quite nice. The only suggestion I have is to create an external config file, so the end user could change the onscreen displayed text to whatever they want to, so it could be used without fuss by international community.
I added this mod as a flavor/optional modification/supplemental for YAUP in the installation guide (link comes from here; no hardlinking either), because having a picklocking assistance is quite nice. The only suggestion I have is to create an external config file, so the end user could change the onscreen displayed text to whatever they want to, so it could be used without fuss by international community.
Cheers!
Glad to hear
That's certainly doable, maybe i get to implement gothic.ini settings for it today
Ich habe auch noch einige Anmerkungen, wenn ich darf. Punkt 4 ist dabei der wichtigste.
Auch wenn die Wahrscheinlichkeit gering ist, können diese globalen Konstanten gleichnamige aus einer Mod überschreiben. Da je nach Hook oder EngineCall die Adressen leicht unterschiedlich sein können, deren Name sich generell aber an der Enginefunktion orientiert, kann das Abstürze verursachen.
Da alle Konstanten nur in PickLockHelper_Init verwendet werden, würde ich sie einfach - wie sie sind - dort innerhalb der Funktion definieren.
Auch wenn das ebenso weit hergeholt zu sein scheint, tragen diese globalen Variablen keine eindeutigen Namen. Selbst wenn "PickLockHelper" im Namen enthalten ist, wird das trotzdem zu einem Problem, wenn Mod-Entwickler deine Skripte in ihre Mod einbauen (und anpassen). Mit dem Prefix Ninja_PickLockHelper_* statt dessen, ist es wahrscheinlicher, dass Mod-Entwickler die Symbolnamen ändern (um das Wort "Ninja" zu entfernen).
Das wundert mich etwas. In einigen Funktionsnamen ist der Prefix, in anderen nicht.
Du verwendest die Handle-basierten Funktionen zum Erstellen der Views. Handles, die von einem Patch erstellt werden, werden ignoriert und wandern nicht in den Speicherstand. Die Handles werden also jedes Mal neu erstellt. Das bedeutet, dass dein Patch massenhaft Handles verschwendet (auch weil die Views immer wieder gelöscht und neu angelegt werden) - die Anzahl von Handles über den gesamten Spielverlauf ist begrenzt. Benutze stattdessen einfach die analogen Pointer-Funktionen (ViewPtr_* und Print_*Ptr). Die damit erstellten Views bleiben die ganze Spiel-Session erhalten, sodass du Ninja_PickLockHelper_hTextView, Ninja_PickLockHelper_hTextViewLast, Ninja_PickLockHelper_hTextViewLastBg und Ninja_PickLockHelper_hTextViewBg (mit anderem Namen siehe oben) als Konstanten definieren kannst. Wenn du die Views in Ninja_PickLockHelper_RemoveText dann nur schließt anstatt sie immer zu löschen, brauchen diese auch nur einmalig erstellt und anschließend nur noch geöffnet und geschlossen werden.
Die Textur "Black.tga" mag nicht in jeder Mod existieren. Zwar ist sie in den Ressourcen des Orginialspiels enthalten(?), allerdings könnte eine Mod sie mindestens verändert haben. Ich empfehle entweder deine eigene Textur (z.B. "Ninja_PickLockHelper_Black.tga") mitzuliefern, oder viel besser: Keine festlegen (die Default-Textur wird verwendet) und stattdessen die Farbe auf schwarz setzen mit ViewPtr_SetColor bevor du den Alpha-Wert anpasst.
Deine LeGo-Initialisierung verstehe ich nicht ganz. Warum fügst du Pakete hinzu die du gar nicht verwendest? Wenn deine Absicht war, alle Abhängigkeiten der Pakete untereinander abzudecken, würde ich dir davon abraten. LeGo wird noch (mehr oder weniger) aktiv weiterentwickelt und diese Abhängigkeiten können sich ändern. LeGo nimmt die Auflösung dieser Abhängigkeiten selbst vor, sodass du nur unnötig Pakete initialisierst. Für beste Kompatibilität sollte ein Patch nur so viel machen wie nötig. Meine Empfehlung wäre die Initialisierung von nur LeGo_Interface | LeGo_View - das ist alles.
Dieser Kommentar ist irreführend, die Funktion PickLockHelper_Init wird nicht von Ninja aufgerufen. Ich nehme an das ist ein Überbleibselfehler.
Eine kleine Schönheitssache zum Schluss: Benutze hier einfach if (GOTHIC_BASE_VERSION == 1). Das ist um einiges leserlicher als MEMINT_SwitchG1G2(1, 0).
Das ist eine ganz schön lange Liste, aber damit kannst du sicherstellen, dass dein Patch höchst möglich kompatibel ist und bleibt.
PS: Hast du auch vor die Vorschläge in den BetterOrcSlayer Patch einzuarbeiten? Es gab glaube ich vermehrt Anfragen deine Patches in Spine aufzunehmen. Vielleicht schaffst du es sie zu aktualisieren bevor sie Bonne hochläd.
PSS: Hast du zufällig mal ausprobiert, ob der die Y/N-Abfragen im Batchskript funktionieren, wenn du Z anstatt Y verwendest?
Ich habe auch noch einige Anmerkungen, wenn ich darf. Punkt 4 ist dabei der wichtigste.
Auch wenn die Wahrscheinlichkeit gering ist, können diese globalen Konstanten gleichnamige aus einer Mod überschreiben. Da je nach Hook oder EngineCall die Adressen leicht unterschiedlich sein können, deren Name sich generell aber an der Enginefunktion orientiert, kann das Abstürze verursachen.
Da alle Konstanten nur in PickLockHelper_Init verwendet werden, würde ich sie einfach - wie sie sind - dort innerhalb der Funktion definieren.
Auch wenn das ebenso weit hergeholt zu sein scheint, tragen diese globalen Variablen keine eindeutigen Namen. Selbst wenn "PickLockHelper" im Namen enthalten ist, wird das trotzdem zu einem Problem, wenn Mod-Entwickler deine Skripte in ihre Mod einbauen (und anpassen). Mit dem Prefix Ninja_PickLockHelper_* statt dessen, ist es wahrscheinlicher, dass Mod-Entwickler die Symbolnamen ändern (um das Wort "Ninja" zu entfernen).
Das wundert mich etwas. In einigen Funktionsnamen ist der Prefix, in anderen nicht.
Du verwendest die Handle-basierten Funktionen zum Erstellen der Views. Handles, die von einem Patch erstellt werden, werden ignoriert und wandern nicht in den Speicherstand. Die Handles werden also jedes Mal neu erstellt. Das bedeutet, dass dein Patch massenhaft Handles verschwendet (auch weil die Views immer wieder gelöscht und neu angelegt werden) - die Anzahl von Handles über den gesamten Spielverlauf ist begrenzt. Benutze stattdessen einfach die analogen Pointer-Funktionen (ViewPtr_* und Print_*Ptr). Die damit erstellten Views bleiben die ganze Spiel-Session erhalten, sodass du Ninja_PickLockHelper_hTextView, Ninja_PickLockHelper_hTextViewLast, Ninja_PickLockHelper_hTextViewLastBg und Ninja_PickLockHelper_hTextViewBg (mit anderem Namen siehe oben) als Konstanten definieren kannst. Wenn du die Views in Ninja_PickLockHelper_RemoveText dann nur schließt anstatt sie immer zu löschen, brauchen diese auch nur einmalig erstellt und anschließend nur noch geöffnet und geschlossen werden.
Die Textur "Black.tga" mag nicht in jeder Mod existieren. Zwar ist sie in den Ressourcen des Orginialspiels enthalten(?), allerdings könnte eine Mod sie mindestens verändert haben. Ich empfehle entweder deine eigene Textur (z.B. "Ninja_PickLockHelper_Black.tga") mitzuliefern, oder viel besser: Keine festlegen (die Default-Textur wird verwendet) und stattdessen die Farbe auf schwarz setzen mit ViewPtr_SetColor bevor du den Alpha-Wert anpasst.
Deine LeGo-Initialisierung verstehe ich nicht ganz. Warum fügst du Pakete hinzu die du gar nicht verwendest? Wenn deine Absicht war, alle Abhängigkeiten der Pakete untereinander abzudecken, würde ich dir davon abraten. LeGo wird noch (mehr oder weniger) aktiv weiterentwickelt und diese Abhängigkeiten können sich ändern. LeGo nimmt die Auflösung dieser Abhängigkeiten selbst vor, sodass du nur unnötig Pakete initialisierst. Für beste Kompatibilität sollte ein Patch nur so viel machen wie nötig. Meine Empfehlung wäre die Initialisierung von nur LeGo_Interface | LeGo_View - das ist alles.
Dieser Kommentar ist irreführend, die Funktion PickLockHelper_Init wird nicht von Ninja aufgerufen. Ich nehme an das ist ein Überbleibselfehler.
Eine kleine Schönheitssache zum Schluss: Benutze hier einfach if (GOTHIC_BASE_VERSION == 1). Das ist um einiges leserlicher als MEMINT_SwitchG1G2(1, 0).
Das ist eine ganz schön lange Liste, aber damit kannst du sicherstellen, dass dein Patch höchst möglich kompatibel ist und bleibt.
PS: Hast du auch vor die Vorschläge in den BetterOrcSlayer Patch einzuarbeiten? Es gab glaube ich vermehrt Anfragen deine Patches in Spine aufzunehmen. Vielleicht schaffst du es sie zu aktualisieren bevor sie Bonne hochläd.
PSS: Hast du zufällig mal ausprobiert, ob der die Y/N-Abfragen im Batchskript funktionieren, wenn du Z anstatt Y verwendest?
1 + 2 + 3:
- Da war ich wohl nachlässig was das refaktorieren angeht
4 + 5:
Die LeGo Views waren schon ein Kampf für sich, trotz der vorhandenen Dokumentation hatte ich es damals nicht hinbekommen "einfach" den Hintergrund des Views Schwarz zu setzen
Ich schau mal, das ich demnächst die Handles durch Pointer ersetz und das mit dem Background probiere ich auch mal wieder (wobei es mich ja wundert, warum die Handles nicht geschlossen/freigegeben werden wenn ich diese explizit über Print_DeleteText "schließe")
6:
Ich denke mal das ist noch übergeblieben vom ganzen rumgeteste mit den LeGo Views - werde ich dann bei Zeiten abändern
7:
8:
Habe hier lediglich den Code von F A W K E S übernommen, passe das aber gerne an, wenn schon gewünscht.
bzgl. PS:
Ich hätte schwören können, das ich hier schon einige Dinge übernommen hatte....
bzgl. PSS:
noch nicht - habe aktuell nicht viel Freizeit was das programmieren angeht ... Es ist ende des Jahres und einige Projekte auf der Arbeit erfordern mehr Aufmerksamkeit als ich's gern hätte...
(wobei es mich ja wundert, warum die Handles nicht geschlossen/freigegeben werden wenn ich diese explizit über Print_DeleteText "schließe")
Die Handles werden vernünftig gelöscht, allerdings ist ein Handle erst einmal nichts anderes als eine Zahl. Für eindeutige Zuordnung erhöht also jedes Erstellen eines Handles den Handle-Count. Wenn du bei jedem Schlösserknacken also drei neue Handles anlegst (die durchs Löschen anschließend unbrauchbar sind), treibst du den Handle-Count ziemlich schnell hoch.
Anstelle von Handles werden nun lediglich Pointer verwendet
Die LeGo Views werden wiederverwendet anstatt immer wieder neu erzeugt und zerstört zu werden
Ninja_PickLockHelper-Prefix zu allen globalen Variablen/Funktionen hinzugefügt
aktuelle Version ist nun in zSpy einsehbar wenn loglevel information eingeschaltet.
Verwendet nun DEFAULT.TGA anstelle von BLACK.TGA (hier gilt ggf. noch zu prüfen ob vielleicht doch lieber eine eigene Textur mitgeliefert werden sollte)
Download wie gehabt über die GitHub Release Seite welche im Startpost erwähnt ist. (oder direkt für diese Version hier: v1.2.0)
Mir sind (leider ) noch ein paar Dinge aufgefallen. Um dir mal endlich Ruhe zu lassen, beschränke ich mich auf kritische Dinge:
Da die Views und Prints nur per Session existieren, solltest du ihre Pointer nicht in Variablen speichern, sondern in Konstanten (wie schon angedeutet). Denn da Variablen in den Speicherstand wandern, sind die Abfragen in Ninja_PickLockHelper_RemoveText immer TRUE und damit hinfällig. Wird die Funktion nun aufgerufen bevor die Views erstellt wurden, kommt es zu einem Absturz. Natürlich rufst du die Funktion nie vor Ninja_PickLockHelper_DisplayText auf, aber darauf würde ich mich nicht verlassen; es gibt ein paar Bugs mit der Benutzung von Vobs. (Siehe z.B. wenn man beim Schlösseröffnen seine Waffe zieht.) Gleiches gilt für die Variable Ninja_PickLockHelper_DisplayIsShown. Wenn du die fünf Variablen stattdessen als Konstanten mit null initialisierst, ist alles sicherer.
Die Variable Ninja_PickLockHelper_pickLockString_Mob enthält einen Pointer, der nach Spielladen sicher nicht mehr auf das selbe Mob zeigt. Diese Variable (kannst du auch als Konstante anlegen) solltest du während der Initialisierung des Patches auf null setzen.
Falls du noch an weiteren, optionalen Verbesserungsvorschlägen interessiert bist hier noch eine kleine Liste
Spoiler:(zum lesen bitte Text markieren)
Am Ende der Initialisierungsfunktion wird ausgegeben, dass der Patch erfolgreich initialisiert wurde. Das ist ein guter Anhaltspunkt, falls ein Spieler mal Troubleshooting machen muss. Theoretisch ist es eine vertane Chance, dass diese Ausgabe immer kommt - auch wenn z.B. aus irgendeinem Grund die G_PickLock in der Mod nicht existiert. Du könntest deine Initialisierung so ändern.
Code:
func void Ninja_PickLockHelper_Init_Internal() {
// Ikarus is necessary already here, LeGo not yet
MEM_InitAll();
// Check if function even exists before anything
if (MEM_FindParserSymbol("G_PickLock") != -1) {
// Only when really initializing the patch, will we need LeGo
LeGo_MergeFlags(LeGo_Interface | LeGo_View);
// All other initializations here
MEM_Info("PickLockHelper 1.2 was initialized successfully.");
} else {
MEM_SendToSpy(zERR_TYPE_WARN, "PickLockHelper 1.2 failed to initialize."); // Warning without stack trace
};
};
Beim Schreiben ist mir gerade ein Fehler aufgefallen in einem meiner Vorschläge für den BetterOrcSlayer-Patch. Ich poste da gleich was in den Thread.
Wenn du die Variablen der View-Pointer in Konstanten geändert hast, kannst du anstatt der Konstante displayIsCreated einfach einer der View-Pointer Konstanten gegen null abfragen.
Anstatt der extra Variable Ninja_PickLockHelper_DisplayIsShown, könntest du auch einfach überprüfen, ob (einer der) Views geöffnet ist. Hier ein Beispiel aus GFA. Das ist aber nur Zucker und funktioniert schon so wie es ist.
Die Funktionen Ninja_PickLockHelper_HOOK_MOBINTER_ENDINTERACTION und Ninja_PickLockHelper_HOOK_MOBINTER_STOPINTERACTION scheinen identisch zu sein. Du kannst also einfach die gleiche verwenden:
Kleine Frage aus Interesse zum Ende:
Du schreibst im Hinblick auf die Default-Textur:
Zitat von Kirides
hier gilt ggf. noch zu prüfen ob vielleicht doch lieber eine eigene Textur mitgeliefert werden sollte
Warum? Was genau willst du überprüfen? Die Default-Textur existiert in jeder Gothic-Installation, ansonsten startet das Spiel nicht. Wenn diese Methode wie sie jetzt ist funktioniert, ist sie um einiges eleganter als eine eigene Textur mitzuliefern.