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"