ID:2112024
 
(See the best response by Lummox JR.)
Hey, I'm not exactly sure how to explain this all that well in detail but here it goes.

I'm looking to add a delay to certain actions within my code so it doesn't repeat.

So my death check method appends experience to the player... If you spam click the command in the panel it will appear as many times as you can click it in 3 seconds (Since I'm using sleep(3) for all obs so I can append death animations) It also still deals damage when the npc is dead

TLDR; Need to figure out a way to have a delay in this proc so players can only receive xp once & disallow damage to keep being dealt.

http://prntscr.com/bpo0jm

Code:
    proc
DeathCheck()
if (src.HP <= 0)
usr << "You killed [src] and gained [src.expAmount] experience!"
src.icon_state = "death"
sleep(30)
experience += expAmount
del(src)


    verb
Attack(mob/M as mob in oview(1))
usr << "You attack [M]!"
var/damage = usr.strength - M.defence
if(damage <= 0)
usr << "[M] easily dodges your attack!"
else
M.HP -= damage
usr << "You deal [damage] damage!"
M:DeathCheck()
Best response
First off, usr has no place in a proc. You should send the killer as an argument to your DeathCheck() proc.

All you really need here is a simple var that says whether the mob is dead or not.

mob
var/deadm

proc/DeathCheck(mob/killer)
if(dead || HP > 0) return
dead = 1
killer << "You killed [src] and gained [expAmount] experience!"
icon_state = "death"
if(killer) killer.experience += expAmount
spawn(30) del(src)

When you call DeathCheck(), be sure to send the killer as an argument.

Eventually, you may find you've expanded the possible states beyond just death, to include unconsciousness, swimming, flying, etc. If so, you may want to look into using a single state var that has different states as bitflags. But that's for later consideration.
In response to Lummox JR
Lummox JR wrote:
First off, usr has no place in a proc. You should send the killer as an argument to your DeathCheck() proc.

All you really need here is a simple var that says whether the mob is dead or not.

mob
> var/deadm
>
> proc/DeathCheck(mob/killer)
> if(dead || HP > 0) return
> dead = 1
> killer << "You killed [src] and gained [expAmount] experience!"
> icon_state = "death"
> if(killer) killer.experience += expAmount
> spawn(30) del(src)

When you call DeathCheck(), be sure to send the killer as an argument.

Eventually, you may find you've expanded the possible states beyond just death, to include unconsciousness, swimming, flying, etc. If so, you may want to look into using a single state var that has different states as bitflags. But that's for later consideration.

Ah, I see. Thanks!

I just realized I was sending the output message before the chunk of code with the sleep delay. Using logic and your advice this is my code now: Everything seems to be working as it should.
    proc
DeathCheck(mob/killer)
if (src.HP <= 0)
src.icon_state = "death"
spawn(30)
if(killer)
killer.experience += expAmount
killer << "You killed [src] and gained [src.expAmount] experience!"
del(src)

verb
Attack(mob/victim as mob in oview(1))
if (victim.HP <= 0) return
var/damage = usr.strength - victim.defence
if(damage <= 0)
usr << "[victim] easily dodges your attack!"
else
victim.HP -= damage
usr << "You deal [damage] damage on [victim]!"
victim:DeathCheck()


If you have any more suggestions on how to improve this chunk of code I appreciate it.

One thing I was curious about is setting variables specific to the player. So for example just having

var/strength in the mob tree so that all mobs have it. What I can't grasp is how to assign a base level for players only.

I'm probably overthinking it as I always do but this was my idea:

mob
Player
var/mob/strength = 10;

a) So the player references that but since the value for the player is being set to 10 how is the player going to progress from it?

b) Would usr.strength reference the value of strength in your save file or the value 10 we set in the code?

c) How would I make a way to have sole player variables apart from npc ones? What I mean by this is like if I have a woodcutting skill, how would I be able to tell the server that this variable can only be used by a player?

mob
Player
var
woodcutting = 1
strength = 10
attack = 1
defence = 12


That's my idea but I have no idea how the server will be referencing it. Kind of like the attack verb for mobs. I know it works because it is in the mob tree so mobs get the Attack command.

Sorry I'm rambling but if you can decipher anything that I said. thanks lol

I just want to know what I'm adding before I do something and sometimes the reference/docs are unclear (to me at least)
In response to Nirvana1994
Nirvana1994 wrote:
One thing I was curious about is setting variables specific to the player. So for example just having

var/strength in the mob tree so that all mobs have it. What I can't grasp is how to assign a base level for players only.

I'm probably overthinking it as I always do but this was my idea:

mob
Player
var/mob/strength = 10;

IMO, I'd make that a var for all mobs, not just players. Odds are it applies to monsters, and for NPCs you can just ignore it.

a) So the player references that but since the value for the player is being set to 10 how is the player going to progress from it?

I'm going to throw two important terms at you here: Prototype, and instance. A prototype is where you define a type of object, such as /obj/item or /mob/player; it's where you setup vars and give them default values, and where you add procs. An instance is an actual object that's been created from a prototype. The prototype is like a blueprint; the object you make with it is an instance, and it's said to be instantiated.

If the prototype has a strength var, and it defaults to 10, then any new mobs you make with that prototype will have a strength of 10 to start with. But you can modify that mob's strength, and it won't impact any other mobs, because you're changing the instance and not the prototype.

b) Would usr.strength reference the value of strength in your save file or the value 10 we set in the code?

Well first, don't throw usr around willy-nilly; it's only really valid in verbs.

As per the above point about prototype vs. instance, when you reference a mob's strength var, you're dealing with that particular instance. (Savefiles are actually a separate thing altogether.)

c) How would I make a way to have sole player variables apart from npc ones? What I mean by this is like if I have a woodcutting skill, how would I be able to tell the server that this variable can only be used by a player?

Well, you could have different type branches, so for instance you might have /mob/player, /mob/monster, and /mob/NPC. All three of them are derived from /mob, their parent, but they don't share any other vars or procs.

In practice, you probably don't want to separate very much; it's easier to keep as much as you can under /mob. NPCs simply aren't going to go wandering off on their own and chopping trees without being told to, so having a woodcutting skill var doesn't matter. (Vars don't actually take up any memory for an instance until you make a change from their default value. Each instance has a list of vars that have been changed, and if you try to lookup a var that isn't in that list, it'll look at the prototype.)

Here's an idea of a basic RPG mob framework:

mob
var/HP=1, maxHP=1
var/XP=0, nextXP=10
var/level=1
var/dead

proc/TakeDamage(amount, mob/attacker)
if(dead) return
HP = max(min(HP-amount, maxHP), 0)
if(HP <= 0)
dead = 1
Die(attacker)

proc/Die(mob/attacker)
// simple death proc that just dies
del(src)

proc/AddXP(amount)
XP += amount
while(XP >= nextXP)
++level
XP -= nextXP
nextXP = round(nextXP * 1.5, 1)

You could make modifications for subtypes from there. For instance, /mob/NPC/TakeDamage() could be changed to do nothing so that NPCs can't die.
In response to Lummox JR
Very helpful thanks for taking the time to answer my questions and give examples, I appreciate it.

My only follow up to that is what this line of code is doing:

 HP = max(min(HP-amount, maxHP), 0)


And is using usr fine in a proc if it's only to output a message?

usr << "You leveled up! You are now Level [level]."
In response to Nirvana1994
Nirvana1994 wrote:
Very helpful thanks for taking the time to answer my questions and give examples, I appreciate it.

My only follow up to that is what this line of code is doing:

HP = max(min(HP-amount, maxHP), 0)


You could simply use HP -= amount, but there are two potential problems: If the HP is displayed anywhere, like for instance if this is your mob, you don't want to see a negative value; hence the max(...,0) part. And if you were to impart negative damage, to heal, you don't want to go over the maxHP value, hence the min(...,maxHP) part. They're combined together so that the result of the subtraction is clamped between 0 and maxHP.

And is using usr fine in a proc if it's only to output a message?

usr << "You leveled up! You are now Level [level]."

No, because usr has no place in a proc; it's really only relevant to verbs.

When you do any command, usr is set to your mob when the verb is called. If that verb calls any procs, usr gets passed along as a sort of hidden argument. However, there are many procs that a verb might call that might also be called by something other than a verb. For instance, when you move, usr gets passed along to Move() and therefore also to procs like Enter() and such. But if you set an obj moving via walk(), it will call Move() and such but without usr, because that movement was initiated by code rather than by a user action.

For this reason, even though procs inherit the value of usr from the verb (if any) that called them, usr is considered unreliable. It's a frequent source of difficult-to-diagnose runtime errors and logic errors, because quite often the code makes assumptions about usr that aren't valid.

For a level-up message, the check for leveling up should only be done in a proc that belongs to the winner; some might have a proc called LevelUp(), or in the example code I used AddXP(). In those procs, the winner is src and the loser of the battle isn't relevant (so it isn't sent as an argument), so src would be the appropriate target for the level-up message.
In response to Lummox JR
Ah, makes sense. thank you