ID:138796
 
Code:
/obj/Attack
icon='hud.dmi'
icon_state="attack_up"
layer=MOB_LAYER + 20
New(client/C)
screen_loc="1,1"
C.screen+=src
Click()
usr:Attack()
MouseDown()
icon_state="attack_down"
MouseUp()
icon_state="attack_up"

/mob/verb/Attack(mob/M as mob in oview(1))
var/damage=usr.str - M.def
if(M.hp >0)
if(damage<=0)
usr<<"[usr] missed the attack by a mile!"
else
M.hp-=damage
usr<<"[usr] attacked [M] for [damage] damage!"
M:deathcheck()


Problem description: I have a verb called Attack() and i want it to be called when i click a button on my HUD. When compiling i dont get any errors, but when i run the game and click the HUD button i get "runtime error: Cannot read null.def
verb name: Attack (/mob/verb/Attack)
usr: Baltraven (/mob)
src: Baltraven (/mob)
call stack:
Baltraven (/mob): Attack(null)
Attack (/obj/Attack): Click(null, "mapwindow.map", "icon-x=21;icon-y=7;left=1;scre...")
"

when i use the verb from the commands box, it works fine.

It's because you aren't specifying the M argument in the verb's definition. Oh, and since you are using a HUD, I recommend making Attack() a procedure, not a verb. In which case you just need to do Attack(mob/M), and then calculate what M is before calling it.
In response to Albro1 (#1)
So i would need to change Attack into a proc instead of a verb. But then how would i call on it through the HUD object?
In response to Baltraven (#2)
usr.Attack(). The same way you are now.
In response to Albro1 (#3)
Ok, well i have changed the Attack() into a proc instead of a verb, but while playing i still get the run time error. May i ask what you mean when u say i haven't specified the M arguement? I have defined it as a mob in oview(1).
In response to Baltraven (#4)
You are just calling Attack(). In the definition, you have Attack(mob/M). You then use M.def in the proc. Since you don't tell it what M is when you call it, it doesn't know what M's def is.
In response to Albro1 (#5)
Ok. Instead, i used

obj/Attack
icon='hud.dmi'
icon_state="attack_up"
layer=MOB_LAYER + 20
New(client/C)
screen_loc="1,1"
C.screen+=src
Click()
usr.Attack(mob/M as mob in oview(1))
MouseDown()
icon_state="attack_down"
MouseUp()
icon_state="attack_up"


But it gives me errors:
World2.dm:152:error: mob: undefined var
World2.dm:152:error: M: undefined var
World2.dm:152:warning: 1: statement has no effect
World2.dm:152:warning: as: unused label


I don't know why it has a problem here, even when i tell it to call Attack(mob/M) instead of Attack(). It should now be able to identify M.def because it knows M as a mob, but it gives me errors now.
In response to Baltraven (#6)
You give it mob/M, but that's just a blank, undefined mob.

obj/Attack
icon='hud.dmi'
icon_state="attack_up"
layer=MOB_LAYER + 20
New(client/C)
screen_loc="1,1"
C.screen+=src
Click()
var/mob/m
for(var/mob/M in oview(1))
if(M) m = M
if(m) usr.Attack(m)
MouseDown()
icon_state="attack_down"
MouseUp()
icon_state="attack_up"
mob
proc/Attack(mob/m)
if(m)
//do stuff
In response to Albro1 (#7)
Thanks for your help. I solved the runtime error, but now when i call the Attack procedure, it seems as if it is using me as the M. I have this:
mob/proc/Attack(mob/M as mob in oview(1))
var/damage=usr.str - M.def
if(M.hp >0)
if(damage<=0)
usr<<"[usr] missed the attack by a mile!"
else
M.hp-=damage
usr<<"[usr] attacked [M] for [damage] damage!"
M:deathcheck()


obj/Attack
icon='hud.dmi'
icon_state="attack_up"
layer=MOB_LAYER + 20
New(client/C)
screen_loc="1,1"
C.screen+=src
Click()
for(var/mob/M in oview(1))
if(M) usr.Attack(M)
MouseDown()
icon_state="attack_down"
MouseUp()
icon_state="attack_up"


When i attack, it attacks the mob and gives me the "[usr] attacked [M] for [damage] damage!" message, but it also gives me the "[usr] missed the attack by a mile!" message. This means that the Attack proc is attacking the mob, and is attacking myself. Why would it be doing that?
In response to Albro1 (#7)
The if(M) check really isn't necessary. And it could always be shortened this way:

obj/Attack
icon='hud.dmi'
icon_state="attack_up"
layer=MOB_LAYER + 20
New(client/C)
screen_loc="1,1"
C.screen+=src
Click()
var/mob/m
for(var/mob/M in oview(1))
usr.Attack(M)
break
MouseDown()
icon_state="attack_down"
MouseUp()
icon_state="attack_up"
mob
proc/Attack(mob/m)
if(m)
//do stuff
In response to ExPixel (#9)
That gives me a warning saying /m is defined but not used. I think u may have messed up there. The problem now is that when i call the Attack proc by clicking the Attack obj, it uses my own character as the mob AS WELL as the mob im attacking (see my previous post). Why would it be doing that?
In response to Baltraven (#10)
The way to fix that is pretty damn obvious...remove the definition of m.
In response to ExPixel (#11)
I know that... I removed the definition of m. But that still doesnt stop the game from attacking me as the mob as well as the mob i want to attack. heres the code:

obj/Attack
icon='hud.dmi'
icon_state="attack_up"
layer=MOB_LAYER + 20
New(client/C)
screen_loc="1,1"
C.screen+=src
Click()
for(var/mob/M as mob in oview(1))
usr.Attack(M)
MouseDown()
icon_state="attack_down"
MouseUp()
icon_state="attack_up"


mob/proc/Attack(mob/M as mob in oview(1))
var/damage=usr.str - M.def
if(M.hp >0)
if(damage<=0)
usr<<"[usr] missed the attack by a mile!"
else
M.hp-=damage
usr<<"[usr] attacked [M] for [damage] damage!"
M:deathcheck()


It gives me the message that i missed the attack by a mile (meaning im attacking myself), AND it gives me the message showing i successfully attacked M for x damage. It uses two different mobs as M (me, and the treemonster mob i want to attack) where i only want to use the latter as the mob i want to attack.
In response to Baltraven (#12)
obj/Attack
icon='hud.dmi'
icon_state="attack_up"
layer=MOB_LAYER + 20
New(client/C)
screen_loc="1,1"
C.screen+=src
Click()
for(var/mob/M in get_step(usr, usr.dir))// So it only attacks the mobs directly in front of you.
if(M == usr) continue // Don't keep going if it's the user.
usr.Attack(M)
break //So it stops after attacking one mob. You can remove this if you don't want that.
MouseDown()
icon_state="attack_down"
MouseUp()
icon_state="attack_up"


mob/proc/Attack(mob/M as mob in oview(1))
var/damage=usr.str - M.def
if(M.hp >0)
if(damage<=0)
usr<<"[usr] missed the attack by a mile!"
else
M.hp-=damage
usr<<"[usr] attacked [M] for [damage] damage!"
M:deathcheck()
In response to Baltraven (#12)
To elaborate on ExPixel's post, so that you know what is happening instead of just being given an answer, you used oview(1). oview() will register you as well. You either needed to use EP's way(Which is better, because it attacks someone in front of you, not behind, beside, OR in front of you), or do a safety check to make sure M is not you.
In response to ExPixel (#13)
Thanks alot ExPixel! You solved my problem. So instead of using mob/M as mob in oview(1) i should used M in get_step(usr,usr.dir). This is better anyway since it would still work whether the mob is an AI mob or a player controlled mob. Thanks alot, ive been stuck on this for longer than i think i should have been!
In response to Albro1 (#14)
Isn't ExPixel's solution doing both? Checking that M is not usr and using get_step instead of oview().
In response to Baltraven (#16)
I gave you two suggestions, stating that ExPixel's way was better. To answer your question directly, you needed to check to make sure M was not you. ExPixel went above and beyond and gave you the way to attack ONLY someone in front of you.
In response to Albro1 (#17)
OK. I understood you the first time... I was just saying that he made sure M was not me by the if(M==usr) and he made sure i can only attack in front by mob/M in get_step(usr, usr.dir). I am still a beginner and am just making sure that was how he did those things.