ID:2339283
 
Code:
mob
var
Beam/myBeam
techs[0]
target

Beam
parent_type=/obj/
layer = 300
icon='Beams.dmi'

var
mob/owner
beam_image
speed
dist
ki_damage
bType
objName
bumping
parts[0]

Techs

Kamehameha
icon_state="kame_head"
beam_image="kame"
objName = "Kamehameha Wave"
bType = "Big"
ki_damage = 10
speed=0.75
dist=35

New()
..()
newBeam(usr, src.objName, src.bType, src.beam_image, src.ki_damage, src.speed, src.dist)

Bump(atom/a)
if(istype(a,/mob/))
var/mob/m=a
restart
if(src.bumping != 5)
owner.target = m.charName
m.collidingBeam = 1
m.damage(src.ki_damage)
m.stunned = 1
m.running = 0
m.vel_x = 0
m.objName = src.objName
if(bType == "Big")
if(owner.dir == EAST)
m.dir = WEST
m:vel_x = 18
else
if(owner.dir == WEST)
m.dir = EAST
m:vel_x = -18
src.bumping ++
spawn(10)
goto restart
return
else
if(src.bumping == 5)
owner.target = null
m.collidingBeam = 0
src.clearBeam()

if(istype(a,/turf/))
src.clearBeam()


Move(NewLoc)
if(locate(/Beam/tail/) in src.loc)
..()
else
var/Beam/middle/_middle=new(src.loc)
_middle.dir=src.dir
_middle.owner=src.owner
_middle.icon_state="[src.beam_image]_[_middle.icon_state]"
src.parts+=_middle

if(dir == EAST)
_middle.pixel_x = 70
else
if(dir == WEST)
_middle.pixel_x = -100
..()

proc/clearBeam()
for(var/mob/m in world)
if(owner.target == m.charName)
m.collidingBeam = 0
owner.target = null
src.owner.myBeam=null
for(var/Beam/b in src.parts)
del(b)
del(src)

head
density=1
icon_state="head"
proc/shoot()
var/dense[0]
for(var/atom/a in src.loc)
if(a.density && a != src) dense+=a
if(dense.len)
for(var/atom/a in dense) src.Bump(a)
while(src.dist--)
step(src, src.dir)
sleep(src.speed)
src.clearBeam()

New()
..()
if(usr.dir == EAST)
pixel_x = 50
pixel_y = -40
else
if(usr.dir == WEST)
pixel_x = -100
pixel_y = -40

tail
icon_state="tail"
New()
..()
if(usr.dir == EAST)
pixel_x = 7
pixel_y = -40
else
if(usr.dir == WEST)
pixel_x = -100
pixel_y = -40

middle
icon_state="middle"
density = 1

proc
newBeam(mob/m, beamName as text, beamType as text, beamState as text, beamDam as num, beamSpeed as num, beamDist as num)
var/Beam/tail/_tail=new(get_step(m,m.dir))
_tail.owner=m
_tail.dir=m.dir
_tail.icon_state="[beamState]_[_tail.icon_state]"
var/Beam/head/_head=new(get_step(m,m.dir))
m.myBeam=_head
_head.owner=m
_head.icon_state="[beamState]_[_head.icon_state]"
_head.beam_image=beamState
_head.parts+=_tail
_head.objName=beamName
_head.dir=m.dir
_head.bType=beamType
_head.ki_damage=beamDam
_head.speed=beamSpeed
_head.dist=beamDist
sleep(beamSpeed)
_head.shoot()


Problem description:

What It Does: So in my game (Dragon Ball Z inspired) beams start off with the tail or part where the beam fires off, then the head fires from the tail's location. The head is what deals damage if you bump into it. While the head is moving, it creates a trail between the tail and itself (this does nothing).

Actual Issue: So I'm trying to figure out two things. The first is, when a player intersects/jumps into the trail, I want the head to move in front of the player and delete all trails that aren't connected to the head anymore. The second issue is the beam not hitting the player from point-blank range.
Cross()/Crossed()/Uncross()/Uncrossed() are only called alongside Move().

When you create the movable at a specific location, Move() is not called:

new(get_step(m,m.dir))


You either need to use Move() to put the beam at the starting loc, or you need to search for anything the beam would collide with after you set the location manually.
In response to Ter13
The way to fix the beam not hitting point-blank makes sense to me. But I'm trying to use Cross() for when a player intersects the beam to reposition the head of the beam in front of the player. This is what really is confusing me.

When someone crosses the middle, I want the head to be immediately moved to the player who caused the cross to happen. And to then delete all trails that are no longer connected to the head.

    middle
icon_state="middle"
New()
..()
Cross(atom/A)


Every time I even input the Cross() it just says "atom: undefined var" same w/ "A" as well.
Cross() is one of a pair of procs that handle related events; the other one is Crossed(). Obstacle.Cross(Mover) is called when Mover tries to cross over Obstacle. If Obstacle.Cross(Mover) returns TRUE, Mover will be allowed to cross. If it returns FALSE, Mover won't be allowed to cross (movement will fail). Obstacle.Crossed(Mover) is called immediately after Mover has crossed Obstacle.

In your case (player moving onto beam), the Mover is the player, and the Obstacle is the beam. We don't want to do anything in Beam.Cross(Player), because that's to determine if the player can cross or not. What we should do is put something in Beam.Crossed(Player):

middle
Crossed(atom/movable/crosser)
// Do the default behavior
. = ..()
// If the crosser isn't a fighter, do nothing else.
var /fighter/crossFighter = crosser
if(!istype(crossFighter)) return
// Attack the fighter
beam.owner.attack(crossFighter)


From the way that you've put the Cross() statement in New(), and tried to pass (atom/A) to it, it looks like you don't fully understand control flow. It's a tough - but important! - concept in programming. I wrote an article a while ago to help with this exact situation. Give it a read, and it could save you a lot of time as you work on your game. Good luck with the project!
In response to IainPeregrine
Thanks! This helped a lot. Sorry, to ask. But I have one more question. When the beam moves it's location to the player who crossed into it, the trail of the beam remains. I don't know a work around for this, or can't typically think of one.

Visual Example:

Goku: ====== (KAMEHAMEHA) ===>

Goku: =====>[Vegeta]===

(= is the trail and I'm trying to figure how to get rid of that after the Cross.)

    middle
icon_state="middle"
Crossed(atom/A)
. = ..()
if(!ismob(A))
return
else
for(var/Beam/head/_head in world)
if(_head.owner == src.owner)
if(_head.dir == EAST)
_head.loc = locate(A.x-1,A.y,A.z)
else if(_head.dir == WEST)
_head.loc = locate(A.x+1,A.y,A.z)
In response to Selected
I get it. When the prince of all saiyans steps into the beam of a low class warrior, the beam stops and bows in front of him. What we definitely don't want to do is loop through all of something in the world.

There are a bunch of ways we could solve this problem. The one that comes to mind first is a linked list. When we create the beam, we could give each beam segment a variable with a reference to the next beam segment. When someone steps in front of the beam, we could just go down that linked list of beam segments and delete everything until we found the head, then place the head in front of the hit player. If you feel confident implementing that, then go for it.

If not, then what's simpler is to do the same thing, but using the map instead. What I mean is that instead of stepping through a linked list, we just step through the map.

middle
icon_state="middle"
Crossed(atom/crosser)
// Do the default behavior
. = ..()
// Make sure the crosser is a mob
if(!ismob(A)) return
// Find the beam head by stepping down the Beam path
var/Beam/head/own_head
var/turf/current_step = loc
var/list/beam_segments = list(src)
while(!own_head)
current_step = get_step(current_step, dir)
own_head = locate() in current_step
if(own_head) break
var/Beam/Middle/next_segment = locate() in current_step
if(!next_segment) break
beam_segments.Add(next_segment)
// Delete old beam segments
for(var/Beam/Middle/segment in beam_segments)
del segment
// If we didn't find a beam head, create one
if(!own_head)
own_head = new()
SETUP_BEAM_HEAD_MAGIC()
// Move the beam head in front of the player
var/opposite_direction = turn(dir, 180)
var/turf/adjacent_turf = get_step(src, opposite_direction)
var/Beam/Middle/adjacent_beam = locate() in adjacent_turf
if(adjacent_beam) del adjacent_beam
own_head.Move(adjacent_turf)


I don't know much about how your game works, but that should work for simple cases with beams the same size as world.icon_size and non-pixel movement.

There's a couple tricky parts in there, so let's look at them in more detail. First, the while() loop:
// Find the beam head by stepping down the Beam path
01 var/Beam/head/own_head
02 var/turf/current_step = loc
03 var/list/beam_segments = list(src)
04 while(!own_head)
05 current_step = get_step(current_step, dir)
06 own_head = locate() in current_step
07 if(own_head) break
08 var/Beam/Middle/next_segment = locate() in current_step
09 if(!next_segment) break
10 beam_segments.Add(next_segment)


What we're doing here is starting at the current segment (where the player stepped into the beam) and stepping in the direction of the beam until we find the head. Along the way, we also add any beam segments at each step to a list of beam segments, so we can delete them later. What makes this tricky is that it can lock up the CPU if we're not careful. Imagine that, for some reason, the beam head got deleted. If we didn't check for that, then the while(!own_head) loop would keep going forever. To prevent this, we break out of the loop as soon as we stop finding beam segments (line 09). We also break out of the loop as soon as we find the head segment (07) because we don't need to keep looking anymore.

The next tricky part is that we have to create a beam head if we didn't find one. If there's any time between the beam head being deleted (by hitting a wall, or because of a player skill) and the rest of the beam being deleted, there's a chance a player could step into a beam that doesn't have a head. So we check if there's no head and make a new one - otherwise you'd get runtime errors. I don't know what setup is needed for the beam heads, like giving them a direction and an owner, etc. So that's what SETUP_BEAM_HEAD_MAGIC() means: you have to fill in the blanks there.

And as general advice, the more easily readable you make your code, the more easily you'll be able to understand the code you're writting. Use variable names that describe the relationship your variable has to the code. For instance, instead of Crossed(atom/A), you could write Crossed(atom/movable/theMovableAtom). That's slightly better, but it tells us what the things /is/ not what its /relationship/ is. Even better would be Crossed(atom/movable/crossingPlayer). That tells us exactly what the variable represents in our code. You should also learn the skill of writting comments as you write the code. It helps you to structure what you're trying to do before you start writing the code that does it.

Hope that helps :)
In response to IainPeregrine
So I was trying to adjust this to my game and some things caught my eye. The first thing was the final part, I noticed this makes it always change the direction it's traveling and return in the direction of the crosser instead of immediately repositioning itself from it's current location to the crosser's location while keeping it's same direction.

(It causes this to happen: https://gyazo.com/238c7fcd34f8ab889ef79e78df86bbf4 )

And I don't honestly know if a new list for only segments is necessary or not. To keep it nice and clean. Because the first post in this thread (the code). I use Move() to create new segments.

    Move(NewLoc)
if(locate(/Beam/tail/) in src.loc)
..()
else
var/Beam/middle/segment = new(src.loc)
segment.dir=src.dir
segment.owner=src.owner
segment.icon_state="[src.beam_image]_[segment.icon_state]"
src.parts += segment

if(dir == EAST)
segment.pixel_x = 70
else
if(dir == WEST)
segment.pixel_x = -100
..()


This is what the current code I put looks like this, I cut out some things like the creation of a new head if one doesn't exist. But the beam starts w/ the head and then creates the trails so I thought this wasn't needed. I removed the list for segments and changed it to parts[0] which contains the head, tail (the beginning of the beam that doesn't move) and the segment/middle.

    middle
icon_state="middle"
Crossed(atom/movable/crossingPlayer)
. = ..()
if(!ismob(crossingPlayer)) return

var/Beam/head/own_head
var/turf/current_step = loc
while(!own_head)
current_step = get_step(current_step, dir)
own_head = locate() in current_step
if(own_head) break
var/Beam/middle/next_segment = locate() in current_step
if(!next_segment) break
parts.Add(next_segment)
// Delete old beam segments
for(var/Beam/middle/segment in parts)
del segment
// Move the beam head in front of the player
var/opposite_direction = turn(dir, 180)
var/turf/adjacent_turf = get_step(src, opposite_direction)
var/Beam/middle/adjacent_beam = locate() in adjacent_turf
if(adjacent_beam) del adjacent_beam
own_head.Move(adjacent_turf)


But who am I to correct someone, this "beam" system is very very new to me.
In response to Selected
Selected wrote:
But who am I to correct someone, this "beam" system is very very new to me.

You're the programmer :)
When we write code here on the forum, we have to make a lot of guesses about what the rest of your program looks like. That's why I take time to explain What I'm Trying To Do, because what I'm trying to do is more important than how I did it. In the end, the code snippets we provide have to be edited by _you_ to fit your game.

So I was trying to adjust this to my game and some things caught my eye. [...]

At the end of my code example I used Move() to change the location of the beam head. This could lead to some of the problems you're encountering. (Move doesn't just change location, it can also change direction and call other procs like Exit and Entered). For now, try just changing the beam head's loc directly, like: loc = adjacent_turf

And I don't honestly know if a new list for only segments is necessary or not.

I could be wrong, but it sounds like you don't fully understand the scope of variables. From what I can tell from your code, the beam head keeps a list of its parts in a variable called "parts". When we define a new variable in the Crossed() proc, that variable only exists between the time that proc is called and when it returns. The purpose of this new list, beam_segments, is to temporarily hold a list of all beam segments we encounter while we search from the spot the player interrupted the beam to the beam head. This new list ceases to exist as soon as Crossed() is done executing.

That being said, we probably don't have to use that list or that loop anymore - Let's try a different solution. Knowing that each beam head keeps a list of its parts means that we don't have search the map for the parts or the head. What we'll do first is change the beam middle so each beam segment keeps a reference to its head. That'll look something like this:

Beam/middle
var
Beam/head/own_head
Beam/head/Move(NewLoc)
[cut out code to make the forum post smaller]
// Create Beam segment and add it to parts
var/Beam/middle/segment = new(src.loc)
segment.dir = src.dir
segment.owner = src.owner
segment.icon_state = "[src.beam_image]_[segment.icon_state]"
src.parts += segment
segment.own_head = src
[cut out code to make the forum post smaller]


Now when we define middle/Crossed we don't have to go searching for the beam head, because each segment has a variable pointing to its own beam head. So we'll move that beam head directly where we want it, and then delete all beam segments that are no longer needed. Also, it looks like you're saving a reference to the player who shot the beam in head.owner. That'll be useful, too.

middle/Crossed(atom/movable/crossingPlayer)
// Do the default behavior
. = ..()
// Cancel out if the thing that crossed isn't a mob
if(!ismob(crossingPlayer)) return
// Move the beam head to directly in front of the player
var/opposite_direction = turn(dir, 180)
var/turf/adjacent_turf = get_step(src, opposite_direction)
own_head.loc = adjacent_turf
/* Delete extra beam segments. That means any beam is is
farther away from the player than the head is,
or in the same place as the head.*/

var/head_distance = get_dist(owner, own_head)
for(var/Beam/middle/segment in own_head.parts)
var/segment_distance = get_dist(owner, segment)
if(segment_distance >= head_distance)
del segment


Let me know if that doesn't help out :)
You know, I probably could have helped out a lot better by reading the first post. I don't know why I didn't realize until now that you posted the entire beam code in the first post. My bad.
In response to IainPeregrine
Everything works except this part right here.
            var/head_distance = get_dist(owner, own_head)
for(var/Beam/middle/segment in own_head.parts)
var/segment_distance = get_dist(owner, segment)
if(segment_distance >= head_distance)
del segment


So the beam's head replaces itself properly but the trail remains beyond the player after the beam has moved.
I'm going through the full code now. I'm having a hard time making specific small changes because there are big structure problems that I think are the root cause of the little problems further down. Additionally, I can't restructure the code because there are places I don't understand what the purpose of the code is. I'll give examples of both.

So, first, there's a structure issue with how the types of object in this code were defined. Here's the path tree:
Beam
Techs
Kamehameha
head
tail
middle


From that, I can assume what we're _trying_ to do is make it so players can fire BEAM objects, and these beam objects have heads, middles, and tails. Also, we want there to be a lot different kinds of beams they can fire (techs) like the Kamehameha.

So what _should_ happen is that the player fires a beam, and a new BEAM object is created and stored in player.myBeam. But that's not what _actually_ happens. Instead of creating a new beam object, we have to create a new tech object (which is a type of beam). That tech object then calls newBeam() which constructs a beam head (along with a tail and middle) and stores the beam _head_ in player.myBeam. Confusing.

To make things even more confusing, head tail & middle all inherit from Beam, meaning that every head, tail, and middle segment all have their own list of parts, their own bumping variable, etc. Nothing is centralized.

I'm trying to restructure it now. I'll post when I have something to show. Even then, it won't work perfectly because there are parts that I don't understand. You'll have to use my work as an example guideline, not just copy/paste.
This is how I'd attempt to rewrite the beam code you first posted:
//-- Beams - Moving Ki blast attacks -------------------------------------------


//-- Mob configuration for firing beams ----------
mob
var
Beam/myBeam
techs[0]
target
proc
fire_beam(beam_type) // beam_type is a type path to a beam object
myBeam = new beam_type(loc, src)
return myBeam


//-- Basic Beam Definition -----------------------
Beam
parent_type = /obj
layer = 300
icon = 'Beams.dmi'
density = 1
var
// Configurable Variables
beam_image
speed
distance
ki_damage
bType
objName
// Nonconfigurable Variables
mob/owner
bumping
parts = list()
Beam/tail/tail

New(new_loc, firing_player)
// Set Owner
owner = firing_player
// Configure Head
dir = owner.dir
icon_state = "[beam_image]_head"
if(dir == EAST)
pixel_x = 50
pixel_y = -40
else if(dir == WEST)
pixel_x = -100
pixel_y = -40
// Create and Configure Tail
tail = new(get_step(owner , owner.dir), src)
parts.Add(tail)
// Place the beam into location at new_loc
. = ..()
// Attack (Bump into) all dense obstructions at new_loc
for(var/atom/obstruction in loc)
if(obstruction.density && obstruction != src)
Bump(obstruction)
// Start movement forward at a delay of src.speed
walk(src, dir, speed)

Del()
// Remove "collidingBeam" status from any effected mobs
// This could be changed to not use a loop through all mobs in the world,
// ...if I understood what collidingBeam & bumping == 5 were all about.
for(var/mob/m in world)
if(owner.target == m.charName)
m.collidingBeam = 0
// Delete all segments (including tail)
for(var/Beam/segment/beam_segment in parts)
del beam_segment
// Delete self. This will null out any references to this object, like at mob.myBeam.
del(src)


//-- Beam Movement (includes attacking) ----------
Bump(atom/obstruction)
// Delete the beam if we hit a wall
if(istype(obstruction, /turf))
del src
return // Not strictly necessary. del src causes immediate return
// Do nothing if the obstruction isn't a mob
// This is the logic of the original bump function.
// Might want to reconsider this; maybe del src like above.
if(!istype(obstruction, /mob))
return
// Now that we know the obstruction is a mob, attack it.
var/mob/target_player = obstruction
// What even is this? I don't know. I have no clue what the purpose of this is.
restart
if(src.bumping != 5)
owner.target = target_player.charName
// This should be factored out into it's own proc, like player/hit_by_beam()
// v
target_player.collidingBeam = 1
target_player.damage(src.ki_damage)
target_player.stunned = 1
target_player.running = 0
target_player.vel_x = 0
target_player.objName = src.objName
if(bType == "Big")
if(owner.dir == EAST)
target_player.dir = WEST
target_player:vel_x = 18
else if(owner.dir == WEST)
target_player.dir = EAST
target_player:vel_x = -18
// ^
bumping++
spawn(10)
goto restart
return
else
if(src.bumping == 5)
owner.target = null
target_player.collidingBeam = 0
del src

Move(NewLoc)
// Check if the beam is beyond its max distance. Delete if it is.
if(beam.distance-- <= 0)
del src
// If there isn't already a beam segment at current loc, create one.
var/Beam/segment/new_segment = locate() in loc
if(!new_segment)
new_segment = new(loc, src)
parts.Add(new_segment)
// Move like normal (do the default behavior)
..()


//-- Beam Segments -------------------------------
segment
parent_type = /obj
var
Beam/beam
New(new_loc, Beam/parent_beam)
// Do the Default Behavior (place at new_loc)
..()
// Setup based on parent beam
beam = parent_beam
icon = beam.icon
icon_state = "[beam.beam_image]_middle"
dir = beam.dir
// Offset graphic based on direction
if(dir == EAST)
pixel_x = 70
else if(dir == WEST)
pixel_x = -100

tail
New(new_loc, Beam/parent_beam)
// Do the Default Behavior (place, configure based on parent)
..()
// Change graphic to tail state (Override from segment)
icon_state = "[beam.beam_image]_tail"
// Offset graphic based on direction (Override from segment)
dir = beam.dir
if(dir == EAST)
pixel_x = 7
pixel_y = -40
else if(dir == WEST)
pixel_x = -100
pixel_y = -40


//-- Types of Beam Techs -------------------------

Beam/Techs
Kamehameha
icon_state="kame_head"
beam_image="kame"
objName = "Kamehameha Wave"
bType = "Big"
ki_damage = 10
speed=0.75
dist=35


This probably won't work right out of the box. There are parts of the code that I don't understand that use risky programming practices. There are very few places where a goto statement isn't a sure sign of major structural issues, and the Bump() proc is messing with the internals of the player it bumped into. Without knowing the intention behind those parts of the code, I can't restructure this fully (which is another good reason to always comment your code). Also, without the rest of the project, there was no way for me to compile this to test it, so there may be syntax problems here and there.

The big thing to notice is that now you just create a Beam, and that Beam is what you deal with. You don't create a Beam, which calls a function, which creates a head, and you deal with the head. Beam is the object. You make a Beam. Beams also need owners, and usr isn't safe outside variables, so now we have a mob function, fire_beam(beam_type), that you use to fire your beams.

Here's hoping this works.

Edit: One last note. There are a lot of unexplained numbers floating around in this code, like "pixel_x = -100". We call these "Magic Numbers" because no one knows what they mean, and things break when you change them. Wherever there's a magic number, that should be replaced with a variable that means something. Like, if you're offsetting pixel_x by 100 because 100 is the width of the character firing the beam, then you should really be doing "pixel_x = -character.bound_width". That does three important things: 1) If we change the width of characters later, it won't break the code. 2) We can make lots of characters with different widths, and it won't break the code. 3) We can edit the code to because we know what these values mean.
And one last cleanup. This includes the Crossed() proc (the whole reason we were doing this in the first place) and fixes some basic indentation errors (etc) that I made.

//-- Beams - Moving Ki blast attacks -------------------------------------------


//-- Mob configuration for firing beams ----------
mob
var
Beam/myBeam
techs = list()
target
proc
fire_beam(beam_type) // beam_type is a type path to a beam object
myBeam = new beam_type(loc, src)
return myBeam


//-- Basic Beam Definition -----------------------
Beam
parent_type = /obj
layer = 300
icon = 'Beams.dmi'
density = 1
var
// Configurable Variables
beam_image
speed
distance
ki_damage
bType
objName
// Nonconfigurable Variables
mob/owner
bumping
list/parts = list()
Beam/segment/tail/tail

New(new_loc, firing_player)
// Set Owner
owner = firing_player
// Configure Head
dir = owner.dir
icon_state = "[beam_image]_head"
if(dir == EAST)
pixel_x = 50
pixel_y = -40
else if(dir == WEST)
pixel_x = -100
pixel_y = -40
// Create and Configure Tail
tail = new(get_step(owner , owner.dir), src)
parts.Add(tail)
// Place the beam into location at new_loc
. = ..()
// Attack (Bump into) all dense obstructions at new_loc
for(var/atom/obstruction in loc)
if(obstruction.density && obstruction != src)
Bump(obstruction)
// Start movement forward at a delay of src.speed
walk(src, dir, speed)

Del()
// Remove "collidingBeam" status from any effected mobs
// This could be changed to not use a loop through all mobs in the world,
// ...if I understood what collidingBeam & bumping == 5 were all about.
for(var/mob/m in world)
if(owner.target == m.charName)
m.collidingBeam = 0
// Delete all segments (including tail)
for(var/Beam/segment/beam_segment in parts)
del beam_segment
// Delete self. This will null out any references to this object, like at mob.myBeam.
del(src)


//-- Beam Movement -------------------------------
Bump(atom/obstruction)
// Delete the beam if we hit a wall
if(istype(obstruction, /turf))
del src
return // Not strictly necessary. del src causes immediate return
// Do nothing if the obstruction isn't a mob
// This is the logic of the original bump function.
// Might want to reconsider this; maybe del src like above.
if(!istype(obstruction, /mob))
return
// Now that we know the obstruction is a mob, attack it.
attack(obstruction)

Move(NewLoc)
// Check if the beam is beyond its max distance. Delete if it is.
if(distance-- <= 0)
del src
// If there isn't already a beam segment at current loc, create one.
var/Beam/segment/new_segment = locate() in loc
if(!new_segment)
new_segment = new(loc, src)
parts.Add(new_segment)
// Move like normal (do the default behavior)
..()


//-- Beam Attacking ------------------------------
proc/attack(mob/target_player)
set waitfor = FALSE
// What even is this? I don't know. I have no clue what the purpose of this is.
// Attack 5 times, I guess.
while(bumping < 5)
owner.target = target_player.charName
// This should be factored out into it's own proc, like player/hit_by_beam()
// v
target_player.collidingBeam = 1
target_player.damage(src.ki_damage)
target_player.stunned = 1
target_player.running = 0
target_player.vel_x = 0
target_player.objName = src.objName
if(bType == "Big")
if(owner.dir == EAST)
target_player.dir = WEST
target_player:vel_x = 18
else if(owner.dir == WEST)
target_player.dir = EAST
target_player:vel_x = -18
// ^
bumping++
sleep(10)
// Unconfigure stuff that we configured on the target that we really shouldn't have
owner.target = null
target_player.collidingBeam = 0
// Clean up
del src



//-- Beam Segments -------------------------------------------------------------

Beam/segment
parent_type = /obj
var
Beam/beam


//-- Configuration of Middle & Tail --------------

New(new_loc, Beam/parent_beam)
// Do the Default Behavior (place at new_loc)
..()
// Setup based on parent beam
beam = parent_beam
icon = beam.icon
icon_state = "[beam.beam_image]_middle"
dir = beam.dir
// Offset graphic based on direction
if(dir == EAST)
pixel_x = 70
else if(dir == WEST)
pixel_x = -100

tail
New(new_loc, Beam/parent_beam)
// Do the Default Behavior (place, configure based on parent)
..()
// Change graphic to tail state (Override from segment)
icon_state = "[beam.beam_image]_tail"
// Offset graphic based on direction (Override from segment)
dir = beam.dir
if(dir == EAST)
pixel_x = 7
pixel_y = -40
else if(dir == WEST)
pixel_x = -100
pixel_y = -40


//-- Attacking Crossing Players ------------------

Crossed(atom/movable/crosser)
// Check if the crossing atom is a player. Cancel out if it isn't.
if(!ismob(crosser))
return
// Move the head into position
beam.loc = loc
// Delete all parts ahead of beam (except src, that would end the proc)
var/head_distance = get_dist(beam.owner, beam)
for(var/Beam/segment/beam_segment in beam.parts)
var/segment_distance = get_dist(beam.owner, beam_segment)
if(segment_distance < head_distance)
continue
beam.parts.Remove(beam_segment)
if(beam_segment == src)
loc = null
else
del beam_segment
// Make the beam head attack the crossing player
beam.attack(crosser)
// delete src
del src



//-- Types of Beam Techs -------------------------------------------------------

Beam/Techs
Kamehameha
icon_state = "kame_head"
beam_image = "kame"
objName = "Kamehameha Wave"
bType = "Big"
ki_damage = 10
speed = 0.75
distance = 35
In response to IainPeregrine
So when the beam is being used. It's linked to the "1." key. So when the player presses the key the technique activates, etc. and creates the beam in the process.

new/Beam/Techs/Kamehameha()


And that is what spawned the beam before. But with this configuration none of this uses anything under "Beam/Techs".
I'm guessing you have a verb that "1." is macro'd to, like:
mob/verb
fireMaLazor()
new /Beam/Techs/Kamehameha()


What you're going to do now is very similar:
mob/verb
fireMaLazor()
fire_beam(/Beam/Techs/Kamehameha)


When you call fire_beam() you pass it the type of beam you want to fire. This means that a single character can have a lot of different attacks:
mob/Vegeta/verb
final_flash() // Macro'd to 1
fire_beam(/Beam/Techs/final_flash)
garlic_gun() // Macro'd to 3
fire_beam(/Beam/Techs/galick_gun)

Login to reply.