Larian Banner: Baldur's Gate Patch 9
Previous Thread
Next Thread
Print Thread
Page 1 of 2 1 2
#642926 28/02/18 01:44 AM
Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
I need a script that will fire from a PC running through a trigger and clean up monster corpses in the level. Been looking for some type of command to do this with.

Can anyone please point me in the right direction?

Joined: Mar 2016
Location: Belgium
T
addict
Offline
addict
T
Joined: Mar 2016
Location: Belgium
I'm not sure what you mean by "clean up". There is no way to permanently remove characters (dead or alive) from a game, unless they were created as temporary characters in the first place (in which case they will be removed automatically when dying and saving/reloading the game). At most, you can set them them off-stage.

To iterate over all characters in the current level, you can use CharacterLaunchIterator.

Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
Hay Tinkerer,

It could be my misunderstanding the of how this is handled.

With my issue. After revisiting the level and the creatures respawning we now have a pile of dead monsters copse everywhere. Takes away from the environment, they don't seem to disappear even reading that the way I spawned them in my script, makes the corpse temporary and it should disappear after some time I guess. But its in no way fast enough or seems to even ever happen in testing...

so I need a trigger at the front door to deal with this.
Off-stage I guess will work... I just don't want to see that and the PC has already looted them.

CharacterLaunchIterator looks sway outside my scripting level.

In nwn we had a script that would call on Onenter, or Onexit. Great place to fire a cleanup script. To remove junk players drop and any corpses. If you return to the level later on. The monsters have respawned. But their not standing in a corpse of themselves ...

I need to add context I think.
I have an ecosystem type mod. I can reuse a lot of areas, so there is maintenance that need done on each area , like cleaning the last run through that dungeon. Re-setting the chests and loot.

Also, there are effects on the corpses that are very distracting.

You would thing the corpse type on the object would handle this? What's the logic in the type of container, as compared to the ability to "de-spawn" " off-stage" a looted corpse?

dose not compute...

Last edited by Detect; 01/03/18 01:27 AM.
Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
Full Definition(s)
call CharacterLaunchIterator((STRING)_Event)
Description
Causes story event _Event to be thrown for every character in the current level. Once the last such character has been iterated, an additional story event _Event will be thrown for object NULL_00000000-0000-0000-0000-000000000000, so that you can perform any necessary clean-up or finalise processing.

lol what does this mean in human terms>?
is there a away to identify looted, corpse for removal(off-stage")? is Null a dead monster?

Last edited by Detect; 01/03/18 01:54 AM.
Joined: May 2017
enthusiast
Offline
enthusiast
Joined: May 2017
Hi Detect. CharacterLaunchIterator calls the event you specify (a string value) on every character in the current level.

Example:
Code
IF
GameStarted(_,_)
THEN
CharacterLaunchIterator("Detect_Events_OnCharacterIterate");

IF
StoryEvent((CHARACTERGUID)_Character, "Detect_Events_OnCharacterIterate")
AND
_Character != NULL_00000000-0000-0000-0000-000000000000
AND
CharacterGetDisplayName(_Character,_,_Name)
AND
StringConcatenate("[Detect] Iterator ran on ", _Name, _Message)
THEN
DebugBreak(_Message);

IF
StoryEvent(NULL_00000000-0000-0000-0000-000000000000, "Detect_Events_OnCharacterIterate")
THEN
DebugBreak("[Detect] Character iteration finished.");


As you can see here, it's pretty straightforward. The iterator simply creates an event, with the name you specify, for each character in the current level. Then, once that's done, it calls the event one last time for the value of a null GUIDSTRING (NULL_00000000-0000-0000-0000-000000000000).

Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
LaughingLeader you always make the scripting seem so simple. Thanks for this again man!

I will test this out and report back shortly... amazing!!

Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
Assumptions -
The script runs "on start" so no trigger needed?
Runs when the area is loaded/reloaded?
Is a final Null state after the character is looted?
Is there implications in terms of always iterating or will it only run once?

the scrip complies with no errors.

trying to figure out the best way to test it...
macgyver some rig in my builder area, or just go prod on a group run through...


Joined: May 2017
enthusiast
Offline
enthusiast
Joined: May 2017
The sample script I supplied runs when the game is "started", after the level has loaded and is ready on the client side:
GameStarted

Figuring out when to run the iterator for your specific needs is going to be up to you of course, but here's some ideas for events to use:
RegionStarted
RegionEnded
DB_CurrentLevel

For me personally, if I were to make a level that would be continuously populated with new enemies, I would use one of the temporary character creation queries to create my enemies:
TemporaryCharacterCreateAtTrigger
TemporaryCharacterCreateAtPosition
TemporaryCharacterCreateAtPositionOutOfSightTo

The advantage of temporary characters are their corpses aren't saved when reloading the level.

If you want loot to remain despite their corpse disappearing, one possible solution would be to create a new container when they die (like a backpack or a bag) and transfer their loot to that container. Then that container could always be destroyed after it's been emptied.

Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
hmmm this is some interesting advice

TemporaryCharacterCreateAtTrigger
TemporaryCharacterCreateAtPosition
TemporaryCharacterCreateAtPositionOutOfSightTo

I am using "CharacterCreateAtPosition" can I just find and replace?

Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
obviously swapping out CharacterCreateAtPosition for TemporaryCharacterCreateAtPosition is going to solve this issue.

lets see what testing brings...

Last edited by Detect; 03/03/18 12:43 AM.
Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
Here's were the script landed, but in testing it does not work.

However, the script did help me find a major bug that I was able to crush. So now all my towns people don't come to assist the PC in this one silly situation.

So here's where I placed the script. It was placed in my story level:

IF
DB_CheckLevelStart("TheOldRuin")
AND
DB_CurrentLevel("NorthHall")
THEN
GoalCompleted;

IF
GameStarted(_,_)
THEN
CharacterLaunchIterator("Detect_Events_OnCharacterIterate");

IF
StoryEvent((CHARACTERGUID)_Character, "Detect_Events_OnCharacterIterate")
AND
_Character != NULL_00000000-0000-0000-0000-000000000000
AND
CharacterGetDisplayName(_Character,_,_Name)
AND
StringConcatenate("[Detect] Iterator ran on ", _Name, _Message)
THEN
DebugBreak(_Message);

IF
StoryEvent(NULL_00000000-0000-0000-0000-000000000000, "Detect_Events_OnCharacterIterate")
THEN
DebugBreak("[Detect] Character iteration finished.");



testing:
start game run outside the town walls kill something, run back inside town walls, zone into another level. Then zone back to the level we are testing run out to the monster copse and its still there.

Also,
CharacterCreateAtPosition &
TemporaryCharacterCreateAtTrigger
this seems to be a query or something, so I can't just find and replace...


Last edited by Detect; 04/03/18 12:57 PM.
Joined: Nov 2017
L
member
Offline
member
L
Joined: Nov 2017
Gamestarted is probably too early for your script to work. As per the notes in its documentation https://docs.larian.game/Osiris/API/GameStarted, the level isn't ready on the server side until the RegionStarted event.

As a more general problem:
Is this your level wrapper goal?
It should close when the level is loaded (as per the very first lines you quoted,
IF
DB_CheckLevelStart("TheOldRuin")
AND
DB_CurrentLevel("NorthHall")
THEN
GoalCompleted;),
so that all of the level-specific goals, which should be parented to it, activate. This means that placing the logic here can't work: either the level isn't ready, or the logic is inactive.

As per the documentation for that call, https://docs.larian.game/Osiris/API/TemporaryCharacterCreateAtPosition, TemporaryCharacterCreateAtPosition returns the character being summoned, as you may want to perform actions on it when it is spawned.

Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
Thanks. Moved it out of the level story and placed it as a script under the story level. Now its not inside those its in its on script with no GoalCompleted, its not working. I know its a sample script but its a real challenge to test it.

RegionStarted and GameStarted are both events right? Do I need RegionStarted some place to get the script to fire?


Last edited by Detect; 04/03/18 02:59 PM.
Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
I will mess about with:
DB_CurrentLevel((STRING)_LevelName)

see if this will get the script to trigger

Last edited by Detect; 04/03/18 03:31 PM.
Joined: Mar 2016
Location: Belgium
T
addict
Offline
addict
T
Joined: Mar 2016
Location: Belgium
Originally Posted by Detect

So here's where I placed the script. It was placed in my story level:

IF
DB_CheckLevelStart("TheOldRuin")
AND
DB_CurrentLevel("NorthHall")
THEN
GoalCompleted;

IF
GameStarted(_,_)
THEN
CharacterLaunchIterator("Detect_Events_OnCharacterIterate");

IF
StoryEvent((CHARACTERGUID)_Character, "Detect_Events_OnCharacterIterate")
AND
_Character != NULL_00000000-0000-0000-0000-000000000000
AND
CharacterGetDisplayName(_Character,_,_Name)
AND
StringConcatenate("[Detect] Iterator ran on ", _Name, _Message)
THEN
DebugBreak(_Message);

IF
StoryEvent(NULL_00000000-0000-0000-0000-000000000000, "Detect_Events_OnCharacterIterate")
THEN
DebugBreak("[Detect] Character iteration finished.");


testing:
start game run outside the town walls kill something, run back inside town walls, zone into another level. Then zone back to the level we are testing run out to the monster copse and its still there.

LaughingLeader's example was an illustration of how to use the CharacterLaunchIterator() Osiris call, not code that literally does what you asked. The above code logs each character as it receives the event from the iterator (which you can see in the editor log or in the osirislog.log file). It does not actually set anything off-stage.

I would strongly recommend you to watch the Osiris tutorial videos linked from the Osiris wiki page and to then read the "Osiris Overview" linked from the same page. LaughingLeader's own tutorial on Osiris is also good.

Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
Design: Osiris triggered an assert: [Detect] Iterator ran on Master Of Time
Timestamp: 04-03-2018 16:11:15:128
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:129
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Warrior Trader
Timestamp: 04-03-2018 16:11:15:131
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:131
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Boon Boy
Timestamp: 04-03-2018 16:11:15:132
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:133
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Master Of Eternal Darkness
Timestamp: 04-03-2018 16:11:15:133
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:134
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Master Of Knowledge
Timestamp: 04-03-2018 16:11:15:134
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:134
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Character iteration finished.
Timestamp: 04-03-2018 16:11:15:135
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Master Of Time
Timestamp: 04-03-2018 16:11:15:135
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:135
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Warrior Trader
Timestamp: 04-03-2018 16:11:15:137
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:138
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Boon Boy
Timestamp: 04-03-2018 16:11:15:138
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:139
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Master Of Eternal Darkness
Timestamp: 04-03-2018 16:11:15:140
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:140
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Master Of Knowledge
Timestamp: 04-03-2018 16:11:15:140
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Iterator ran on Deer
Timestamp: 04-03-2018 16:11:15:141
Function: esv::OsirisGameFunctions::DebugBreak
Location: EoCServer\Server\OsirisGameFunctions.cpp (1922)
Design: Osiris triggered an assert: [Detect] Character iteration finished.


nothing is happening to the corpse

Last edited by Detect; 04/03/18 04:44 PM.
Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
Guys,

I really appreciate the help and tough love, but I am a freacking builder. I am way over my head here! I will fumble through with all of your help. But c"mon give a guy a break here.

Seriously. Logic: I need the corpse to disappear.

But I will go away with my tail between my legs!


I think*
The solution is to; from the beginning use:
TemporaryCharacterCreateAtPosition
I will have to rebuild all my scripts that are spawning monsters in each level to accommodate:
TemporaryCharacterCreateAtPosition
Current state is: CharacterCreateAtPosition

this is a bit of work because I don't have a working TemporaryCharacterCreateAtPosition
script created.

Big design error on my part!!
Any scripters out there want to take the wheel on this project... IN terms of scripting lead role; or at lease let me consult with you on future things.
Kindly let me know.


I just fired my self for this role!

One of those record-scratching-to-halt moments...


* I think, but not sure. That's called "I think". Other uses: IIRC.


Last edited by Detect; 04/03/18 05:27 PM.
Joined: Jul 2015
D
Detect Offline OP
member
OP Offline
member
D
Joined: Jul 2015
just tested this and still the corpse remains.

re-zoned a few times...

/*MOB Spawner UPTOP Boar 1*/
IF
CharacterEnteredTrigger(_Character, TRIGGERGUID_Monkworks_SpawnTrigger_Boar000_001_0470adec-fec8-49c4-be07-4b8b89999bba)
AND
GetPosition(TRIGGERGUID_Monkworks_SpawnPoint_Boar_001_8124395a-3428-4b8c-a17d-ac7aaed382ce, _X, _Y, _Z)
AND
NOT DB_MonkWorksBoar000Database(_)
AND
TemporaryCharacterCreateAtPosition(_X, _Y, _Z, "CHARACTERGUID_Monkworks_Boar000_7c0d5742-cda0-4381-b41f-9dccfba783cb", 1, _Boar000)
THEN
DB_MonkWorksBoar000Database(_Boar000);
IF
CharacterDied(_Boar000)
AND
DB_MonkWorksBoar000Database(_Boar000)
THEN
NOT DB_MonkWorksBoar000Database(_Boar000);

Joined: Nov 2017
L
member
Offline
member
L
Joined: Nov 2017
These characters are temporary in the sense that they aren't saved - they should disappear on save-load. Does that work?

I may be reading over it - where in your code do you expect your character to be set off-stage?

Joined: May 2017
enthusiast
Offline
enthusiast
Joined: May 2017
As Tinkerer said, my example was just that - an example on how the character iterator works. You can see it actually worked correctly, given the log messages you shared:
Originally Posted by Detect
Design: Osiris triggered an assert: [Detect] Iterator ran on Master Of Time


As for when to set your characters off-stage, one idea would be to do so when the player steps into a trigger you designate as the "level transition start" trigger. This task would also be easier if you added all your monsters to a shared database, instead of each type getting its own. If you need to later retrieve certain types of monsters, you could just add a shared value in the DB like so:
Code
TemporaryCharacterCreateAtPosition(_X, _Y, _Z, "CHARACTERGUID_Monkworks_Boar000_7c0d5742-cda0-4381-b41f-9dccfba783cb", 1, _Boar000)
THEN
DB_MonkWorks_SpawnedEnemies("Boar", _Boar000);

I suggest leaving their database entries in-tact when they die, and removing them when you actually set them off-stage:

INIT:
Code
//Replace this with your actual trigger
DB_MonkWorks_LevelTransitionTrigger((TRIGGERGUID)LevelTransition_Level1_bdc5b3da-041a-4d98-bb68-149869203e83);

KB:
Code
IF
CharacterEnteredTrigger(_Character, _Trigger)
AND
DB_IsPlayer(_Character)
AND
DB_MonkWorks_LevelTransitionTrigger(_Trigger)
AND
NOT DB_MonkWorks_CleanupRunning(_)
AND
MonkWorks_QRY_CleanupNeeded()
THEN
DB_MonkWorks_CleanupRunning(1);
MonkWorks_System_Cleanup();

QRY
MonkWorks_QRY_CleanupNeeded()
AND
SysCount("DB_MonkWorks_SpawnedEnemies", 2, _TotalEnemies)
AND
_TotalEnemies > 0
THEN
DB_NOOP(1);

PROC
MonkWorks_System_Cleanup()
AND
DB_MonkWorks_SpawnedEnemies(_Type, _Enemy)
THEN
SetOnStage(_Enemy, 0);
NOT DB_MonkWorks_SpawnedEnemies(_Type, _Enemy);

PROC
MonkWorks_System_Cleanup()
AND
NOT DB_MonkWorks_SpawnedEnemies(_,_)
THEN
NOT DB_MonkWorks_CleanupRunning(1);


As you can see here, I made both a custom query to check if we need to clean up (if any spawned enemies are in the database), and a proc for actually iterating through said database and setting the enemy off stage.

This is pretty basic stuff (once you're familar with the story scripting syntax/basics!), so I recommend giving this a read, and the articles listed under "Recommended Reading" as well.

Page 1 of 2 1 2

Link Copied to Clipboard
Powered by UBB.threads™ PHP Forum Software 7.7.5