ID:2207711
 
Code:
obj
paths
New()
icon = null
proc
pathFind(mob/clone/C, mob/target)
if(height == 0)
return TRUE
if(neg == 0 || neg == null)
if(!(target.py > C.py + height))
return FALSE
else if(neg == 1)
if(!(target.py < C.py + -height))
return FALSE
else if(neg == -1)
if(!(target.py < C.py + -height) && !(target.py > C.py + height))
return FALSE
return TRUE
var/height = 16
var/maxHeight
var/neg
var/mapArea
icon = 'Paths.dmi'
jumpArea
icon_state = "jumpArea"
pathFind(mob/clone/C, mob/target)
if(..())
if(mapArea)
for(var/obj/paths/mapArea/A in target.inside())
if(A.mapArea != mapArea)
return 1
else
C.jump(1)
return -3
else
C.jump(1)
turnLeft
icon_state = "turnLeft"
pathFind(mob/clone/C, mob/target)
if(..())
if(mapArea)
for(var/obj/paths/mapArea/A in target.inside())
if(A.mapArea != mapArea)
return 1
else
C.dir = WEST
return -3
else
C.dir = WEST
return -3
turnRight
icon_state = "turnRight"
pathFind(mob/clone/C, mob/target)
if(..())
if(mapArea)
for(var/obj/paths/mapArea/A in target.inside())
if(A.mapArea != mapArea)
return 1
else
C.dir = EAST
return -3
else
C.dir = EAST
return -3
turnUp
icon_state = "turnUp"
pathFind(mob/clone/C, mob/target)
if(..())
if(mapArea)
for(var/obj/paths/mapArea/A in target.inside())
if(A.mapArea != mapArea)
return 1
else
C.climb(UP)
return -3
else
C.climb(UP)
noTurn
icon_state = "noTurn"
pathFind(mob/clone/C, mob/target)
if(..())
if(mapArea)
for(var/obj/paths/mapArea/A in target.inside())
if(A.mapArea != mapArea)
return 1
else
return -3
else
return -3
noClimb
icon_state = "noClimb"
pathFind(mob/clone/C, mob/target)
if(mapArea)
for(var/obj/paths/mapArea/A in target.inside())
if(A.mapArea != mapArea)
return 1
else
return -3
else
return -1
edgeForce
icon_state = "edgeForce"
pathFind(mob/clone/C, mob/target)
if(mapArea)
for(var/obj/paths/mapArea/A in target.inside())
if(A.mapArea != mapArea)
return 1
else
return -3
else
return -2
mapArea
mob
clone
projDense = 1
move_speed = 6
healthRegen = 0
chakraRegen = 0
staminaRegen = 0
var
damage
dname
mob/target
mob/owner
enemies
lastCheck
lifeTime = 1800
id
clone = 1
density = 0
decel = 100
set_state()
if(("STATE_WALLR" in effects) && !on_ground)
if(dir == EAST)
icon_state = "upright"
else
icon_state = "upleft"
else if(!on_ground)
icon_state = "jumping"
else
icon_state = "running"
jump(f)
if(world.time - lastJump > 5 && on_ground || f)
lastJump = world.time
vel_y = jump_speed
New(mob/own, mH, mA, icon/mI, dN, hR)
id = world.time
maxHealth = mH
health = mH
damage = mA
icon = mI
dname = dN
owner = own
dir = own.dir
target = own
team = own.team
set_pos(own.px, own.py, own.z)
if(hR)
healthRegen = 1
healthRegeneration = hR
Regeneration()
spawn(lifeTime)
del(src)
Del()
for(var/mob/M in world)
if(M.effects.Find("[dname]_[owner.charName]"))
M.effects.Remove("[dname]_[owner.charName]")
..()
action()
set waitfor = FALSE
set background = TRUE
var/X
for(var/mob/clone/C in inside())
vel_x = -vel_x
if(("STATE_WALL" in effects) || ("STATE_WALLR" in effects))
var/w
for(var/turf/walls/t in front(8))
if(t.wall)
w++
if(!w)
effects.Remove("STATE_WALL", "STATE_WALLR")
if(target.py > py + 8 || target.py < py - 8)
for(var/obj/paths/T in inside())
if(T.mapArea)
X = T.pathFind(src, target)
if(!(X == -3))
for(var/obj/paths/T in inside())
X = T.pathFind(src, target)
if(at_edge() && target.py > py + 16 && !(X == -1) && !(X == -3))
jump()
if(X == -2 && at_edge())
jump(1)
for(var/turf/walls/t in front(8))
if(on_ground && t.wall)
jump()
else if(t.wall && !(X == -1) && target.py > py)
climb(UP)
for(var/mob/projectiles/P in front(72))
jump()
move(dir)
if(!(X == -3) && target)
if(tDir(target))
dir = WEST
else
dir = EAST
if(world.time >= lastCheck)
lastCheck = world.time + 10
enemies = 0
var/tarView = 0
for(var/mob/M in obounds(src, 448))
if(target == M)
tarView++
for(var/mob/M in obounds(src, 448))
if(M.team != team && !M.dead)
enemies++
if(target == owner || !target || !tarView)
target = M
if(!enemies)
target = owner

slow_down()
if(!on_ground)
lastJump = world.time
bump(atom/a)
set waitfor = FALSE
set background = TRUE
if(a.player || istype(a, /mob/clone) && a.team != team)
if(!a:charName)
return
var/x = world.time
if(a.team == team)
return
if(a:effects.Find("[dname]_[owner.charName]"))
return
a:effects.Add("[dname]_[owner.charName]")
a:effects.Add("CLONE_[x]")
spawn(7)
if(a:effects.Find("CLONE_[x]"))
a:effects.Remove("CLONE_[x]")
a:effects.Remove("[dname]_[owner.charName]")
a:healthChange(-damage, "[owner.charName]'s [dname]", owner.charName)
knockback(src, a, 10, 6)
flick(pick("attack1", "attack2"), src)
else if(istype(a, /mob/clone) && a.team == team)
return
else if(a.projDense)
del(src)
can_bump(atom/a)
set waitfor = FALSE
set background = TRUE
if(isturf(a))
return a.density
else
if(a.wall)
if(team == a.team || !density)
return 0
else
return 1
if(a.clone)
return 1
return a.density


Problem description:
This code seems to be inefficient; either that, or BYOND simply can not handle the number of NPCs I want (50~, plus 40 players on at any time) at 60FPS.
This code is kind of incomprehensible. I'm not even clear on what it's trying to do. It's also loaded down with some really bad practices, like abuse of the : operator and some bizarre operators. Like this:

if(height == 0)
return TRUE
if(neg == 0 || neg == null)
if(!(target.py > C.py + height))
return FALSE
else if(neg == 1)
if(!(target.py < C.py + -height))
return FALSE
else if(neg == -1)
if(!(target.py < C.py + -height) && !(target.py > C.py + height))
return FALSE

Why use height == 0 when !height is quicker? Why use + -height when - height is more direct (and again, quicker)? Why check for neg == 0 || neg == null when !neg is, once again, quicker and easier? Why are you using ==1 and ==-1 when >0 and a simple else are better?

What the heck is with !(X == 1) instead of X != 1? Again you're using two operators where one is better.

Then we get into the weirdness with the way these procs behave. What is pathFind() supposed to do? Why is only py, never px, taken into account? Why does the default implementation of pathFind() return true or false values, but the overrides return 1 or -3?

Your operator oddities are wasting valuable CPU cycles, and if you have patterns like that all over the code, that's something you definitely need to fix. The logic being convoluted is probably not helping either. The more you can simplify things on each tick, the more performance you'll get.

So now the big-ticket things. What are those NPCs doing? Are they all moving around on their own? And more importantly, do they all have to be moving all the time? The less you can have going on during each tick, again the better performance you'll have.

At 40 players, you're going to want to look into how everything performs--even with simple things like chat and text output. Do some profiling, and see which procs are taking the most time. You may find some routines you can tighten up.

Finally, 60 FPS is an ambitious target, especially with 40 players. You may well be better off using client.fps to give a faster response time on the client but run the server at a lower rate, which 511 can do. At 60 FPS, the game basically has 16 ms to get everything done that it needs to on each tick, and that includes map output to all the players. That's not a lot, and when you include the activities of 40 players in that list, that takes up time too.