|
Post by gauntlet101010 on Nov 8, 2020 18:38:55 GMT -5
I'd like to loop the standing crow effect to create a "pet" of a sort. This is what I have
<L n="client_states">
<U>
<T n="key">98745<!--GenericFX_On--></T>
<U n="value">
<V n="vfx_state" t="apply_new_value">
<V n="apply_new_value" t="start_vfx">
<V n="start_vfx" t="multiple_effects">
<U n="multiple_effects">
<L n="vfx_list">
<U>
<T n="auto_on_effect">True</T>
<T n="effect_name">crow_standing</T>
<V n="joint_name" t="Slot">
<T n="Slot">_FX_</T>
</V>
</U>
</L>
</U>
</V>
</V>
</V>
</U>
</U>
<U>
<T n="key">98746<!--GenericFX_Off--></T>
</U>
</L>
But it just disappears!!! I suppose that's because it's just part of a sequence, but I just want it to loop infinitely. But I can't find any XML on an object that does that. Is there any way to get this effect to do what I want?
|
|
|
Post by andrew on Nov 8, 2020 19:50:58 GMT -5
Hi gauntlet101010 , I think the two options that you have are: 1. Edit the VFX resource to make it loop. Studio does not have this capability yet, but I'm told that there is a tool for that. 2. Use a script to repeat the effect with a timer. The VFX player object that I made uses this method. I also have a spare script lying around that I had planned for a future tutorial on custom scripted objects (a simple decorative gnome with a script that repeats an effect) that does this. If you go this route, maybe this script can help: import alarms import clock import objects import objects.game_object import vfx
class EffectGnomeObject(objects.game_object.GameObject): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._vfx: vfx.PlayEffect = None self._timeout_handle: alarms.AlarmHandle = None self.vfx_slot_hash: int = objects.sims4.hash_util.hash32('_FX_') self.fx: str = 'chemistry_lab_explosion_flash' self.loop_interval: float = 10 self.delay: float = 0
def on_add(self) -> None: super().on_add()
def alarm_callback(self, handle, alarm_data=None): self._vfx = vfx.PlayEffect(self, self.fx, self.vfx_slot_hash) self._vfx.start()
self._timeout_handle = alarms.add_alarm( owner=self, time_span=clock.interval_in_real_seconds(float(self.delay)), callback=alarm_callback, repeating=True, repeating_time_span=clock.interval_in_real_seconds(float(self.loop_interval)) )
def on_remove(self) -> None: if self._vfx is not None: self._vfx.stop(True) self._vfx = None
if self._timeout_handle: alarms.cancel_alarm(self._timeout_handle) self._timeout_handle = None super().on_remove()
|
|
|
Post by gauntlet101010 on Nov 9, 2020 22:51:38 GMT -5
I haven't done a script, ever, but it seems as if the TS4 VFX tool is mostly good for particle effects. And crashes when I try to complete a save. :-/
Thanks for the snippit of code. I might very well try to make my first script based mod. I mean. It's just looping the effect constantly. Seems like an easy place to start. I'll let you know how it goes when I get into it.
|
|
|
Post by gauntlet101010 on Nov 10, 2020 20:41:52 GMT -5
Question: How do I marry this script to my object?
|
|
|
Post by andrew on Nov 12, 2020 0:38:37 GMT -5
In short, you would need to give your object a custom tuning, and then point your custom tuning to use the custom script instead of the default GameObject. At the top of the object tuning, there are the attributes m and c for module and class. Module will typically be the name of your python script without the .py extension (unless you made submodules) and c is just the python class name from the script.
So if your tuning has this (as most objects do):
c="GameObject" i="object" m="objects.game_object"
And you compiled the example above as test_gnome.py, you would replace it with this:
c="EffectGnomeObject" i="object" m="test_gnome"
|
|
|
Post by gauntlet101010 on Nov 13, 2020 12:49:06 GMT -5
I'm getting an error:
TypeError: alarm_callback()missing 1 required positional argument: 'handle'
But it does seem to attach to my object, which is good.
|
|
|
Post by andrew on Nov 14, 2020 11:07:54 GMT -5
Hmm I guess I must have rearranged the code some since I last tried it and ended up with a bug. I think that you just need to move the alarm_callback out of the on_add like this:
def alarm_callback(self, handle, alarm_data=None): self._vfx = vfx.PlayEffect(self, self.fx, self.vfx_slot_hash) self._vfx.start() def on_add(self) -> None: super().on_add() self._timeout_handle = alarms.add_alarm( owner=self, time_span=clock.interval_in_real_seconds(float(self.delay)), callback=alarm_callback, repeating=True, repeating_time_span=clock.interval_in_real_seconds(float(self.loop_interval))
|
|
|
Post by gauntlet101010 on Nov 14, 2020 12:43:40 GMT -5
So it doesn't throw an error, but it isn't calling on the effect either. And the custom tuning I have is: <I c="G101010RavenPerchedObject" i="object" m="FPt4_RavenPerched" n="G101010_RavenPerched" s="6183087615564780068"> Here's a download link to the .package and the .py file. www.simfileshare.net/download/2150809/Thanks for helping me with this. I'm not sure what I'm missing.
|
|
|
Post by andrew on Nov 15, 2020 1:44:32 GMT -5
It looks like the functions got accidentally indented wrong. Both the on_add and the alarm_callback got indented forward so that they became local nested functions of the __init__ that were never seen by the game. I un-indented them and found another issue that I missed in my last post. After moving the alarm callback out of the on_add, it needs to be referred to as "self.alarm_callback". After fixing those two things, I saw the raven spawn repeatedly (maybe the time needs to be adjusted because I saw multiple ravens at a time). import alarms import clock import objects import objects.game_object import vfx
class G101010RavenPerchedObject(objects.game_object.GameObject): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._vfx: vfx.PlayEffect = None self._timeout_handle: alarms.AlarmHandle = None self.vfx_slot_hash: int = objects.sims4.hash_util.hash32('_FX_') self.fx: str = 'crow_standing' self.loop_interval: float = 1 self.delay: float = 0
def alarm_callback(self, handle, alarm_data=None): self._vfx = vfx.PlayEffect(self, self.fx, self.vfx_slot_hash) self._vfx.start()
def on_add(self) -> None: super().on_add() self._timeout_handle = alarms.add_alarm( owner=self, time_span=clock.interval_in_real_seconds(float(self.delay)), callback=self.alarm_callback, repeating=True, repeating_time_span=clock.interval_in_real_seconds(float(self.loop_interval)) )
def on_remove(self) -> None: if self._vfx is not None: self._vfx.stop(True) self._vfx = None
if self._timeout_handle: alarms.cancel_alarm(self._timeout_handle) self._timeout_handle = None super().on_remove()
drive.google.com/file/d/1JPlVKoZ87uzlmUwL6IrOTPMJGPwSawDy/view?usp=sharing
|
|
|
Post by gauntlet101010 on Nov 15, 2020 11:37:50 GMT -5
Alright, that works better. If there a way to clear the effect immediately before placing in a new one? For most effects I don't think that would work, but with the bird just standing there I think it'd be alright.
Part of the timing problem is that the game lags a bit. I'm noticing that, even if there's a visible break in the loop duplicate ravens can still appear.
|
|
|
Post by andrew on Nov 15, 2020 11:49:15 GMT -5
You could try adding:
self._vfx.stop(immediate=True) in the alarm_callback right before self._vfx.start() to clear out the previous raven.
|
|
|
Post by gauntlet101010 on Nov 15, 2020 14:10:40 GMT -5
That doesn't clear the animation.
If there's no way to clear it maybe it's fine that there's some overlap.
|
|
|
Post by andrew on Nov 21, 2020 13:34:08 GMT -5
I am out of ideas if stopping it doesn't clear it. If there is no amount of time to delay that won't reliably prevent the overlap, maybe the only way to get rid of it is to edit the effect to make it into a looping effect. I don't really see anything in the EA python scripts that use VFX that gives the script any indication that the effect has finished.
|
|
|
Post by gauntlet101010 on Nov 29, 2020 18:10:31 GMT -5
Regardless, thanks for the help. If that's the best I got, then it's fine.
|
|