ID:159546
 
I've been struggling with my flame thrower. I switched a number of my other explosion effects over to flick with much success. I'm trying to get my 'flame' for my 'flame thrower' switched over to something other than an obj but I'm at a loss right now.

The only reason I want to switch away from using an obj is because the animation doesn't always work.

My attacking unit has an attack tick which calls the FlameThrower() function once every second or so.

FlameThrower()
var/obj/FlameThrower/ft = src
if(ft.oFlame == null)
ft.oFlame = new/obj/flame
ft.oFlame.dir = get_dir(ft, m)
ft.oFlame.loc = m.loc


The above block of code works 95% of the time as long as the FlameThrower changes directions between bursts. If the direction remains the same, the animation does not appear.

Any suggestions?
Kaioken, if you read this post, I appreciate your efforts with that previous mess. I just had a ton of issues to work through and your comments helped me do that. I had typed out several long replies trying to explain the whole thing but I knew it was just too convoluted and unfair to post. So thanks for trying.

ts
I have a few questions to ask.

First, why do you have to make a new variable if you could simply use src?

Second, what is m?

Lastly, when and how are you calling the FlameThrower() proc?
If objs arbitrarily refuse to display or something like that, there's a BYOND bug here. But I wouldn't know if that's quite the case. I presume you have things like layer and getting rid of the obj later done with. Also, we don't get to see what 'm' is here. It may be that m.loc is invalid (null or somehow the wrong place) therefore the obj isn't placed where it should and isn't seen.
I'd also write that code slightly differently (though I'd also try to use overlays for effects); it won't make an earthbreaking difference but it's a little improvement.
FlameThrower()
if(!src.oFlame)
src.oFlame = new /obj/flame(m.loc)
src.oFlame.dir = get_dir(src, m)
In response to Kaioken
    flame
{
icon='Icon.dmi'
icon_state = "flame"
density = 0
layer = FLY_LAYER
mouse_opacity = 0
animate_movement = NO_STEPS
}

FlameThrower(var/mob/monster/m)
var/obj/FlameThrower/ft = src
if(!src.oFlame)
src.oFlame = new /obj/flame(m.loc)
src.oFlame.dir = get_dir(src, m)
ft.oFlame.loc = m.loc


I updated the function to show where m comes from. Sorry, missed that.

Example scenario.
FlameThrower is in corner of map and surrounded by two walls. It's only space available for attacking is the one diagonal space toward the middle. Monster mobs come through this one space. The flame appears for the first two attacks and then stops rendering for future attacks. Mobs can either die or live and it doesn't make a difference whether the flame appears or not. The flame reappears now and again every so often which I haven't been able to determine the cause.

[edit]
forgot a few lines
In response to Tsfreaks
if(!src.oFlame)
src.oFlame = new /obj/flame(m.loc)


When the flame is deleted, it's probably still referenced as the flamethrowers' oFlame var, so it fails to create a new flame? I don't know :/

When deleting the flame, manually make the oFlame var null.
In response to Warlord23
I don't delete the flame though. I tried and it makes no difference in the behavior.
In response to Tsfreaks
Does the flame still cause damage, even when it has no icon? Perhaps there is no movement state for the flame?
In response to Tsfreaks
Can you post the rest of the related code (like the proc that calls FlameThrower() and when that entire chain is invoked)? How are you managing/what are you doing with oFlame? I can't find anything wrong with just what you've posted, and the fault may be in how or how often (etc) it's being called, or something else. If you post enough of the related code readers could even test it on their own to see the problem if all else fails.
In response to Warlord23
Other than garbage collecting objects, DM also always automatically clears out references to an object when it's deleted and sets them to null on its own, so that's never an issue. I wonder what he's doing with the oFlame var though; if he's not using it to later delete the obj, what else does he do with that var?
In response to Kaioken
This is a function of the object which shoots the flame. I'm ok with the flame overriding the previous flame (stopping it) in order to turn and shoot at something else.

BeOnAlert()
.. clear target list
.. Locate mobs around me and store in target list
.. If I have a target already skip next step
.... pick random target from list based on distance
if target isn't null
.... set direction of attacker toward target
Attack(target)
spawn(10)
BeOnAlert()


This is a function of the object which shoots the flame
Attack(var/mob/monster/m)
switch(name)
if("something else")
...
if("Flame Thrower")
var/obj/FlameThrower/ft = src // flame thrower has special oFlame obj which other attackers don't have.
if(ft.oFlame == null)
ft.oFlame = new/obj/flame
ft.oFlame.dir = get_dir(ft, m)
ft.oFlame.loc = m.loc
m.MobDamageSelf(attackDamage)
In response to Tsfreaks
Tsfreaks wrote:

This is a function of the object which shoots the flame
> Attack(var/mob/monster/m)
> switch(name)
> if("something else")
> ...
> if("Flame Thrower")
> var/obj/FlameThrower/ft = src // flame thrower has special oFlame obj which other attackers don't have.
> if(ft.oFlame == null)
> ft.oFlame = new/obj/flame
> ft.oFlame.dir = get_dir(ft, m)
> ft.oFlame.loc = m.loc
> m.MobDamageSelf(attackDamage)
>


Hmm....
You're setting the flamethrower as the flame itself. In this case, the flamethrower is no longer a flamethrower because it isn't throwing flames, but it's throwing itself, acting as a flame.

Sorry if I confused you (sure confused me).

Why dont you make a new /obj/FlameBurst and create a new one if oFlame var is null, instead of using the flamethrower itself? That looks silly. Maybe if you showed more code we could help you more.
In response to Tsfreaks
I think I see now why you've needed to retypecast the src, but that's because you're not going about it too well. You should manage special objects' functionality under their own proc nodes, where src will also be defined as of that node's type.
Identifying objects by their name is pretty bad and not so robust; you should identify them by type or derived type instead (look up 'type' and 'istype()' for info on that). But here, you should just use an object oriented approach to it, and not have a bunch of if()s (or a switch()) checking what object it is at all, but override the proc for child types that need to do special stuff. Here's an example:
obj/trap //or attacker, or whatever
var/damage = 1
proc/BeOnAlert()
...
if(found target) src.Attack(target)
proc/Attack(mob/creature/target)
target.TakeDamage(src.damage) //default action is give HP dmg
flamethrower_trap
var/obj/flame/flame
Attack(mob/creature/M) //overriding the proc
if(!src.flame)
src.flame = new(M.loc)
..() //do the parent action (hp dmg)
//an example with a different trap
projectile_trap
var/projectile_type
arrow_trap/projectile_type = /obj/projectile/arrow
Attack(trg)
var/obj/projectile/P = new src.projectile_type(src.loc)
walk(P,get_dir(P,trg),2)


I'm still not sure what you could have that makes an obj not appear other than hitting the obj limit or overriding some level of New() that causes issues, it could also be that the code creating the obj is never reached. It may be fixed by an improved design, but if not I suggest adding some debugging outputs to see if the obj is being created right. Something like this:
world << "creating flame"
(Line where new /obj/flame() is)
world << "flame created ([flame]) at ([flame.loc])"

obj/flame/New(loc)
world << "New([loc]) called on flame"
//..() //see if keeping this commented out helps, otherwise uncomment
In response to Warlord23
Uh, as far as I can see that's precisely what he's doing, other than the obj being /obj/flame and not /obj/FlameBurst. :P
In response to Kaioken
Indeed. Just noticed.
In response to Kaioken
Ah, well that's a clever way to do things. I like it and it's clearly a better way to go.

As for the debugging. Here are the results.

creating flame
New() called on flame
flame created (the flame) at ()


Even though the flame thrower attacked a couple dozen times in the same direction. The visual result was two flames and then nothing. When I allowed the mobs to pass at a cardinal direction, the flame was visible and continued to be visible as the flame thrower would shoot from cardinal to diagonal as the mobs passed by.

There was no new logs after making that change.
In response to Kaioken
An additional note.
If I remove the null check and just create the obj each time, the problem goes away. The flame appears every time but I end up with an obj leak.

var/obj/unit/FlameTower/ft = src;
ft.oFlame = new/obj/flame
ft.oFlame.dir = get_dir(ft, m);
ft.oFlame.loc = m.loc;
In response to Tsfreaks
Does the victim still receive damage, even though the flame has no icon?
In response to Ruben7
Yes. They receive the damage and death checks after the animation.
In response to Tsfreaks
Are you passing the first argument in new()'s parentheses? I'm guessing not, but that screws with the output since New() isn't given any loc, and the object doesn't have a loc by the time it's called either. Don't set the loc manually with the = operator after creating the object with new() - use new()'s loc arg, call new(Location) and it will be put there.
If you already did this, then you can safely say the location you're using is null if that's the output you're getting.
In response to Kaioken
I found that the flame really only animates once. I had a an extra frame in my diagonal animation which made it look like it was shooting twice.

My flame by the way is a single animation which reverses. Not sure if that matters but I don't think so.

What your suggesting is based on me leaving out the null check which means I would create the object every time it shoots. How do I clean the obj up because it increments the games obj count by one for each burst. By the end of my game, I would have thousands of flame objs.

ts
Page: 1 2