diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm
index 2223f2f9f9..8e63131cdb 100644
--- a/code/modules/power/port_gen.dm
+++ b/code/modules/power/port_gen.dm
@@ -1,45 +1,3 @@
-//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
-
-/* new portable generator - work in progress
-
-/obj/machinery/power/port_gen
- name = "portable generator"
- desc = "A portable generator used for emergency backup power."
- icon = 'generator.dmi'
- icon_state = "off"
- density = 1
- anchored = 0
- directwired = 0
- var/t_status = 0
- var/t_per = 5000
- var/filter = 1
- var/tank = null
- var/turf/inturf
- var/starter = 0
- var/rpm = 0
- var/rpmtarget = 0
- var/capacity = 1e6
- var/turf/outturf
- var/lastgen
-
-
-/obj/machinery/power/port_gen/process()
-ideally we're looking to generate 5000
-
-/obj/machinery/power/port_gen/attackby(obj/item/weapon/W, mob/user)
-tank [un]loading stuff
-
-/obj/machinery/power/port_gen/attack_hand(mob/user)
-turn on/off
-
-/obj/machinery/power/port_gen/examine(mob/user)
-display round(lastgen) and phorontank amount
-
-*/
-
-//Previous code been here forever, adding new framework for portable generators
-
-
//Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power).
/obj/machinery/power/port_gen
name = "Placeholder Generator" //seriously, don't use this. It can't be anchored without VV magic.
@@ -56,6 +14,9 @@ display round(lastgen) and phorontank amount
var/recent_fault = 0
var/power_output = 1
+/obj/machinery/power/port_gen/proc/IsBroken()
+ return (crit_fail || (stat & BROKEN|EMPED))
+
/obj/machinery/power/port_gen/proc/HasFuel() //Placeholder for fuel check.
return 1
@@ -69,11 +30,10 @@ display round(lastgen) and phorontank amount
return
/obj/machinery/power/port_gen/process()
- if(active && HasFuel() && !crit_fail && anchored && powernet)
+ if(active && HasFuel() && !IsBroken() && anchored && powernet)
add_avail(power_gen * power_output)
UseFuel()
src.updateDialog()
-
else
active = 0
icon_state = initial(icon_state)
@@ -96,20 +56,57 @@ display round(lastgen) and phorontank amount
else
usr << "\blue The generator is off."
+/obj/machinery/power/port_gen/emp_act(severity)
+ var/duration = 6000 //ten minutes
+ switch(severity)
+ if(1)
+ stat &= BROKEN
+ if(prob(75)) explode()
+ if(2)
+ if(prob(25)) stat &= BROKEN
+ if(prob(10)) explode()
+ if(3)
+ if(prob(10)) stat &= BROKEN
+ duration = 300
+
+ stat |= EMPED
+ if(duration)
+ spawn(duration)
+ stat &= ~EMPED
+
+/obj/machinery/power/port_gen/proc/explode()
+ explosion(src.loc, -1, 3, 5, -1)
+ del(src)
+
+#define TEMPERATURE_DIVISOR 40
+#define TEMPERATURE_CHANGE_MAX 20
+
//A power generator that runs on solid plasma sheets.
/obj/machinery/power/port_gen/pacman
- name = "P.A.C.M.A.N.-type Portable Generator"
- var/sheets = 0
- var/max_sheets = 100
- var/sheet_name = ""
- var/sheet_path = /obj/item/stack/sheet/mineral/phoron
- var/board_path = "/obj/item/weapon/circuitboard/pacman"
- var/sheet_left = 0 // How much is left of the sheet
- var/heat = 0
+ name = "\improper P.A.C.M.A.N.-type Portable Generator"
+ desc = "A power generator that runs on solid plasma sheets. Rated for 80 kW max safe output."
- //produces up to 80 kW and lasts for 20 minutes with 50 sheets
- var/time_per_sheet = 96
- power_gen = 20000
+ var/sheet_name = "solid plasma sheet"
+ var/sheet_path = /obj/item/stack/sheet/mineral/phoron
+ var/board_path = "/obj/item/weapon/circuitboard/pacman"
+
+ /*
+ These values were chosen so that the generator can run safely up to 80 kW
+ A full 50 phoron sheet stack should last 20 minutes at power_output = 4
+ temperature_gain and max_temperature are set so that the max safe power level is 4.
+ Setting to 5 or higher can only be done temporarily before the generator overheats.
+ */
+ power_gen = 20000 //Watts output per power_output level
+ var/max_power_output = 5 //The maximum power setting without emagging.
+ var/time_per_sheet = 96 //fuel efficiency - how long 1 sheet lasts at power level 1
+ var/max_sheets = 100 //max capacity of the hopper
+ var/max_temperature = 300 //max temperature before overheating increases
+ var/temperature_gain = 50 //how much the temperature increases per power output level, in degrees per level
+
+ var/sheets = 0 //How many sheets of material are loaded in the generator
+ var/sheet_left = 0 //How much is left of the current sheet
+ var/temperature = 0 //The current temperature
+ var/overheating = 0 //if this gets high enough the generator explodes
/obj/machinery/power/port_gen/pacman/initialize()
..()
@@ -135,76 +132,127 @@ display round(lastgen) and phorontank amount
/obj/machinery/power/port_gen/pacman/RefreshParts()
var/temp_rating = 0
- var/temp_reliability = 0
for(var/obj/item/weapon/stock_parts/SP in component_parts)
if(istype(SP, /obj/item/weapon/stock_parts/matter_bin))
max_sheets = SP.rating * SP.rating * 50
else if(istype(SP, /obj/item/weapon/stock_parts/micro_laser) || istype(SP, /obj/item/weapon/stock_parts/capacitor))
temp_rating += SP.rating
+
+ var/temp_reliability = 0
+ var/part_count = 0
for(var/obj/item/weapon/CP in component_parts)
temp_reliability += CP.reliability
- reliability = min(round(temp_reliability / 4), 100)
+ part_count++
+
+ reliability = min(round(temp_reliability / part_count), 100)
power_gen = round(initial(power_gen) * (max(2, temp_rating) / 2))
/obj/machinery/power/port_gen/pacman/examine(mob/user)
..(user)
- user << "\blue The generator has [sheets] units of [sheet_name] fuel left, producing [power_gen] per cycle."
- if(crit_fail) user << "\red The generator seems to have broken down."
+ user << "\The [src] appears to be producing [power_gen*power_output] W."
+ user << "There are [sheets] [sheet_name]\s left in the hopper."
+ if(IsBroken()) user << "\The [src] seems to have broken down."
+ if(overheating) user << "\The [src] is overheating!"
/obj/machinery/power/port_gen/pacman/HasFuel()
- if(sheets >= 1 / (time_per_sheet / power_output) - sheet_left)
+ var/needed_sheets = power_output / time_per_sheet
+ if(sheets >= needed_sheets - sheet_left)
return 1
return 0
+//Removes one stack's worth of material from the generator.
/obj/machinery/power/port_gen/pacman/DropFuel()
if(sheets)
- var/fail_safe = 0
- while(sheets > 0 && fail_safe < 100)
- fail_safe += 1
- var/obj/item/stack/sheet/S = new sheet_path(loc)
- var/amount = min(sheets, S.max_amount)
- S.amount = amount
- sheets -= amount
+ var/obj/item/stack/sheet/S = new sheet_path(loc)
+ var/amount = min(sheets, S.max_amount)
+ S.amount = amount
+ sheets -= amount
/obj/machinery/power/port_gen/pacman/UseFuel()
- var/needed_sheets = 1 / (time_per_sheet / power_output)
- var/temp = min(needed_sheets, sheet_left)
- needed_sheets -= temp
- sheet_left -= temp
- sheets -= round(needed_sheets)
- needed_sheets -= round(needed_sheets)
- if (sheet_left <= 0 && sheets > 0)
- sheet_left = 1 - needed_sheets
+ //break down sometimes
+ if (reliability < 100)
+ if (prob(1) && prob(1) && prob(100 - reliability))
+ stat |= BROKEN
+ crit_fail = 1
+ if (prob(100 - reliability))
+ explode()
+
+ //how much material are we using this iteration?
+ var/needed_sheets = power_output / time_per_sheet
+
+ //HasFuel() should guarantee us that there is enough fuel left, so no need to check that
+ //the only thing we need to worry about is if we are going to rollover to the next sheet
+ if (needed_sheets > sheet_left)
sheets--
-
- var/lower_limit = 56 + power_output * 10
- var/upper_limit = 76 + power_output * 10
- var/bias = 0
- if (power_output > 4)
- upper_limit = 400
- bias = power_output * 3
- if (heat < lower_limit)
- heat += 3
+ sheet_left = (1 + sheet_left) - needed_sheets
else
- heat += rand(-7 + bias, 7 + bias)
- if (heat < lower_limit)
- heat = lower_limit
- if (heat > upper_limit)
- heat = upper_limit
+ sheet_left -= needed_sheets
- if (heat > 300)
+ //calculate the "target" temperature range
+ //This should probably depend on the external temperature somehow, but whatever.
+ var/lower_limit = 56 + power_output * temperature_gain
+ var/upper_limit = 76 + power_output * temperature_gain
+
+ /*
+ Hot or cold environments can affect the equilibrium temperature
+ The lower the pressure the less effect it has. I guess it cools using a radiator or something when in vacuum.
+ Gives traitors more opportunities to sabotage the generator or allows enterprising engineers to build additional
+ cooling in order to get more power out.
+ */
+ var/datum/gas_mixture/environment = loc.return_air()
+ if (environment)
+ var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1)
+ var/ambient = environment.temperature - T20C
+ lower_limit += ambient*ratio
+ upper_limit += ambient*ratio
+
+ var/average = (upper_limit + lower_limit)/2
+
+ //calculate the temperature increase
+ var/bias = 0
+ if (temperature < lower_limit)
+ bias = min(round((average - temperature)/TEMPERATURE_DIVISOR, 1), TEMPERATURE_CHANGE_MAX)
+ else if (temperature > upper_limit)
+ bias = max(round((temperature - average)/TEMPERATURE_DIVISOR, 1), -TEMPERATURE_CHANGE_MAX)
+
+ temperature += rand(-7 + bias, 7 + bias)
+
+ if (temperature > max_temperature)
overheat()
- del(src)
- return
+ else if (overheating > 0)
+ overheating--
/obj/machinery/power/port_gen/pacman/handleInactive()
-
- if (heat > 0)
- heat = max(heat - 2, 0)
+ var/cooling_temperature = 20
+ var/datum/gas_mixture/environment = loc.return_air()
+ if (environment)
+ var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1)
+ var/ambient = environment.temperature - T20C
+ cooling_temperature += ambient*ratio
+
+ if (temperature > cooling_temperature)
+ var/temp_loss = (temperature - cooling_temperature)/TEMPERATURE_DIVISOR
+ temp_loss = between(2, round(temp_loss, 1), TEMPERATURE_CHANGE_MAX)
+ temperature = max(temperature - temp_loss, cooling_temperature)
src.updateDialog()
/obj/machinery/power/port_gen/pacman/proc/overheat()
- explosion(src.loc, 2, 5, 2, -1)
+ overheating++
+ if (overheating > 60)
+ explode()
+
+/obj/machinery/power/port_gen/pacman/explode()
+ //Vapourize all the phoron
+ //When ground up in a grinder, 1 sheet produces 20 u of phoron -- Chemistry-Machinery.dm
+ //1 mol = 10 u? I dunno. 1 mol of carbon is definitely bigger than a pill
+ var/phoron = (sheets+sheet_left)*20
+ var/datum/gas_mixture/environment = loc.return_air()
+ if (environment)
+ environment.adjust_gas_temp("phoron", phoron/10, temperature + T0C)
+
+ sheets = 0
+ sheet_left = 0
+ ..()
/obj/machinery/power/port_gen/pacman/attackby(var/obj/item/O as obj, var/mob/user as mob)
if(istype(O, sheet_path))
@@ -213,16 +261,16 @@ display round(lastgen) and phorontank amount
if(amount < 1)
user << "\blue The [src.name] is full!"
return
- user << "\blue You add [amount] sheets to the [src.name]."
+ user << "\blue You add [amount] [addstack.singular_name]\s to the [src.name]."
sheets += amount
addstack.use(amount)
updateUsrDialog()
return
else if (istype(O, /obj/item/weapon/card/emag))
emagged = 1
- emp_act(1)
+ if (active && prob(25))
+ explode() //if they're foolish enough to emag while it's running
else if(!active)
-
if(istype(O, /obj/item/weapon/wrench))
if(!anchored)
@@ -245,18 +293,9 @@ display round(lastgen) and phorontank amount
else if(istype(O, /obj/item/weapon/crowbar) && open)
var/obj/machinery/constructable_frame/machine_frame/new_frame = new /obj/machinery/constructable_frame/machine_frame(src.loc)
for(var/obj/item/I in component_parts)
- if(I.reliability < 100)
- I.crit_fail = 1
I.loc = src.loc
while ( sheets > 0 )
- var/obj/item/stack/sheet/G = new sheet_path(src.loc)
-
- if ( sheets > 50 )
- G.amount = 50
- else
- G.amount = sheets
-
- sheets -= G.amount
+ DropFuel()
new_frame.state = 2
new_frame.icon_state = "box_1"
@@ -266,7 +305,6 @@ display round(lastgen) and phorontank amount
..()
if (!anchored)
return
-
interact(user)
/obj/machinery/power/port_gen/pacman/attack_ai(mob/user as mob)
@@ -289,9 +327,11 @@ display round(lastgen) and phorontank amount
dat += text("[capitalize(sheet_name)]: [sheets] - Eject
")
var/stack_percent = round(sheet_left * 100, 1)
dat += text("Current stack: [stack_percent]%
")
- dat += text("Power output: - [power_gen * power_output] +
")
+ dat += text("Power output: - [power_gen * power_output] Watts+
")
dat += text("Power current: [(powernet == null ? "Unconnected" : "[avail()]")]
")
- dat += text("Heat: [heat]
")
+
+ var/tempstr = "Temperature: [temperature]°C
"
+ dat += (overheating)? "[tempstr]" : tempstr
dat += "
Close"
user << browse("[dat]", "window=port_gen")
onclose(user, "port_gen")
@@ -303,7 +343,7 @@ display round(lastgen) and phorontank amount
src.add_fingerprint(usr)
if(href_list["action"])
if(href_list["action"] == "enable")
- if(!active && HasFuel() && !crit_fail)
+ if(!active && HasFuel() && !IsBroken())
active = 1
icon_state = "portgen1"
src.updateUsrDialog()
@@ -321,7 +361,7 @@ display round(lastgen) and phorontank amount
power_output--
src.updateUsrDialog()
if (href_list["action"] == "higher_power")
- if (power_output < 4 || emagged)
+ if (power_output < max_power_output || (emagged && power_output < round(max_power_output*2.5)))
power_output++
src.updateUsrDialog()
if (href_list["action"] == "close")
@@ -330,26 +370,46 @@ display round(lastgen) and phorontank amount
/obj/machinery/power/port_gen/pacman/super
name = "S.U.P.E.R.P.A.C.M.A.N.-type Portable Generator"
+ desc = "A power generator that utilizes uranium sheets as fuel. Can run for much longer than the standard PACMAN type generators. Rated for 80 kW max safe output."
icon_state = "portgen1"
sheet_path = /obj/item/stack/sheet/mineral/uranium
+ time_per_sheet = 576 //same power output, but a 50 sheet stack will last 2 hours at max safe power
board_path = "/obj/item/weapon/circuitboard/pacman/super"
+
+/obj/machinery/power/port_gen/pacman/super/UseFuel()
+ //produces a tiny amount of radiation when in use
+ if (prob(2*power_output))
+ for (var/mob/living/L in range(src, 5))
+ L.apply_effect(1, IRRADIATE) //should amount to ~5 rads per minute at max safe power
+ ..()
+
+/obj/machinery/power/port_gen/pacman/super/explode()
+ //a nice burst of radiation
+ var/rads = 50 + (sheets + sheet_left)*1.5
+ for (var/mob/living/L in range(src, 10))
+ //should really fall with the square of the distance, but that makes the rads value drop too fast
+ //I dunno, maybe physics works different when you live in 2D -- SM radiation also works like this, apparently
+ L.apply_effect(max(20, round(rads/get_dist(L,src))), IRRADIATE)
- //produces 80 kW like the PACMAN but 50 sheets will last for 2 hours
- power_gen = 20000
- time_per_sheet = 576
-
- overheat()
- explosion(src.loc, 3, 3, 3, -1)
+ explosion(src.loc, 3, 3, 5, 3)
+ del(src)
/obj/machinery/power/port_gen/pacman/mrs
name = "M.R.S.P.A.C.M.A.N.-type Portable Generator"
+ desc = "An advanced power generator that runs on tritium. Rated for 200 kW maximum safe output!"
icon_state = "portgen2"
- sheet_path = /obj/item/stack/sheet/mineral/tritium
+ sheet_path = /obj/item/stack/sheet/mineral/tritium
+
+ //I don't think tritium has any other use, so we might as well make this rewarding for players
+ //max safe power output (power level = 8) is 200 kW and lasts for 1 hour - 3 or 4 of these could power the station
+ power_gen = 25000 //watts
+ max_power_output = 10
+ time_per_sheet = 576
+ max_temperature = 800
+ temperature_gain = 90
board_path = "/obj/item/weapon/circuitboard/pacman/mrs"
- //produces 200 kW and lasts for 1 hour with 50 sheets
- power_gen = 50000
- time_per_sheet = 288
-
- overheat()
- explosion(src.loc, 4, 4, 4, -1)
+/obj/machinery/power/port_gen/pacman/mrs/explode()
+ //no special effects, but the explosion is pretty big (same as a supermatter shard).
+ explosion(src.loc, 3, 6, 12, 16, 1)
+ del(src)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 375d45fd54..5cd7e640b1 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -52,200 +52,194 @@
var/agony = 0
var/embed = 0 // whether or not the projectile can embed itself in the mob
- //TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not
- proc/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
- if(blocked >= 2) return 0//Full block
- if(!isliving(target)) return 0
- if(isanimal(target)) return 0
- var/mob/living/L = target
- L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked) // add in AGONY!
+//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not
+/obj/item/projectile/proc/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
+ if(blocked >= 2) return 0//Full block
+ if(!isliving(target)) return 0
+ if(isanimal(target)) return 0
+ var/mob/living/L = target
+ L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked) // add in AGONY!
+ return 1
+
+//called when the projectile stops flying because it collided with something
+/obj/item/projectile/proc/on_impact(var/atom/A)
+ return
+
+//return 1 if the projectile should be allowed to pass through after all, 0 if not.
+/obj/item/projectile/proc/on_penetrate(var/atom/A)
+ return 1
+
+/obj/item/projectile/proc/check_fire(var/mob/living/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not.
+ if(!istype(target) || !istype(user))
+ return 0
+ var/obj/item/projectile/test/in_chamber = new /obj/item/projectile/test(get_step_to(user,target)) //Making the test....
+ in_chamber.target = target
+ in_chamber.flags = flags //Set the flags...
+ in_chamber.pass_flags = pass_flags //And the pass flags to that of the real projectile...
+ in_chamber.firer = user
+ var/output = in_chamber.process() //Test it!
+ del(in_chamber) //No need for it anymore
+ return output //Send it back to the gun!
+
+//called to launch a projectile from a gun
+/obj/item/projectile/proc/launch(atom/target, mob/user, obj/item/weapon/gun/launcher, var/target_zone, var/x_offset=0, var/y_offset=0, var/px=null, var/py=null)
+ var/turf/curloc = get_turf(user)
+ var/turf/targloc = get_turf(target)
+ if (!istype(targloc) || !istype(curloc))
return 1
- //called when the projectile stops flying because it collided with something
- proc/on_impact(var/atom/A)
- return
-
- //return 1 if the projectile should be allowed to pass through after all, 0 if not.
- proc/on_penetrate(var/atom/A)
- return 1
-
- proc/check_fire(var/mob/living/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not.
- if(!istype(target) || !istype(user))
- return 0
- var/obj/item/projectile/test/in_chamber = new /obj/item/projectile/test(get_step_to(user,target)) //Making the test....
- in_chamber.target = target
- in_chamber.flags = flags //Set the flags...
- in_chamber.pass_flags = pass_flags //And the pass flags to that of the real projectile...
- in_chamber.firer = user
- var/output = in_chamber.process() //Test it!
- del(in_chamber) //No need for it anymore
- return output //Send it back to the gun!
-
- //called to launch a projectile from a gun
- proc/launch(atom/target, mob/user, obj/item/weapon/gun/launcher, var/target_zone, var/x_offset=0, var/y_offset=0, var/px=null, var/py=null)
- var/turf/curloc = get_turf(user)
- var/turf/targloc = get_turf(target)
- if (!istype(targloc) || !istype(curloc))
- return 1
-
- firer = user
- def_zone = user.zone_sel.selecting
-
- if(user == target) //Shooting yourself
- user.bullet_act(src, target_zone)
- del(src)
- return 0
- if(targloc == curloc) //Shooting the ground
- targloc.bullet_act(src, target_zone)
- del(src)
- return 0
-
- original = target
- loc = curloc
- starting = curloc
- current = curloc
- yo = targloc.y - curloc.y + y_offset
- xo = targloc.x - curloc.x + x_offset
- if(!isnull(py)) p_y = py
- if(!isnull(px)) p_x = px
-
- shot_from = launcher
- silenced = launcher.silenced
-
- spawn()
- process()
+ firer = user
+ def_zone = user.zone_sel.selecting
+ if(user == target) //Shooting yourself
+ user.bullet_act(src, target_zone)
+ del(src)
+ return 0
+ if(targloc == curloc) //Shooting the ground
+ targloc.bullet_act(src, target_zone)
+ del(src)
return 0
- //Used to change the direction of the projectile in flight.
- proc/redirect(var/new_x, var/new_y, var/atom/starting_loc, var/mob/new_firer=null)
- original = locate(new_x, new_y, src.z)
- starting = starting_loc
- current = starting_loc
- if(new_firer)
- firer = src
+ original = target
+ loc = curloc
+ starting = curloc
+ current = curloc
+ yo = targloc.y - curloc.y + y_offset
+ xo = targloc.x - curloc.x + x_offset
+ if(!isnull(py)) p_y = py
+ if(!isnull(px)) p_x = px
- yo = new_y - starting_loc.y
- xo = new_x - starting_loc.x
+ shot_from = launcher
+ silenced = launcher.silenced
- //Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying.
- proc/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier = -30)
- //accuracy bonus from aiming
- if (istype(shot_from, /obj/item/weapon/gun)) //If you aim at someone beforehead, it'll hit more often.
- var/obj/item/weapon/gun/daddy = shot_from //Kinda balanced by fact you need like 2 seconds to aim
- if (daddy.target && original in daddy.target) //As opposed to no-delay pew pew
- miss_modifier += -30
+ spawn()
+ process()
- //roll to-hit
- var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, max(miss_modifier + 15*distance, 0))
- if(!hit_zone)
- visible_message("\The [src] misses [target_mob] narrowly!")
- return 0
+ return 0
- //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
- def_zone = hit_zone
+//Used to change the direction of the projectile in flight.
+/obj/item/projectile/proc/redirect(var/new_x, var/new_y, var/atom/starting_loc, var/mob/new_firer=null)
+ original = locate(new_x, new_y, src.z)
+ starting = starting_loc
+ current = starting_loc
+ if(new_firer)
+ firer = src
- //hit messages
- if(silenced)
- target_mob << "You've been hit in the [parse_zone(def_zone)] by \the [src]!"
+ yo = new_y - starting_loc.y
+ xo = new_x - starting_loc.x
+
+//Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying.
+/obj/item/projectile/proc/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier = -30)
+ //accuracy bonus from aiming
+ if (istype(shot_from, /obj/item/weapon/gun)) //If you aim at someone beforehead, it'll hit more often.
+ var/obj/item/weapon/gun/daddy = shot_from //Kinda balanced by fact you need like 2 seconds to aim
+ if (daddy.target && original in daddy.target) //As opposed to no-delay pew pew
+ miss_modifier += -30
+
+ //roll to-hit
+ var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, max(miss_modifier + 15*distance, 0))
+ if(!hit_zone)
+ visible_message("\The [src] misses [target_mob] narrowly!")
+ return 0
+
+ //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
+ def_zone = hit_zone
+
+ //hit messages
+ if(silenced)
+ target_mob << "You've been hit in the [parse_zone(def_zone)] by \the [src]!"
+ else
+ visible_message("\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
+
+ //admin logs
+ if(istype(firer, /mob))
+ target_mob.attack_log += "\[[time_stamp()]\] [firer]/[firer.ckey] shot [target_mob]/[target_mob.ckey] with a [src.type]"
+ firer.attack_log += "\[[time_stamp()]\] [firer]/[firer.ckey] shot [target_mob]/[target_mob.ckey] with a [src.type]"
+ msg_admin_attack("[firer] ([firer.ckey]) shot [target_mob] ([target_mob.ckey]) with a [src] (JMP)") //BS12 EDIT ALG
+ else
+ target_mob.attack_log += "\[[time_stamp()]\] UNKNOWN SUBJECT (No longer exists) shot [target_mob]/[target_mob.ckey] with a [src]"
+ msg_admin_attack("UNKNOWN shot [target_mob] ([target_mob.ckey]) with a [src] (JMP)") //BS12 EDIT ALG
+
+ //sometimes bullet_act() will want the projectile to continue flying
+ if (target_mob.bullet_act(src, def_zone) == -1)
+ return 0
+
+ return 1
+
+/obj/item/projectile/Bump(atom/A as mob|obj|turf|area)
+ if(A == src)
+ return 0 //no
+
+ if(A == firer)
+ loc = A.loc
+ return 0 //cannot shoot yourself
+
+ if(bumped)
+ return 0
+
+ var/passthrough = 0 //if the projectile should continue flying
+ var/distance = get_dist(starting,loc)
+
+ bumped = 1
+ if(ismob(A))
+ var/mob/M = A
+ if(istype(A, /mob/living))
+ passthrough = !attack_mob(M, distance)
else
- visible_message("\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
+ passthrough = 1 //so ghosts don't stop bullets
+ else
+ passthrough = (A.bullet_act(src, def_zone) == -1) //backwards compatibility
+ if(isturf(A))
+ for(var/obj/O in A)
+ O.bullet_act(src)
+ for(var/mob/M in A)
+ attack_mob(M, distance)
- //admin logs
- if(istype(firer, /mob))
- target_mob.attack_log += "\[[time_stamp()]\] [firer]/[firer.ckey] shot [target_mob]/[target_mob.ckey] with a [src.type]"
- firer.attack_log += "\[[time_stamp()]\] [firer]/[firer.ckey] shot [target_mob]/[target_mob.ckey] with a [src.type]"
- msg_admin_attack("[firer] ([firer.ckey]) shot [target_mob] ([target_mob.ckey]) with a [src] (JMP)") //BS12 EDIT ALG
+ //penetrating projectiles can pass through things that otherwise would not let them
+ if(penetrating > 0)
+ if(on_penetrate(A))
+ passthrough = 1
+ penetrating--
+
+ //the bullet passes through a dense object!
+ if(passthrough)
+ bumped = 0 //reset bumped variable!
+ if(istype(A, /turf))
+ loc = A
else
- target_mob.attack_log += "\[[time_stamp()]\] UNKNOWN SUBJECT (No longer exists) shot [target_mob]/[target_mob.ckey] with a [src]"
- msg_admin_attack("UNKNOWN shot [target_mob] ([target_mob.ckey]) with a [src] (JMP)") //BS12 EDIT ALG
-
- //sometimes bullet_act() will want the projectile to continue flying
- if (target_mob.bullet_act(src, def_zone) == -1)
- return 0
-
- return 1
-
- Bump(atom/A as mob|obj|turf|area)
- if(A == src)
- return 0 //no
-
- if(A == firer)
loc = A.loc
- return 0 //cannot shoot yourself
+ permutated.Add(A)
+ return 0
- if(bumped)
- return 0
+ //stop flying
+ on_impact(A)
+
+ density = 0
+ invisibility = 101
+ del(src)
+ return 1
- var/passthrough = 0 //if the projectile should continue flying
- var/distance = get_dist(starting,loc)
+/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(air_group || (height==0)) return 1
- bumped = 1
- if(ismob(A))
- var/mob/M = A
- if(istype(A, /mob/living))
- passthrough = !attack_mob(M, distance)
- else
- passthrough = 1 //so ghosts don't stop bullets
- else
- passthrough = (A.bullet_act(src, def_zone) == -1) //backwards compatibility
- if(isturf(A))
- for(var/obj/O in A)
- O.bullet_act(src)
- for(var/mob/M in A)
- attack_mob(M, distance)
-
- //penetrating projectiles can pass through things that otherwise would not let them
- if(penetrating > 0)
- if(on_penetrate(A))
- passthrough = 1
- penetrating--
-
- //the bullet passes through a dense object!
- if(passthrough)
- bumped = 0 //reset bumped variable!
- if(istype(A, /turf))
- loc = A
- else
- loc = A.loc
- permutated.Add(A)
- return 0
-
- //stop flying
- on_impact(A)
-
- density = 0
- invisibility = 101
- del(src)
+ if(istype(mover, /obj/item/projectile))
+ return prob(95) //ha
+ else
return 1
- CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return 1
-
- if(istype(mover, /obj/item/projectile))
- return prob(95) //ha
- else
- return 1
-
- process()
- if(kill_count < 1)
- del(src)
- kill_count--
- spawn while(src)
- if((!( current ) || loc == current))
- current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z)
- if((x == 1 || x == world.maxx || y == 1 || y == world.maxy))
- del(src)
- return
- step_towards(src, current)
- sleep(1)
- if(!bumped && !isturf(original))
- if(loc == get_turf(original))
- if(!(original in permutated))
- Bump(original)
- sleep(1)
+/obj/item/projectile/process()
+ if(kill_count < 1)
+ del(src)
return
+ step_towards(src, current)
+ sleep(1)
+ if(!bumped && !isturf(original))
+ if(loc == get_turf(original))
+ if(!(original in permutated))
+ Bump(original)
+ sleep(1)
+//"Tracing" projectile
/obj/item/projectile/test //Used to see if you can hit them.
invisibility = 101 //Nope! Can't see me!
yo = null
@@ -253,36 +247,36 @@
var/target = null
var/result = 0 //To pass the message back to the gun.
- Bump(atom/A as mob|obj|turf|area)
- if(A == firer)
- loc = A.loc
- return //cannot shoot yourself
- if(istype(A, /obj/item/projectile))
- return
- if(istype(A, /mob/living))
- result = 2 //We hit someone, return 1!
- return
- result = 1
+/obj/item/projectile/test/Bump(atom/A as mob|obj|turf|area)
+ if(A == firer)
+ loc = A.loc
+ return //cannot shoot yourself
+ if(istype(A, /obj/item/projectile))
return
+ if(istype(A, /mob/living))
+ result = 2 //We hit someone, return 1!
+ return
+ result = 1
+ return
- process()
- var/turf/curloc = get_turf(src)
- var/turf/targloc = get_turf(target)
- if(!curloc || !targloc)
- return 0
- yo = targloc.y - curloc.y
- xo = targloc.x - curloc.x
- target = targloc
- while(src) //Loop on through!
- if(result)
- return (result - 1)
- if((!( target ) || loc == target))
- target = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge
- step_towards(src, target)
- var/mob/living/M = locate() in get_turf(src)
- if(istype(M)) //If there is someting living...
- return 1 //Return 1
- else
- M = locate() in get_step(src,target)
- if(istype(M))
- return 1
+/obj/item/projectile/test/process()
+ var/turf/curloc = get_turf(src)
+ var/turf/targloc = get_turf(target)
+ if(!curloc || !targloc)
+ return 0
+ yo = targloc.y - curloc.y
+ xo = targloc.x - curloc.x
+ target = targloc
+ while(src) //Loop on through!
+ if(result)
+ return (result - 1)
+ if((!( target ) || loc == target))
+ target = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge
+ step_towards(src, target)
+ var/mob/living/M = locate() in get_turf(src)
+ if(istype(M)) //If there is someting living...
+ return 1 //Return 1
+ else
+ M = locate() in get_step(src,target)
+ if(istype(M))
+ return 1
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index 4ad1a3fab2..ecea772e70 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -17,68 +17,69 @@ var/list/beam_master = list()
flag = "laser"
eyeblur = 4
var/frequency = 1
- process()
- var/reference = "\ref[src]" //So we do not have to recalculate it a ton
- var/first = 1 //So we don't make the overlay in the same tile as the firer
- spawn while(src) //Move until we hit something
- if((!( current ) || loc == current)) //If we pass our target
- current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z)
- if((x == 1 || x == world.maxx || y == 1 || y == world.maxy))
- del(src) //Delete if it passes the world edge
- return
- step_towards(src, current) //Move~
+/obj/item/projectile/beam/process()
+ var/reference = "\ref[src]" //So we do not have to recalculate it a ton
+ var/first = 1 //So we don't make the overlay in the same tile as the firer
+ spawn while(src) //Move until we hit something
- if(kill_count < 1)
- del(src)
- kill_count--
+ if((!( current ) || loc == current)) //If we pass our target
+ current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z)
+ if((x == 1 || x == world.maxx || y == 1 || y == world.maxy))
+ del(src) //Delete if it passes the world edge
+ return
+ step_towards(src, current) //Move~
- if(!bumped && !isturf(original))
- if(loc == get_turf(original))
- if(!(original in permutated))
- Bump(original)
+ if(kill_count < 1)
+ del(src)
+ kill_count--
- if(!first) //Add the overlay as we pass over tiles
- var/target_dir = get_dir(src, current) //So we don't call this too much
+ if(!bumped && !isturf(original))
+ if(loc == get_turf(original))
+ if(!(original in permutated))
+ Bump(original)
- //If the icon has not been added yet
- if( !("[icon_state][target_dir]" in beam_master) )
- var/image/I = image(icon,icon_state,10,target_dir) //Generate it.
- beam_master["[icon_state][target_dir]"] = I //And cache it!
+ if(!first) //Add the overlay as we pass over tiles
+ var/target_dir = get_dir(src, current) //So we don't call this too much
- //Finally add the overlay
- src.loc.overlays += beam_master["[icon_state][target_dir]"]
+ //If the icon has not been added yet
+ if( !("[icon_state][target_dir]" in beam_master) )
+ var/image/I = image(icon,icon_state,10,target_dir) //Generate it.
+ beam_master["[icon_state][target_dir]"] = I //And cache it!
- //Add the turf to a list in the beam master so they can be cleaned up easily.
- if(reference in beam_master)
- var/list/turf_master = beam_master[reference]
- if("[icon_state][target_dir]" in turf_master)
- var/list/turfs = turf_master["[icon_state][target_dir]"]
- turfs += loc
- else
- turf_master["[icon_state][target_dir]"] = list(loc)
+ //Finally add the overlay
+ src.loc.overlays += beam_master["[icon_state][target_dir]"]
+
+ //Add the turf to a list in the beam master so they can be cleaned up easily.
+ if(reference in beam_master)
+ var/list/turf_master = beam_master[reference]
+ if("[icon_state][target_dir]" in turf_master)
+ var/list/turfs = turf_master["[icon_state][target_dir]"]
+ turfs += loc
else
- var/list/turfs = list()
- turfs["[icon_state][target_dir]"] = list(loc)
- beam_master[reference] = turfs
+ turf_master["[icon_state][target_dir]"] = list(loc)
else
- first = 0
- cleanup(reference)
- return
+ var/list/turfs = list()
+ turfs["[icon_state][target_dir]"] = list(loc)
+ beam_master[reference] = turfs
+ else
+ first = 0
+ cleanup(reference)
+ return
- Del()
- cleanup("\ref[src]")
- ..()
+/obj/item/projectile/beam/Del()
+ cleanup("\ref[src]")
+ ..()
- proc/cleanup(reference) //Waits .3 seconds then removes the overlay.
- src = null //we're getting deleted! this will keep the code running
- spawn(3)
- var/list/turf_master = beam_master[reference]
- for(var/laser_state in turf_master)
- var/list/turfs = turf_master[laser_state]
- for(var/turf/T in turfs)
- T.overlays -= beam_master[laser_state]
- return
+/obj/item/projectile/beam/proc/cleanup(reference) //Waits .3 seconds then removes the overlay.
+ src = null //we're getting deleted! this will keep the code running
+ spawn(3)
+ var/list/turf_master = beam_master[reference]
+ for(var/laser_state in turf_master)
+ var/list/turfs = turf_master[laser_state]
+ for(var/turf/T in turfs)
+ T.overlays -= beam_master[laser_state]
+ return
/obj/item/projectile/beam/practice
name = "laser"
@@ -89,7 +90,6 @@ var/list/beam_master = list()
flag = "laser"
eyeblur = 2
-
/obj/item/projectile/beam/heavylaser
name = "heavy laser"
icon_state = "heavylaser"
@@ -115,7 +115,6 @@ var/list/beam_master = list()
icon_state = "emitter"
damage = 30
-
/obj/item/projectile/beam/lastertag/blue
name = "lasertag beam"
icon_state = "bluelaser"
@@ -124,12 +123,12 @@ var/list/beam_master = list()
damage_type = BURN
flag = "laser"
- on_hit(var/atom/target, var/blocked = 0)
- if(istype(target, /mob/living/carbon/human))
- var/mob/living/carbon/human/M = target
- if(istype(M.wear_suit, /obj/item/clothing/suit/redtag))
- M.Weaken(5)
- return 1
+/obj/item/projectile/beam/lastertag/blue/on_hit(var/atom/target, var/blocked = 0)
+ if(istype(target, /mob/living/carbon/human))
+ var/mob/living/carbon/human/M = target
+ if(istype(M.wear_suit, /obj/item/clothing/suit/redtag))
+ M.Weaken(5)
+ return 1
/obj/item/projectile/beam/lastertag/red
name = "lasertag beam"
@@ -139,12 +138,12 @@ var/list/beam_master = list()
damage_type = BURN
flag = "laser"
- on_hit(var/atom/target, var/blocked = 0)
- if(istype(target, /mob/living/carbon/human))
- var/mob/living/carbon/human/M = target
- if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag))
- M.Weaken(5)
- return 1
+/obj/item/projectile/beam/lastertag/red/on_hit(var/atom/target, var/blocked = 0)
+ if(istype(target, /mob/living/carbon/human))
+ var/mob/living/carbon/human/M = target
+ if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag))
+ M.Weaken(5)
+ return 1
/obj/item/projectile/beam/lastertag/omni//A laser tag bolt that stuns EVERYONE
name = "lasertag beam"
@@ -154,12 +153,12 @@ var/list/beam_master = list()
damage_type = BURN
flag = "laser"
- on_hit(var/atom/target, var/blocked = 0)
- if(istype(target, /mob/living/carbon/human))
- var/mob/living/carbon/human/M = target
- if((istype(M.wear_suit, /obj/item/clothing/suit/bluetag))||(istype(M.wear_suit, /obj/item/clothing/suit/redtag)))
- M.Weaken(5)
- return 1
+/obj/item/projectile/beam/lastertag/omni/on_hit(var/atom/target, var/blocked = 0)
+ if(istype(target, /mob/living/carbon/human))
+ var/mob/living/carbon/human/M = target
+ if((istype(M.wear_suit, /obj/item/clothing/suit/bluetag))||(istype(M.wear_suit, /obj/item/clothing/suit/redtag)))
+ M.Weaken(5)
+ return 1
/obj/item/projectile/beam/sniper
name = "sniper beam"