ID:1079064
 
(See the best response by Lugia319.)
Problem description: I was wondering if anyone could give me hints on making a system to check if the player is in Combat or not. The way I see the idea in my head is, you won't get in combat until someone hits you, and the effect lasts for about 3-4 seconds before the system indicates that the player isn't in combat.
I supposed the easiest solution is if you get hit, change a variable InCombat to 1 and call a function the sets it to zero after a while.
That depends on how your combat system is.
Yea, you can do that. It pretty much depends on your game and what this "in combat" indicator is for.
Lugia319 wrote:
I supposed the easiest solution is if you get hit, change a variable InCombat to 1 and call a function the sets it to zero after a while.

- I thought about that also, but I remember whenever I tried doing that in the past, the var always reset while in the fight because I gave it a 4 second delay. I want this system to be able to check if I'm still fighting or not for it won't reset the var until 4 seconds after the fight.

Jemai1 wrote:
Yea, you can do that. It pretty much depends on your game and what this "in combat" indicator is for.

- It's a health regeneration proc.
Best response
I don't fully know the mechanics of how you want InCombat to work so I made two solutions

/* Solution 1 - Absolute Solution. If you are attacked,
the timer goes up and will continue to go up each time you
are attacked */


mob
verb
Attack()
// You're attacking the mob
M.InCombat += 4 // Add 4 (seconds) to InCombat
M.CombatTimer()

proc
CombatTimer()
while(src.InCombat)
sleep(10)
src.InCombat --

/* Solution 2 - Reset Solution
In this solution, the timer is reset up attack*/


mob
verb
Attack()
// Combat stuff here
if(!InCombat)
M.InCombat = 1 ; M.CombatDelay = 4
M.CombatTimer()
else
M.CombatDelay = 4

proc
CombatTimer()
while(src.CombatDelay)
sleep(10)
src.CombatDelay --
InCombat = 0


In the first solution, upon each attack, 4 seconds of InCombat are added. The combat timer will keep getting ticks if you keep getting attacked.

In the second solution, there's a secondary CombatDelay variable which keeps track of the time you're in battle (in seconds). Since you're setting the CombatDelay upon each attack, it'll keep doing the while loop if you get attacked at say, CombatDelay = 2.

EDIT: Forgot my if() The if will prevent you from calling CombatTimer() more than once per CombatDelay = 1
The second solution looks pretty much what I was thinking, the first solution could be bad. Just imagine getting ganged up on by 5 people, that's a 20 second cool-down. Thanks Lugia. :D
The easiest way is to have a counter that increments and decrements naturally.
mob
var/tmp/incombat = 0 // you don't want to save this

proc
Combat(delay=40) // default delay is 40 ticks (4 secs)
incombat++
spawn(delay)
incombat--

Whenever you attack / take damage, call the Combat proc. The Combat proc will increment incombat and will negate that increment after the specified delay which is 4 secs by default.

The value of incombat does not really matter. You just have to determine if it is 0 or not. It will reset to 0 on time. Checking if your mob is in combat is as easy as
if(mob.incombat) // where mob is your mob
// in combat
else
// not in combat
Why not just have an incombat variable that get flipped to 1 when you attack, get attacked, or what have you, then calls a looping function or a function after each combat action that checks for any enemies within your "combat range". If it finds none, flip the variable back off.

I do something similar where the player accumulates threat during combat and a function checks after every action. If no enemies are found, your threat is cleared.
In response to Pyro_dragons
Pyro_dragons wrote:
Why not just have an incombat variable that get flipped to 1 when you attack, get attacked, or what have you, then calls a looping function or a function after each combat action that checks for any enemies within your "combat range". If it finds none, flip the variable back off.

You could do that, but it take a bit more processing. It's better to use a more fundamental variable, and take advantage of the way if(a) works...which is, true when a is any number other than zero.
The difference in processing is negligible. It doesn't take much to do a for(enemy in view(5)) or whatever the range is. Especially since you would just stop the for() loop after finding the first one. It's easier to just do a quick check for enemies and flip the flag based on what it finds, in my opinion.

You could do something like this every time something dies:
mob/proc
Clear_Combat()
var/enemy = 0
for(var/mob/Enemy/M in oview(src.combat_range, src))
enemy = 1
break
if(!enemy)
src.in_combat= 0
Well..

mob
var/list/attackers//make a container for mobs attacking
proc/Damage(n as num, m as mob)//Basic damage proc that should be included in every game with combat
hp-=min(hp,n)//remove the smaller out of the current hp and the damage dealt so the hp shouldn't go below 0
attackers+=m//Add the attacker to the container of attacking mobs for src
spawn(40) attackers-=m//after 4 seconds, remove the attacker from the container
//This may present a issue, because m could be deleted or removed within this time, that is easy enough to work around but I don't wish to go into depth on this
//Not to mention the multiples in the container
verb/Attack(mob/m as mob in oview(1))//basic attack verb to test
m.Damage(5,src)//run the damage proc on the selected mob
verb/CheckCombat()//simple verb to test if the in-combat setup works
if(attackers.len)//if there are attackers in the container
for(var/mob/m in attackers)//cycle through them
src<<"[m] is still attacking you!"//and alert the src to who it is still attacking them, which can be multiples, to which you will be alerted to each attacker
else src<<"You're not currently being attacked!"//If the container is empty, tell the src so.


Lugia does a pretty good job on a in-combat timer, my setup is more open-ended, to which it falls under my own project's set up, which I use the container for 'shared exp' on kills, based on attackers. Lugia's will work better than mine if you only intend to use it for that purpose, no reason not to have both other than the whole clutter issue.

However, let me just point some things out for Lugia:

mob/proc/CombatTimer()//Same proc as displayed in their post
if(CombatTimerOpen) return//Checks if the proc is already running and only runs if not
CombatTimerOpen=1//sets the variable so you know if the proc is active
while(InCombatTimer)//resume the proc as before
sleep(DeterminedTime)
InCombatTimer--
src<<"You are now out of combat!"
CombatTimerOpen=0//Reset the variable from earlier so you can call the proc again later
//Just so you don't have to assign it outside the proc, saving yourself the time of assign it everywhere you call the proc. Forgot to include this note when I first posted, lol.
If you don't like my first suggestion which keeps the number of combat interactions for the past 4 secs, here is another approach
mob
var/tmp/last_combat_time = -1.#INF

proc
Combat()
last_combat_time = world.time

InCombat(time=40)
return world.time >= last_combat_time + time

No sleep/spawn. No loops.

Good thing about it is that you can check if the mob is in combat for, say, 6 secs by calling mob.InCombat(60) which would return TRUE or FALSE.
In response to Pyro_dragons
Pyro_dragons wrote:
The difference in processing is negligible. It doesn't take much to do a for(enemy in view(5)) or whatever the range is.

Well in that case, it "doesn't take much" to scale an icon to 2x size, and change a range of rgb values to a different range of rgb values. But try doing it 4,000 times and you may have a different experience