From 0b344ff76a1532cae792b9117b7c4b1c82862b2c Mon Sep 17 00:00:00 2001 From: Duck- Date: Mon, 22 Dec 2014 00:31:54 -0500 Subject: [PATCH] TRAINS CHOO CHOO --- aurora.dme | 3 + code/datums/supplypacks.dm | 16 + code/game/objects/items/weapons/cards_ids.dm | 9 +- code/modules/mob/living/carbon/human/human.dm | 3 +- code/modules/mob/mob_movement.dm | 6 +- code/modules/vehicles/cargo_train.dm | 303 +++++++++++++++ code/modules/vehicles/train.dm | 229 +++++++++++ code/modules/vehicles/vehicle.dm | 358 ++++++++++++++++++ data/investigate/gravity.html | 30 +- icons/obj/vehicles.dmi | Bin 6970 -> 10594 bytes 10 files changed, 938 insertions(+), 19 deletions(-) create mode 100644 code/modules/vehicles/cargo_train.dm create mode 100644 code/modules/vehicles/train.dm create mode 100644 code/modules/vehicles/vehicle.dm diff --git a/aurora.dme b/aurora.dme index 858cb0a4..c1df4764 100644 --- a/aurora.dme +++ b/aurora.dme @@ -1441,6 +1441,9 @@ #include "code\modules\telesci\gps.dm" #include "code\modules\telesci\telepad.dm" #include "code\modules\telesci\telesci_computer.dm" +#include "code\modules\vehicles\cargo_train.dm" +#include "code\modules\vehicles\train.dm" +#include "code\modules\vehicles\vehicle.dm" #include "code\modules\virus2\analyser.dm" #include "code\modules\virus2\antibodies.dm" #include "code\modules\virus2\centrifuge.dm" diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index f09a659a..6616fda5 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -251,6 +251,22 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee containername = "MULEbot Crate" group = "Operations" +/datum/supply_packs/cargotrain + name = "Cargo Train Tug" + contains = list(/obj/vehicle/train/cargo/engine) + cost = 30 + containertype = /obj/structure/largecrate + containername = "Cargo Train Tug Crate" + group = "Operations" + +/datum/supply_packs/cargotrailer + name = "Cargo Train Trolley" + contains = list(/obj/vehicle/train/cargo/trolley) + cost = 20 + containertype = /obj/structure/largecrate + containername = "Cargo Train Trolley Crate" + group = "Operations" + /datum/supply_packs/lisa name = "Corgi Crate" contains = list() diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index 931edd3d..71b1629f 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -100,7 +100,8 @@ /obj/machinery/bot, /obj/machinery/door, /obj/machinery/telecomms, - /obj/machinery/mecha_part_fabricator + /obj/machinery/mecha_part_fabricator, + /obj/vehicle ) @@ -200,7 +201,7 @@ access = list(access_maint_tunnels, access_syndicate, access_external_airlocks) origin_tech = "syndicate=3" var/registered_user=null - + /obj/item/weapon/card/id/syndicate/New(mob/user as mob) ..() if(!isnull(user)) // Runtime prevention on laggy starts or where users log out because of lag at round start. @@ -208,7 +209,7 @@ else registered_name = "Agent Card" assignment = "Agent" - name = "[registered_name]'s ID Card ([assignment])" + name = "[registered_name]'s ID Card ([assignment])" /obj/item/weapon/card/id/syndicate/afterattack(var/obj/item/weapon/O as obj, mob/user as mob, proximity) if(!proximity) return @@ -239,7 +240,7 @@ registered_user = user else if(!registered_user || registered_user == user) - if(!registered_user) registered_user = user // + if(!registered_user) registered_user = user // switch(alert("Would you like to display the ID, or retitle it?","Choose.","Rename","Show")) if("Rename") diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 6d81acee..49f0c755 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -478,10 +478,9 @@ var/obj/machinery/bot/mulebot/MB = AM MB.RunOver(src) -/* if(istype(AM, /obj/vehicle)) + if(istype(AM, /obj/vehicle)) var/obj/vehicle/V = AM V.RunOver(src) -*/ //gets assignment from ID or ID inside PDA or PDA itself //Useful when player do something with computers diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index ca8015e6..3e58626b 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -203,7 +203,8 @@ if(!mob.canmove) - return + if (mob.buckled && (istype(mob.buckled, /obj/structure/stool/bed/chair/wheelchair) || istype(mob.buckled, /obj/vehicle))) // Exception for wheelchairs + else return //if(istype(mob.loc, /turf/space) || (mob.flags & NOGRAV)) // if(!mob.Process_Spacemove(0)) return 0 @@ -257,6 +258,9 @@ if(mob.machine.relaymove(mob,direct)) return + if(istype(mob.buckled, /obj/vehicle)) + return mob.buckled.relaymove(mob,direct) + if(mob.pulledby || mob.buckled) // Wheelchair driving! if(istype(mob.loc, /turf/space)) return // No wheelchair driving in space diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm new file mode 100644 index 00000000..ea3f7b72 --- /dev/null +++ b/code/modules/vehicles/cargo_train.dm @@ -0,0 +1,303 @@ +/obj/vehicle/train/cargo/engine + name = "cargo train tug" + desc = "A ridable electric car designed for pulling cargo trolleys." + icon = 'icons/obj/vehicles.dmi' + icon_state = "cargo_engine" + on = 0 + powered = 1 + locked = 0 + + load_item_visible = 1 + load_offset_x = 0 + mob_offset_y = 7 + + var/car_limit = 3 //how many cars an engine can pull before performance degrades + active_engines = 1 + var/obj/item/weapon/key/cargo_train/key + +/obj/item/weapon/key/cargo_train + name = "key" + desc = "A keyring with a small steel key, and a yellow fob reading \"Choo Choo!\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "train_keys" + w_class = 1 + +/obj/vehicle/train/cargo/trolley + name = "cargo train trolley" + icon = 'icons/obj/vehicles.dmi' + icon_state = "cargo_trailer" + anchored = 0 + passenger_allowed = 0 + locked = 0 + + load_item_visible = 1 + load_offset_x = 0 + load_offset_y = 4 + mob_offset_y = 8 + +//------------------------------------------- +// Standard procs +//------------------------------------------- +/obj/vehicle/train/cargo/engine/New() + ..() + cell = new /obj/item/weapon/cell/high + verbs -= /atom/movable/verb/pull + key = new() + var/image/I = new(icon = 'icons/obj/vehicles.dmi', icon_state = "cargo_engine_overlay", layer = src.layer + 0.2) //over mobs + overlays += I + turn_off() //so engine verbs are correctly set + +/obj/vehicle/train/cargo/engine/Move() + if(on && cell.charge < charge_use) + turn_off() + update_stats() + if(load && is_train_head()) + load << "The drive motor briefly whines, then drones to a stop." + + if(is_train_head() && !on) + return 0 + + return ..() + +/obj/vehicle/train/cargo/trolley/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(open && istype(W, /obj/item/weapon/wirecutters)) + passenger_allowed = !passenger_allowed + user.visible_message("[user] [passenger_allowed ? "cuts" : "mends"] a cable in [src].","You [passenger_allowed ? "cut" : "mend"] the load limiter cable.") + else + ..() + +/obj/vehicle/train/cargo/engine/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/key/cargo_train)) + if(!key) + user.drop_item() + W.forceMove(src) + key = W + verbs += /obj/vehicle/train/cargo/engine/verb/remove_key + return + ..() + +/obj/vehicle/train/cargo/update_icon() + if(open) + icon_state = initial(icon_state) + "_open" + else + icon_state = initial(icon_state) + +/obj/vehicle/train/cargo/trolley/insert_cell(var/obj/item/weapon/cell/C, var/mob/living/carbon/human/H) + return + +/obj/vehicle/train/cargo/engine/insert_cell(var/obj/item/weapon/cell/C, var/mob/living/carbon/human/H) + ..() + update_stats() + +/obj/vehicle/train/cargo/engine/remove_cell(var/mob/living/carbon/human/H) + ..() + update_stats() + +/obj/vehicle/train/cargo/engine/Bump(atom/Obstacle) + var/obj/machinery/door/D = Obstacle + var/mob/living/carbon/human/H = load + if(istype(D) && istype(H)) + D.Bumped(H) //a little hacky, but hey, it works, and respects access rights + + ..() + +/obj/vehicle/train/cargo/trolley/Bump(atom/Obstacle) + if(!lead) + return //so people can't knock others over by pushing a trolley around + ..() + +//------------------------------------------- +// Train procs +//------------------------------------------- +/obj/vehicle/train/cargo/engine/turn_on() + if(!key) + return + else + ..() + update_stats() + + verbs -= /obj/vehicle/train/cargo/engine/verb/stop_engine + verbs -= /obj/vehicle/train/cargo/engine/verb/start_engine + + if(on) + verbs += /obj/vehicle/train/cargo/engine/verb/stop_engine + else + verbs += /obj/vehicle/train/cargo/engine/verb/start_engine + +/obj/vehicle/train/cargo/engine/turn_off() + ..() + + verbs -= /obj/vehicle/train/cargo/engine/verb/stop_engine + verbs -= /obj/vehicle/train/cargo/engine/verb/start_engine + + if(!on) + verbs += /obj/vehicle/train/cargo/engine/verb/start_engine + else + verbs += /obj/vehicle/train/cargo/engine/verb/stop_engine + +/obj/vehicle/train/cargo/RunOver(var/mob/living/carbon/human/H) + var/list/parts = list("head", "chest", "l_leg", "r_leg", "l_arm", "r_arm") + + H.apply_effects(5, 5) + for(var/i = 0, i < rand(1,3), i++) + H.apply_damage(rand(1,5), BRUTE, pick(parts)) + +/obj/vehicle/train/cargo/trolley/RunOver(var/mob/living/carbon/human/H) + ..() + attack_log += text("\[[time_stamp()]\] ran over [H.name] ([H.ckey])") + +/obj/vehicle/train/cargo/engine/RunOver(var/mob/living/carbon/human/H) + ..() + + if(is_train_head() && istype(load, /mob/living/carbon/human)) + var/mob/living/carbon/human/D = load + D << "\red \b You ran over [H]!" + visible_message("\red \The [src] ran over [H]!") + attack_log += text("\[[time_stamp()]\] ran over [H.name] ([H.ckey]), driven by [D.name] ([D.ckey])") + msg_admin_attack("[D.name] ([D.ckey]) ran over [H.name] ([H.ckey]). (JMP)") + else + attack_log += text("\[[time_stamp()]\] ran over [H.name] ([H.ckey])") + + +//------------------------------------------- +// Interaction procs +//------------------------------------------- +/obj/vehicle/train/cargo/engine/relaymove(mob/user, direction) + if(user != load) + return 0 + + if(is_train_head()) + if(direction == reverse_direction(dir) && tow) + return 0 + if(Move(get_step(src, direction))) + return 1 + return 0 + else + return ..() + +/obj/vehicle/train/cargo/engine/examine() + ..() + + if(!istype(usr, /mob/living/carbon/human)) + return + + if(get_dist(usr,src) <= 1) + usr << "The power light is [on ? "on" : "off"].\nThere are[key ? "" : " no"] keys in the ignition." + usr << "The charge meter reads [cell? round(cell.percent(), 0.01) : 0]%" + +/obj/vehicle/train/cargo/engine/verb/start_engine() + set name = "Start engine" + set category = "Object" + set src in view(1) + + if(!istype(usr, /mob/living/carbon/human)) + return + + if(on) + usr << "The engine is already running." + return + + turn_on() + if (on) + usr << "You start [src]'s engine." + else + if(cell.charge < charge_use) + usr << "[src] is out of power." + else + usr << "[src]'s engine won't start." + +/obj/vehicle/train/cargo/engine/verb/stop_engine() + set name = "Stop engine" + set category = "Object" + set src in view(1) + + if(!istype(usr, /mob/living/carbon/human)) + return + + if(!on) + usr << "The engine is already stopped." + return + + turn_off() + if (!on) + usr << "You stop [src]'s engine." + +/obj/vehicle/train/cargo/engine/verb/remove_key() + set name = "Remove key" + set category = "Object" + set src in view(1) + + if(!istype(usr, /mob/living/carbon/human)) + return + + if(!key || (load && load != usr)) + return + + if(on) + turn_off() + + key.loc = usr.loc + if(!usr.get_active_hand()) + usr.put_in_hands(key) + key = null + + verbs -= /obj/vehicle/train/cargo/engine/verb/remove_key + +//------------------------------------------- +// Loading/unloading procs +//------------------------------------------- +/obj/vehicle/train/cargo/trolley/load(var/atom/movable/C) + if(ismob(C) && !passenger_allowed) + return 0 + if(!istype(C,/obj/machinery) && !istype(C,/obj/structure/closet) && !istype(C,/obj/structure/largecrate) && !istype(C,/obj/structure/reagent_dispensers) && !istype(C,/obj/structure/ore_box) && !istype(C, /mob/living/carbon/human)) + return 0 + + ..() + + if(load) + return 1 + +/obj/vehicle/train/cargo/engine/load(var/atom/movable/C) + if(!istype(C, /mob/living/carbon/human)) + return 0 + + return ..() + + +//------------------------------------------------------- +// Stat update procs +// +// Update the trains stats for speed calculations. +// The longer the train, the slower it will go. car_limit +// sets the max number of cars one engine can pull at +// full speed. Adding more cars beyond this will slow the +// train proportionate to the length of the train. Adding +// more engines increases this limit by car_limit per +// engine. +//------------------------------------------------------- +/obj/vehicle/train/cargo/engine/update_car(var/train_length, var/active_engines) + src.train_length = train_length + src.active_engines = active_engines + + //Update move delay + if(!is_train_head() || !on) + move_delay = initial(move_delay) //so that engines that have been turned off don't lag behind + else + move_delay = max(0, (-car_limit * active_engines) + train_length - active_engines) //limits base overweight so you cant overspeed trains + move_delay *= (1 / max(1, active_engines)) * 2 //overweight penalty (scaled by the number of engines) + move_delay += config.run_speed //base reference speed + move_delay *= 1.1 //makes cargo trains 10% slower than running when not overweight + +/obj/vehicle/train/cargo/trolley/update_car(var/train_length, var/active_engines) + src.train_length = train_length + src.active_engines = active_engines + + if(!lead && !tow) + anchored = 0 + if(verbs.Find(/atom/movable/verb/pull)) + return + else + verbs += /atom/movable/verb/pull + else + anchored = 1 + verbs -= /atom/movable/verb/pull diff --git a/code/modules/vehicles/train.dm b/code/modules/vehicles/train.dm new file mode 100644 index 00000000..d8e372d8 --- /dev/null +++ b/code/modules/vehicles/train.dm @@ -0,0 +1,229 @@ +/obj/vehicle/train + name = "train" + dir = 4 + + move_delay = 1 + + health = 100 + maxhealth = 100 + fire_dam_coeff = 0.7 + brute_dam_coeff = 0.5 + + var/passenger_allowed = 1 + + var/active_engines = 0 + var/train_length = 0 + + var/obj/vehicle/train/lead + var/obj/vehicle/train/tow + + +//------------------------------------------- +// Standard procs +//------------------------------------------- +/obj/vehicle/train/initialize() + for(var/obj/vehicle/train/T in orange(1, src)) + latch(T) + +/obj/vehicle/train/Move() + var/old_loc = get_turf(src) + if(..()) + if(tow) + tow.Move(old_loc) + return 1 + else + if(lead) + unattach() + return 0 + +/obj/vehicle/train/Bump(atom/Obstacle) + if(!istype(Obstacle, /atom/movable)) + return + var/atom/movable/A = Obstacle + + if(!A.anchored) + var/turf/T = get_step(A, dir) + if(isturf(T)) + A.Move(T) //bump things away when hit + + if(emagged) + if(istype(A, /mob/living)) + var/mob/living/M = A + visible_message("\red [src] knocks over [M]!") + M.apply_effects(5, 5) //knock people down if you hit them + M.apply_damages(22 / move_delay) // and do damage according to how fast the train is going + if(istype(load, /mob/living/carbon/human)) + var/mob/living/D = load + D << "\red You hit [M]!" + msg_admin_attack("[D.name] ([D.ckey]) hit [M.name] ([M.ckey]) with [src]. (JMP)") + + +//------------------------------------------- +// Vehicle procs +//------------------------------------------- +/obj/vehicle/train/explode() + if (tow) + tow.unattach() + unattach() + ..() + + +//------------------------------------------- +// Interaction procs +//------------------------------------------- +/obj/vehicle/train/relaymove(mob/user, direction) + var/turf/T = get_step_to(src, get_step(src, direction)) + if(!T) + user << "You can't find a clear area to step onto." + return 0 + + if(user != load) + if(user in src) //for handling players stuck in src - this shouldn't happen - but just in case it does + user.forceMove(T) + return 1 + return 0 + + unload(user, direction) + + user << "\blue You climb down from [src]." + + return 1 + +/obj/vehicle/train/MouseDrop_T(var/atom/movable/C, mob/user as mob) + if(user.buckled || user.stat || user.restrained() || !Adjacent(user) || !user.Adjacent(C) || !istype(C) || (user == C && !user.canmove)) + return + if(istype(C,/obj/vehicle/train)) + latch(C, user) + else + if(!load(C)) + user << "\red You were unable to load [C] on [src]." + +/obj/vehicle/train/attack_hand(mob/user as mob) + if(user.stat || user.restrained() || !Adjacent(user)) + return 0 + + if(user != load && (user in src)) + user.forceMove(loc) //for handling players stuck in src + else if(load) + unload(user) //unload if loaded + else if(!load && !user.buckled) + load(user) //else try climbing on board + else + return 0 + +/obj/vehicle/train/verb/unlatch_v() + set name = "Unlatch" + set desc = "Unhitches this train from the one in front of it." + set category = "Object" + set src in view(1) + + if(!istype(usr, /mob/living/carbon/human)) + return + + if(!usr.canmove || usr.stat || usr.restrained() || !Adjacent(usr)) + return + + unattach(usr) + + +//------------------------------------------- +// Latching/unlatching procs +//------------------------------------------- + +//attempts to attach src as a follower of the train T +/obj/vehicle/train/proc/attach_to(obj/vehicle/train/T, mob/user) + if (get_dist(src, T) > 1) + user << "\red [src] is too far away from [T] to hitch them together." + return + + if (lead) + user << "\red [src] is already hitched to something." + return + + if (T.tow) + user << "\red [T] is already towing something." + return + + //check for cycles. + var/obj/vehicle/train/next_car = T + while (next_car) + if (next_car == src) + user << "\red That seems very silly." + return + next_car = next_car.lead + + //latch with src as the follower + lead = T + T.tow = src + dir = lead.dir + + if(user) + user << "\blue You hitch [src] to [T]." + + update_stats() + + +//detaches the train from whatever is towing it +/obj/vehicle/train/proc/unattach(mob/user) + if (!lead) + user << "\red [src] is not hitched to anything." + return + + lead.tow = null + lead.update_stats() + + user << "\blue You unhitch [src] from [lead]." + lead = null + + update_stats() + +/obj/vehicle/train/proc/latch(obj/vehicle/train/T, mob/user) + if(!istype(T) || !Adjacent(T)) + return 0 + + var/T_dir = get_dir(src, T) //figure out where T is wrt src + + if(dir == T_dir) //if car is ahead + src.attach_to(T, user) + else if(reverse_direction(dir) == T_dir) //else if car is behind + T.attach_to(src, user) + +//returns 1 if this is the lead car of the train +/obj/vehicle/train/proc/is_train_head() + if (lead) + return 0 + return 1 + +//------------------------------------------------------- +// Stat update procs +// +// Used for updating the stats for how long the train is. +// These are useful for calculating speed based on the +// size of the train, to limit super long trains. +//------------------------------------------------------- +/obj/vehicle/train/update_stats() + //first, seek to the end of the train + var/obj/vehicle/train/T = src + while(T.tow) + //check for cyclic train. + if (T.tow == src) + lead.tow = null + lead.update_stats() + + lead = null + update_stats() + return + T = T.tow + + //now walk back to the front. + var/active_engines = 0 + var/train_length = 0 + while(T) + train_length++ + if (powered && on) + active_engines++ + T.update_car(train_length, active_engines) + T = T.lead + +/obj/vehicle/train/proc/update_car(var/train_length, var/active_engines) + return diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm new file mode 100644 index 00000000..76fd7d89 --- /dev/null +++ b/code/modules/vehicles/vehicle.dm @@ -0,0 +1,358 @@ +/obj/vehicle + name = "vehicle" + icon = 'icons/obj/vehicles.dmi' + layer = MOB_LAYER + 0.1 //so it sits above objects including mobs + density = 1 + anchored = 1 + animate_movement=1 + luminosity = 3 + + var/attack_log = null + var/on = 0 + var/health = 0 //do not forget to set health for your vehicle! + var/maxhealth = 0 + var/fire_dam_coeff = 1.0 + var/brute_dam_coeff = 1.0 + var/open = 0 //Maint panel + var/locked = 1 + var/stat = 0 + var/emagged = 0 + var/powered = 0 //set if vehicle is powered and should use fuel when moving + var/move_delay = 1 //set this to limit the speed of the vehicle + var/movable = 1 + + var/obj/item/weapon/cell/cell + var/charge_use = 5 //set this to adjust the amount of power the vehicle uses per move + + var/atom/movable/load //all vehicles can take a load, since they should all be a least drivable + var/load_item_visible = 1 //set if the loaded item should be overlayed on the vehicle sprite + var/load_offset_x = 0 //pixel_x offset for item overlay + var/load_offset_y = 0 //pixel_y offset for item overlay + var/mob_offset_y = 0 //pixel_y offset for mob overlay + +//------------------------------------------- +// Standard procs +//------------------------------------------- +/obj/vehicle/New() + ..() + //spawn the cell you want in each vehicle + +/obj/vehicle/Move() + if(world.time > l_move_time + move_delay) + if(on && powered && cell.charge < charge_use) + turn_off() + + var/init_anc = anchored + anchored = 0 + if(!..()) + anchored = init_anc + return 0 + + anchored = init_anc + + if(on && powered) + cell.use(charge_use) + + if(load) + load.forceMove(loc)// = loc + load.dir = dir + + return 1 + else + return 0 + +/obj/vehicle/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/hand_labeler)) + return + if(istype(W, /obj/item/weapon/screwdriver)) + if(!locked) + open = !open + update_icon() + user << "Maintenance panel is now [open ? "opened" : "closed"]." + else if(istype(W, /obj/item/weapon/crowbar) && cell && open) + remove_cell(user) + + else if(istype(W, /obj/item/weapon/cell) && !cell && open) + insert_cell(W, user) + else if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/T = W + if(T.welding) + if(health < maxhealth) + if(open) + health = min(maxhealth, health+10) + user.visible_message("\red [user] repairs [src]!","\blue You repair [src]!") + else + user << "Unable to repair with the maintenance panel closed." + else + user << "[src] does not need a repair." + else + user << "Unable to repair while [src] is off." + else if(istype(W, /obj/item/weapon/card/emag) && !emagged) + Emag(user) + else if(hasvar(W,"force") && hasvar(W,"damtype")) + switch(W.damtype) + if("fire") + health -= W.force * fire_dam_coeff + if("brute") + health -= W.force * brute_dam_coeff + ..() + healthcheck() + else + ..() + +/obj/vehicle/attack_animal(var/mob/living/simple_animal/M as mob) + if(M.melee_damage_upper == 0) return + health -= M.melee_damage_upper + src.visible_message("\red [M] has [M.attacktext] [src]!") + M.attack_log += text("\[[time_stamp()]\] attacked [src.name]") + if(prob(10)) + new /obj/effect/decal/cleanable/blood/oil(src.loc) + healthcheck() + +/obj/vehicle/bullet_act(var/obj/item/projectile/Proj) + health -= Proj.damage + ..() + healthcheck() + +/obj/vehicle/meteorhit() + explode() + return + +/obj/vehicle/blob_act() + src.health -= rand(20,40)*fire_dam_coeff + healthcheck() + return + +/obj/vehicle/ex_act(severity) + switch(severity) + if(1.0) + explode() + return + if(2.0) + health -= rand(5,10)*fire_dam_coeff + health -= rand(10,20)*brute_dam_coeff + healthcheck() + return + if(3.0) + if (prob(50)) + health -= rand(1,5)*fire_dam_coeff + health -= rand(1,5)*brute_dam_coeff + healthcheck() + return + return + +/obj/vehicle/emp_act(severity) + var/was_on = on + stat |= EMPED + var/obj/effect/overlay/pulse2 = new/obj/effect/overlay ( src.loc ) + pulse2.icon = 'icons/effects/effects.dmi' + pulse2.icon_state = "empdisable" + pulse2.name = "emp sparks" + pulse2.anchored = 1 + pulse2.dir = pick(cardinal) + + spawn(10) + pulse2.delete() + if(on) + turn_off() + spawn(severity*300) + stat &= ~EMPED + if(was_on) + turn_on() + +/obj/vehicle/attack_ai(mob/user as mob) + return + +/obj/vehicle/proc/handle_rotation() + return + +//------------------------------------------- +// Vehicle procs +//------------------------------------------- +/obj/vehicle/proc/turn_on() + if(stat) + return 0 + if(powered && cell.charge < charge_use) + return 0 + on = 1 + luminosity = initial(luminosity) + update_icon() + return 1 + +/obj/vehicle/proc/turn_off() + on = 0 + luminosity = 0 + update_icon() + +/obj/vehicle/proc/Emag(mob/user as mob) + emagged = 1 + + if(locked) + locked = 0 + user << "You bypass [src]'s controls." + +/obj/vehicle/proc/explode() + src.visible_message("\red [src] blows apart!", 1) + var/turf/Tsec = get_turf(src) + + new /obj/item/stack/rods(Tsec) + new /obj/item/stack/rods(Tsec) +// new /obj/item/stack/cable_coil/cut(Tsec) THIS DOESN'T WORK SO I'M GETTING RID OF IT. + + if(cell) + cell.forceMove(Tsec) + cell.update_icon() + cell = null + + //stuns people who are thrown off a train that has been blown up + if(istype(load, /mob/living)) + var/mob/living/M = load + M.apply_effects(5, 5) + + unload() + + new /obj/effect/gibspawner/robot(Tsec) + new /obj/effect/decal/cleanable/blood/oil(src.loc) + + del(src) + +/obj/vehicle/proc/healthcheck() + if(health <= 0) + explode() + +/obj/vehicle/proc/powercheck() + if(!cell && !powered) + return + + if(!cell && powered) + turn_off() + return + + if(cell.charge < charge_use) + turn_off() + return + + if(cell && powered) + turn_on() + return + +/obj/vehicle/proc/insert_cell(var/obj/item/weapon/cell/C, var/mob/living/carbon/human/H) + if(cell) + return + if(!istype(C)) + return + + H.drop_from_inventory(C) + C.forceMove(src) + cell = C + powercheck() + usr << "You install [C] in [src]." + +/obj/vehicle/proc/remove_cell(var/mob/living/carbon/human/H) + if(!cell) + return + + usr << "You remove [cell] from [src]." + cell.forceMove(get_turf(H)) + H.put_in_hands(cell) + cell = null + powercheck() + +/obj/vehicle/proc/RunOver(var/mob/living/carbon/human/H) + return //write specifics for different vehicles + +//------------------------------------------- +// Loading/unloading procs +// +// Set specific item restriction checks in +// the vehicle load() definition before +// calling this parent proc. +//------------------------------------------- +/obj/vehicle/proc/load(var/atom/movable/C) + //define allowed items for loading in specific vehicle definitions + + if(!isturf(C.loc)) //To prevent loading things from someone's inventory, which wouldn't get handled properly. + return 0 + if(load || C.anchored) + return 0 + + // if a create/closet, close before loading + var/obj/structure/closet/crate = C + if(istype(crate)) + crate.close() + + C.forceMove(loc) + C.dir = dir + C.anchored = 1 + + load = C + + if(load_item_visible) + C.pixel_x += load_offset_x + if(ismob(C)) + C.pixel_y += mob_offset_y + else + C.pixel_y += load_offset_y + C.layer = layer + 0.1 //so it sits above the vehicle + + if(ismob(C)) + var/mob/M = C + M.buckled = src + M.update_canmove() + + return 1 + + +/obj/vehicle/proc/unload(var/mob/user, var/direction) + if(!load) + return + + var/turf/dest = null + + //find a turf to unload to + if(direction) //if direction specified, unload in that direction + dest = get_step(src, direction) + else if(user) //if a user has unloaded the vehicle, unload at their feet + dest = get_turf(user) + + if(!dest) + dest = get_step_to(src, get_step(src, turn(dir, 90))) //try unloading to the side of the vehicle first if neither of the above are present + + //if these all result in the same turf as the vehicle or nullspace, pick a new turf with open space + if(!dest || dest == get_turf(src)) + var/list/options = new() + for(var/test_dir in alldirs) + var/new_dir = get_step_to(src, get_step(src, test_dir)) + if(new_dir && load.Adjacent(new_dir)) + options += new_dir + if(options.len) + dest = pick(options) + else + dest = get_turf(src) //otherwise just dump it on the same turf as the vehicle + + if(!isturf(dest)) //if there still is nowhere to unload, cancel out since the vehicle is probably in nullspace + return 0 + + load.forceMove(dest) + load.dir = get_dir(loc, dest) + load.anchored = initial(load.anchored) + load.pixel_x = initial(load.pixel_x) + load.pixel_y = initial(load.pixel_y) + load.layer = initial(load.layer) + + if(ismob(load)) + var/mob/M = load + M.buckled = null + M.anchored = initial(M.anchored) + M.update_canmove() + + load = null + + return 1 + + +//------------------------------------------------------- +// Stat update procs +//------------------------------------------------------- +/obj/vehicle/proc/update_stats() + return \ No newline at end of file diff --git a/data/investigate/gravity.html b/data/investigate/gravity.html index cbab9d0b..7166030e 100644 --- a/data/investigate/gravity.html +++ b/data/investigate/gravity.html @@ -1,12 +1,18 @@ -22:30 [0x20078ca] (92,152,1) || the gravitational generator has regained power.
-22:30 [0x20078ca] (92,152,1) || the gravitational generator is now charging.
-22:31 [0x20078ca] (92,152,1) || the gravitational generator has regained power.
-22:31 [0x20078ca] (92,152,1) || the gravitational generator is now charging.
-22:31 [0x20078ca] (92,152,1) || the gravitational generator has regained power.
-22:31 [0x20078ca] (92,152,1) || the gravitational generator is now charging.
-22:36 [0x20078ca] (92,152,1) || the gravitational generator has regained power.
-22:36 [0x20078ca] (92,152,1) || the gravitational generator is now charging.
-22:41 [0x20078ca] (92,152,1) || the gravitational generator has regained power.
-22:41 [0x20078ca] (92,152,1) || the gravitational generator is now charging.
-22:45 [0x20078ca] (92,152,1) || the gravitational generator has regained power.
-22:45 [0x20078ca] (92,152,1) || the gravitational generator is now charging.
+23:57 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+23:57 [0x200793a] (92,152,1) || the gravitational generator is now charging.
+23:57 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+23:57 [0x200793a] (92,152,1) || the gravitational generator is now charging.
+23:58 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+23:58 [0x200793a] (92,152,1) || the gravitational generator is now charging.
+00:03 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+00:03 [0x200793a] (92,152,1) || the gravitational generator is now charging.
+00:08 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+00:08 [0x200793a] (92,152,1) || the gravitational generator is now charging.
+00:12 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+00:12 [0x200793a] (92,152,1) || the gravitational generator is now charging.
+00:17 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+00:17 [0x200793a] (92,152,1) || the gravitational generator is now charging.
+00:22 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+00:22 [0x200793a] (92,152,1) || the gravitational generator is now charging.
+00:27 [0x200793a] (92,152,1) || the gravitational generator has regained power.
+00:27 [0x200793a] (92,152,1) || the gravitational generator is now charging.
diff --git a/icons/obj/vehicles.dmi b/icons/obj/vehicles.dmi index fd6daee8bd4b59531061149219c3ccd5e641631f..e70275591fc4ea810bbdf564cc1ad63db9bbad72 100644 GIT binary patch literal 10594 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 bpsMmHsSvRVSf+W literal 6970 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