ID:178750
 
Hey, everybody.

I'm having a little problem with this very, very, very basic battle engine I've written, and quite frankly, it's driving me crazy. I can't add any depth to this proc unless I can figure out why it doesn't always work. Here's the deal: every once in a while I get this error.

runtime error: Cannot read null.NPC
proc name: Fight (/mob/proc/Fight)
usr: Player (/mob)
src: the thug (/mob/newbierapist)
call stack:
the thug (/mob/newbierapist): Fight(null)
the thug (/mob/newbierapist): Fight(Player (/mob))
Player (/mob): Fight(the thug (/mob/newbierapist))

No idea why. I'd say that it happens maybe once in every 25 times I select the attack option, but if it happens at all, I'd say it's too much. If anybody here could take a look at the code for my Fight proc and just take a stab at what might be generating that error, I would really appreciate it. Personally, I can't see how it could be the code at this point, and it does seem to me as though I usually get this error if I try to execute the fight command very shortly after logging in (say, within 5 seconds). But take a look and tell me if there's something I'm missing.

Fight(mob/M)
var/sleeptime = round(2000/src.quickness)
if (sleeptime > 55) sleeptime = 55
else if (sleeptime < 20) sleeptime = 20
do
if ((M.NPC) && (M.warmode == 0))
M.warmode = 1
M.enemy = usr
sleep(10)
spawn M.Fight(M.enemy)
else if (src.NPC)
var/mob/topscorer
topscorer = MostDamage(src)
if (topscorer != src.enemy)
src.enemy = topscorer
Fight(src.enemy)
return

if (!src.CanHit(M))
usr << {"

"}
sleep(sleeptime)
else
var/damage = rand(src.low_dam,src.high_dam)

if (M.NPC)
AddDamage(M,damage)
M.HP -= damage
usr << {"

"}
sleep(sleeptime)
while (src.warmode == 1)

Could it be an issue with the Dream Maker client, itself? If so, I could probably write a routine to prevent the user from attacking anything for, say, five seconds after login. If you have any questions about what's going on in that code, just ask me. I realise it's pretty darn sloppy. I'm sort of a "get it working and then clean it up" kind of guy.

Thanks!

-Justin

</20>
TheWatcherUatu wrote:
runtime error: Cannot read null.NPC
proc name: Fight (/mob/proc/Fight)
usr: Player (/mob)
src: the thug (/mob/newbierapist)
call stack:
the thug (/mob/newbierapist): Fight(null)
the thug (/mob/newbierapist): Fight(Player (/mob))
Player (/mob): Fight(the thug (/mob/newbierapist))

Fight(mob/M)
var/sleeptime = round(2000/src.quickness)
if (sleeptime > 55) sleeptime = 55
else if (sleeptime < 20) sleeptime = 20
do
if ((M.NPC) && (M.warmode == 0))
M.warmode = 1
M.enemy = usr
sleep(10)
spawn M.Fight(M.enemy)
else if (src.NPC)
var/mob/topscorer
topscorer = MostDamage(src)
if (topscorer != src.enemy)
src.enemy = topscorer
Fight(src.enemy)
return

if (!src.CanHit(M))
usr << {"

<font color="grey" [src.charname] misses [M.charname].</font>"}

sleep(sleeptime)
else
var/damage = rand(src.low_dam,src.high_dam)

if (M.NPC)
AddDamage(M,damage)
M.HP -= damage
usr << {"

<font color="grey" [src.charname] hits [M.charname] for [damage] points of damage.</font>"}

sleep(sleeptime)
while (src.warmode == 1)


From the error output...I can tell that the error is coming up when the thug calls the fight proc for the second time... The error output call stack works from the bottom up... So it says that the player called the proc...then the thug called it... And then the thug called it again...and that's where the error is coming from...

And the error is saying that the M argument in the Fight(mob/M) proc is null when the proc is called that second time...

So we take a look at the code and narrow the problem down to this section:

var/mob/topscorer
topscorer = MostDamage(src)
if (topscorer != src.enemy)
src.enemy = topscorer
Fight(src.enemy)
return

...since this is the section of code that is called when an NPC uses this proc... More specifically...since we know that the problem comes from the second call to Fight()...we know that the problem is in the line I've marked in bold...

So what does that mean? It means that src.enemy is null at this point... Or at least not a mob reference... So when Fight(src.enemy) is being used to call Fight(mob/M)...src.enemy isn't being successfully passed (for whatever reason) to mob/M...and it's being defaulted to null and giving you the error...

I'd say that the problem is in the MostDamage(src) proc... topscorer is being set to whatever that proc returns...and then that is being set to src.enemy... So whatever that proc is returning is not compatible with mob/M...

Perhaps this is enough info for you to fix the problem... If not...I think posting your code for the MostDamage() proc will let us give you the answer you're looking for...

Sorry if most of this sounds too complicated... It makes sense in my own head...but that may only be because I know enough to understand what's going on... Although your code seems knowledgeable enough that I think you'll understand what I'm saying... So hopefully I've been a help...lol

In response to SuperSaiyanGokuX
Yeah. I was a Computer Science major for a few years in college, so I know what you're saying. Thanks for the push, by the way. You said exactly what I need to hear to get me to look closer at my function in the right way, even though it still took me a few minutes to figure out how to remedy the problem.

MostDamage is a procedure that checks to see who, of the users who have attacked the NPC, has done the most damage. In addition to using it for simple AI (MOB goes after person hurting it the most), I also use it (or will be using it) for assigning the kill and experience (even though you get experience just for fighting, you get a bonus if you actually get the kill). In case you're curious, I'll let you know where my code was falling apart.

Each NPC carries a list of users that have attacked it, along with their respective damage amounts. That's accomplished through the AddDamage routine that I call after a damage amount is determined. It looks like this:

AddDamage(mob/M,D)
var/p
var/found = 0
for (p in M.attackedby)
if (usr == p)
M.attackedby[p] += D
found = 1
if (found == 0)
M.attackedby.Add(usr)
M.attackedby[usr] = D

Then the MostDamage function searches through that list looking for the biggest threat to the NPC.

MostDamage(mob/M)
var/p
var/mob/topscorer
var/topscore = 0 //Needed to keep track of who did the most damage to MOB
for (p in M.attackedby)
if (M.attackedby[p] > topscore)
topscorer = p
topscore = M.attackedby[p]
world << "[topscorer.charname] did the most damage"
return topscorer

I *think* both of those functions work the way they're supposed to. I haven't had the chance to extensively test them, but logically they seem okay to me. The problem, however, was that when you attacked the NPC and you missed on your first swing, your name never got added to the NPC's $#!t list, so obviously when he looked for you, he came up with null. The way I remedied that was by simpling including the AddDamage call in the event of a miss, as well, but it took me a while to figure out why even that wasn't working. Turns out you need to pass in *some* value, or else your user mob won't turn up when MostDamage loops through the list. So if you pass in 1, as in AddDamage(M,1), that does the trick. Gives you a point even for missing, but I can live with that.

Again, thanks for the help. I really appreciate it. I just found this program on Saturday, so I'm slowly muddling through learning the syntax and rules and everything. I'll probably be posting every once in a while. :)

Edit: Yeah, I'm a moron. The reason passing 0 in for a miss didn't work is because I initialise topscore to 0 in the MostDamage proc and I only check to see if the mob's damage amount is greater than top score rather than greater than or equal to.

-Justin
In response to TheWatcherUatu
You're very much welcome... I'm glad that I actually helped...lol

I figured you were fairly experienced with this sort of thing... The code you posted seemed to suggest that you already knew a decent amount of how things work...