|
-
Wäre es sinnvoll, ScriptBin ebenfalls zu GitHub zu transferieren, und bei der Gelegenheit die weiteren hier im Thema genannten und aktualisierten Skripte einzubauen?
-
Zitat von Milky-Way
Wäre es sinnvoll, ScriptBin ebenfalls zu GitHub zu transferieren, und bei der Gelegenheit die weiteren hier im Thema genannten und aktualisierten Skripte einzubauen?
Vielleicht sogar nicht als ein Repository, sondern angelegt als "Organisation" (das ist eine Kollektion von Repositories). Dann kann jedes Skript(-paket) in einem eigenen Repository bleiben und es ist einfacher sich ein bestimmtes Skript-Feature heraus zu picken (wenn man an all den anderen Skripten nicht interessiert ist). Die bisherige Struktur vom SkriptBin Repo kann schnell unübersichtlich werden und man kann teilweise nur schwer zuordnen welche Skripte oder Funktionen jetzt zu dem gewünschten Skript dazu gehören. Verlinken von einem Skript macht das auch einfacher. Auch das hinzufügen von neuen Skripten bedurfte bisher eher aufwändiges eingliedern in die Ordnerstruktur und in die SRC-Datei.
Die "Gefahr" ist aber, dass es zu viele Repos werden.
Das sind so meine Gedanken dazu.
-
Eine Frage: Wie rufe ich stopAllSounds beim Spiel Laden auf? Beim Spiel Start hab wird meine (lange) Gamestart.wav schon schön abgebrochen, aber beim Spiel Laden noch nicht.
-
Naja wo rufst du es denn momentan auf? Generell ist die INIT_Global (Startup.d) die Funktion, die sowohl beim Start eines neuen Spiels als auch beim Laden eines Speicherstandes aufgerufen wird.
-
Zitat von Lehona
Naja wo rufst du es denn momentan auf? Generell ist die INIT_Global (Startup.d) die Funktion, die sowohl beim Start eines neuen Spiels als auch beim Laden eines Speicherstandes aufgerufen wird.
Okay, danke. Ich hatte sie nicht in INIT_Global eingetragen. Jetzt funktioniert's.
-
Apprentice
Few days ago I wrote a script that creates in inventory every existing in the game item. Maybe someone will make use of it
Code:
func void CreateAllItems(var c_npc slf) {
var int i;
repeat(i, MEM_Parser.symtab_table_numInArray);
var zCPar_Symbol symb; symb = _^(MEM_ReadIntArray(MEM_Parser.symtab_table_array, i));
if ((symb.bitfield & zCPar_Symbol_bitfield_type) != zPAR_TYPE_INSTANCE || STR_IndexOf(symb.name, ".") > 0 || !symb.parent) {
continue;
};
symb = _^(symb.parent);
if (Hlp_StrCmp(symb.name, "C_ITEM")) {
CreateInvItems(slf, i, 1);
}
else if (symb.parent && (symb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_PROTOTYPE) {
symb = _^(symb.parent);
if (Hlp_StrCmp(symb.name, "C_ITEM")) {
CreateInvItems(slf, i, 1);
};
};
end;
};
The game may freeze for a while because table of parser symbols is very huge.
-
Nicely written code, thanks for sharing!
-
G1 NPC VobList functions
I will also add something I believe can be useful:
NPCVobList.d – functions will showcase better control over what NPCs can 'detect' in specified range.
Code:
//Tested with G1 Ikarus 1.2.1
//Maybe also compatible with G2A - I never tested :)
/***
NPC_CreateVobList (var int slfInstance, var int range)
Pretty much same as NPC_PerceiveAll - however allows you to control range in which Vobs will be collected.
When used all collected vobs will be available in slf.vobList_array.
***/
FUNC VOID NPC_CreateVobList (var int slfInstance, var int range)
{
//006B7110 .text Debug data ?CreateVobList@oCNpc@@QAEXM@Z
const int oCNpc__CreateVobList_G1 = 7041296;
//0x0075DA40 public: void __thiscall oCNpc::CreateVobList(float)
const int oCNpc__CreateVobList_G2 = 7723584;
var C_NPC slf;
slf = Hlp_GetNPC (slfInstance);
CALL_IntParam (mkf (range));
CALL__thiscall (MEM_InstToPtr (slf), MEMINT_SwitchG1G2 (oCNpc__CreateVobList_G1, oCNpc__CreateVobList_G2));
};
/***
NPC_ClearVobList (var int slfInstance)
Clears slf.vobList_array - not sure if this useful - I never used it :)
***/
FUNC VOID NPC_ClearVobList (var int slfInstance)
{
//006B6EB0 .text Debug data ?ClearVobList@oCNpc@@QAEXXZ
const int oCNpc__ClearVobList_G1 = 7040688;
//0x0075D7F0 public: void __thiscall oCNpc::ClearVobList(void)
const int oCNpc__ClearVobList_G2 = 7722992;
var C_NPC slf;
slf = Hlp_GetNPC (slfInstance);
CALL__thiscall (MEM_InstToPtr (slf), MEMINT_SwitchG1G2 (oCNpc__ClearVobList_G1, oCNpc__ClearVobList_G2));
};
/***
NPC_RemoveFromVobList (var int slfInstance, var int vobPtr)
Removes specific vobPtr from slf.vobList_array
***/
FUNC VOID NPC_RemoveFromVobList (var int slfInstance, var int vobPtr)
{
//006B7080 .text Debug data ?RemoveFromVobList@oCNpc@@QAEXPAVzCVob@@@Z
const int oCNpc__RemoveFromVobList_G1 = 7041152;
//0x0075D9B0 public: void __thiscall oCNpc::RemoveFromVobList(class zCVob *)
const int oCNpc__RemoveFromVobList_G2 = 7723440;
if (!vobPtr) { return; };
var C_NPC slf;
slf = Hlp_GetNPC (slfInstance);
var zCVob Vob;
Vob = _^ (vobPtr);
CALL_PtrParam (MEM_InstToPtr (Vob));
CALL__thiscall (MEM_InstToPtr (slf), MEMINT_SwitchG1G2 (oCNpc__RemoveFromVobList_G1, oCNpc__RemoveFromVobList_G2));
};
/***
NPC_VobList_RemoveDeadNPC (var int slfInstance)
Will remove dead NPC from slf.vobList_array.
***/
FUNC VOID NPC_VobList_RemoveDeadNPC (var int slfInstance)
{
var oCNPC slf;
slf = Hlp_GetNPC (slfInstance);
var int i; i = 0;
var int loop;
MEM_InitLabels ();
loop = MEM_StackPos.position;
if (slf.vobList_numInArray > 0)
{
var int vobPtr; vobPtr = MEM_ReadIntArray (slf.vobList_array, i);
if (Hlp_Is_oCNpc (vobPtr))
{
var C_NPC npc;
npc = MEM_PtrToInst (vobPtr);
if (NPC_IsDead (npc))
{
//remove npc from vobList_array
NPC_RemoveFromVobList (slfInstance, vobPtr);
//restart loop
i = 0;
MEM_StackPos.position = loop;
};
};
i += 1;
if (i < slf.vobList_numInArray) {
MEM_StackPos.position = loop;
};
};
};
/***
NPC_VobList_RemovePlayer (var int slfInstance)
Will remove player from slf.vobList_array.
***/
FUNC VOID NPC_VobList_RemovePlayer (var int slfInstance)
{
var oCNPC slf;
slf = Hlp_GetNPC (slfInstance);
var int i; i = 0;
var int loop;
MEM_InitLabels ();
loop = MEM_StackPos.position;
if (slf.vobList_numInArray > 0)
{
var int vobPtr; vobPtr = MEM_ReadIntArray (slf.vobList_array, i);
if (Hlp_Is_oCNpc (vobPtr))
{
var C_NPC npc;
npc = MEM_PtrToInst (vobPtr);
if (NPC_IsPlayer (npc))
{
//remove npc from vobList_array
NPC_RemoveFromVobList (slfInstance, vobPtr);
//restart loop
i = 0;
MEM_StackPos.position = loop;
};
};
i += 1;
if (i < slf.vobList_numInArray) {
MEM_StackPos.position = loop;
};
};
};
/***
NPC_VobList_FindMobInter (var int slfInstance, var string scemeName)
Function removes from slf.vobList_array everything that is not oCMobInter and does not have sceme equal to scemeName.
//Example will detect VWHEEL object:
var int ptr;
NPC_CreateVobList (self, 800);
ptr = NPC_VobList_FindMobInter (self, "VWHEEL");
//Example will detect chest (CHESTBIG or CHESTMEDIUM or CHESTSMALL) object:
var int ptr;
NPC_CreateVobList (self, 800);
ptr = NPC_VobList_FindMobInter (self, "CHESTBIG");
if (ptr == 0) {
NPC_CreateVobList (self, 800);
ptr = NPC_VobList_FindMobInter (self, "CHESTMEDIUM");
};
if (ptr == 0) {
NPC_CreateVobList (self, 800);
ptr = NPC_VobList_FindMobInter (self, "CHESTSMALL");
};
***/
FUNC INT NPC_VobList_FindMobInter (var int slfInstance, var string scemeName)
{
var oCNPC slf;
slf = Hlp_GetNPC (slfInstance);
scemeName = STR_Upper (scemeName);
var int i; i = 0;
var int loop;
var int vobPtr;
var int flgRemove;
MEM_InitLabels ();
loop = MEM_StackPos.position;
if (slf.vobList_numInArray > 0)
{
flgRemove = TRUE;
vobPtr = MEM_ReadIntArray (slf.vobList_array, i);
if (Hlp_Is_oCMobInter (vobPtr)) {
var oCMobInter mob;
mob = MEM_PtrToInst (vobPtr);
if (Hlp_StrCmp (STR_Upper (mob.sceme), scemeName)) {
flgRemove = FALSE;
};
};
if (flgRemove) {
//remove npc from vobList_array
NPC_RemoveFromVobList (slfInstance, vobPtr);
//restart loop
i = 0;
MEM_StackPos.position = loop;
};
i += 1;
if (i < slf.vobList_numInArray) {
MEM_StackPos.position = loop;
};
};
i = 0;
loop = MEM_StackPos.position;
if (slf.vobList_numInArray > 0)
{
vobPtr = MEM_ReadIntArray (slf.vobList_array, i);
return vobPtr;
};
return 0;
};
/***
NPC_FreeLineOfSight (var int slfInstance, var int vobPtr)
Checks if NPC can see vobPtr. Used further below by function NPC_VobList_DetectObject.
***/
FUNC INT NPC_FreeLineOfSight (var int slfInstance, var int vobPtr)
{
//0069DE50 .text Debug data ?FreeLineOfSight@oCNpc@@QAEHPAVzCVob@@@Z
const int oCNpc__FreeLineOfSight_G1 = 6938192;
//0x007418E0 public: int __thiscall oCNpc::FreeLineOfSight(class zCVob *)
const int oCNpc__FreeLineOfSight_G2 = 7608544;
if (!vobPtr) { return 0; };
var C_NPC slf;
slf = Hlp_GetNPC (slfInstance);
var zCVob Vob;
Vob = _^ (vobPtr);
CALL_PtrParam (MEM_InstToPtr (Vob));
CALL__thiscall (MEM_InstToPtr (slf), MEMINT_SwitchG1G2 (oCNpc__FreeLineOfSight_G1, oCNpc__FreeLineOfSight_G2));
return CALL_RetValAsInt();
};
/***
NPC_VobList_DetectObject (var int slfInstance, var int freeLOS, var int vtbl, var int objInstance)
Allows you to detect specific objects. Returns object pointer.
freeLOS: is object in Free Line of Sight? If TRUE - will return object pointer only if in Free Line of Sight, if FALSE - will return object regardless of whether it is in Free Line of sight or not.
object vtbl: oCMobFire_vtbl, zCMover_vtbl, oCMob_vtbl, oCMobInter_vtbl, oCMobContainer_vtbl, oCMobDoor_vtbl, oCMobContainer_vtbl, oCNpc_vtbl, oCItem_vtbl, zCVobLight_vtbl
objInstance: detection of specific instance. (use 0 if you don't want to detect instances)
//Example will detect npc, Bloodfly, which is in Free Line of Sight
NPC_CreateVobList (self, 3000);
if (NPC_VobList_DetectObject (self, TRUE, oCNpc_vtbl, Bloodfly)) {
...
};
//Example will detect item, ItMi_Plants_Swampherb_01, which is in Free Line of Sight
NPC_CreateVobList (self, 1500);
if (NPC_VobList_DetectObject (self, TRUE, oCItem_vtbl, ItMi_Plants_Swampherb_01)) {
...
};
//Example will detect chest, which is in Free Line of Sight
var int ptr;
NPC_CreateVobList (self, 1000);
ptr = NPC_VobList_DetectObject (self, TRUE, oCMobContainer_vtbl, 0);
if (ptr) {
var oCMobLockable chestLock;
chestLock = MEM_PtrToInst (ptr);
//Is chest locked?
if (chestLock.bitfield & oCMobLockable_bitfield_locked) {
var oCMobContainer chest;
chest = MEM_PtrToInst (ptr);
PrintScreen (chest._oCMobLockable_pickLockStr, -1, _YPOS_MESSAGE_LOGENTRY, "font_old_10_white.tga", _TIME_MESSAGE_LOGENTRY);
};
};
//Example will detect door, which is in Free Line of Sight
var int ptr;
NPC_CreateVobList (self, 1000);
ptr = NPC_VobList_DetectObject (self, TRUE, oCMobDoor_vtbl, 0);
if (ptr) {
var oCMobLockable doorLock;
doorLock = MEM_PtrToInst (ptr);
//Is door locked?
if (doorLock.bitfield & oCMobLockable_bitfield_locked) {
var oCMobDoor door;
door = MEM_PtrToInst (ptr);
PrintScreen (door._oCMobLockable_pickLockStr, -1, _YPOS_MESSAGE_LOGENTRY, "font_old_10_white.tga", _TIME_MESSAGE_LOGENTRY);
};
};
***/
FUNC INT NPC_VobList_DetectObject (var int slfInstance, var int freeLOS, var int vtbl, var int objInstance)
{
var oCNPC slf;
slf = Hlp_GetNPC (slfInstance);
var int i; i = 0;
var int loop;
//flags
var int fFreeLOS;
var int fVtbl;
var int fObjInstance;
//loop through objects
MEM_InitLabels ();
loop = MEM_StackPos.position;
fVtbl = FALSE;
fFreeLOS = FALSE;
fObjInstance = FALSE;
if (!objInstance) { fObjInstance = TRUE; };
if (slf.vobList_numInArray > 0)
{
var int vobPtr; vobPtr = MEM_ReadIntArray (slf.vobList_array, i);
var int vobVtbl; vobVtbl = MEM_ReadInt (vobPtr);
//oCMobFire_vtbl
if (vtbl == oCMobFire_vtbl) {
if (vobVtbl == oCMobFire_vtbl) {
fVtbl = TRUE;
};
} else
//zCMover_vtbl
if (vtbl == zCMover_vtbl) {
if (vobVtbl == zCMover_vtbl) {
fVtbl = TRUE;
};
} else
//oCMob_vtbl
if (vtbl == oCMob_vtbl) {
//
if (vobVtbl == oCMob_vtbl)
|| (vobVtbl == oCMobInter_vtbl)
|| (vobVtbl == oCMobContainer_vtbl)
|| (vobVtbl == oCMobDoor_vtbl) {
fVtbl = TRUE;
};
} else
//oCMobInter_vtbl
if (vtbl == oCMobInter_vtbl) {
//
if (vobVtbl == oCMobInter_vtbl)
|| (vobVtbl == oCMobContainer_vtbl)
|| (vobVtbl == oCMobDoor_vtbl) {
fVtbl = TRUE;
};
} else
//oCMobLockable (does not exist in G1 Ikarus as a constant
//if (vtbl == oCMobLockable_vtbl) {
if (vtbl == oCMobContainer_vtbl) {
//
if (vobVtbl == oCMobContainer_vtbl) {
fVtbl = TRUE;
};
} else
if (vtbl == oCMobDoor_vtbl) {
if (vobVtbl == oCMobDoor_vtbl) {
fVtbl = TRUE;
};
} else
//oCNpc_vtbl
if (vtbl == oCNpc_vtbl) {
//
if (vobVtbl == oCNpc_vtbl) {
//Check NPC instance if required
if (objInstance) {
var C_NPC npc;
npc = _^ (vobPtr);
if (Hlp_GetInstanceID (npc) == objInstance) {
fObjInstance = TRUE;
};
};
fVtbl = TRUE;
};
} else
//oCItem_vtbl
if (vtbl == oCItem_vtbl) {
//
if (vobVtbl == oCItem_vtbl) {
//Check item instance if required
if (objInstance) {
var C_Item itm;
itm = _^ (vobPtr);
if (Hlp_GetInstanceID (itm) == objInstance) {
fObjInstance = TRUE;
};
};
fVtbl = TRUE;
};
} else
//zCVobLight_vtbl
if (vtbl == zCVobLight_vtbl) {
//
if (vobVtbl == zCVobLight_vtbl) {
fVtbl = TRUE;
};
};
//Free Line of Sight
if (freeLOS) {
if (NPC_FreeLineOfSight (slf, vobPtr)) {
fFreeLOS = TRUE;
};
} else {
fFreeLOS = TRUE;
};
if (fVtbl) && (fFreeLOS) && (fObjInstance) {
return vobPtr;
};
i += 1;
if (i < slf.vobList_numInArray) {
MEM_StackPos.position = loop;
};
};
return 0;
};
-
G1 In-game object moving
Hello folks,
Today I want to demonstrate how powerful npc.vobList_array can actually be - even I did not fully realize it till today!
I always wanted to move around in-game objects while playing the game, but was not really able to - as I was not able to detect what kind of object is in front of hero. (in case it would not be focus-able)
I know you can use trace rays somehow, but that is just .. out of my league! But with NPC_CreateVobList I can detect all objects which are nearby hero, and then I can just check, whether hero can see an object or not.
If he can see object, then it means object is in front of hero - and we can assume that he can move it around:
[Video]
Here is complete code in case you would like to try it yourself.
Required:
Ikarus 1.2.1
Function NPC_CanSeeVob from:
https://forum.worldofplayers.de/foru...1#post26055633
Function SetVobToFloor from Scriptbin\InsertAnything:
(@mud-freak thank you!)
https://forum.worldofplayers.de/foru...1#post25712257
Functions from Scriptbin\NPCVobList.d:
https://forum.worldofplayers.de/foru...1#post26052199
+ Functions Vob_Move & moveVobInFront:
(@Lehona thank you!)
Code:
const int zCVob__Move_G1 = 6217184; //0x5EDDE0
const int zCVob__Move_G2 = 6402784; //0x61B2E0
func void Vob_Move (var int ptr, var int x, var int y, var int z)
{
CALL_FloatParam (x);
CALL_FloatParam (y);
CALL_FloatParam (z);
CALL__thiscall (ptr, MEMINT_SwitchG1G2 (zCVob__Move_G1, zCVob__Move_G2));
};
/***
Function moveVobInFront move vob in front of NPC
***/
func void moveVobInFront (var int slfInstance, var int vobPtr)
{
var zCVob slf; slf = Hlp_GetNPC (slfInstance);
var zCVob vob; vob = _^ (vobPtr);
// trafo kopieren
MEM_CopyBytes(_@(slf) + 60, vobPtr + 60, 64);
// Vob vor den Helden setzen
var int delta; delta = mkf(150);
Vob_Move (vobPtr, mulf (slf.trafoObjToWorld[10], delta), mulf (slf.trafoObjToWorld[6], delta), mulf (slf.trafoObjToWorld[2], delta));
};
I have hooked function oCGame__HandleEvent - within this function I am checking if player pressed '[' key to start/stop object moving mode, or key ']' to enable/disable floor aligning mode:
Code:
//Global variables
var int vobTransportPtr;
var int vobTransportMode;
const int cvobTransportMode_Disabled = 0;
const int cvobTransportMode_Init = 1;
const int cvobTransportMode_Moving = 2;
const int cvobTransportMode_Done = 3;
var int vobTransportModeFloor;
var int vobTransportCollBits;
const int oCGame__HandleEvent_G1 = 6680288; //0x65EEE0
const int oCGame__HandleEvent_G2 = 7324016; //0x6FC170
FUNC VOID _HOOK_GAME_HANDLEEVENT ()
{
var int inputKey;
inputKey = MEM_KeyState (KEY_LBRACKET);
//Vob moving on/off
if (inputKey == KEY_PRESSED) || (inputKey == KEY_HOLD)
{
if (vobTransportMode == cvobTransportMode_Disabled)
{
vobTransportMode = cvobTransportMode_Init;
};
if (vobTransportMode == cvobTransportMode_Moving)
{
vobTransportMode = cvobTransportMode_Done;
};
};
//Vob surface align mode on/off
inputKey = MEM_KeyState (KEY_RBRACKET);
if (inputKey == KEY_PRESSED) || (inputKey == KEY_HOLD)
{
vobTransportModeFloor = !vobTransportModeFloor;
};
};
//Hook this
HookEngine (MEMINT_SwitchG1G2(oCGame__HandleEvent_G1, oCGame__HandleEvent_G2), 6, "_HOOK_GAME_HANDLEEVENT");
And finally FrameHandler_VobTransport function - is the one responsible for object detection and moving (should be called every frame):
Code:
/***
VobGetCollBits will return collision bitfield values
***/
FUNC INT VobGetCollBits (var int vobPtr)
{
if (!vobPtr) { return; };
var int collBits; collBits = (zCVob_bitfield0_collDetectionStatic + zCVob_bitfield0_collDetectionDynamic);
var zCVob vob; vob = MEM_PtrToInst (vobPtr);
return (vob.bitfield[0] & (collBits));
};
/***
VobRemoveCollBits - will remove collisions based on input bitfield values in collBits
***/
FUNC VOID VobRemoveCollBits (var int vobPtr, var int collBits)
{
if (!vobPtr) { return; };
var zCVob vob; vob = MEM_PtrToInst (vobPtr);
vob.bitfield[0] = vob.bitfield[0] & ~ (collBits);
};
/***
VobRestoreCollBits - will apply collisions based on input bitfield values in collBits
***/
FUNC VOID VobRestoreCollBits (var int vobPtr, var int collBits)
{
if (!vobPtr) { return; };
var zCVob vob; vob = MEM_PtrToInst (vobPtr);
vob.bitfield[0] = vob.bitfield[0] | (collBits);
};
/***
Hlp_Is_zcVob - validates whether ptr object is zcVob
***/
FUNC INT Hlp_Is_zcVob (var int ptr) {
if (!ptr) { return 0; };
var int vtbl;
vtbl = MEM_ReadInt (ptr);
return (vtbl == zCVob_vtbl);
};
/***
Hlp_Is_oCMobLadder - validates whether ptr object is oCMobLadder
***/
FUNC INT Hlp_Is_oCMobLadder (var int ptr) {
if (!ptr) { return 0; };
var int vtbl;
vtbl = MEM_ReadInt (ptr);
return (vtbl == oCMobLadder_vtbl);
};
/*** Why are these 2 above not in Ikarus by default :-O ***/
/***
FrameHandler_VobTransport
Call this function every frame, in your frame trigger function for example.
Code within this function identifies object which should be moved around.
***/
FUNC VOID FrameHandler_VobTransport ()
{
//Identification of object which should be moved around
if (vobTransportMode == cvobTransportMode_Init)
{
//Safety-check
if (Hlp_IsValidNPC (hero))
{
var oCNPC her;
her = Hlp_GetNPC (hero);
var zCVob vobHero;
var zCVob vob;
//Is there anything in hero's focus ?
if (her.focus_vob)
{
//Move around following objects
if (Hlp_Is_oCMob (her.focus_vob))
|| (Hlp_Is_oCMobInter (her.focus_vob))
|| (Hlp_Is_oCMobFire (her.focus_vob))
|| (Hlp_Is_oCMobLockable (her.focus_vob))
// || (Hlp_Is_zCMover (her.focus_vob))
|| (Hlp_Is_oCMobContainer (her.focus_vob))
|| (Hlp_Is_oCMobDoor (her.focus_vob))
|| (Hlp_Is_oCMobLadder (her.focus_vob))
{
//Get pointer of moved object - it's in our focus
vobTransportPtr = her.focus_vob;
//Backup collision bitfields
vobTransportCollBits = VobGetCollBits (vobTransportPtr);
//Remove active collisions
VobRemoveCollBits (vobTransportPtr, vobTransportCollBits);
//Change vobTransportMode
vobTransportMode = cvobTransportMode_Moving;
};
} else
//Detect vob, which cannot be in her.focus_vob (e.g. zcVobs)
{
//Detect all nearby vobs
NPC_ClearVobList (hero);
NPC_CreateVobList (hero, 120);
//Loop through all nearby vobs
var int i; i = 0;
var int loop;
var int vobPtr;
MEM_InitLabels ();
loop = MEM_StackPos.position;
if (her.vobList_numInArray > 0)
{
vobPtr = MEM_ReadIntArray (her.vobList_array, i);
//Is this object zcVob ?
if (Hlp_Is_zcVob (vobPtr))
{
//Is this vob in front of player - can player see it?
if (NPC_CanSeeVob (hero, vobPtr))
{
//Get pointer of moved object
vobTransportPtr = vobPtr;
//Backup collision bitfields
vobTransportCollBits = VobGetCollBits (vobTransportPtr);
//Remove active collisions
VobRemoveCollBits (vobTransportPtr, vobTransportCollBits);
//Change vobTransportMode
vobTransportMode = cvobTransportMode_Moving;
};
};
//If vobTransportMode was not changed ... continue in loop (or exit)
if (vobTransportMode == cvobTransportMode_Init)
{
i += 1;
if (i < her.vobList_numInArray) {
MEM_StackPos.position = loop;
};
};
};
};
};
//If vobTransportMode was not changed ... then there was no object detected - disable vobTransportMode
if (vobTransportMode == cvobTransportMode_Init)
{
vobTransportMode = cvobTransportMode_Disabled;
};
};
//Moving mode
if (vobTransportMode == cvobTransportMode_Moving)
{
//Move object in front of hero
MoveVobInFront (hero, vobTransportPtr);
//Align vob to floor
if (vobTransportModeFloor == TRUE) {
SetVobToFloor (vobTransportPtr);
};
};
//Do we want to stop moving object around?
if (vobTransportMode == cvobTransportMode_Done)
{
//Restore collision bitfields
VobRestoreCollBits (vobTransportPtr, vobTransportCollBits);
//Disable vobTransportMode
vobTransportMode = cvobTransportMode_Disabled;
};
};
Geändert von F a w k e s (11.03.2019 um 12:10 Uhr)
-
Thanks for sharing. A couple of suggestions:
- Why are you hooking oCGame::HandleEvent, but then use MEM_KeyState? HandleEvent already provides the pressed key. Using MEM_KeyState is unreliable, because if called with the same key at another place, KEY_PRESSED might never be detected. To add new key detection directly to oCGame::HandleEvent, see this post.
Zitat von F a w k e s
/*** Why are these 2 above not in Ikarus by default :-O ***/
Hlp_Is_oCMobLadder could be added. But Hlp_Is_zcVob - the way you have it - does something different from Ikarus. The Hlp_Is_* functions in Ikarus are designed to include all inherited classes. Your function explicitly checks for zCVob only. This is of course not "illegal" but the name of the function is misleading compared to the Ikarus functions.- Because of this inheritance checking, you will only need to check Hlp_Is_oCMob and Hlp_Is_zCMover. All other checks are redundant.
- Are you sure you want to allow moving movers? This probably won't move their key frames causing it to "jump" around when triggered.
- You are traversing the voblist, why the need to check if a respective vob is a zCVob?
-
Zitat von mud-freak
Thanks for sharing. A couple of suggestions:
Why are you hooking oCGame::HandleEvent, but then use MEM_KeyState? HandleEvent already provides the pressed key. Using MEM_KeyState is unreliable, because if called with the same key at another place, KEY_PRESSED might never be detected. To add new key detection directly to oCGame::HandleEvent, see this post.
I actually saw your post - and that's why I decided to hook this one specifically. In Gothic 1 however ESI does not return pressed key. I was printing on screen all registers - and seems like EAX is kind of returning pressed key (if you hold it long enough) - however if you just quickly tap a key - this was not working. Then I tested MEM_KeyState - and this worked reliably for both tapping and pressing-holding a key. Maybe it's because I was not directly hooking this specific address oCGame__HandleEvent_dfltCase - here I have no idea how to find this address in G1.
Zitat von mud-freak
Hlp_Is_oCMobLadder could be added. But Hlp_Is_zcVob - the way you have it - does something different from Ikarus. The Hlp_Is_* functions in Ikarus are designed to include all inherited classes. Your function explicitly checks for zCVob only. This is of course not "illegal" but the name of the function is misleading compared to the Ikarus functions.
Ok, I see your point here - Hlp_Is_zcVob might be misleading then as all inherited classes belong to zCVob. What would be a better name then?
Zitat von mud-freak
Because of this inheritance checking, you will only need to check Hlp_Is_oCMob and Hlp_Is_zCMover. All other checks are redundant.
Good point - thank you
Zitat von mud-freak
Are you sure you want to allow moving movers? This probably won't move their key frames causing it to "jump" around when triggered.
Another good one! I actually do not want to move movers!
Zitat von mud-freak
You are traversing the voblist, why the need to check if a respective vob is a zCVob?
Sooo this npc.vobList_array actually does not contain only zCVobs - when you call function NPC_CreateVobList (npc, range) - function collects all objects - NPCs, Items, Vobs, mobs - everything in specified range.
If I would not be verifying that object in a list and in front of hero is zCVob only - I would be able to move unintentionally around mobs (which I don't want to, if I want to move mobs, I will have them in a focus), Items and even NPCs!
[Video]
Geändert von F a w k e s (11.03.2019 um 12:26 Uhr)
-
Zitat von F a w k e s
I actually saw your post - and that's why I decided to hook this one specifically. In Gothic 1 however ESI does not return pressed key. I was printing on screen all registers - and seems like EAX is kind of returning pressed key (if you hold it long enough) - however if you just quickly tap a key - this was not working. Then I tested MEM_KeyState - and this worked reliably for both tapping and pressing-holding a key. Maybe it's because I was not directly hooking this specific address oCGame__HandleEvent_dfltCase - here I have no idea how to find this address in G1.
Yes, I forgot you are talking about Gothic 1. When I find the time, I will look up the respective address in Gothic 1. Essentially, this address is reached only when no other game key is pressed - the perfect address to "inject" new key strokes for a running game, without having to perform all the checks that are already passed.
Zitat von F a w k e s
Ok, I see your point here - Hlp_Is_zcVob might be misleading then as all inherited classes belong to zCVob. What would be a better name then?
Because you are using this check explicitly and only once, I suppose you could just use the code inline.
Code:
//Is this object zcVob ?
if (MEM_ReadInt(vobPtr) == zCVob_vtbl)
{
Zitat von F a w k e s
Sooo this npc.vobList_array actually does not contain only zCVobs - when you call function NPC_CreateVobList (npc, range) - function collects all objects - NPCs, Items, Vobs, mobs - everything in specified range.
If I would not be verifying that object in a list and in front of hero is zCVob only - I would be able to move unintentionally around mobs (which I don't want to, if I want to move mobs, I will have them in a focus), Items and even NPCs!
Right! Seems I already got confused by Hlp_Is_zcVob.
You might also want to exclude doors (the player could just move locked doors to places that they are not supposed to reach yet) and ladders (the player could reach places that are not intended to be accessible - same problem as with the elevation and blink spell btw).
You could simplify your condition to:
Code:
if ((Hlp_Is_oCMob(her.focus_vob))
|| (Hlp_Is_oCMobFire (her.focus_vob)))
&& (!Hlp_Is_oCMobDoor(her.focus_vob))
&& (!Hlp_Is_oCMobLadder (her.focus_vob))
{
You should also keep in mind that oCMobInters might be in use by other NPCs at the moment. Furthermore, once you moved a particular oCMobInter (e.g. a bench) NPCs whose daily routine depends on it might not find it anymore.
-
Thanks for sharing Fawkes!
-
I looked up the address within oCGame::HandleEvent. I made into a universally usable script for Gothic 1 and Gothic 2.
One could argue now that this is not much different from a FrameFunction with MEM_KeyState. The difference is that this approach saves the extra work of checking if any menu is open, whether the player is in a dialog, whether the player may move, etc. Also this function is "event driven", meaning it is really only called when a key is pressed/held instead of every frame in vain. So it's arguably more performant.
gameKeyEvents.d – Adding new key events to oCGame::HandleEvent.
Geändert von mud-freak (30.10.2020 um 10:26 Uhr)
Grund: Fixed script
-
Thank you !! Works like a charm
Why is there a + in front of MEMINT_Switch (works with & without it, so was wondering if this is just a typo)?
Code:
HookEngineF(+MEMINT_SwitchG1G2(oCGame__HandleEvent_dfltCase_G1,
oCGame__HandleEvent_dfltCase_G2), 6, Game_KeyEvent_);
-
Zitat von Rayzer
Few days ago I wrote a script that creates in inventory every existing in the game item. Maybe someone will make use of it
Code:
func void CreateAllItems(var c_npc slf) {
var int i;
repeat(i, MEM_Parser.symtab_table_numInArray);
var zCPar_Symbol symb; symb = _^(MEM_ReadIntArray(MEM_Parser.symtab_table_array, i));
if ((symb.bitfield & zCPar_Symbol_bitfield_type) != zPAR_TYPE_INSTANCE || STR_IndexOf(symb.name, ".") > 0 || !symb.parent) {
continue;
};
symb = _^(symb.parent);
if (Hlp_StrCmp(symb.name, "C_ITEM")) {
CreateInvItems(slf, i, 1);
}
else if (symb.parent && (symb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_PROTOTYPE) {
symb = _^(symb.parent);
if (Hlp_StrCmp(symb.name, "C_ITEM")) {
CreateInvItems(slf, i, 1);
};
};
end;
};
The game may freeze for a while because table of parser symbols is very huge.
What's the idea here that the variable i is the item to be created and not something using symb? Looking at the code, I would have thought that i is just an array index rather than the actual address of the object.
-
Zitat von Milky-Way
What's the idea here that the variable i is the item to be created and not something using symb? Looking at the code, I would have thought that i is just an array index rather than the actual address of the object.
An instance, like you would normally pass to CreateInvItem, is internally just an id (integer). The script iterates over all possible ids and if they are C_ITEMs, it creates them via the External.
-
Zitat von F a w k e s
Thank you !! Works like a charm
Why is there a + in front of MEMINT_Switch (works with & without it, so was wondering if this is just a typo)?
You could say, it is used to make the parser resolve a variable (lvalue) into its actual value (rvalue) before pushing it. You can find it a lot in Ikarus. At some point I had similar(?) code that would only work like that. I cannot remember the exact circumstances but I've grown accustomed to using it, when using MEMINT_SwitchG1G2 inside HookEngineF. Thanks for letting me know that it works regardless. I must have remembered it wrong.
-
For a bug to occur without the '+', the following conditions are necessary:
A function f that returns a variable directly, not a computation (return x; vs return x+1)
And a caller has to use f in a way such that it is called twice before either return value is used in a computation. Essentially:
Code:
func int thingy(var int x) {
return x;
};
func int bug() {
// Prints 8, not 7
print(IntToString(thingy(3)+thingy(4)));
};
Changing thingy to 'return +x;' instead fixes the bug.
-
Zitat von Lehona
An instance, like you would normally pass to CreateInvItem, is internally just an id (integer). The script iterates over all possible ids and if they are C_ITEMs, it creates them via the External.
Thanks for the explanation, that's exactly what I needed
Berechtigungen
- Neue Themen erstellen: Nein
- Themen beantworten: Nein
- Anhänge hochladen: Nein
- Beiträge bearbeiten: Nein
|
|