diff --git a/code/modules/vehicles/grocery_cart_scooter.dm b/code/modules/vehicles/grocery_cart_scooter.dm
new file mode 100644
index 00000000..4d0bfa99
--- /dev/null
+++ b/code/modules/vehicles/grocery_cart_scooter.dm
@@ -0,0 +1,168 @@
+/obj/vehicle/ridden/grocery_cart/motorized
+ name = "motorized grocery scooter"
+ desc = "A motorized scooter with a basket."
+ max_integrity = 150
+ var/speed = 2
+ var/power_efficiency = 1
+ var/power_usage = 50
+ var/panel_open = FALSE
+ var/list/required_parts = list(/obj/item/stock_parts/manipulator,
+ /obj/item/stock_parts/manipulator,
+ /obj/item/stock_parts/capacitor)
+ var/obj/item/stock_parts/cell/power_cell
+
+/obj/vehicle/ridden/grocery_cart/motorized/CheckParts(list/parts_list)
+ ..()
+ refresh_parts()
+
+/obj/vehicle/ridden/grocery_cart/motorized/proc/refresh_parts()
+ speed = 1 // Should never be under 1
+ for(var/obj/item/stock_parts/manipulator/M in contents)
+ speed += M.rating
+ for(var/obj/item/stock_parts/capacitor/C in contents)
+ power_efficiency = C.rating
+ var/datum/component/riding/D = GetComponent(/datum/component/riding)
+ D.vehicle_move_delay = round(CONFIG_GET(number/movedelay/run_delay) * delay_multiplier) / speed
+
+/obj/vehicle/ridden/grocery_cart/motorized/obj_destruction(damage_flag)
+ var/turf/T = get_turf(src)
+ for(var/atom/movable/A in contents)
+ A.forceMove(T)
+ //if(isliving(A))
+ // var/mob/living/L = A
+ // L.update_mobility()
+ ..()
+
+/obj/vehicle/ridden/grocery_cart/motorized/driver_move(mob/living/user, direction)
+ if(istype(user))
+ if(!canmove)
+ return FALSE
+ if(!power_cell)
+ to_chat(user, "There seems to be no cell installed in [src].")
+ canmove = FALSE
+ addtimer(VARSET_CALLBACK(src, canmove, TRUE), 20)
+ return FALSE
+ if(power_cell.charge < power_usage / max(power_efficiency, 1))
+ to_chat(user, "The display on [src] blinks 'Out of Power'.")
+ canmove = FALSE
+ addtimer(VARSET_CALLBACK(src, canmove, TRUE), 20)
+ return FALSE
+ if(user.get_num_arms() < arms_required)
+ to_chat(user, "You don't have enough arms to operate the motor controller!")
+ canmove = FALSE
+ addtimer(VARSET_CALLBACK(src, canmove, TRUE), 20)
+ return FALSE
+ power_cell.use(power_usage / max(power_efficiency, 1))
+ return ..()
+
+/obj/vehicle/ridden/wheelchair/motorized/set_move_delay(mob/living/user)
+ return
+
+/obj/vehicle/ridden/grocery_cart/motorized/post_buckle_mob(mob/living/user)
+ . = ..()
+ density = TRUE
+
+/obj/vehicle/ridden/grocery_cart/motorized/post_unbuckle_mob()
+ . = ..()
+ density = FALSE
+
+/obj/vehicle/ridden/grocery_cart/motorized/attack_hand(mob/living/user)
+ if(power_cell && panel_open)
+ power_cell.update_icon()
+ user.put_in_hands(power_cell)
+ power_cell = null
+ to_chat(user, "You remove the power cell from [src].")
+ return
+ return ..()
+
+/obj/vehicle/ridden/grocery_cart/motorized/attackby(obj/item/I, mob/user, params)
+ if(I.tool_behaviour == TOOL_SCREWDRIVER)
+ I.play_tool_sound(src)
+ panel_open = !panel_open
+ user.visible_message("[user] [panel_open ? "opens" : "closes"] the maintenance panel on [src].", "You [panel_open ? "open" : "close"] the maintenance panel.")
+ return
+ if(panel_open)
+ if(istype(I, /obj/item/stock_parts/cell))
+ if(power_cell)
+ to_chat(user, "There is a power cell already installed.")
+ else
+ I.forceMove(src)
+ power_cell = I
+ to_chat(user, "You install the [I].")
+ refresh_parts()
+ return
+ if(istype(I, /obj/item/stock_parts))
+ var/obj/item/stock_parts/B = I
+ var/P
+ for(var/obj/item/stock_parts/A in contents)
+ for(var/D in required_parts)
+ if(ispath(A.type, D))
+ P = D
+ break
+ if(istype(B, P) && istype(A, P))
+ if(B.get_part_rating() > A.get_part_rating())
+ B.forceMove(src)
+ user.put_in_hands(A)
+ user.visible_message("[user] replaces [A] with [B] in [src].", "You replace [A] with [B].")
+ break
+ refresh_parts()
+ return
+ return ..()
+
+/obj/vehicle/ridden/grocery_cart/motorized/wrench_act(mob/living/user, obj/item/I)
+ to_chat(user, "You begin to detach the wheels...")
+ if(I.use_tool(src, user, 40, volume=50))
+ to_chat(user, "You detach the wheels and deconstruct the chair.")
+ new /obj/item/stack/rods(drop_location(), 8)
+ new /obj/item/stack/sheet/metal(drop_location(), 10)
+ var/turf/T = get_turf(src)
+ for(var/atom/movable/A in contents)
+ A.forceMove(T)
+ //if(isliving(A))
+ // var/mob/living/L = A
+ // L.update_mobility()
+ qdel(src)
+ return TRUE
+
+/obj/vehicle/ridden/grocery_cart/motorized/examine(mob/user)
+ . = ..()
+ if((obj_flags & EMAGGED) && panel_open)
+ . += "There is a bomb under the maintenance panel."
+ . += "There is a small screen on it, [(in_range(user, src) || isobserver(user)) ? "[power_cell ? "it reads:" : "but it is dark."]" : "but you can't see it from here."]"
+ if(!power_cell || (!in_range(user, src) && !isobserver(user)))
+ return
+ . += "Speed: [speed]"
+ . += "Energy efficiency: [power_efficiency]"
+ . += "Power: [power_cell.charge] out of [power_cell.maxcharge]"
+
+/obj/vehicle/ridden/grocery_cart/motorized/Bump(atom/movable/M)
+ . = ..()
+ // Here is the shitty emag functionality.
+ if(obj_flags & EMAGGED && (istype(M, /turf/closed) || isliving(M)))
+ explosion(src, -1, 1, 3, 2, 0)
+ visible_message("[src] explodes!!")
+ return
+ // If the speed is higher than delay_multiplier throw the person on the wheelchair away
+ if(M.density && speed > delay_multiplier && has_buckled_mobs())
+ var/mob/living/H = buckled_mobs[1]
+ var/atom/throw_target = get_edge_target_turf(H, pick(GLOB.cardinals))
+ unbuckle_mob(H)
+ H.throw_at(throw_target, 2, 3)
+ H.Knockdown(100)
+ H.adjustStaminaLoss(40)
+ if(isliving(M))
+ var/mob/living/D = M
+ throw_target = get_edge_target_turf(D, pick(GLOB.cardinals))
+ D.throw_at(throw_target, 2, 3)
+ D.Knockdown(80)
+ D.adjustStaminaLoss(35)
+ visible_message("[src] crashes into [M], sending [H] and [D] flying!")
+ else
+ visible_message("[src] crashes into [M], sending [H] flying!")
+ playsound(src, 'sound/effects/bang.ogg', 50, 1)
+
+/obj/vehicle/ridden/grocery_cart/motorized/emag_act(mob/user)
+ if((obj_flags & EMAGGED) || !panel_open)
+ return
+ to_chat(user, "A bomb appears in [src], what the fuck?")
+ obj_flags |= EMAGGED
diff --git a/code/modules/vehicles/grocery_cart_scooter_unmortorized.dm b/code/modules/vehicles/grocery_cart_scooter_unmortorized.dm
new file mode 100644
index 00000000..65fbd48d
--- /dev/null
+++ b/code/modules/vehicles/grocery_cart_scooter_unmortorized.dm
@@ -0,0 +1,116 @@
+/obj/vehicle/ridden/grocery_cart //ported from Hippiestation (by Jujumatic)
+ name = "grocery cart"
+ desc = "A cart you can use to hold groceries, or ride in."
+ icon = 'icons/obj/vehicles.dmi'
+ icon_state = "grocery_cart"
+ layer = OBJ_LAYER
+ max_integrity = 100
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 30) //grocery_carts aren't super tough yo
+ legs_required = 0 //You'll probably be using this if you don't have legs
+ canmove = TRUE
+ density = FALSE //Thought I couldn't fix this one easily, phew
+ // Run speed delay is multiplied with this for vehicle move delay.
+ var/delay_multiplier = 6.7
+ size_sensitive = FALSE
+
+/obj/vehicle/ridden/grocery_cart/Initialize()
+ . = ..()
+ var/datum/component/riding/D = LoadComponent(/datum/component/riding)
+ D.vehicle_move_delay = 3
+ D.set_vehicle_dir_layer(SOUTH, OBJ_LAYER)
+ D.set_vehicle_dir_layer(NORTH, ABOVE_MOB_LAYER)
+ D.set_vehicle_dir_layer(EAST, OBJ_LAYER)
+ D.set_vehicle_dir_layer(WEST, OBJ_LAYER)
+
+/obj/vehicle/ridden/grocery_cart/ComponentInitialize() //Since it's technically a chair I want it to have chair properties
+ . = ..()
+ AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE, CALLBACK(src, .proc/can_user_rotate),CALLBACK(src, .proc/can_be_rotated),null)
+
+/obj/vehicle/ridden/grocery_cart/obj_destruction(damage_flag)
+ new /obj/item/stack/rods(drop_location(), 1)
+ new /obj/item/stack/sheet/metal(drop_location(), 1)
+ ..()
+
+/obj/vehicle/ridden/grocery_cart/Destroy()
+ if(has_buckled_mobs())
+ var/mob/living/carbon/H = buckled_mobs[1]
+ unbuckle_mob(H)
+ return ..()
+
+/obj/vehicle/ridden/grocery_cart/driver_move(mob/living/user, direction)
+ if(istype(user))
+ if(canmove && (user.get_num_arms() < arms_required))
+ to_chat(user, "You don't have enough arms to operate the wheels!")
+ canmove = FALSE
+ addtimer(VARSET_CALLBACK(src, canmove, TRUE), 20)
+ return FALSE
+ set_move_delay(user)
+ return ..()
+
+/obj/vehicle/ridden/grocery_cart/proc/set_move_delay(mob/living/user)
+ var/datum/component/riding/D = GetComponent(/datum/component/riding)
+ //1.5 (movespeed as of this change) multiplied by 6.7 gets ABOUT 10 (rounded), the old constant for the grocery_cart that gets divided by how many arms they have
+ //if that made no sense this simply makes the grocery_cart speed change along with movement speed delay
+ D.vehicle_move_delay = round(CONFIG_GET(number/movedelay/run_delay) * delay_multiplier) / min(user.get_num_arms(), 2)
+
+/obj/vehicle/ridden/grocery_cart/Moved()
+ . = ..()
+ cut_overlays()
+ playsound(src, 'sound/effects/roll.ogg', 75, TRUE)
+ if(has_buckled_mobs())
+ handle_rotation_overlayed()
+
+
+/obj/vehicle/ridden/grocery_cart/post_buckle_mob(mob/living/user)
+ . = ..()
+ handle_rotation_overlayed()
+
+/obj/vehicle/ridden/grocery_cart/post_unbuckle_mob()
+ . = ..()
+ cut_overlays()
+
+/obj/vehicle/ridden/grocery_cart/setDir(newdir)
+ ..()
+ handle_rotation(newdir)
+
+/obj/vehicle/ridden/grocery_cart/wrench_act(mob/living/user, obj/item/I) //Attackby should stop it attacking the grocery_cart after moving away during decon
+ ..()
+ to_chat(user, "You begin to detach the wheels...")
+ if(I.use_tool(src, user, 40, volume=50))
+ to_chat(user, "You detach the wheels and deconstruct the chair.")
+ new /obj/item/stack/rods(drop_location(), 6)
+ new /obj/item/stack/sheet/metal(drop_location(), 4)
+ qdel(src)
+ return TRUE
+
+/obj/vehicle/ridden/grocery_cart/proc/handle_rotation(direction)
+ if(has_buckled_mobs())
+ handle_rotation_overlayed()
+ for(var/m in buckled_mobs)
+ var/mob/living/buckled_mob = m
+ buckled_mob.setDir(direction)
+
+/obj/vehicle/ridden/grocery_cart/proc/handle_rotation_overlayed()
+ cut_overlays()
+ var/image/V = image(icon = icon, icon_state = "grocery_cart_overlay", layer = FLY_LAYER, dir = src.dir)
+ add_overlay(V)
+
+
+
+/obj/vehicle/ridden/grocery_cart/proc/can_be_rotated(mob/living/user)
+ return TRUE
+
+/obj/vehicle/ridden/grocery_cart/proc/can_user_rotate(mob/living/user)
+ var/mob/living/L = user
+ if(istype(L))
+ if(!user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
+ return FALSE
+ if(isobserver(user) && CONFIG_GET(flag/ghost_interaction))
+ return TRUE
+ return FALSE
+
+/obj/vehicle/ridden/grocery_cart/the_whip/driver_move(mob/living/user, direction)
+ if(istype(user))
+ var/datum/component/riding/D = GetComponent(/datum/component/riding)
+ D.vehicle_move_delay = round(CONFIG_GET(number/movedelay/run_delay) * 6.7) / user.get_num_arms()
+ return ..()
diff --git a/icons/obj/vehicles.dmi b/icons/obj/vehicles.dmi
index 67079de3..cf82f3d7 100644
Binary files a/icons/obj/vehicles.dmi and b/icons/obj/vehicles.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 1d06e1ab..3e090c81 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3018,6 +3018,8 @@
#include "code\modules\vehicles\_vehicle.dm"
#include "code\modules\vehicles\atv.dm"
#include "code\modules\vehicles\bicycle.dm"
+#include "code\modules\vehicles\grocery_cart_scooter.dm"
+#include "code\modules\vehicles\grocery_cart_scooter_unmortorized.dm"
#include "code\modules\vehicles\lavaboat.dm"
#include "code\modules\vehicles\motorized_wheelchair.dm"
#include "code\modules\vehicles\pimpin_ride.dm"