diff --git a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm index aeaf849b73..2e066413f7 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm @@ -270,7 +270,7 @@ /turf/open/floor/wood, /area/ruin/powered/beach) "be" = ( -/obj/vehicle/scooter/skateboard{ +/obj/vehicle/ridden/scooter/skateboard{ dir = 4 }, /turf/open/floor/plating/beach/sand, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm index af51457db9..cc961656f6 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm @@ -528,7 +528,7 @@ /turf/open/floor/plasteel/white, /area/ruin/powered/animal_hospital) "bL" = ( -/obj/vehicle/scooter/skateboard{ +/obj/vehicle/ridden/scooter/skateboard{ dir = 4 }, /turf/open/floor/grass{ diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm index edc6dcffac..593cbdb665 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm @@ -170,7 +170,7 @@ }, /area/ruin/powered/snow_biodome) "aJ" = ( -/obj/vehicle/atv, +/obj/vehicle/ridden/atv, /turf/open/floor/plating/asteroid/snow{ initial_gas_mix = "o2=22;n2=82;TEMP=180" }, diff --git a/_maps/RandomZLevels/Cabin.dmm b/_maps/RandomZLevels/Cabin.dmm index dd40e3c024..4cd42ea994 100644 --- a/_maps/RandomZLevels/Cabin.dmm +++ b/_maps/RandomZLevels/Cabin.dmm @@ -825,7 +825,7 @@ /turf/open/floor/plating/snowed/temperatre, /area/awaymission/cabin) "cT" = ( -/obj/vehicle/atv, +/obj/vehicle/ridden/atv, /turf/open/floor/plating/snowed/temperatre, /area/awaymission/cabin) "cU" = ( diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm index e9f6caa5fb..7e416c0aae 100644 --- a/_maps/RandomZLevels/snowdin.dmm +++ b/_maps/RandomZLevels/snowdin.dmm @@ -15,7 +15,7 @@ /area/awaymission/snowdin/post) "ae" = ( /obj/effect/decal/cleanable/oil, -/obj/vehicle/atv, +/obj/vehicle/ridden/atv, /turf/open/floor/plating{ baseturf = /turf/open/floor/plating/asteroid/snow }, @@ -41,7 +41,7 @@ }, /area/awaymission/snowdin/post) "ai" = ( -/obj/vehicle/atv, +/obj/vehicle/ridden/atv, /turf/open/floor/plating{ baseturf = /turf/open/floor/plating/asteroid/snow }, @@ -1842,7 +1842,7 @@ /turf/open/floor/plating/asteroid/snow, /area/awaymission/snowdin) "eZ" = ( -/obj/vehicle/atv, +/obj/vehicle/ridden/atv, /turf/open/floor/plating/asteroid/snow{ temperature = 140 }, diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index 5c99a5cbcc..9b5e5bf265 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -33081,7 +33081,7 @@ /obj/machinery/camera{ c_tag = "Custodial Closet" }, -/obj/vehicle/janicart, +/obj/vehicle/ridden/janicart, /turf/open/floor/plasteel, /area/janitor) "bDL" = ( @@ -57645,7 +57645,7 @@ /area/space/nearstation) "QoV" = ( /obj/structure/window/reinforced, -/obj/vehicle/secway, +/obj/vehicle/ridden/secway, /obj/item/key/security, /obj/machinery/door/window/eastleft{ name = "Secway Docking Port" @@ -57655,7 +57655,7 @@ /area/security/main) "QoW" = ( /obj/structure/window/reinforced, -/obj/vehicle/secway, +/obj/vehicle/ridden/secway, /obj/item/key/security, /obj/machinery/door/window/eastleft{ name = "Secway Docking Port" diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 0479e1ec28..a05ca17d4b 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -10106,7 +10106,7 @@ /turf/open/floor/plasteel/neutral, /area/janitor) "avA" = ( -/obj/vehicle/janicart, +/obj/vehicle/ridden/janicart, /obj/machinery/status_display{ pixel_y = 32 }, @@ -48974,7 +48974,7 @@ /turf/open/floor/plating, /area/ai_monitored/security/armory) "bVp" = ( -/obj/vehicle/secway, +/obj/vehicle/ridden/secway, /turf/open/floor/plasteel/vault{ dir = 8 }, diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index f0f8fa160c..eade14e21e 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -8769,7 +8769,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 4 }, -/obj/vehicle/secway, +/obj/vehicle/ridden/secway, /obj/item/key/security, /turf/open/floor/plasteel/red/side{ dir = 10 @@ -29270,7 +29270,7 @@ name = "Station Intercom (General)"; pixel_y = -28 }, -/obj/vehicle/janicart, +/obj/vehicle/ridden/janicart, /obj/item/key/janitor, /turf/open/floor/plating, /area/janitor) diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index 00a601dba2..49d03878bb 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -6043,7 +6043,7 @@ /turf/open/floor/plating, /area/security/brig) "akW" = ( -/obj/vehicle/secway, +/obj/vehicle/ridden/secway, /obj/machinery/light{ dir = 8 }, @@ -19057,7 +19057,7 @@ /turf/open/floor/plasteel, /area/janitor) "aJJ" = ( -/obj/vehicle/janicart, +/obj/vehicle/ridden/janicart, /obj/effect/decal/cleanable/dirt, /obj/item/storage/bag/trash, /obj/item/key/janitor, diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index 7b2b7dbea1..67a8b2ec47 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -2614,7 +2614,7 @@ /turf/open/floor/plasteel/dark, /area/security/armory) "ail" = ( -/obj/vehicle/secway, +/obj/vehicle/ridden/secway, /turf/open/floor/plasteel/dark, /area/security/armory) "aim" = ( @@ -2647,7 +2647,7 @@ /turf/open/floor/plasteel/showroomfloor, /area/security/main) "air" = ( -/obj/vehicle/secway, +/obj/vehicle/ridden/secway, /obj/item/key/security, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel/showroomfloor, @@ -21624,7 +21624,7 @@ /turf/open/floor/plasteel/neutral/corner, /area/hallway/primary/central) "baX" = ( -/obj/vehicle/janicart, +/obj/vehicle/ridden/janicart, /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 4a0f5fdb02..007fffd634 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -11604,7 +11604,7 @@ /turf/open/floor/plasteel, /area/wizard_station) "Ej" = ( -/obj/vehicle/scooter/skateboard{ +/obj/vehicle/ridden/scooter/skateboard{ icon_state = "skateboard"; dir = 4 }, diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 244f383ceb..4a50214bb7 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -59,6 +59,8 @@ #define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (/atom/movable) #define COMSIG_MOVABLE_COLLIDE "movable_collide" //from base of atom/movable/Collide(): (/atom) #define COMSIG_MOVABLE_IMPACT "movable_impact" //from base of atom/movable/throw_impact(): (/atom, throwingdatum) +#define COMSIG_MOVABLE_BUCKLE "buckle" //from base of atom/movable/buckle_mob(): (mob, force) +#define COMSIG_MOVABLE_UNBUCKLE "unbuckle" //from base of atom/movable/unbuckle_mob(): (mob, force) // /obj/item signals #define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user) @@ -79,3 +81,7 @@ // /obj/machinery signals #define COMSIG_MACHINE_PROCESS "machine_process" //from machinery subsystem fire(): () #define COMSIG_MACHINE_PROCESS_ATMOS "machine_process_atmos" //from air subsystem process_atmos_machinery(): () + +// /mob/living/carbon/human signals +#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target) +#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker) diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm index 1320250f89..cdd2739b3c 100644 --- a/code/__DEFINES/maps.dm +++ b/code/__DEFINES/maps.dm @@ -48,4 +48,4 @@ Last space-z level = empty #define ZLEVEL_SPACEMIN 3 #define ZLEVEL_SPACEMAX 13 -#define SPACERUIN_MAP_EDGE_PAD 15 \ No newline at end of file +#define SPACERUIN_MAP_EDGE_PAD 15 diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 7cc2e4537f..55b29e64a5 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -4,6 +4,11 @@ // #define EAST 4 // #define WEST 8 +#define TEXT_NORTH "[NORTH]" +#define TEXT_SOUTH "[SOUTH]" +#define TEXT_EAST "[EAST]" +#define TEXT_WEST "[WEST]" + //These get to go at the top, because they're special //You can use these defines to get the typepath of the currently running proc/verb (yes procs + verbs are objects) /* eg: @@ -483,3 +488,5 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE #define NO_SPAWN 0 #define HOSTILE_SPAWN 1 #define FRIENDLY_SPAWN 2 + +#define RIDING_OFFSET_ALL "ALL" diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index c340943a36..02cdfe7c13 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -25,7 +25,9 @@ if(override) return + SendSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, A) A.attack_hand(src) + SendSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY, src) /atom/proc/attack_hand(mob/user) return diff --git a/code/datums/components/riding.dm b/code/datums/components/riding.dm new file mode 100644 index 0000000000..4b18f91014 --- /dev/null +++ b/code/datums/components/riding.dm @@ -0,0 +1,326 @@ +/datum/component/riding + var/next_vehicle_move = 0 //used for move delays + var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower + var/keytype + + var/slowed = FALSE + var/slowvalue = 1 + + var/list/riding_offsets = list() //position_of_user = list(dir = list(px, py)), or RIDING_OFFSET_ALL for a generic one. + var/list/directional_vehicle_layers = list() //["[DIRECTION]"] = layer. Don't set it for a direction for default, set a direction to null for no change. + var/list/directional_vehicle_offsets = list() //same as above but instead of layer you have a list(px, py) + var/list/allowed_turf_typecache + var/list/forbid_turf_typecache //allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence. + var/allow_one_away_from_valid_turf = TRUE //allow moving one tile away from a valid turf but not more. + var/override_allow_spacemove = FALSE + var/drive_verb = "drive" + var/ride_check_rider_incapacitated = FALSE + var/ride_check_rider_restrained = FALSE + var/ride_check_ridden_incapacitated = FALSE + +/datum/component/riding/Initialize() + if(!ismovableatom(parent)) + . = COMPONENT_INCOMPATIBLE + CRASH("RIDING COMPONENT ASSIGNED TO NON ATOM MOVABLE!") + RegisterSignal(COMSIG_MOVABLE_BUCKLE, .proc/vehicle_mob_buckle) + RegisterSignal(COMSIG_MOVABLE_UNBUCKLE, .proc/vehicle_mob_unbuckle) + RegisterSignal(COMSIG_MOVABLE_MOVED, .proc/vehicle_moved) + +/datum/component/riding/proc/vehicle_mob_unbuckle(mob/living/M, force = FALSE) + restore_position(M) + unequip_buckle_inhands(M) + +/datum/component/riding/proc/vehicle_mob_buckle(mob/living/M, force = FALSE) + handle_vehicle_offsets() + +/datum/component/riding/proc/handle_vehicle_layer() + var/atom/movable/AM = parent + var/static/list/defaults = list(TEXT_NORTH = OBJ_LAYER, TEXT_SOUTH = ABOVE_MOB_LAYER, TEXT_EAST = ABOVE_MOB_LAYER, TEXT_WEST = ABOVE_MOB_LAYER) + . = defaults["[AM.dir]"] + if(directional_vehicle_layers["[AM.dir]"]) + . = directional_vehicle_layers["[AM.dir]"] + if(isnull(.)) //you can set it to null to not change it. + . = AM.layer + AM.layer = . + +/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer) + directional_vehicle_layers["[dir]"] = layer + +/datum/component/riding/proc/vehicle_moved() + var/atom/movable/AM = parent + for(var/i in AM.buckled_mobs) + ride_check(i) + handle_vehicle_offsets() + handle_vehicle_layer() + +/datum/component/riding/proc/ride_check(mob/living/M) + var/atom/movable/AM = parent + var/mob/AMM = AM + if((ride_check_rider_restrained && M.restrained(TRUE)) || (ride_check_rider_incapacitated && M.incapacitated(FALSE, TRUE)) || (ride_check_ridden_incapacitated && istype(AMM) && AMM.incapacitated(FALSE, TRUE))) + AM.visible_message("[M] falls off of [AM]!") + AM.unbuckle_mob(M) + return TRUE + +/datum/component/riding/proc/force_dismount(mob/living/M) + var/atom/movable/AM = parent + AM.unbuckle_mob(M) + +/datum/component/riding/proc/handle_vehicle_offsets() + var/atom/movable/AM = parent + var/AM_dir = "[AM.dir]" + var/passindex = 0 + if(AM.has_buckled_mobs()) + for(var/m in AM.buckled_mobs) + passindex++ + var/mob/living/buckled_mob = m + var/list/offsets = get_offsets(passindex) + var/rider_dir = get_rider_dir(passindex) + buckled_mob.setDir(rider_dir) + dir_loop: + for(var/offsetdir in offsets) + if(offsetdir == AM_dir) + var/list/diroffsets = offsets[offsetdir] + buckled_mob.pixel_x = diroffsets[1] + if(diroffsets.len >= 2) + buckled_mob.pixel_y = diroffsets[2] + if(diroffsets.len == 3) + buckled_mob.layer = diroffsets[3] + break dir_loop + var/list/static/default_vehicle_pixel_offsets = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0)) + var/px = default_vehicle_pixel_offsets[AM_dir] + var/py = default_vehicle_pixel_offsets[AM_dir] + if(directional_vehicle_offsets[AM_dir]) + if(isnull(directional_vehicle_offsets[AM_dir])) + px = AM.pixel_x + py = AM.pixel_y + else + px = directional_vehicle_offsets[AM_dir][1] + py = directional_vehicle_offsets[AM_dir][2] + AM.pixel_x = px + AM.pixel_y = py + +/datum/component/riding/proc/set_vehicle_dir_offsets(dir, x, y) + directional_vehicle_offsets["[dir]"] = list(x, y) + +//Override this to set your vehicle's various pixel offsets +/datum/component/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer) + . = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0)) + if(riding_offsets["[pass_index]"]) + . = riding_offsets["[pass_index]"] + else if(riding_offsets["[RIDING_OFFSET_ALL]"]) + . = riding_offsets["[RIDING_OFFSET_ALL]"] + +/datum/component/riding/proc/set_riding_offsets(index, list/offsets) + if(!islist(offsets)) + return FALSE + riding_offsets["[index]"] = offsets + +//Override this to set the passengers/riders dir based on which passenger they are. +//ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc. +/datum/component/riding/proc/get_rider_dir(pass_index) + var/atom/movable/AM = parent + return AM.dir + +//KEYS +/datum/component/riding/proc/keycheck(mob/user) + return !keytype || user.is_holding_item_of_type(keytype) + +//BUCKLE HOOKS +/datum/component/riding/proc/restore_position(mob/living/buckled_mob) + if(buckled_mob) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 0 + if(buckled_mob.client) + buckled_mob.client.change_view(world.view) + +//MOVEMENT +/datum/component/riding/proc/turf_check(turf/next, turf/current) + if(allowed_turf_typecache && !allowed_turf_typecache[next.type]) + return (allow_one_away_from_valid_turf && allowed_turf_typecache[current.type]) + else if(forbid_turf_typecache && forbid_turf_typecache[next.type]) + return (allow_one_away_from_valid_turf && !forbid_turf_typecache[current.type]) + return TRUE + +/datum/component/riding/proc/handle_ride(mob/user, direction) + var/atom/movable/AM = parent + if(user.incapacitated()) + Unbuckle(user) + return + + if(world.time < next_vehicle_move) + return + next_vehicle_move = world.time + vehicle_move_delay + + if(keycheck(user)) + var/turf/next = get_step(AM, direction) + var/turf/current = get_turf(AM) + if(!istype(next) || !istype(current)) + return //not happening. + if(!turf_check(next, current)) + to_chat(user, "Your \the [AM] can not go onto [next]!") + return + if(!Process_Spacemove(direction) || !isturf(AM.loc)) + return + step(AM, direction) + + handle_vehicle_layer() + handle_vehicle_offsets() + else + to_chat(user, "You'll need the keys in one of your hands to [drive_verb] [AM].") + +/datum/component/riding/proc/Unbuckle(atom/movable/M) + addtimer(CALLBACK(parent, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE) + +/datum/component/riding/proc/Process_Spacemove(direction) + var/atom/movable/AM = parent + return override_allow_spacemove || AM.has_gravity() + +/datum/component/riding/proc/account_limbs(mob/living/M) + if(M.get_num_legs() < 2 && !slowed) + vehicle_move_delay = vehicle_move_delay + slowvalue + slowed = TRUE + else if(slowed) + vehicle_move_delay = vehicle_move_delay - slowvalue + slowed = FALSE + +///////Yes, I said humans. No, this won't end well...////////// +/datum/component/riding/human + +/datum/component/riding/human/Initialize() + . = ..() + RegisterSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee) + +/datum/component/riding/human/proc/on_host_unarmed_melee(atom/target) + var/mob/living/carbon/human/AM = parent + if(AM.a_intent == INTENT_DISARM && (target in AM.buckled_mobs)) + force_dismount(target) + +/datum/component/riding/human/handle_vehicle_layer() + var/atom/movable/AM = parent + if(AM.buckled_mobs && AM.buckled_mobs.len) + if(AM.dir == SOUTH) + AM.layer = ABOVE_MOB_LAYER + else + AM.layer = OBJ_LAYER + else + AM.layer = MOB_LAYER + +/datum/component/riding/human/force_dismount(mob/living/user) + var/atom/movable/AM = parent + AM.unbuckle_mob(user) + user.Knockdown(60) + user.visible_message("[AM] pushes [user] off of them!") + +/datum/component/riding/cyborg + +/datum/component/riding/cyborg/ride_check(mob/user) + var/atom/movable/AM = parent + if(user.incapacitated()) + var/kick = TRUE + if(iscyborg(AM)) + var/mob/living/silicon/robot/R = AM + if(R.module && R.module.ride_allow_incapacitated) + kick = FALSE + if(kick) + to_chat(user, "You fall off of [AM]!") + Unbuckle(user) + return + if(iscarbon(user)) + var/mob/living/carbon/carbonuser = user + if(!carbonuser.get_num_arms()) + Unbuckle(user) + to_chat(user, "You can't grab onto [AM] with no hands!") + return + +/datum/component/riding/cyborg/handle_vehicle_layer() + var/atom/movable/AM = parent + if(AM.buckled_mobs && AM.buckled_mobs.len) + if(AM.dir == SOUTH) + AM.layer = ABOVE_MOB_LAYER + else + AM.layer = OBJ_LAYER + else + AM.layer = MOB_LAYER + +/datum/component/riding/cyborg/get_offsets(pass_index) // list(dir = x, y, layer) + return list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(-6, 3), TEXT_WEST = list( 6, 3)) + +/datum/component/riding/cyborg/handle_vehicle_offsets() + var/atom/movable/AM = parent + if(AM.has_buckled_mobs()) + for(var/mob/living/M in AM.buckled_mobs) + M.setDir(AM.dir) + if(iscyborg(AM)) + var/mob/living/silicon/robot/R = AM + if(istype(R.module)) + M.pixel_x = R.module.ride_offset_x[dir2text(AM.dir)] + M.pixel_y = R.module.ride_offset_y[dir2text(AM.dir)] + else + ..() + +/datum/component/riding/cyborg/force_dismount(mob/living/M) + var/atom/movable/AM = parent + AM.unbuckle_mob(M) + var/turf/target = get_edge_target_turf(AM, AM.dir) + var/turf/targetm = get_step(get_turf(AM), AM.dir) + M.Move(targetm) + M.visible_message("[M] is thrown clear of [AM]!") + M.throw_at(target, 14, 5, AM) + M.Knockdown(60) + +/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1) + var/atom/movable/AM = parent + var/amount_equipped = 0 + for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--) + var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user) + inhand.rider = user + inhand.parent = AM + if(user.put_in_hands(inhand, TRUE)) + amount_equipped++ + else + break + if(amount_equipped >= amount_required) + return TRUE + else + unequip_buckle_inhands(user) + return FALSE + +/datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user) + var/atom/movable/AM = parent + for(var/obj/item/riding_offhand/O in user.contents) + if(O.parent != AM) + CRASH("RIDING OFFHAND ON WRONG MOB") + continue + if(O.selfdeleting) + continue + else + qdel(O) + return TRUE + +/obj/item/riding_offhand + name = "offhand" + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "offhand" + w_class = WEIGHT_CLASS_HUGE + flags_1 = ABSTRACT_1 | DROPDEL_1 | NOBLUDGEON_1 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/mob/living/carbon/rider + var/mob/living/parent + var/selfdeleting = FALSE + +/obj/item/riding_offhand/dropped() + selfdeleting = TRUE + . = ..() + +/obj/item/riding_offhand/equipped() + if(loc != rider) + selfdeleting = TRUE + qdel(src) + . = ..() + +/obj/item/riding_offhand/Destroy() + var/atom/movable/AM = parent + if(selfdeleting) + if(rider in AM.buckled_mobs) + AM.unbuckle_mob(rider) + . = ..() diff --git a/code/datums/riding.dm b/code/datums/riding.dm deleted file mode 100644 index 7cd12ae6c8..0000000000 --- a/code/datums/riding.dm +++ /dev/null @@ -1,442 +0,0 @@ -/datum/riding - var/next_vehicle_move = 0 //used for move delays - var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower - var/keytype = null - var/atom/movable/ridden = null - - var/slowed = FALSE - var/slowvalue = 1 - -/datum/riding/New(atom/movable/_ridden) - ridden = _ridden - -/datum/riding/Destroy() - ridden = null - return ..() - -/datum/riding/proc/handle_vehicle_layer() - if(ridden.dir != NORTH) - ridden.layer = ABOVE_MOB_LAYER - else - ridden.layer = OBJ_LAYER - -/datum/riding/proc/on_vehicle_move() - for(var/mob/living/M in ridden.buckled_mobs) - ride_check(M) - handle_vehicle_offsets() - handle_vehicle_layer() - -/datum/riding/proc/ride_check(mob/living/M) - return TRUE - -/datum/riding/proc/force_dismount(mob/living/M) - ridden.unbuckle_mob(M) - -/datum/riding/proc/handle_vehicle_offsets() - var/ridden_dir = "[ridden.dir]" - var/passindex = 0 - if(ridden.has_buckled_mobs()) - for(var/m in ridden.buckled_mobs) - passindex++ - var/mob/living/buckled_mob = m - var/list/offsets = get_offsets(passindex) - var/rider_dir = get_rider_dir(passindex) - buckled_mob.setDir(rider_dir) - dir_loop: - for(var/offsetdir in offsets) - if(offsetdir == ridden_dir) - var/list/diroffsets = offsets[offsetdir] - buckled_mob.pixel_x = diroffsets[1] - if(diroffsets.len >= 2) - buckled_mob.pixel_y = diroffsets[2] - if(diroffsets.len == 3) - buckled_mob.layer = diroffsets[3] - break dir_loop - - -//Override this to set your vehicle's various pixel offsets -/datum/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0, 0), "[SOUTH]" = list(0, 0), "[EAST]" = list(0, 0), "[WEST]" = list(0, 0)) - -//Override this to set the passengers/riders dir based on which passenger they are. -//ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc. -/datum/riding/proc/get_rider_dir(pass_index) - return ridden.dir - -//KEYS -/datum/riding/proc/keycheck(mob/user) - if(keytype) - if(user.is_holding_item_of_type(keytype)) - return TRUE - else - return TRUE - return FALSE - -//BUCKLE HOOKS -/datum/riding/proc/restore_position(mob/living/buckled_mob) - if(istype(buckled_mob)) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 0 - if(buckled_mob.client) - buckled_mob.client.change_view(world.view) - -//MOVEMENT -/datum/riding/proc/handle_ride(mob/user, direction) - if(user.incapacitated()) - Unbuckle(user) - return - - if(world.time < next_vehicle_move) - return - next_vehicle_move = world.time + vehicle_move_delay - if(keycheck(user)) - if(!Process_Spacemove(direction) || !isturf(ridden.loc)) - return - step(ridden, direction) - - handle_vehicle_layer() - handle_vehicle_offsets() - else - to_chat(user, "You'll need the keys in one of your hands to drive \the [ridden.name].") - -/datum/riding/proc/Unbuckle(atom/movable/M) - addtimer(CALLBACK(ridden, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE) - -/datum/riding/proc/Process_Spacemove(direction) - if(ridden.has_gravity()) - return 1 - - return 0 - -/datum/riding/space/Process_Spacemove(direction) - return 1 - - -//atv -/datum/riding/atv - keytype = /obj/item/key - vehicle_move_delay = 1 - -/datum/riding/atv/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0, 4), "[SOUTH]" = list(0, 4), "[EAST]" = list(0, 4), "[WEST]" = list( 0, 4)) - - -/datum/riding/atv/handle_vehicle_layer() - if(ridden.dir == SOUTH) - ridden.layer = ABOVE_MOB_LAYER - else - ridden.layer = OBJ_LAYER - -/datum/riding/atv/turret - var/obj/machinery/porta_turret/syndicate/vehicle_turret/turret = null - -/datum/riding/atv/turret/handle_vehicle_layer() - if(ridden.dir == SOUTH) - ridden.layer = ABOVE_MOB_LAYER - else - ridden.layer = OBJ_LAYER - - if(turret) - if(ridden.dir == NORTH) - turret.layer = ABOVE_MOB_LAYER - else - turret.layer = OBJ_LAYER - - -/datum/riding/atv/turret/handle_vehicle_offsets() - ..() - if(turret) - turret.forceMove(get_turf(ridden)) - switch(ridden.dir) - if(NORTH) - turret.pixel_x = 0 - turret.pixel_y = 4 - if(EAST) - turret.pixel_x = -12 - turret.pixel_y = 4 - if(SOUTH) - turret.pixel_x = 0 - turret.pixel_y = 4 - if(WEST) - turret.pixel_x = 12 - turret.pixel_y = 4 - - -//pimpin ride -/datum/riding/janicart - keytype = /obj/item/key/janitor - - -/datum/riding/janicart/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0, 4), "[SOUTH]" = list(0, 7), "[EAST]" = list(-12, 7), "[WEST]" = list( 12, 7)) - -//scooter -/datum/riding/scooter/handle_vehicle_layer() - if(ridden.dir == SOUTH) - ridden.layer = ABOVE_MOB_LAYER - else - ridden.layer = OBJ_LAYER - -/datum/riding/scooter/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0), "[SOUTH]" = list(-2), "[EAST]" = list(0), "[WEST]" = list( 2)) - -/datum/riding/scooter/handle_vehicle_offsets() - ..() - if(ridden.has_buckled_mobs()) - for(var/m in ridden.buckled_mobs) - var/mob/living/buckled_mob = m - if(buckled_mob.get_num_legs() > 0) - buckled_mob.pixel_y = 5 - else - buckled_mob.pixel_y = -4 - -/datum/riding/proc/account_limbs(mob/living/M) - if(M.get_num_legs() < 2 && !slowed) - vehicle_move_delay = vehicle_move_delay + slowvalue - slowed = TRUE - else if(slowed) - vehicle_move_delay = vehicle_move_delay - slowvalue - slowed = FALSE - -/datum/riding/scooter/skateboard - vehicle_move_delay = 0//fast - - -//secway -/datum/riding/secway - keytype = /obj/item/key/security - -/datum/riding/secway/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0, 4), "[SOUTH]" = list(0, 4), "[EAST]" = list(0, 4), "[WEST]" = list( 0, 4)) - -//i want to ride my -/datum/riding/bicycle - keytype = null - vehicle_move_delay = 0 - -/datum/riding/bicycle/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0, 4), "[SOUTH]" = list(0, 4), "[EAST]" = list(0, 4), "[WEST]" = list( 0, 4)) - -//speedbike -/datum/riding/space/speedbike - keytype = null - vehicle_move_delay = 0 - -/datum/riding/space/speedbike/handle_vehicle_layer() - switch(ridden.dir) - if(NORTH,SOUTH) - ridden.pixel_x = -16 - ridden.pixel_y = -16 - if(EAST,WEST) - ridden.pixel_x = -18 - ridden.pixel_y = 0 - -/datum/riding/space/speedbike/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0, -8), "[SOUTH]" = list(0, 4), "[EAST]" = list(-10, 5), "[WEST]" = list( 10, 5)) - -//SPEEDUWAGON - -/datum/riding/space/speedwagon - vehicle_move_delay = 0 - -/datum/riding/space/speedwagon/handle_vehicle_layer() - ridden.layer = BELOW_MOB_LAYER - -/datum/riding/space/speedwagon/get_offsets(pass_index) // list(dir = x, y, layer) - switch(pass_index) - if(1) - return list("[NORTH]" = list(-10, -4), "[SOUTH]" = list(16, 3), "[EAST]" = list(-4, 30), "[WEST]" = list(4, -3)) - if(2) - return list("[NORTH]" = list(19, -5, 4), "[SOUTH]" = list(-13, 3, 4), "[EAST]" = list(-4, -3, 4.1), "[WEST]" = list(4, 28, 3.9)) - if(3) - return list("[NORTH]" = list(-10, -18, 4.2), "[SOUTH]" = list(16, 25, 3.9), "[EAST]" = list(-22, 30), "[WEST]" = list(22, -3, 4.1)) - if(4) - return list("[NORTH]" = list(19, -18, 4.2), "[SOUTH]" = list(-13, 25, 3.9), "[EAST]" = list(-22, 3, 3.9), "[WEST]" = list(22, 28)) - -///////////////BOATS//////////// -/datum/riding/boat - keytype = /obj/item/oar - -/datum/riding/boat/handle_ride(mob/user, direction) - var/turf/next = get_step(ridden, direction) - var/turf/current = get_turf(ridden) - - if(islava(next) || islava(current)) //We can move from land to lava, or lava to land, but not from land to land - ..() - else - to_chat(user, "Boats don't go on land!") - return 0 - -/datum/riding/boat/dragon - keytype = null - vehicle_move_delay = 1 - -/datum/riding/boat/dragon/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(1, 2), "[SOUTH]" = list(1, 2), "[EAST]" = list(1, 2), "[WEST]" = list( 1, 2)) - -///////////////ANIMALS//////////// -//general animals -/datum/riding/animal - keytype = null - -/datum/riding/animal/handle_ride(mob/user, direction) - if(user.incapacitated()) - Unbuckle(user) - return - - if(world.time < next_vehicle_move) - return - - next_vehicle_move = world.time + vehicle_move_delay - if(keycheck(user)) - if(!isturf(ridden.loc)) - return - step(ridden, direction) - - handle_vehicle_layer() - handle_vehicle_offsets() - else - to_chat(user, "You'll need something to guide the [ridden.name].") - -///////Humans. Yes, I said humans. No, this won't end well...////////// -/datum/riding/human - keytype = null - -/datum/riding/human/ride_check(mob/living/M) - var/mob/living/carbon/human/H = ridden //IF this runtimes I'm blaming the admins. - if(M.incapacitated(FALSE, TRUE) || H.incapacitated(FALSE, TRUE)) - M.visible_message("[M] falls off [ridden]!") - Unbuckle(M) - return FALSE - if(M.restrained(TRUE)) - M.visible_message("[M] can't hang onto [ridden] with their hands cuffed!") //Honestly this should put the ridden mob in a chokehold. - Unbuckle(M) - return FALSE - if(H.pulling == M) - H.stop_pulling() - -/datum/riding/human/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0, 6), "[SOUTH]" = list(0, 6), "[EAST]" = list(-6, 4), "[WEST]" = list( 6, 4)) - - -/datum/riding/human/handle_vehicle_layer() - if(ridden.buckled_mobs && ridden.buckled_mobs.len) - if(ridden.dir == SOUTH) - ridden.layer = ABOVE_MOB_LAYER - else - ridden.layer = OBJ_LAYER - else - ridden.layer = MOB_LAYER - -/datum/riding/human/force_dismount(mob/living/user) - ridden.unbuckle_mob(user) - user.Knockdown(60) - user.visible_message("[ridden] pushes [user] off of them!") - -/datum/riding/cyborg - keytype = null - -/datum/riding/cyborg/ride_check(mob/user) - if(user.incapacitated()) - var/kick = TRUE - if(iscyborg(ridden)) - var/mob/living/silicon/robot/R = ridden - if(R.module && R.module.ride_allow_incapacitated) - kick = FALSE - if(kick) - to_chat(user, "You fall off of [ridden]!") - Unbuckle(user) - return - if(iscarbon(user)) - var/mob/living/carbon/carbonuser = user - if(!carbonuser.get_num_arms()) - Unbuckle(user) - to_chat(user, "You can't grab onto [ridden] with no hands!") - return - -/datum/riding/cyborg/handle_vehicle_layer() - if(ridden.buckled_mobs && ridden.buckled_mobs.len) - if(ridden.dir == SOUTH) - ridden.layer = ABOVE_MOB_LAYER - else - ridden.layer = OBJ_LAYER - else - ridden.layer = MOB_LAYER - -/datum/riding/cyborg/get_offsets(pass_index) // list(dir = x, y, layer) - return list("[NORTH]" = list(0, 4), "[SOUTH]" = list(0, 4), "[EAST]" = list(-6, 3), "[WEST]" = list( 6, 3)) - -/datum/riding/cyborg/handle_vehicle_offsets() - if(ridden.has_buckled_mobs()) - for(var/mob/living/M in ridden.buckled_mobs) - M.setDir(ridden.dir) - if(iscyborg(ridden)) - var/mob/living/silicon/robot/R = ridden - if(istype(R.module)) - M.pixel_x = R.module.ride_offset_x[dir2text(ridden.dir)] - M.pixel_y = R.module.ride_offset_y[dir2text(ridden.dir)] - else - ..() - -/datum/riding/cyborg/force_dismount(mob/living/M) - ridden.unbuckle_mob(M) - var/turf/target = get_edge_target_turf(ridden, ridden.dir) - var/turf/targetm = get_step(get_turf(ridden), ridden.dir) - M.Move(targetm) - M.visible_message("[M] is thrown clear of [ridden]!") - M.throw_at(target, 14, 5, ridden) - M.Knockdown(60) - -/datum/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1) - var/amount_equipped = 0 - for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--) - var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user) - inhand.rider = user - inhand.ridden = ridden - if(user.put_in_hands(inhand, TRUE)) - amount_equipped++ - else - break - if(amount_equipped >= amount_required) - return TRUE - else - unequip_buckle_inhands(user) - return FALSE - -/datum/riding/proc/unequip_buckle_inhands(mob/living/carbon/user) - for(var/obj/item/riding_offhand/O in user.contents) - if(O.ridden != ridden) - CRASH("RIDING OFFHAND ON WRONG MOB") - continue - if(O.selfdeleting) - continue - else - qdel(O) - return TRUE - -/obj/item/riding_offhand - name = "offhand" - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "offhand" - w_class = WEIGHT_CLASS_HUGE - flags_1 = ABSTRACT_1 | DROPDEL_1 | NOBLUDGEON_1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/mob/living/carbon/rider - var/mob/living/ridden - var/selfdeleting = FALSE - -/obj/item/riding_offhand/dropped() - selfdeleting = TRUE - . = ..() - -/obj/item/riding_offhand/equipped() - if(loc != rider) - selfdeleting = TRUE - qdel(src) - . = ..() - -/obj/item/riding_offhand/Destroy() - if(selfdeleting) - if(rider in ridden.buckled_mobs) - ridden.unbuckle_mob(rider) - . = ..() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 9d3b208acc..f73de90536 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -26,6 +26,7 @@ appearance_flags = TILE_BOUND|PIXEL_SCALE var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm var/floating = FALSE + var/movement_type = GROUND //Incase you have multiple types, you automatically use the most useful one. IE: Skating on ice, flippers on water, flying over chasm/space, etc. /atom/movable/vv_edit_var(var_name, var_value) var/static/list/banned_edits = list("step_x", "step_y", "step_size") diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 0c72e63feb..2e13cb16c9 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -65,6 +65,7 @@ M.throw_alert("buckled", /obj/screen/alert/restrained/buckled) post_buckle_mob(M) + SendSignal(COMSIG_MOVABLE_BUCKLE, M, force) return TRUE /obj/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE) @@ -82,8 +83,9 @@ buckled_mob.update_canmove() buckled_mob.clear_alert("buckled") buckled_mobs -= buckled_mob + SendSignal(COMSIG_MOVABLE_UNBUCKLE, buckled_mob, force) - post_buckle_mob(.) + post_unbuckle_mob(.) /atom/movable/proc/unbuckle_all_mobs(force=FALSE) if(!has_buckled_mobs()) @@ -91,11 +93,12 @@ for(var/m in buckled_mobs) unbuckle_mob(m, force) -//Handle any extras after buckling/unbuckling -//Called on buckle_mob() and unbuckle_mob() +//Handle any extras after buckling +//Called on buckle_mob() /atom/movable/proc/post_buckle_mob(mob/living/M) - return +//same but for unbuckle +/atom/movable/proc/post_unbuckle_mob(mob/living/M) //Wrapper procs that handle sanity and user feedback /atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE) diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index d1de04923c..acdb0b874d 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -102,7 +102,11 @@ appearance = saved_appearance if(istype(M.buckled, /obj/vehicle)) var/obj/vehicle/V = M.buckled - V.riding_datum.force_dismount(M) + GET_COMPONENT_FROM(VRD, /datum/component/riding, V) + if(VRD) + VRD.force_dismount(M) + else + V.unbuckle_mob(M, force = TRUE) M.loc = src master = C master.active_dummy = src diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 7b7c9c8ee5..c112e103b2 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -474,7 +474,7 @@ attack_verb = list("smacked", "whacked", "slammed", "smashed") /obj/item/melee/skateboard/attack_self(mob/user) - new /obj/vehicle/scooter/skateboard(get_turf(user)) + new /obj/vehicle/ridden/scooter/skateboard(get_turf(user)) qdel(src) /obj/item/melee/baseball_bat diff --git a/code/game/objects/structures/beds_chairs/alien_nest.dm b/code/game/objects/structures/beds_chairs/alien_nest.dm index f840316aa9..d3b62f35f4 100644 --- a/code/game/objects/structures/beds_chairs/alien_nest.dm +++ b/code/game/objects/structures/beds_chairs/alien_nest.dm @@ -66,16 +66,16 @@ "You hear squelching...") /obj/structure/bed/nest/post_buckle_mob(mob/living/M) - if(M in buckled_mobs) - M.pixel_y = 0 - M.pixel_x = initial(M.pixel_x) + 2 - M.layer = BELOW_MOB_LAYER - add_overlay(nest_overlay) - else - M.pixel_x = M.get_standard_pixel_x_offset(M.lying) - M.pixel_y = M.get_standard_pixel_y_offset(M.lying) - M.layer = initial(M.layer) - cut_overlay(nest_overlay) + M.pixel_y = 0 + M.pixel_x = initial(M.pixel_x) + 2 + M.layer = BELOW_MOB_LAYER + add_overlay(nest_overlay) + +/obj/structure/bed/nest/post_unbuckle_mob(mob/living/M) + M.pixel_x = M.get_standard_pixel_x_offset(M.lying) + M.pixel_y = M.get_standard_pixel_y_offset(M.lying) + M.layer = initial(M.layer) + cut_overlay(nest_overlay) /obj/structure/bed/nest/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) switch(damage_type) diff --git a/code/game/objects/structures/beds_chairs/bed.dm b/code/game/objects/structures/beds_chairs/bed.dm index 9ad24d1cdb..ab285cbbd6 100644 --- a/code/game/objects/structures/beds_chairs/bed.dm +++ b/code/game/objects/structures/beds_chairs/bed.dm @@ -85,21 +85,21 @@ qdel(src) /obj/structure/bed/roller/post_buckle_mob(mob/living/M) - if(M in buckled_mobs) - density = TRUE - icon_state = "up" - M.pixel_y = initial(M.pixel_y) - else - density = FALSE - icon_state = "down" - M.pixel_x = M.get_standard_pixel_x_offset(M.lying) - M.pixel_y = M.get_standard_pixel_y_offset(M.lying) + density = TRUE + icon_state = "up" + M.pixel_y = initial(M.pixel_y) /obj/structure/bed/roller/Moved() . = ..() if(has_gravity()) playsound(src, 'sound/effects/roll.ogg', 100, 1) +/obj/structure/bed/roller/post_unbuckle_mob(mob/living/M) + density = FALSE + icon_state = "down" + M.pixel_x = M.get_standard_pixel_x_offset(M.lying) + M.pixel_y = M.get_standard_pixel_y_offset(M.lying) + /obj/item/roller name = "roller bed" desc = "A collapsed roller bed that can be carried around." diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 6c1c0392b3..2fb2d5253d 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -91,7 +91,11 @@ layer = OBJ_LAYER /obj/structure/chair/post_buckle_mob(mob/living/M) - ..() + . = ..() + handle_layer() + +/obj/structure/chair/post_unbuckle_mob() + . = ..() handle_layer() /obj/structure/chair/proc/spin() @@ -167,12 +171,18 @@ return ..() /obj/structure/chair/comfy/post_buckle_mob(mob/living/M) - ..() + . = ..() + update_armrest() + +/obj/structure/chair/comfy/proc/update_armrest() if(has_buckled_mobs()) add_overlay(armrest) else cut_overlay(armrest) +/obj/structure/chair/comfy/post_unbuckle_mob() + . = ..() + update_armrest() /obj/structure/chair/comfy/brown color = rgb(255,113,0) diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index c4c6899683..13325a23c8 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -54,7 +54,7 @@ name = "Biker Gang Kit" //TUNNEL SNAKES OWN THIS TOWN cost = 2000 contraband = TRUE - contains = list(/obj/vehicle/atv, + contains = list(/obj/vehicle/ridden/atv, /obj/item/key, /obj/item/clothing/suit/jacket/leather/overcoat, /obj/item/clothing/gloves/color/black, diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index 0378d44809..7ad03c6e9d 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -357,7 +357,7 @@ /datum/crafting_recipe/skateboard name = "Skateboard" - result = /obj/vehicle/scooter/skateboard + result = /obj/vehicle/ridden/scooter/skateboard time = 60 reqs = list(/obj/item/stack/sheet/metal = 5, /obj/item/stack/rods = 10) @@ -365,7 +365,7 @@ /datum/crafting_recipe/scooter name = "Scooter" - result = /obj/vehicle/scooter + result = /obj/vehicle/ridden/scooter time = 65 reqs = list(/obj/item/stack/sheet/metal = 5, /obj/item/stack/rods = 12) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 18eb52c8f8..9a4918e1fa 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -469,16 +469,19 @@ //Boat -/obj/vehicle/lavaboat +/obj/vehicle/ridden/lavaboat name = "lava boat" desc = "A boat used for traversing lava." icon_state = "goliath_boat" icon = 'icons/obj/lavaland/dragonboat.dmi' resistance_flags = LAVA_PROOF | FIRE_PROOF + can_buckle = TRUE -/obj/vehicle/lavaboat/buckle_mob(mob/living/M, force = 0, check_loc = 1) +/obj/vehicle/ridden/lavaboat/Initialize() . = ..() - riding_datum = new/datum/riding/boat + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.keytype = /obj/item/oar + D.allowed_turf_typecache = typecacheof(/turf/open/lava) /obj/item/oar name = "oar" @@ -501,7 +504,7 @@ /datum/crafting_recipe/boat name = "goliath hide boat" - result = /obj/vehicle/lavaboat + result = /obj/vehicle/ridden/lavaboat reqs = list(/obj/item/stack/sheet/animalhide/goliath_hide = 3) time = 50 category = CAT_PRIMAL @@ -518,17 +521,20 @@ /obj/item/ship_in_a_bottle/attack_self(mob/user) to_chat(user, "You're not sure how they get the ships in these things, but you're pretty sure you know how to get it out.") playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, 1) - new /obj/vehicle/lavaboat/dragon(get_turf(src)) + new /obj/vehicle/ridden/lavaboat/dragon(get_turf(src)) qdel(src) -/obj/vehicle/lavaboat/dragon +/obj/vehicle/ridden/lavaboat/dragon name = "mysterious boat" desc = "This boat moves where you will it, without the need for an oar." icon_state = "dragon_boat" -/obj/vehicle/lavaboat/dragon/buckle_mob(mob/living/M, force = 0, check_loc = 1) - ..() - riding_datum = new/datum/riding/boat/dragon +/obj/vehicle/ridden/lavaboat/dragon/Initialize() + . = ..() + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.vehicle_move_delay = 1 + D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(1, 2), TEXT_SOUTH = list(1, 2), TEXT_EAST = list(1, 2), TEXT_WEST = list( 1, 2))) + D.keytype = null //Potion of Flight /obj/item/reagent_containers/glass/bottle/potion diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 7d4c62d9e1..f0c630421d 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -890,8 +890,11 @@ if(!is_type_in_typecache(M, can_ride_typecache)) M.visible_message("[M] really can't seem to mount [src]...") return - if(!riding_datum) - riding_datum = new /datum/riding/human(src) + var/datum/component/riding/human/riding_datum = LoadComponent(/datum/component/riding/human) + riding_datum.ride_check_rider_incapacitated = TRUE + riding_datum.ride_check_ridden_incapacitated = TRUE + riding_datum.ride_check_rider_restrained = TRUE + riding_datum.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(-6, 4), TEXT_WEST = list( 6, 4))) if(buckled_mobs && ((M in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled || (M.stat != CONSCIOUS)) return visible_message("[M] starts to climb onto [src]...") @@ -908,13 +911,6 @@ else visible_message("[M] fails to climb onto [src]!") -/mob/living/carbon/human/unbuckle_mob(mob/living/M, force=FALSE) - if(iscarbon(M)) - if(riding_datum) - riding_datum.unequip_buckle_inhands(M) - riding_datum.restore_position(M) - . = ..(M, force) - /mob/living/carbon/human/species var/race = null diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index a96052a37e..e8d4baba63 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -188,12 +188,8 @@ return if(ishuman(user)) var/mob/living/carbon/human/H = user - if(H.a_intent == INTENT_DISARM) - if(H.buckled_mobs && (src in H.buckled_mobs) && H.riding_datum) - H.riding_datum.force_dismount(src) dna.species.spec_attack_hand(H, src) - /mob/living/carbon/human/attack_paw(mob/living/carbon/monkey/M) var/dam_zone = pick("chest", "l_hand", "r_hand", "l_leg", "r_leg") var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone)) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index fc8fdac04c..c90d1a0231 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -58,11 +58,6 @@ //End bloody footprints S.step_action() -/mob/living/carbon/human/Moved() - . = ..() - if(buckled_mobs && buckled_mobs.len && riding_datum) - riding_datum.on_vehicle_move() - /mob/living/carbon/human/Process_Spacemove(movement_dir = 0) //Temporary laziness thing. Will change to handles by species reee. if(..()) return 1 diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index c656dd7494..9f541aaf67 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -491,14 +491,14 @@ . = ..() if(.) user.spin(20, 1) - if(iscyborg(user)) + if(iscyborg(user) && user.has_buckled_mobs()) var/mob/living/silicon/robot/R = user - if(R.buckled_mobs) + GET_COMPONENT_FROM(riding_datum, /datum/component/riding, R) + if(riding_datum) for(var/mob/M in R.buckled_mobs) - if(R.riding_datum) - R.riding_datum.force_dismount(M) - else - R.unbuckle_all_mobs() + riding_datum.force_dismount(M) + else + R.unbuckle_all_mobs() /datum/emote/living/circle diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 8849d52f22..7c91dd1ce6 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -39,7 +39,6 @@ ranged_ability.remove_ranged_ability(src) if(buckled) buckled.unbuckle_mob(src,force=1) - QDEL_NULL(riding_datum) for(var/mob/living/simple_animal/drone/D in GLOB.player_list) for(var/image/I in staticOverlays) @@ -955,11 +954,6 @@ "[C] leaps out of [src]'s way!")]") C.Knockdown(40) -/mob/living/post_buckle_mob(mob/living/M) - if(riding_datum) - riding_datum.handle_vehicle_offsets() - riding_datum.handle_vehicle_layer() - /mob/living/ConveyorMove() if((movement_type & FLYING) && !stat) return diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 72dc341276..e8b05bac9e 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1151,8 +1151,7 @@ if(!is_type_in_typecache(M, can_ride_typecache)) M.visible_message("[M] really can't seem to mount [src]...") return - if(!riding_datum) - riding_datum = new /datum/riding/cyborg(src) + var/datum/component/riding/riding_datum = LoadComponent(/datum/component/riding/cyborg) if(buckled_mobs) if(buckled_mobs.len >= max_buckled_mobs) return @@ -1175,7 +1174,8 @@ /mob/living/silicon/robot/unbuckle_mob(mob/user) if(iscarbon(user)) - if(riding_datum) + GET_COMPONENT(riding_datum, /datum/component/riding) + if(istype(riding_datum)) riding_datum.unequip_buckle_inhands(user) riding_datum.restore_position(user) . = ..(user) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index 762d86dee9..a1f0dbf4be 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -1,26 +1,21 @@ -/mob/living/silicon/robot/Process_Spacemove(movement_dir = 0) - if(ionpulse()) - return 1 - return ..() - -/mob/living/silicon/robot/movement_delay() - . = ..() - var/static/config_robot_delay - if(isnull(config_robot_delay)) - config_robot_delay = CONFIG_GET(number/robot_delay) - . += speed + config_robot_delay - -/mob/living/silicon/robot/mob_negates_gravity() - return magpulse - -/mob/living/silicon/robot/mob_has_gravity() - return ..() || mob_negates_gravity() - -/mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction) - if(!magpulse) - return ..() - -/mob/living/silicon/robot/Moved() - . = ..() - if(riding_datum) - riding_datum.on_vehicle_move() +/mob/living/silicon/robot/Process_Spacemove(movement_dir = 0) + if(ionpulse()) + return 1 + return ..() + +/mob/living/silicon/robot/movement_delay() + . = ..() + var/static/config_robot_delay + if(isnull(config_robot_delay)) + config_robot_delay = CONFIG_GET(number/robot_delay) + . += speed + config_robot_delay + +/mob/living/silicon/robot/mob_negates_gravity() + return magpulse + +/mob/living/silicon/robot/mob_has_gravity() + return ..() || mob_negates_gravity() + +/mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction) + if(!magpulse) + return ..() diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 0cba76edf5..828ce6a88b 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -379,12 +379,11 @@ return FALSE /mob/living/simple_animal/bot/mulebot/post_buckle_mob(mob/living/M) - if(M in buckled_mobs) //post buckling - M.pixel_y = initial(M.pixel_y) + 9 - if(M.layer < layer) - M.layer = layer + 0.01 + M.pixel_y = initial(M.pixel_y) + 9 + if(M.layer < layer) + M.layer = layer + 0.01 - else //post unbuckling +/mob/living/simple_animal/bot/mulebot/post_unbuckle_mob(mob/living/M) load = null M.layer = initial(M.layer) M.pixel_y = initial(M.pixel_y) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 72a1ec84c1..a4851bb432 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -521,37 +521,26 @@ client.screen |= l_hand //ANIMAL RIDING -/mob/living/simple_animal/unbuckle_mob(mob/living/buckled_mob, force = 0, check_loc = 1) - if(riding_datum) - riding_datum.restore_position(buckled_mob) - . = ..() - /mob/living/simple_animal/user_buckle_mob(mob/living/M, mob/user) + GET_COMPONENT(riding_datum, /datum/component/riding) if(riding_datum) if(user.incapacitated()) return for(var/atom/movable/A in get_turf(src)) if(A != src && A != M && A.density) return - M.loc = get_turf(src) - riding_datum.handle_vehicle_offsets() - riding_datum.ridden = src + M.forceMove(get_turf(src)) + return ..() /mob/living/simple_animal/relaymove(mob/user, direction) + GET_COMPONENT(riding_datum, /datum/component/riding) if(tame && riding_datum) riding_datum.handle_ride(user, direction) -/mob/living/simple_animal/Moved() - . = ..() - if(riding_datum) - riding_datum.on_vehicle_move() - - /mob/living/simple_animal/buckle_mob(mob/living/buckled_mob, force = 0, check_loc = 1) . = ..() - riding_datum = new/datum/riding/animal - + LoadComponent(/datum/component/riding) /mob/living/simple_animal/proc/toggle_ai(togglestatus) if (AIStatus != togglestatus) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index dd32042849..20509329bb 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -774,14 +774,14 @@ //Default buckling shift visual for mobs /mob/post_buckle_mob(mob/living/M) - if(M in buckled_mobs)//post buckling - var/height = M.get_mob_buckling_height(src) - M.pixel_y = initial(M.pixel_y) + height - if(M.layer < layer) - M.layer = layer + 0.1 - else //post unbuckling - M.layer = initial(M.layer) - M.pixel_y = initial(M.pixel_y) + var/height = M.get_mob_buckling_height(src) + M.pixel_y = initial(M.pixel_y) + height + if(M.layer < layer) + M.layer = layer + 0.1 + +/mob/post_unbuckle_mob(mob/living/M) + M.layer = initial(M.layer) + M.pixel_y = initial(M.pixel_y) //returns the height in pixel the mob should have when buckled to another mob. /mob/proc/get_mob_buckling_height(mob/seat) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 58cb82714e..d9dd2a0eee 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -33,7 +33,6 @@ var/obj/machinery/machine = null var/other_mobs = null var/disabilities = 0 //Carbon - var/movement_type = GROUND //Incase you have multiple types, you automatically use the most useful one. IE: Skating on ice, flippers on water, flying over chasm/space, etc. var/atom/movable/pulling = null var/grab_state = 0 diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 83957a7285..6f9db6c8e2 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -343,8 +343,8 @@ if(istype(C, /obj/vehicle)) var/obj/vehicle/V = C - var/datum/riding/R = V.riding_datum - if(V.riding_datum) + var/datum/component/riding/R = V.GetComponent(/datum/component/riding) + if(R) if(R.vehicle_move_delay <= 0 ) to_chat(user, "The [C] can't be made any faster!") return ..() @@ -355,7 +355,6 @@ C.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY) qdel(src) - /obj/item/slimepotion/fireproof name = "slime chill potion" desc = "A potent chemical mix that will fireproof any article of clothing. Has three uses." diff --git a/code/modules/space_transition/space_transition.dm b/code/modules/space_transition/space_transition.dm index e1a4c743bf..2a8be8a761 100644 --- a/code/modules/space_transition/space_transition.dm +++ b/code/modules/space_transition/space_transition.dm @@ -1,10 +1,5 @@ //This is a simple 3 by 3 grid working off the corpse of the space torus. The donut is dead, cube has been avenged! -#define Z_LEVEL_NORTH "1" -#define Z_LEVEL_SOUTH "2" -#define Z_LEVEL_EAST "4" -#define Z_LEVEL_WEST "8" - GLOBAL_LIST_EMPTY(z_levels_list) /datum/space_level @@ -19,7 +14,7 @@ GLOBAL_LIST_EMPTY(z_levels_list) linked = transition_type if(linked == SELFLOOPING) neigbours = list() - var/list/L = list(Z_LEVEL_NORTH,Z_LEVEL_SOUTH,Z_LEVEL_EAST,Z_LEVEL_WEST) + var/list/L = list(TEXT_NORTH,TEXT_SOUTH,TEXT_EAST,TEXT_WEST) for(var/A in L) neigbours[A] = src @@ -27,18 +22,18 @@ GLOBAL_LIST_EMPTY(z_levels_list) for(var/datum/point/P in L) if(P.x == xi) if(P.y == yi+1) - neigbours[Z_LEVEL_NORTH] = P.spl - P.spl.neigbours[Z_LEVEL_SOUTH] = src + neigbours[TEXT_NORTH] = P.spl + P.spl.neigbours[TEXT_SOUTH] = src else if(P.y == yi-1) - neigbours[Z_LEVEL_SOUTH] = P.spl - P.spl.neigbours[Z_LEVEL_NORTH] = src + neigbours[TEXT_SOUTH] = P.spl + P.spl.neigbours[TEXT_NORTH] = src else if(P.y == yi) if(P.x == xi+1) - neigbours[Z_LEVEL_EAST] = P.spl - P.spl.neigbours[Z_LEVEL_WEST] = src + neigbours[TEXT_EAST] = P.spl + P.spl.neigbours[TEXT_WEST] = src else if(P.x == xi-1) - neigbours[Z_LEVEL_WEST] = P.spl - P.spl.neigbours[Z_LEVEL_EAST] = src + neigbours[TEXT_WEST] = P.spl + P.spl.neigbours[TEXT_EAST] = src /datum/point //this is explicitly utilitarian datum type made specially for the space map generation and are absolutely unusable for anything else var/list/neigbours = list() @@ -157,8 +152,3 @@ GLOBAL_LIST_EMPTY(z_levels_list) for(var/A in grid) GLOB.z_levels_list[A] = grid[A] - -#undef Z_LEVEL_NORTH -#undef Z_LEVEL_SOUTH -#undef Z_LEVEL_EAST -#undef Z_LEVEL_WEST diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm new file mode 100644 index 0000000000..4cedfdf3e3 --- /dev/null +++ b/code/modules/vehicles/_vehicle.dm @@ -0,0 +1,140 @@ +#define VEHICLE_CONTROL_PERMISSION 1 +#define VEHICLE_CONTROL_DRIVE 2 + +/obj/vehicle + name = "generic vehicle" + desc = "Yell at coderbus." + icon = 'icons/obj/vehicles.dmi' + icon_state = "fuckyou" + max_integrity = 300 + armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) + density = TRUE + anchored = FALSE + var/list/mob/occupants //mob = bitflags of their control level. + var/max_occupants = 1 + var/max_drivers = 1 + var/movedelay = 2 + var/lastmove = 0 + var/key_type + var/obj/item/key/inserted_key + var/key_type_exact = TRUE //can subtypes work + var/canmove = TRUE + var/emulate_door_bumps = TRUE //when bumping a door try to make occupants bump them to open them. + var/default_driver_move = TRUE //handle driver movement instead of letting something else do it like riding datums. + var/list/autogrant_actions_passenger //plain list of typepaths + var/list/autogrant_actions_controller //assoc list "[bitflag]" = list(typepaths) + var/list/mob/occupant_actions //assoc list mob = list(type = action datum assigned to mob) + +/obj/vehicle/Initialize(mapload) + . = ..() + occupants = list() + autogrant_actions_passenger = list() + autogrant_actions_controller = list() + occupant_actions = list() + generate_actions() + +/obj/vehicle/proc/is_key(obj/item/I) + return I? (key_type_exact? (I.type == key_type) : istype(I, key_type)) : FALSE + +/obj/vehicle/proc/return_occupants() + return occupants + +/obj/vehicle/proc/occupant_amount() + return length(occupants) + +/obj/vehicle/proc/return_amount_of_controllers_with_flag(flag) + . = 0 + for(var/i in occupants) + if(occupants[i] & flag) + .++ + +/obj/vehicle/proc/return_controllers_with_flag(flag) + . = list() + for(var/i in occupants) + if(occupants[i] & flag) + . += i + +/obj/vehicle/proc/return_drivers() + return return_controllers_with_flag(VEHICLE_CONTROL_DRIVE) + +/obj/vehicle/proc/driver_amount() + return return_amount_of_controllers_with_flag(VEHICLE_CONTROL_DRIVE) + +/obj/vehicle/proc/is_driver(mob/M) + return is_occupant(M) && occupants[M] & VEHICLE_CONTROL_DRIVE + +/obj/vehicle/proc/is_occupant(mob/M) + return !isnull(occupants[M]) + +/obj/vehicle/proc/add_occupant(mob/M, control_flags) + if(!istype(M) || occupants[M]) + return FALSE + occupants[M] = NONE + add_control_flags(M, control_flags) + grant_passenger_actions(M) + after_add_occupant(M) + return TRUE + +/obj/vehicle/proc/after_add_occupant(mob/M) + auto_assign_occupant_flags(M) + +/obj/vehicle/proc/auto_assign_occupant_flags(mob/M) //override for each type that needs it. Default is assign driver if drivers is not at max. + if(driver_amount() < max_drivers) + add_control_flags(M, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_PERMISSION) + +/obj/vehicle/proc/remove_occupant(mob/M) + if(!istype(M)) + return FALSE + remove_control_flags(M, ALL) + occupants -= M + remove_passenger_actions(M) + cleanup_actions_for_mob(M) + after_remove_occupant(M) + return TRUE + +/obj/vehicle/proc/after_remove_occupant(mob/M) + +/obj/vehicle/relaymove(mob/user, direction) + if(is_driver(user)) + return driver_move(user, direction) + return FALSE + +/obj/vehicle/proc/driver_move(mob/user, direction) + if(key_type && !is_key(inserted_key)) + to_chat(user, "[src] has no key inserted!") + return FALSE + if(!default_driver_move) + return + vehicle_move(direction) + +/obj/vehicle/proc/vehicle_move(direction) + if(lastmove + movedelay > world.time) + return FALSE + lastmove = world.time + return step(src, direction) + +/obj/vehicle/proc/add_control_flags(mob/controller, flags) + if(!istype(controller) || !flags) + return FALSE + occupants[controller] |= flags + for(var/i in GLOB.bitflags) + if(flags & i) + grant_controller_actions_by_flag(controller, i) + return TRUE + +/obj/vehicle/proc/remove_control_flags(mob/controller, flags) + if(!istype(controller) || !flags) + return FALSE + occupants[controller] &= ~flags + for(var/i in GLOB.bitflags) + if(flags & i) + remove_controller_actions_by_flag(controller, i) + return TRUE + +/obj/vehicle/Collide(atom/movable/M) + . = ..() + if(emulate_door_bumps) + if(istype(M, /obj/machinery/door) && has_buckled_mobs()) + for(var/m in occupants) + M.CollidedWith(m) + diff --git a/code/modules/vehicles/atv.dm b/code/modules/vehicles/atv.dm index 504e4753df..ac5be5b51c 100644 --- a/code/modules/vehicles/atv.dm +++ b/code/modules/vehicles/atv.dm @@ -1,45 +1,63 @@ -/obj/vehicle/atv +/obj/vehicle/ridden/atv name = "all-terrain vehicle" desc = "An all-terrain vehicle built for traversing rough terrain with ease. One of the few old-Earth technologies that are still relevant on most planet-bound outposts." icon_state = "atv" + key_type = /obj/item/key var/static/mutable_appearance/atvcover -/obj/vehicle/atv/buckle_mob(mob/living/buckled_mob, force = 0, check_loc = 1) +/obj/vehicle/ridden/atv/Initialize() . = ..() - riding_datum = new/datum/riding/atv + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.vehicle_move_delay = 1 + D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(0, 4), TEXT_WEST = list( 0, 4))) + D.set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER) + D.set_vehicle_dir_layer(NORTH, OBJ_LAYER) + D.set_vehicle_dir_layer(EAST, OBJ_LAYER) + D.set_vehicle_dir_layer(WEST, OBJ_LAYER) -/obj/vehicle/atv/Initialize() - . = ..() - atvcover = atvcover || mutable_appearance(icon, "atvcover", ABOVE_MOB_LAYER) +/obj/vehicle/ridden/atv/post_buckle_mob(mob/living/M) + add_overlay(atvcover) + return ..() - -/obj/vehicle/atv/post_buckle_mob(mob/living/M) - if(has_buckled_mobs()) - add_overlay(atvcover) - else +/obj/vehicle/ridden/atv/post_unbuckle_mob(mob/living/M) + if(!has_buckled_mobs()) cut_overlay(atvcover) - - + return ..() //TURRETS! -/obj/vehicle/atv/turret +/obj/vehicle/ridden/atv/turret var/obj/machinery/porta_turret/syndicate/vehicle_turret/turret = null - /obj/machinery/porta_turret/syndicate/vehicle_turret name = "mounted turret" scan_range = 7 emp_vunerable = 1 density = FALSE - -/obj/vehicle/atv/turret/Initialize() +/obj/vehicle/ridden/atv/turret/Initialize() . = ..() turret = new(loc) turret.base = src -/obj/vehicle/atv/turret/buckle_mob(mob/living/buckled_mob, force = 0, check_loc = 1) +/obj/vehicle/ridden/atv/turret/Moved() . = ..() - riding_datum = new/datum/riding/atv/turret - + if(turret) + turret.forceMove(get_turf(src)) + switch(dir) + if(NORTH) + turret.pixel_x = 0 + turret.pixel_y = 4 + turret.layer = ABOVE_MOB_LAYER + if(EAST) + turret.pixel_x = -12 + turret.pixel_y = 4 + turret.layer = OBJ_LAYER + if(SOUTH) + turret.pixel_x = 0 + turret.pixel_y = 4 + turret.layer = OBJ_LAYER + if(WEST) + turret.pixel_x = 12 + turret.pixel_y = 4 + turret.layer = OBJ_LAYER diff --git a/code/modules/vehicles/bicycle.dm b/code/modules/vehicles/bicycle.dm index cd93196b69..a6818d8bf3 100644 --- a/code/modules/vehicles/bicycle.dm +++ b/code/modules/vehicles/bicycle.dm @@ -7,9 +7,12 @@ var/static/list/bike_music = list('sound/misc/bike1.mid', 'sound/misc/bike2.mid', 'sound/misc/bike3.mid') + /obj/vehicle/bicycle/Initialize() . = ..() - riding_datum = new/datum/riding/bicycle + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(0, 4), TEXT_WEST = list( 0, 4))) + D.vehicle_move_delay = 0 /obj/vehicle/bicycle/buckle_mob(mob/living/M, force = 0, check_loc = 1) if(prob(easter_egg_chance) || (SSevents.holidays && SSevents.holidays[APRIL_FOOLS])) @@ -24,7 +27,7 @@ /obj/vehicle/bicycle/tesla_act() // :::^^^))) name = "fried bicycle" desc = "Well spent." - riding_datum = null color = rgb(63, 23, 4) + can_buckle = FALSE for(var/m in buckled_mobs) unbuckle_mob(m,1) diff --git a/code/modules/vehicles/entered.dm b/code/modules/vehicles/entered.dm new file mode 100644 index 0000000000..d0fb93f7f0 --- /dev/null +++ b/code/modules/vehicles/entered.dm @@ -0,0 +1,56 @@ +/obj/vehicle/sealed + var/enter_delay = 20 + +/obj/vehicle/sealed/generate_actions() + . = ..() + initialize_passenger_action_type(/datum/action/vehicle/sealed/climb_out) + +/obj/vehicle/sealed/generate_action_type() + var/datum/action/vehicle/sealed/E = ..() + . = E + if(istype(E)) + E.vehicle_entered_target = src + +/obj/vehicle/sealed/MouseDrop_T(atom/dropping, mob/M) + if(!istype(dropping) || !istype(M)) + return ..() + if(M == dropping) + mob_try_enter(M) + return ..() + +/obj/vehicle/sealed/proc/mob_try_enter(mob/M) + if(!istype(M)) + return FALSE + if(occupant_amount() >= max_occupants) + return FALSE + if(do_after(M, get_enter_delay(M), FALSE, src, TRUE)) + mob_enter(M) + return TRUE + return FALSE + +/obj/vehicle/sealed/proc/get_enter_delay(mob/M) + return enter_delay + +/obj/vehicle/sealed/proc/mob_enter(mob/M, silent = FALSE) + if(!istype(M)) + return FALSE + if(!silent) + M.visible_message("[M] climbs into \the [src]!") + M.forceMove(src) + add_occupant(M) + return TRUE + +/obj/vehicle/sealed/proc/mob_try_exit(mob/M, mob/user, silent = FALSE) + mob_exit(M, silent) + +/obj/vehicle/sealed/proc/mob_exit(mob/M, silent = FALSE) + if(!istype(M)) + return FALSE + remove_occupant(M) + M.forceMove(exit_location(M)) + if(!silent) + M.visible_message("[M] drops out of \the [src]!") + return TRUE + +/obj/vehicle/sealed/proc/exit_location(M) + return drop_location() diff --git a/code/modules/vehicles/pimpin_ride.dm b/code/modules/vehicles/pimpin_ride.dm index f8f80819dd..42d863d968 100644 --- a/code/modules/vehicles/pimpin_ride.dm +++ b/code/modules/vehicles/pimpin_ride.dm @@ -1,33 +1,24 @@ //PIMP-CART -/obj/vehicle/janicart +/obj/vehicle/ridden/janicart name = "janicart (pimpin' ride)" desc = "A brave janitor cyborg gave its life to produce such an amazing combination of speed and utility." icon_state = "pussywagon" - + key_type = /obj/item/key/janitor var/obj/item/storage/bag/trash/mybag = null var/floorbuffer = FALSE -/obj/vehicle/janicart/Initialize(mapload) +/obj/vehicle/ridden/janicart/Initialize(mapload) . = ..() update_icon() + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 7), TEXT_EAST = list(-12, 7), TEXT_WEST = list( 12, 7))) -/obj/vehicle/janicart/Destroy() +/obj/vehicle/ridden/janicart/Destroy() if(mybag) qdel(mybag) mybag = null . = ..() -/obj/vehicle/janicart/buckle_mob(mob/living/buckled_mob, force = 0, check_loc = 0) - . = ..() - riding_datum = new/datum/riding/janicart - - - -/obj/item/key/janitor - desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"." - icon_state = "keyjanitor" - - /obj/item/janiupgrade name = "floor buffer upgrade" desc = "An upgrade for mobile janicarts." @@ -35,14 +26,12 @@ icon_state = "upgrade" origin_tech = "materials=3;engineering=4" - -/obj/vehicle/janicart/examine(mob/user) +/obj/vehicle/ridden/janicart/examine(mob/user) ..() if(floorbuffer) to_chat(user, "It has been upgraded with a floor buffer.") - -/obj/vehicle/janicart/attackby(obj/item/I, mob/user, params) +/obj/vehicle/ridden/janicart/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/storage/bag/trash)) if(mybag) to_chat(user, "[src] already has a trashbag hooked!") @@ -64,16 +53,14 @@ else return ..() - -/obj/vehicle/janicart/update_icon() +/obj/vehicle/ridden/janicart/update_icon() cut_overlays() if(mybag) add_overlay("cart_garbage") if(floorbuffer) add_overlay("cart_buffer") - -/obj/vehicle/janicart/attack_hand(mob/user) +/obj/vehicle/ridden/janicart/attack_hand(mob/user) if(..()) return 1 else if(mybag) @@ -82,5 +69,5 @@ mybag = null update_icon() -/obj/vehicle/janicart/upgraded +/obj/vehicle/ridden/janicart/upgraded floorbuffer = TRUE diff --git a/code/modules/vehicles/ridden.dm b/code/modules/vehicles/ridden.dm new file mode 100644 index 0000000000..ea3a05470a --- /dev/null +++ b/code/modules/vehicles/ridden.dm @@ -0,0 +1,76 @@ +/obj/vehicle/ridden + name = "ridden vehicle" + can_buckle = TRUE + max_buckled_mobs = 1 + buckle_lying = FALSE + default_driver_move = FALSE + var/legs_required = 2 + var/arms_requires = 0 //why not? + +/obj/vehicle/ridden/Initialize() + . = ..() + LoadComponent(/datum/component/riding) + +/obj/vehicle/ridden/examine(mob/user) + . = ..() + to_chat(user, "Put a key inside it by clicking it with the key. If there's a key inside, you can remove it via Alt-Click!") + +/obj/vehicle/ridden/generate_action_type(actiontype) + var/datum/action/vehicle/ridden/A = ..() + . = A + if(istype(A)) + A.vehicle_ridden_target = src + +/obj/vehicle/ridden/post_unbuckle_mob(mob/living/M) + remove_occupant(M) + return ..() + +/obj/vehicle/ridden/post_buckle_mob(mob/living/M) + add_occupant(M) + return ..() + +/obj/vehicle/ridden/attackby(obj/item/I, mob/user, params) + if(key_type && !is_key(inserted_key) && is_key(I)) + if(user.transferItemToLoc(I, src)) + to_chat(user, "You insert \the [I] into \the [src].") + if(inserted_key) //just in case there's an invalid key + inserted_key.forceMove(drop_location()) + inserted_key = I + else + to_chat(user, "[I] seems to be stuck to your hand!") + return + return ..() + +/obj/vehicle/ridden/AltClick(mob/user) + if(user.Adjacent(src) && inserted_key) + if(!is_occupant(user)) + to_chat(user, "You must be riding the [src] to remove [src]'s key!") + return + to_chat(user, "You remove \the [inserted_key] from \the [src].") + inserted_key.forceMove(drop_location()) + user.put_in_hands(inserted_key) + inserted_key = null + return ..() + +/obj/vehicle/ridden/driver_move(mob/user, direction) + if(key_type && !is_key(inserted_key)) + to_chat(user, "[src] has no key inserted!") + return FALSE + var/datum/component/riding/R = GetComponent(/datum/component/riding) + R.handle_ride(user, direction) + return ..() + +/obj/vehicle/ridden/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE) + if(user.incapacitated()) + return + for(var/atom/movable/A in get_turf(src)) + if(A.density) + if(A != src && A != M) + return + M.forceMove(get_turf(src)) + . = ..() + +/obj/vehicle/ridden/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE) + if(!force && occupant_amount() >= max_occupants) + return FALSE + return ..() diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm index 24cfd9ab92..66ae84f559 100644 --- a/code/modules/vehicles/scooter.dm +++ b/code/modules/vehicles/scooter.dm @@ -1,14 +1,20 @@ -/obj/vehicle/scooter +/obj/vehicle/ridden/scooter name = "scooter" desc = "A fun way to get around." icon_state = "scooter" -/obj/vehicle/scooter/attackby(obj/item/I, mob/user, params) +/obj/vehicle/ridden/scooter/Initialize() + . = ..() + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0), TEXT_SOUTH = list(-2), TEXT_EAST = list(0), TEXT_WEST = list( 2))) + + +/obj/vehicle/ridden/scooter/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/wrench)) to_chat(user, "You begin to remove the handlebars...") playsound(get_turf(user), 'sound/items/ratchet.ogg', 50, 1) if(do_after(user, 40*I.toolspeed, target = src)) - var/obj/vehicle/scooter/skateboard/S = new /obj/vehicle/scooter/skateboard(get_turf(src)) + var/obj/vehicle/ridden/scooter/skateboard/S = new(loc) new /obj/item/stack/rods(get_turf(src),2) to_chat(user, "You remove the handlebars from [src].") if(has_buckled_mobs()) @@ -17,9 +23,16 @@ S.buckle_mob(H) qdel(src) +/obj/vehicle/ridden/scooter/Moved() + . = ..() + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + if(buckled_mob.get_num_legs() > 0) + buckled_mob.pixel_y = 5 + else + buckled_mob.pixel_y = -4 -/obj/vehicle/scooter/buckle_mob(mob/living/M, force = 0, check_loc = 1) - riding_datum = new/datum/riding/scooter +/obj/vehicle/ridden/scooter/buckle_mob(mob/living/M, force = 0, check_loc = 1) if(!istype(M)) return 0 if(M.get_num_legs() < 2 && M.get_num_arms() <= 0) @@ -27,29 +40,32 @@ return 0 . = ..() -/obj/vehicle/scooter/post_buckle_mob(mob/living/M) - riding_datum.account_limbs(M) - -/obj/vehicle/scooter/skateboard +/obj/vehicle/ridden/scooter/skateboard name = "skateboard" desc = "An unfinished scooter which can only barely be called a skateboard. It's still rideable, but probably unsafe. Looks like you'll need to add a few rods to make handlebars." icon_state = "skateboard" - density = FALSE -/obj/vehicle/scooter/skateboard/buckle_mob(mob/living/M, force = 0, check_loc = 1) +/obj/vehicle/ridden/scooter/skateboard/Initialize() . = ..() - riding_datum = new/datum/riding/scooter/skateboard + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.vehicle_move_delay = 0 + D.set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER) + D.set_vehicle_dir_layer(NORTH, OBJ_LAYER) + D.set_vehicle_dir_layer(EAST, OBJ_LAYER) + D.set_vehicle_dir_layer(WEST, OBJ_LAYER) -/obj/vehicle/scooter/skateboard/post_buckle_mob(mob/living/M)//allows skateboards to be non-dense but still allows 2 skateboarders to collide with each other - if(has_buckled_mobs()) - density = TRUE - else +/obj/vehicle/ridden/scooter/skateboard/post_buckle_mob(mob/living/M)//allows skateboards to be non-dense but still allows 2 skateboarders to collide with each other + density = TRUE + return ..() + +/obj/vehicle/ridden/scooter/skateboard/post_unbuckle_mob(mob/living/M) + if(!has_buckled_mobs()) density = FALSE - ..() + return ..() -/obj/vehicle/scooter/skateboard/Collide(atom/A) - ..() +/obj/vehicle/ridden/scooter/skateboard/Collide(atom/A) + . = ..() if(A.density && has_buckled_mobs()) var/mob/living/carbon/H = buckled_mobs[1] var/atom/throw_target = get_edge_target_turf(H, pick(GLOB.cardinals)) @@ -63,7 +79,7 @@ visible_message("[src] crashes into [A], sending [H] flying!") playsound(src, 'sound/effects/bang.ogg', 50, 1) -/obj/vehicle/scooter/skateboard/MouseDrop(atom/over_object) +/obj/vehicle/ridden/scooter/skateboard/MouseDrop(atom/over_object) var/mob/living/carbon/M = usr if(!istype(M) || M.incapacitated() || !Adjacent(M)) return @@ -102,10 +118,10 @@ return M.use(5) to_chat(user, "You finish making wheels for [src].") - new /obj/vehicle/scooter/skateboard(user.loc) + new /obj/vehicle/ridden/scooter/skateboard(user.loc) qdel(src) -/obj/vehicle/scooter/skateboard/attackby(obj/item/I, mob/user, params) +/obj/vehicle/ridden/scooter/skateboard/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/screwdriver)) to_chat(user, "You begin to deconstruct and remove the wheels on [src]...") playsound(get_turf(user), I.usesound, 50, 1) @@ -129,7 +145,7 @@ return to_chat(user, "You add the rods to [src], creating handlebars.") C.use(2) - var/obj/vehicle/scooter/S = new/obj/vehicle/scooter(get_turf(src)) + var/obj/vehicle/ridden/scooter/S = new(loc) if(has_buckled_mobs()) var/mob/living/carbon/H = buckled_mobs[1] unbuckle_mob(H) diff --git a/code/modules/vehicles/secway.dm b/code/modules/vehicles/secway.dm index c85088ec8d..33f0d794c8 100644 --- a/code/modules/vehicles/secway.dm +++ b/code/modules/vehicles/secway.dm @@ -1,13 +1,12 @@ -/obj/vehicle/secway +/obj/vehicle/ridden/secway name = "secway" desc = "A brave security cyborg gave its life to help you look like a complete tool." icon_state = "secway" + key_type = /obj/item/key/security -/obj/item/key/security - desc = "A keyring with a small steel key, and a rubber stun baton accessory." - icon_state = "keysec" - -/obj/vehicle/secway/buckle_mob(mob/living/buckled_mob, force = 0, check_loc = 1) +/obj/vehicle/ridden/secway/Initialize() . = ..() - riding_datum = new/datum/riding/secway \ No newline at end of file + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.vehicle_move_delay = 1 + D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(0, 4), TEXT_WEST = list( 0, 4))) diff --git a/code/modules/vehicles/speedbike.dm b/code/modules/vehicles/speedbike.dm index 9200a3d696..e05504b0dc 100644 --- a/code/modules/vehicles/speedbike.dm +++ b/code/modules/vehicles/speedbike.dm @@ -1,44 +1,68 @@ -/obj/vehicle/space/speedbike + +/obj/vehicle/ridden/space + name = "Generic Space Vehicle!" + +/obj/vehicle/ridden/space/Initialize() + . = ..() + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.override_allow_spacemove = TRUE + +/obj/vehicle/ridden/space/speedbike name = "Speedbike" icon = 'icons/obj/bike.dmi' icon_state = "speedbike_blue" layer = LYING_MOB_LAYER var/overlay_state = "cover_blue" - var/static/mutable_appearance/overlay + var/mutable_appearance/overlay -/obj/vehicle/space/speedbike/buckle_mob(mob/living/M, force = 0, check_loc = 1) +/obj/vehicle/ridden/space/speedbike/Initialize() . = ..() - riding_datum = new/datum/riding/space/speedbike - -/obj/vehicle/space/speedbike/New() - . = ..() - overlay = overlay || mutable_appearance(icon, overlay_state, ABOVE_MOB_LAYER) + overlay = mutable_appearance(icon, overlay_state, ABOVE_MOB_LAYER) add_overlay(overlay) + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, -8), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(-10, 5), TEXT_WEST = list( 10, 5))) + D.vehicle_move_delay = 0 + D.set_vehicle_dir_offsets(NORTH, -16, -16) + D.set_vehicle_dir_offsets(SOUTH, -16, -16) + D.set_vehicle_dir_offsets(EAST, -18, 0) + D.set_vehicle_dir_offsets(WEST, -18, 0) -/obj/vehicle/space/speedbike/Move(newloc,move_dir) +/obj/vehicle/ridden/space/speedbike/Move(newloc,move_dir) if(has_buckled_mobs()) new /obj/effect/temp_visual/dir_setting/speedbike_trail(loc,move_dir) . = ..() -/obj/vehicle/space/speedbike/red +/obj/vehicle/ridden/space/speedbike/red icon_state = "speedbike_red" overlay_state = "cover_red" //BM SPEEDWAGON -/obj/vehicle/space/speedbike/speedwagon +/obj/vehicle/ridden/space/speedwagon name = "BM Speedwagon" desc = "Push it to the limit, walk along the razor's edge." icon = 'icons/obj/car.dmi' icon_state = "speedwagon" layer = LYING_MOB_LAYER - overlay_state = "speedwagon_cover" + var/static/mutable_appearance/overlay = mutable_appearance(icon, "speedwagon_cover", ABOVE_MOB_LAYER) max_buckled_mobs = 4 var/crash_all = FALSE //CHAOS pixel_y = -48 //to fix the offset when Initialized() pixel_x = -48 -/obj/vehicle/space/speedbike/speedwagon/Collide(atom/movable/A) +/obj/vehicle/ridden/space/speedwagon/Initialize() + . = ..() + add_overlay(overlay) + var/datum/component/riding/D = LoadComponent(/datum/component/riding) + D.vehicle_move_delay = 0 + D.set_riding_offsets(1, list(TEXT_NORTH = list(-10, -4), TEXT_SOUTH = list(16, 3), TEXT_EAST = list(-4, 30), TEXT_WEST = list(4, -3))) + D.set_riding_offsets(2, list(TEXT_NORTH = list(19, -5, 4), TEXT_SOUTH = list(-13, 3, 4), TEXT_EAST = list(-4, -3, 4.1), TEXT_WEST = list(4, 28, 3.9))) + D.set_riding_offsets(3, list(TEXT_NORTH = list(-10, -18, 4.2), TEXT_SOUTH = list(16, 25, 3.9), TEXT_EAST = list(-22, 30), TEXT_WEST = list(22, -3, 4.1))) + D.set_riding_offsets(4, list(TEXT_NORTH = list(19, -18, 4.2), TEXT_SOUTH = list(-13, 25, 3.9), TEXT_EAST = list(-22, 3, 3.9), TEXT_WEST = list(22, 28))) + for(var/i in GLOB.cardinals) + D.set_vehicle_dir_layer(i, BELOW_MOB_LAYER) + +/obj/vehicle/ridden/space/speedwagon/Collide(atom/movable/A) . = ..() if(A.density && has_buckled_mobs()) var/atom/throw_target = get_edge_target_turf(A, dir) @@ -56,11 +80,7 @@ visible_message("[src] crashes into [H]!") playsound(src, 'sound/effects/bang.ogg', 50, 1) -/obj/vehicle/space/speedbike/speedwagon/buckle_mob(mob/living/M, force = 0, check_loc = 1) - . = ..() - riding_datum = new/datum/riding/space/speedwagon - -/obj/vehicle/space/speedbike/speedwagon/Moved() +/obj/vehicle/ridden/space/speedwagon/Moved() . = ..() if(has_buckled_mobs()) for(var/atom/A in range(2, src)) diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm deleted file mode 100644 index e538599754..0000000000 --- a/code/modules/vehicles/vehicle.dm +++ /dev/null @@ -1,108 +0,0 @@ - -/obj/vehicle - name = "vehicle" - desc = "A basic vehicle, vroom." - icon = 'icons/obj/vehicles.dmi' - icon_state = "fuckyou" - density = TRUE - anchored = FALSE - can_buckle = 1 - buckle_lying = 0 - max_integrity = 300 - armor = list(melee = 30, bullet = 30, laser = 30, energy = 0, bomb = 30, bio = 0, rad = 0, fire = 60, acid = 60) - var/auto_door_open = TRUE - var/view_range = 7 - var/datum/riding/riding_datum = null - -/obj/vehicle/Destroy() - QDEL_NULL(riding_datum) - return ..() - -/obj/vehicle/update_icon() - return - -/obj/item/key - name = "key" - desc = "A small grey key." - icon = 'icons/obj/vehicles.dmi' - icon_state = "key" - w_class = WEIGHT_CLASS_TINY - -//BUCKLE HOOKS -/obj/vehicle/unbuckle_mob(mob/living/buckled_mob,force = 0) - if(riding_datum) - riding_datum.restore_position(buckled_mob) - . = ..() - - -/obj/vehicle/user_buckle_mob(mob/living/M, mob/living/user) - if(!istype(user) || user.incapacitated()) - return - for(var/atom/movable/A in get_turf(src)) - if(A.density) - if(A != src && A != M) - return - M.forceMove(get_turf(src)) - ..() - if(user.client) - user.client.change_view(view_range) - if(riding_datum) - riding_datum.ridden = src - riding_datum.handle_vehicle_offsets() - -//MOVEMENT -/obj/vehicle/relaymove(mob/user, direction) - if(riding_datum) - riding_datum.handle_ride(user, direction) - - -/obj/vehicle/Moved() - . = ..() - if(riding_datum) - riding_datum.handle_vehicle_layer() - riding_datum.handle_vehicle_offsets() - - -/obj/vehicle/Collide(atom/movable/M) - . = ..() - if(auto_door_open) - if(istype(M, /obj/machinery/door) && has_buckled_mobs()) - for(var/m in buckled_mobs) - M.CollidedWith(m) - - -/obj/vehicle/Process_Spacemove(direction) - if(has_gravity()) - return 1 - - if(pulledby && (pulledby.loc != loc)) - return 1 - - return 0 - -/obj/vehicle/space - pressure_resistance = INFINITY - - -/obj/vehicle/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - if(damage_flag == "melee" && damage_amount < 20) - return 0 - . = ..() - -/obj/vehicle/deconstruct(disassembled = TRUE) - new /obj/item/stack/sheet/metal (loc, 5) - qdel(src) - -/obj/vehicle/examine(mob/user) - ..() - if(!(resistance_flags & INDESTRUCTIBLE)) - if(resistance_flags & ON_FIRE) - to_chat(user, "It's on fire!") - var/healthpercent = (obj_integrity/max_integrity) * 100 - switch(healthpercent) - if(50 to 99) - to_chat(user, "It looks slightly damaged.") - if(25 to 50) - to_chat(user, "It appears heavily damaged.") - if(0 to 25) - to_chat(user, "It's falling apart!") \ No newline at end of file diff --git a/code/modules/vehicles/vehicle_actions.dm b/code/modules/vehicles/vehicle_actions.dm new file mode 100644 index 0000000000..79a24c8b11 --- /dev/null +++ b/code/modules/vehicles/vehicle_actions.dm @@ -0,0 +1,112 @@ +//VEHICLE DEFAULT HANDLING +/obj/vehicle/proc/generate_actions() + return + +/obj/vehicle/proc/generate_action_type(actiontype) + var/datum/action/vehicle/A = new actiontype + if(!istype(A)) + return + A.vehicle_target = src + return A + +/obj/vehicle/proc/initialize_passenger_action_type(actiontype) + autogrant_actions_passenger += actiontype + for(var/i in occupants) + grant_passenger_actions(i) //refresh + +/obj/vehicle/proc/initialize_controller_action_type(actiontype, control_flag) + LAZYINITLIST(autogrant_actions_controller["[control_flag]"]) + autogrant_actions_controller["[control_flag]"] += actiontype + for(var/i in occupants) + grant_controller_actions(i) //refresh + +/obj/vehicle/proc/grant_action_type_to_mob(actiontype, mob/m) + if(!occupants[m] || !actiontype) + return FALSE + LAZYINITLIST(occupant_actions[m]) + if(occupant_actions[m][actiontype]) + return TRUE + var/datum/action/action = generate_action_type(actiontype) + action.Grant(m) + occupant_actions[m][action.type] = action + return TRUE + +/obj/vehicle/proc/remove_action_type_from_mob(actiontype, mob/m) + if(!occupants[m] || !actiontype) + return FALSE + LAZYINITLIST(occupant_actions[m]) + if(occupant_actions[m][actiontype]) + var/datum/action/action = occupant_actions[m][actiontype] + action.Remove(m) + occupant_actions[m] -= actiontype + return TRUE + +/obj/vehicle/proc/grant_passenger_actions(mob/M) + for(var/v in autogrant_actions_passenger) + grant_action_type_to_mob(v, M) + +/obj/vehicle/proc/remove_passenger_actions(mob/M) + for(var/v in autogrant_actions_passenger) + remove_action_type_from_mob(v, M) + +/obj/vehicle/proc/grant_controller_actions(mob/M) + if(!istype(M) || !occupants[M]) + return FALSE + for(var/i in GLOB.bitflags) + if(occupants[M] & i) + grant_controller_actions_by_flag(M, i) + return TRUE + +/obj/vehicle/proc/remove_controller_actions(mob/M) + if(!istype(M) || !occupants[M]) + return FALSE + for(var/i in GLOB.bitflags) + remove_controller_actions_by_flag(M, i) + return TRUE + +/obj/vehicle/proc/grant_controller_actions_by_flag(mob/M, flag) + if(!istype(M) || !autogrant_actions_controller["[flag]"]) + return FALSE + for(var/v in autogrant_actions_controller["[flag]"]) + grant_action_type_to_mob(v, M) + return TRUE + +/obj/vehicle/proc/remove_controller_actions_by_flag(mob/M, flag) + if(!istype(M) || autogrant_actions_controller["[flag]"]) + return FALSE + for(var/v in autogrant_actions_controller["[flag]"]) + remove_action_type_from_mob(v, M) + return TRUE + +/obj/vehicle/proc/cleanup_actions_for_mob(mob/M) + if(!istype(M)) + return FALSE + LAZYINITLIST(occupant_actions[M]) + for(var/path in occupant_actions[M]) + stack_trace("Leftover action type [path] in vehicle type [type] for mob type [M.type] - THIS SHOULD NOT BE HAPPENING!") + var/datum/action/action = occupant_actions[M] + action.Remove(M) + occupant_actions -= M + return TRUE + +//ACTION DATUMS + +/datum/action/vehicle + check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUN | AB_CHECK_CONSCIOUS + icon_icon = 'icons/mob/actions/actions_vehicle.dmi' + button_icon_state = "vehicle_eject" + var/obj/vehicle/vehicle_target + +/datum/action/vehicle/sealed + var/obj/vehicle/sealed/vehicle_entered_target + +/datum/action/vehicle/sealed/climb_out + name = "Climb Out" + desc = "Climb out of your vehicle!" + +/datum/action/vehicle/sealed/climb_out/Trigger() + if(..() && istype(vehicle_entered_target)) + vehicle_entered_target.mob_try_exit(owner, owner) + +/datum/action/vehicle/ridden + var/obj/vehicle/ridden/vehicle_ridden_target diff --git a/code/modules/vehicles/vehicle_key.dm b/code/modules/vehicles/vehicle_key.dm new file mode 100644 index 0000000000..204a10bd17 --- /dev/null +++ b/code/modules/vehicles/vehicle_key.dm @@ -0,0 +1,15 @@ +/obj/item/key + name = "key" + desc = "A small grey key." + icon = 'icons/obj/vehicles.dmi' + icon_state = "key" + w_class = WEIGHT_CLASS_TINY + +/obj/item/key/security + desc = "A keyring with a small steel key, and a rubber stun baton accessory." + icon_state = "keysec" + +/obj/item/key/janitor + desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"." + icon_state = "keyjanitor" + diff --git a/icons/mob/actions/actions_vehicle.dmi b/icons/mob/actions/actions_vehicle.dmi new file mode 100644 index 0000000000..62b995ef9b Binary files /dev/null and b/icons/mob/actions/actions_vehicle.dmi differ diff --git a/tgstation.dme b/tgstation.dme index d5c699b8e0..d8094b1744 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -306,7 +306,6 @@ #include "code\datums\progressbar.dm" #include "code\datums\radiation_wave.dm" #include "code\datums\recipe.dm" -#include "code\datums\riding.dm" #include "code\datums\ruins.dm" #include "code\datums\saymode.dm" #include "code\datums\shuttles.dm" @@ -342,6 +341,7 @@ #include "code\datums\components\paintable.dm" #include "code\datums\components\rad_insulation.dm" #include "code\datums\components\radioactive.dm" +#include "code\datums\components\riding.dm" #include "code\datums\components\signal_redirect.dm" #include "code\datums\components\slippery.dm" #include "code\datums\components\spooky.dm" @@ -2387,13 +2387,17 @@ #include "code\modules\tgui\states\self.dm" #include "code\modules\tgui\states\zlevel.dm" #include "code\modules\tooltip\tooltip.dm" +#include "code\modules\vehicles\_vehicle.dm" #include "code\modules\vehicles\atv.dm" #include "code\modules\vehicles\bicycle.dm" +#include "code\modules\vehicles\entered.dm" #include "code\modules\vehicles\pimpin_ride.dm" +#include "code\modules\vehicles\ridden.dm" #include "code\modules\vehicles\scooter.dm" #include "code\modules\vehicles\secway.dm" #include "code\modules\vehicles\speedbike.dm" -#include "code\modules\vehicles\vehicle.dm" +#include "code\modules\vehicles\vehicle_actions.dm" +#include "code\modules\vehicles\vehicle_key.dm" #include "code\modules\vore\hook-defs_vr.dm" #include "code\modules\vore\trycatch_vr.dm" #include "code\modules\vore\eating\belly_vr.dm"