mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-29 23:17:02 +01:00
Adds Clown Car, Ports TG Vehicle Code (#29529)
* WIP - Clown Car, Bugged key insert, buckle, driver can't drive * WIP - Needs Movement, Testing * Compiles * Sealed port * Car * Clown Car Cannon * Formats for comments * Linters * Linters again * Linters * Attack chains * Dump mobs on supermatter dust, instead of dusting everything * Supermatter bump change for clown car * Headlights fix * Fixes runechat for car, fixes opening trunk do-after, removed excess code * Another do-after fix * Fixed squishing prone people, fixed buckle bug * Better trait handling, fixes some silicon issues * Fixes emergency lube foam * Fixes AI getting nullspaced on eject * Fixes hands remaining bound when ejected after using cannon mode, fixes cannon mode, adds var-editable thing * Updated clown car actions * Update code/modules/vehicle/tg_vehicles/cars/clowncar.dm Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> --------- Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
This commit is contained in:
co-authored by
Burzah
parent
6dff434902
commit
06f19cdecb
@@ -41,3 +41,7 @@
|
||||
/// from /datum/component/shelver/shelf_items()
|
||||
#define COMSIG_SHELF_ATTEMPT_PICKUP "shelf_attempt_pickup"
|
||||
#define SHELF_PICKUP_FAILURE (1 << 0)
|
||||
|
||||
/// from /datum/component/supermatter_crystal/proc/consume()
|
||||
/// called on the thing consumed, passes the thing which consumed it
|
||||
#define COMSIG_SUPERMATTER_CONSUMED "sm_consumed_this"
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
|
||||
#define ismecha(A) (istype(A, /obj/mecha))
|
||||
|
||||
#define isclowncar(A) (istype(A, /obj/tgvehicle/sealed/car/clowncar))
|
||||
|
||||
#define iseffect(A) (istype(A, /obj/effect))
|
||||
|
||||
#define isclothing(A) (istype(A, /obj/item/clothing))
|
||||
|
||||
@@ -43,6 +43,14 @@
|
||||
|
||||
#define TIMER_COOLDOWN_END(cd_source, cd_index) LAZYREMOVE(cd_source.cooldowns, cd_index)
|
||||
|
||||
|
||||
/// Checks if a timer based cooldown is NOT finished.
|
||||
#define TIMER_COOLDOWN_RUNNING(cd_source, cd_index) LAZYACCESS(cd_source.cooldowns, cd_index)
|
||||
|
||||
/// Checks if a timer based cooldown is finished.
|
||||
#define TIMER_COOLDOWN_FINISHED(cd_source, cd_index) (!TIMER_COOLDOWN_RUNNING(cd_source, cd_index))
|
||||
|
||||
|
||||
/*
|
||||
* Stoppable timer cooldowns.
|
||||
* Use indexes the same as the regular tiemr cooldowns.
|
||||
|
||||
@@ -27,6 +27,14 @@
|
||||
/// Do we need a carbon, a silicon, or a small mob, to ride the vehicle?
|
||||
#define RIDER_CARBON_OR_SILICON_NO_LARGE_MOBS (1<<4)
|
||||
|
||||
///Will this car kidnap people by ramming into them?
|
||||
#define CAN_KIDNAP (1<<0)
|
||||
|
||||
#define CLOWN_CANNON_INACTIVE 0
|
||||
#define CLOWN_CANNON_BUSY 1
|
||||
#define CLOWN_CANNON_READY 2
|
||||
|
||||
|
||||
/// The vehicle being ridden requires pixel offsets for all directions
|
||||
#define RIDING_OFFSET_ALL "ALL"
|
||||
|
||||
|
||||
@@ -455,6 +455,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define DOGGO_SPACESUIT "doggo_spacesuit"
|
||||
#define FLOORCLUWNE "floorcluwne"
|
||||
#define LOCKDOWN_TRAIT "lockdown"
|
||||
#define VEHICLE_TRAIT "vehicle"
|
||||
#define STAT_TRAIT "stat_trait"
|
||||
#define TRANSFORMING_TRAIT "transforming"
|
||||
#define BUCKLING_TRAIT "buckled"
|
||||
@@ -466,6 +467,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define HOLO_CIGAR "holo_cigar"
|
||||
#define GLADIATOR "gladiator"
|
||||
#define PULSEDEMON_TRAIT "pulse_demon"
|
||||
#define TRAIT_CLOWN_CAR_SQUISHED "squished_by_car"
|
||||
/// Mentor observing
|
||||
#define MENTOR_OBSERVING "mobserving"
|
||||
#define TIPPED_OVER "tipped_over"
|
||||
|
||||
@@ -108,6 +108,10 @@
|
||||
var/obj/mecha/M = loc
|
||||
return M.click_action(A, src, params)
|
||||
|
||||
if(isclowncar(loc) && !modifiers["shift"])
|
||||
var/obj/tgvehicle/sealed/car/clowncar/cc = loc
|
||||
return cc.fire_cannon_at(A, src, params)
|
||||
|
||||
if(restrained())
|
||||
RestrainedClickOn(A)
|
||||
return
|
||||
|
||||
@@ -49,6 +49,9 @@
|
||||
if(incapacitated())
|
||||
return
|
||||
|
||||
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
|
||||
return
|
||||
|
||||
if(next_move >= world.time)
|
||||
return
|
||||
|
||||
|
||||
@@ -198,4 +198,8 @@
|
||||
return
|
||||
override_allow_spacemove = FALSE
|
||||
|
||||
/datum/component/riding/vehicle/clowncar
|
||||
vehicle_move_delay = 0.6
|
||||
ride_check_flags = RIDER_NEEDS_LEGS | RIDER_NEEDS_ARMS
|
||||
|
||||
#undef EPSILON
|
||||
|
||||
@@ -49,6 +49,21 @@
|
||||
cost = 5
|
||||
job = list("Clown")
|
||||
|
||||
/datum/uplink_item/jobspecific/clown_car
|
||||
name = "Clown Car"
|
||||
desc = "The Clown Car is the ultimate transportation method for any worthy clown! \
|
||||
Simply insert your bikehorn and get in, and get ready to have the funniest ride of your life! \
|
||||
You can ram any crew you come across and stuff them into your car, kidnapping them and locking them inside until \
|
||||
someone saves them or they manage to crawl out. Be sure not to ram into any walls or vending machines, as the springloaded seats \
|
||||
are very sensitive. Now with our included lube defense mechanism which will protect you against any angry shitcurity! \
|
||||
Premium features can be unlocked with a cryptographic sequencer!"
|
||||
reference = "CCR"
|
||||
item = /obj/tgvehicle/sealed/car/clowncar
|
||||
cost = 50
|
||||
job = list("Clown")
|
||||
surplus = 0
|
||||
hijack_only = TRUE
|
||||
|
||||
//mime
|
||||
/datum/uplink_item/jobspecific/caneshotgun
|
||||
name = "Cane Shotgun and Assassination Shells"
|
||||
|
||||
@@ -2032,6 +2032,8 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
|
||||
/mob/living/carbon/human/update_runechat_msg_location()
|
||||
if(ismecha(loc))
|
||||
runechat_msg_location = loc.UID()
|
||||
else if(istgvehicle(loc))
|
||||
runechat_msg_location = loc.UID()
|
||||
else
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -376,6 +376,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
module_actions.Cut()
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/robot/update_runechat_msg_location()
|
||||
if(istgvehicle(loc))
|
||||
runechat_msg_location = loc.UID()
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/robot/proc/pick_module()
|
||||
if(module)
|
||||
return
|
||||
|
||||
@@ -857,6 +857,7 @@
|
||||
Bumped(A)
|
||||
|
||||
/obj/machinery/atmospherics/supermatter_crystal/proc/Consume(atom/movable/AM)
|
||||
SEND_SIGNAL(AM, COMSIG_SUPERMATTER_CONSUMED, src)
|
||||
if(isliving(AM))
|
||||
var/mob/living/user = AM
|
||||
if(user.status_flags & GODMODE)
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/obj/tgvehicle/sealed/car
|
||||
layer = ABOVE_MOB_LAYER
|
||||
move_resist = MOVE_FORCE_VERY_STRONG
|
||||
|
||||
///Bitflags for special behavior such as kidnapping
|
||||
var/car_traits = NONE
|
||||
///Sound file(s) to play when we drive around
|
||||
var/engine_sound = 'sound/vehicles/carrev.ogg'
|
||||
///Set this to the length of the engine sound.
|
||||
var/engine_sound_length = 2 SECONDS
|
||||
///Time it takes to break out of the car.
|
||||
var/escape_time = 6 SECONDS
|
||||
/// How long it takes to move, cars don't use the riding component similar to mechs so we handle it ourselves
|
||||
var/vehicle_move_delay = 1
|
||||
/// What sound to play if someone was forced in.
|
||||
var/forced_enter_sound
|
||||
/// How long it takes to rev (vrrm vrrm!)
|
||||
COOLDOWN_DECLARE(enginesound_cooldown)
|
||||
|
||||
/obj/tgvehicle/sealed/car/generate_actions()
|
||||
. = ..()
|
||||
if(!isnull(key_type))
|
||||
initialize_controller_action_type(/datum/action/vehicle/sealed/remove_key, VEHICLE_CONTROL_DRIVE)
|
||||
if(car_traits & CAN_KIDNAP)
|
||||
initialize_controller_action_type(/datum/action/vehicle/sealed/dump_kidnapped_mobs, VEHICLE_CONTROL_DRIVE)
|
||||
|
||||
/obj/tgvehicle/sealed/car/MouseDrop_T(mob/living/M, mob/living/user)
|
||||
if(!istype(M))
|
||||
return FALSE
|
||||
if(HAS_TRAIT(M, TRAIT_HANDS_BLOCKED) && !is_driver(M))
|
||||
return FALSE
|
||||
if((car_traits & CAN_KIDNAP) && isliving(user) && M != user)
|
||||
var/mob/living/kidnapped = M
|
||||
kidnapped.visible_message("<span class='warning'>[user] starts forcing [kidnapped] into [src]!</span>")
|
||||
mob_try_forced_enter(user, kidnapped)
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/sealed/car/mob_try_exit(mob/future_pedestrian, mob/user, silent = FALSE)
|
||||
if(future_pedestrian != user || !(LAZYACCESS(occupants, future_pedestrian) & VEHICLE_CONTROL_KIDNAPPED))
|
||||
mob_exit(future_pedestrian, silent)
|
||||
return TRUE
|
||||
if(escape_time > 0)
|
||||
to_chat(user, "<span class='notice'>You push against the back of \the [src]'s trunk to try and get out.</span>")
|
||||
if(!do_after(user, escape_time, target = src))
|
||||
return FALSE
|
||||
to_chat(user, "<span class='danger'>[user] gets out of [src].</span>")
|
||||
mob_exit(future_pedestrian, silent)
|
||||
return TRUE
|
||||
|
||||
/obj/tgvehicle/sealed/car/attack_hand(mob/living/user, list/modifiers)
|
||||
. = ..()
|
||||
if(!(car_traits & CAN_KIDNAP))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You start opening [src]'s trunk.</span>")
|
||||
if(!do_after(user, 30, target = src))
|
||||
return
|
||||
if(return_amount_of_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED))
|
||||
to_chat(user, "<span class='notice'>The people stuck in [src]'s trunk all come tumbling out.</span>?")
|
||||
dump_specific_mobs(VEHICLE_CONTROL_KIDNAPPED)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>It seems [src]'s trunk was empty.</span>")
|
||||
|
||||
/// Attempts to force a mob into the car
|
||||
/obj/tgvehicle/sealed/car/proc/mob_try_forced_enter(mob/forcer, mob/kidnapped, silent = FALSE)
|
||||
if(occupant_amount() >= max_occupants)
|
||||
return FALSE
|
||||
var/atom/old_loc = loc
|
||||
var/enter_delay = get_enter_delay(kidnapped)
|
||||
if(enter_delay == 0 || do_after(forcer, enter_delay, target = kidnapped, extra_checks=CALLBACK(src, TYPE_PROC_REF(/obj/tgvehicle/sealed/car, is_car_stationary), old_loc)))
|
||||
mob_forced_enter(kidnapped, silent)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/// Callback proc to check for
|
||||
/obj/tgvehicle/sealed/car/proc/is_car_stationary(atom/old_loc)
|
||||
return (old_loc == loc)
|
||||
|
||||
/// Proc called when someone is forcefully stuffedd into a car
|
||||
/obj/tgvehicle/sealed/car/proc/mob_forced_enter(mob/kidnapped, silent = FALSE)
|
||||
if(!silent)
|
||||
kidnapped.visible_message("<span class='warning'>[kidnapped] is forced into \the [src]!</span>")
|
||||
if(forced_enter_sound)
|
||||
playsound(src, forced_enter_sound, 70, TRUE)
|
||||
kidnapped.forceMove(src)
|
||||
add_occupant(kidnapped, VEHICLE_CONTROL_KIDNAPPED, TRUE)
|
||||
|
||||
/obj/tgvehicle/sealed/car/Destroy()
|
||||
explosion(src, heavy_impact_range = 1, light_impact_range = 2, flash_range = 3, adminlog = FALSE)
|
||||
log_attack("[src] exploded due to destruction")
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/sealed/car/relaymove(mob/living/user, direction)
|
||||
if(is_driver(user) && canmove && (!key_type || istype(inserted_key, key_type)))
|
||||
vehicle_move(direction)
|
||||
return TRUE
|
||||
|
||||
/obj/tgvehicle/sealed/car/vehicle_move(direction)
|
||||
if(!COOLDOWN_FINISHED(src, cooldown_vehicle_move))
|
||||
return FALSE
|
||||
COOLDOWN_START(src, cooldown_vehicle_move, vehicle_move_delay)
|
||||
|
||||
if(COOLDOWN_FINISHED(src, enginesound_cooldown))
|
||||
COOLDOWN_START(src, enginesound_cooldown, engine_sound_length)
|
||||
playsound(get_turf(src), engine_sound, 100, TRUE)
|
||||
|
||||
..()
|
||||
if(trailer)
|
||||
var/dir_to_move = get_dir(trailer.loc, loc)
|
||||
var/did_move = step(trailer, direction)
|
||||
if(did_move)
|
||||
step(trailer, dir_to_move)
|
||||
return did_move
|
||||
after_move(direction)
|
||||
return step(src, direction)
|
||||
@@ -0,0 +1,297 @@
|
||||
/obj/tgvehicle/sealed/car/clowncar
|
||||
name = "clown car"
|
||||
desc = "How someone could even fit in there is beyond me."
|
||||
icon_state = "clowncar"
|
||||
max_integrity = 150
|
||||
max_occupants = 50
|
||||
key_type = /obj/item/bikehorn
|
||||
enter_sound = 'sound/effects/clowncar/door_close.ogg'
|
||||
exit_sound = 'sound/effects/clowncar/door_open.ogg'
|
||||
car_traits = CAN_KIDNAP
|
||||
armor = list(melee = 70, bullet = 40, laser = 40, energy = 40, bomb = 30, rad = 0, fire = 80, acid = 80)
|
||||
enter_delay = 4 SECONDS
|
||||
access_provider_flags = VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_KIDNAPPED
|
||||
/// list of headlight colors we use to pick through when we have party mode due to emag
|
||||
var/headlight_colors = list(COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_BLUE_LIGHT, COLOR_CYAN, COLOR_PURPLE)
|
||||
/// Cooldown time inbetween [/obj/tgvehicle/sealed/car/clowncar/proc/roll_the_dice()] usages
|
||||
var/dice_cooldown_time = 15 SECONDS
|
||||
/// Current status of the cannon, alternates between CLOWN_CANNON_INACTIVE, CLOWN_CANNON_BUSY and CLOWN_CANNON_READY
|
||||
var/cannonmode = CLOWN_CANNON_INACTIVE
|
||||
/// Does the driver require the clown role to drive it
|
||||
var/enforce_clown_role = TRUE
|
||||
/// Emag Button Cooldown
|
||||
var/last_emag_button_use = 0
|
||||
/// How fast can you fire the cannon
|
||||
var/cannon_fire_delay = 0.5 SECONDS
|
||||
COOLDOWN_DECLARE(cannon_cooldown)
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/Initialize(mapload)
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj,src)
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/process()
|
||||
if(headlights_toggle && emagged)
|
||||
set_light(headlight_range, headlight_power, pick(headlight_colors))
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/generate_actions()
|
||||
. = ..()
|
||||
initialize_controller_action_type(/datum/action/vehicle/sealed/horn, VEHICLE_CONTROL_DRIVE)
|
||||
initialize_controller_action_type(/datum/action/vehicle/sealed/headlights, VEHICLE_CONTROL_DRIVE)
|
||||
initialize_controller_action_type(/datum/action/vehicle/sealed/thank, VEHICLE_CONTROL_KIDNAPPED)
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/auto_assign_occupant_flags(mob/M)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
// Only clowns can drive the car, unless an admin varedits the car to allow someone else to drive.
|
||||
if(H.mind?.assigned_role == "Clown" || (!enforce_clown_role && length(return_controllers_with_flag(VEHICLE_CONTROL_DRIVE)) < 1))
|
||||
add_control_flags(H, VEHICLE_CONTROL_DRIVE)
|
||||
playsound(src, 'sound/effects/clowncar/door_close.ogg', 70, TRUE)
|
||||
log_game("[M] has entered [src] as a possible driver")
|
||||
return
|
||||
add_control_flags(M, VEHICLE_CONTROL_KIDNAPPED)
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/mob_forced_enter(mob/M)
|
||||
. = ..()
|
||||
playsound(src, pick('sound/effects/clowncar/clowncar_load1.ogg', 'sound/effects/clowncar/clowncar_load2.ogg'), 75)
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/forced_mob = M
|
||||
if(forced_mob.reagents.has_reagent("dublindrop"))
|
||||
var/reagent_amount = forced_mob.reagents.get_reagent_amount("dublindrop")
|
||||
forced_mob.reagents.del_reagent("dublindrop")
|
||||
if(reagent_amount >= 30)
|
||||
message_admins("[ADMIN_LOOKUPFLW(forced_mob)] was forced into a clown car with [reagent_amount] unit(s) of Irish Car Bomb, causing an explosion.")
|
||||
audible_message("<span class='warning'>You hear a rattling sound coming from the engine. That can't be good...</span>", null, 1)
|
||||
addtimer(CALLBACK(src, PROC_REF(irish_car_bomb)), 5 SECONDS)
|
||||
|
||||
// We don't want everything in the clown car to get dusted when it touches the supermatter, so dump the mobs.
|
||||
/obj/tgvehicle/sealed/car/clowncar/on_entered_supermatter(atom/movable/vehicle, atom/movable/supermatter)
|
||||
dump_mobs(TRUE)
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/irish_car_bomb()
|
||||
dump_mobs()
|
||||
explosion(src, light_impact_range = 1)
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/attack_animal(mob/living/simple_animal/user, list/modifiers)
|
||||
if((user.loc != src) || user.environment_smash >= ENVIRONMENT_SMASH_WALLS)
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
||||
. = ..()
|
||||
if(prob(33))
|
||||
visible_message("<span class='warning'>[src] spews out a ton of space lube!</span>")
|
||||
var/datum/effect_system/foam_spread/L = new()
|
||||
var/datum/reagents/foamreagent = new /datum/reagents(200)
|
||||
foamreagent.add_reagent("lube", 200)
|
||||
L.set_up(40, get_turf(src), foamreagent)
|
||||
L.start()
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user)
|
||||
. = ..()
|
||||
if(!istype(I, /obj/item/food/grown/banana))
|
||||
return
|
||||
var/obj/item/food/grown/banana/banana = I
|
||||
obj_integrity += min(banana.seed.potency, max_integrity-obj_integrity)
|
||||
to_chat(user, "<span class='warning'>You use the [banana] to repair [src]!</span>")
|
||||
qdel(banana)
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/Bump(atom/bumped)
|
||||
. = ..()
|
||||
if(isliving(bumped))
|
||||
if(ismegafauna(bumped)) // No, you cannot kidnap megafauna with the clown car
|
||||
return
|
||||
var/mob/living/hittarget_living = bumped
|
||||
|
||||
if(istype(hittarget_living, /mob/living/basic/deer))
|
||||
visible_message("<span class='warning'>[src] careens into [hittarget_living]! Oh the humanity!</span>")
|
||||
for(var/mob/living/carbon/carbon_occupant in occupants)
|
||||
if(prob(35)) // Note: The randomstep on dump_mobs throws occupants into each other and often causes wounds regardless.
|
||||
continue
|
||||
hittarget_living.adjustBruteLoss(200)
|
||||
new /obj/effect/decal/cleanable/blood/splatter(get_turf(hittarget_living))
|
||||
|
||||
dump_mobs(TRUE)
|
||||
playsound(src, 'sound/effects/clowncar/car_crash.ogg', 100)
|
||||
return
|
||||
|
||||
if(iscarbon(hittarget_living))
|
||||
var/mob/living/carbon/C = hittarget_living
|
||||
C.Stun(4 SECONDS) // I play to make sprites go horizontal
|
||||
hittarget_living.visible_message("<span class='warning'>[src] rams into [hittarget_living] and sucks [hittarget_living.p_them()] up!</span>")
|
||||
mob_forced_enter(hittarget_living)
|
||||
playsound(src, pick('sound/effects/clowncar/clowncar_ram1.ogg', 'sound/effects/clowncar/clowncar_ram2.ogg', 'sound/effects/clowncar/clowncar_ram3.ogg'), 75)
|
||||
log_attack(src, hittarget_living, "sucked up")
|
||||
return
|
||||
if(isturf(bumped))
|
||||
var/turf/bumped_turf = bumped
|
||||
if(!bumped_turf.is_blocked_turf())
|
||||
return
|
||||
if(istype(bumped, /obj/machinery/door))
|
||||
var/obj/machinery/door/conditionalwall = bumped
|
||||
for(var/mob/occupant as anything in return_controllers_with_flag(access_provider_flags))
|
||||
if(conditionalwall.allowed(occupant))
|
||||
return
|
||||
if(conditionalwall.operating)
|
||||
return
|
||||
visible_message("<span class='warning'>[src] rams into [bumped] and crashes!</span>")
|
||||
playsound(src, pick('sound/effects/clowncar/clowncar_crash1.ogg', 'sound/effects/clowncar/clowncar_crash2.ogg'), 75)
|
||||
playsound(src, 'sound/effects/clowncar/clowncar_crashpins.ogg', 75)
|
||||
dump_mobs(TRUE)
|
||||
log_attack(src, bumped, "crashed into", null)
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/after_move(direction)
|
||||
..()
|
||||
if(!has_gravity(src))
|
||||
return
|
||||
var/mob/possible_pancake = locate(/mob/living/carbon) in loc
|
||||
if(!possible_pancake)
|
||||
return
|
||||
if(possible_pancake.loc == src)
|
||||
return
|
||||
if(!iscarbon(possible_pancake))
|
||||
return
|
||||
var/mob/living/carbon/target_pancake = possible_pancake
|
||||
if(target_pancake.body_position != LYING_DOWN)
|
||||
return
|
||||
if(HAS_TRAIT(target_pancake, TRAIT_CLOWN_CAR_SQUISHED))
|
||||
return
|
||||
target_pancake.visible_message("<span class='warning'>[src] runs over [target_pancake], flattening [target_pancake.p_them()] like a pancake!</span>")
|
||||
handle_squish_carbon(target_pancake, 30)
|
||||
ADD_TRAIT(target_pancake, TRAIT_CLOWN_CAR_SQUISHED, "clown_car")
|
||||
addtimer(CALLBACK(src, PROC_REF(allow_resquish), target_pancake), 5 SECONDS)
|
||||
target_pancake.Stun(2 SECONDS)
|
||||
playsound(target_pancake, 'sound/effects/clowncar/cartoon_splat.ogg', 75)
|
||||
log_attack(src, target_pancake, "ran over")
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/emag_act(mob/user, obj/item/card/emag/emag_card)
|
||||
if(emagged)
|
||||
return FALSE
|
||||
emagged = TRUE
|
||||
to_chat(user, "<span class='warning'>You scramble [src]'s child safety lock, and a panel with six colorful buttons appears!</span>")
|
||||
initialize_controller_action_type(/datum/action/vehicle/sealed/roll_the_dice, VEHICLE_CONTROL_DRIVE)
|
||||
initialize_controller_action_type(/datum/action/vehicle/sealed/cannon, VEHICLE_CONTROL_DRIVE)
|
||||
return TRUE
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/obj_destruction(damage_flag)
|
||||
playsound(src, 'sound/misc/sadtrombone.ogg', 100)
|
||||
STOP_PROCESSING(SSobj,src)
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/allow_resquish(mob/living/carbon/pancake)
|
||||
REMOVE_TRAIT(pancake, TRAIT_CLOWN_CAR_SQUISHED, "clown_car")
|
||||
|
||||
/**
|
||||
* Plays a random funky effect
|
||||
* Only available while car is emagged
|
||||
* Possible effects:
|
||||
* * Spawn bananapeel
|
||||
* * Spawn random reagent foam
|
||||
* * Make the clown car look like a singulo temporarily
|
||||
* * Spawn Laughing chem gas
|
||||
* * Drop oil
|
||||
* * Fart and make everyone nearby laugh
|
||||
*/
|
||||
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/roll_the_dice(mob/user)
|
||||
if(last_emag_button_use + dice_cooldown_time > world.time)
|
||||
to_chat(user, "<span class=notice'>The button panel is currently recharging.</span>")
|
||||
return
|
||||
last_emag_button_use = world.time
|
||||
switch(rand(1,6))
|
||||
if(1)
|
||||
visible_message("<span class='warning'>[user] presses one of the colorful buttons on [src], and a special banana peel drops out of it.</span>")
|
||||
new /obj/item/grown/bananapeel/specialpeel(loc)
|
||||
if(2)
|
||||
visible_message("<span class='warning'>[user] presses one of the colorful buttons on [src], and unknown chemicals flood out of it.</span>")
|
||||
var/datum/reagents/randomchems = new/datum/reagents(300)
|
||||
randomchems.my_atom = src
|
||||
randomchems.add_reagent(get_random_reagent_id(), 100)
|
||||
var/datum/effect_system/foam_spread/foam = new()
|
||||
foam.set_up(200, src, randomchems)
|
||||
foam.start()
|
||||
if(3)
|
||||
visible_message("<span class='warning'>[user] presses one of the colorful buttons on [src], and the clown car turns on its singularity disguise system.</span>")
|
||||
icon = 'icons/obj/singularity.dmi'
|
||||
icon_state = "singularity_s1"
|
||||
addtimer(CALLBACK(src, PROC_REF(reset_icon)), 10 SECONDS)
|
||||
if(4)
|
||||
visible_message("<span class='warning'>[user] presses one of the colorful buttons on [src], and the clown car spews out a cloud of confetti all over the place.</span>")
|
||||
confettisize(src, 50, 8)
|
||||
if(5)
|
||||
visible_message("<span class='warning'>[user] presses one of the colorful buttons on [src], and the clown car starts dropping a lubricant trail.</span>")
|
||||
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(cover_in_oil))
|
||||
addtimer(CALLBACK(src, PROC_REF(stop_dropping_oil)), 3 SECONDS)
|
||||
if(6)
|
||||
visible_message("<span class='warning'>[user] presses one of the colorful buttons on [src], and the clown car lets out a comedic toot.</span>")
|
||||
playsound(src, 'sound/effects/clowncar/clowncar_fart.ogg', 100)
|
||||
for(var/mob/living/L in orange(loc, 6))
|
||||
L.emote("laugh")
|
||||
for(var/mob/living/L as anything in occupants)
|
||||
L.emote("laugh")
|
||||
|
||||
/// resets the icon and iconstate of the clowncar after it was set to singulo states
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/reset_icon()
|
||||
icon = initial(icon)
|
||||
icon_state = initial(icon_state)
|
||||
|
||||
/// Deploys oil when the clowncar moves in oil deploy mode
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/cover_in_oil()
|
||||
SIGNAL_HANDLER
|
||||
var/turf/simulated/T = get_turf(src)
|
||||
T.MakeSlippery(TURF_WET_LUBE)
|
||||
|
||||
/// Stops dropping oil after the time has run up
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/stop_dropping_oil()
|
||||
UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
|
||||
|
||||
/// Toggles the on and off state of the clown cannon that shoots random kidnapped people
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/toggle_cannon(mob/user)
|
||||
if(cannonmode == CLOWN_CANNON_BUSY)
|
||||
to_chat(user, "<span class='notice'>Please wait for the vehicle to finish its current action first.</span>")
|
||||
return
|
||||
if(cannonmode) // cannon active, deactivate
|
||||
flick("clowncar_fromfire", src)
|
||||
icon_state = "clowncar"
|
||||
addtimer(CALLBACK(src, PROC_REF(deactivate_cannon)), 2 SECONDS)
|
||||
playsound(src, 'sound/effects/clowncar/clowncar_cannonmode2.ogg', 75)
|
||||
visible_message("<span class='warning'>[src] starts going back into mobile mode.</span>")
|
||||
else
|
||||
canmove = FALSE // anchor and activate cannon
|
||||
flick("clowncar_tofire", src)
|
||||
icon_state = "clowncar_fire"
|
||||
visible_message("<span class='warning'>[src] opens up and reveals a large cannon.</span>")
|
||||
addtimer(CALLBACK(src, PROC_REF(activate_cannon)), 2 SECONDS)
|
||||
playsound(src, 'sound/effects/clowncar/clowncar_cannonmode1.ogg', 75)
|
||||
cannonmode = CLOWN_CANNON_BUSY
|
||||
|
||||
/// Finalizes cannon activation
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/activate_cannon()
|
||||
var/mouse_pointer = 'icons/effects/mouse_pointers/weapon_pointer.dmi'
|
||||
cannonmode = CLOWN_CANNON_READY
|
||||
for(var/mob/living/driver as anything in return_controllers_with_flag(VEHICLE_CONTROL_DRIVE))
|
||||
REMOVE_TRAIT(driver, TRAIT_HANDS_BLOCKED, VEHICLE_TRAIT)
|
||||
if(driver.client.mouse_pointer_icon == initial(driver.client.mouse_pointer_icon))
|
||||
driver.client.mouse_pointer_icon = mouse_pointer
|
||||
|
||||
/// Finalizes cannon deactivation
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/deactivate_cannon()
|
||||
canmove = TRUE
|
||||
cannonmode = CLOWN_CANNON_INACTIVE
|
||||
for(var/mob/living/driver as anything in return_controllers_with_flag(VEHICLE_CONTROL_DRIVE))
|
||||
driver.client.mouse_pointer_icon = initial(driver.client.mouse_pointer_icon)
|
||||
ADD_TRAIT(driver, TRAIT_HANDS_BLOCKED, VEHICLE_TRAIT)
|
||||
|
||||
/// Fires the cannon where the user clicks
|
||||
/obj/tgvehicle/sealed/car/clowncar/proc/fire_cannon_at(atom/target, mob/user, list/modifiers)
|
||||
if(cannonmode != CLOWN_CANNON_READY || !length(return_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED)))
|
||||
return
|
||||
if(!COOLDOWN_FINISHED(src, cannon_cooldown))
|
||||
return
|
||||
var/mob/living/unlucky_sod = pick(return_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED))
|
||||
mob_exit(unlucky_sod, silent = TRUE)
|
||||
flick("clowncar_recoil", src)
|
||||
playsound(src, pick('sound/effects/clowncar/carcannon1.ogg', 'sound/effects/clowncar/carcannon2.ogg', 'sound/effects/clowncar/carcannon3.ogg'), 75)
|
||||
unlucky_sod.throw_at(target, 10, 2)
|
||||
COOLDOWN_START(src, cannon_cooldown, cannon_fire_delay)
|
||||
log_attack(user, unlucky_sod, "fired towards [target]") // this doesn't catch if the mob hits something between the car and the target
|
||||
return COMSIG_MOB_CANCEL_CLICKON
|
||||
|
||||
@@ -300,16 +300,16 @@
|
||||
/obj/tgvehicle/scooter/skateboard/wrench_act(mob/living/user, obj/item/I)
|
||||
return
|
||||
|
||||
/obj/tgvehicle/scooter/skateboard/improvised/attackby__legacy__attackchain(obj/item/I, mob/user, params)
|
||||
/obj/tgvehicle/scooter/skateboard/improvised/item_interaction(mob/living/user, obj/item/I, list/modifiers)
|
||||
if(!istype(I, /obj/item/stack/rods))
|
||||
return ..()
|
||||
var/obj/item/stack/S = I
|
||||
if(S.get_amount() < 2)
|
||||
return
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
to_chat(user, "<span class='notice'>You begin making handlebars for [src].</span>")
|
||||
if(do_after(user, 2.5 SECONDS, target = src))
|
||||
if(!loc || !S || S.get_amount() < 2 || !S.use(2))
|
||||
return
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
to_chat(user, "<span class='notice'>You add the rods to [src], creating handlebars.</span>")
|
||||
var/obj/tgvehicle/scooter/skaterskoot = new(loc)
|
||||
if(has_buckled_mobs())
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/obj/tgvehicle/sealed
|
||||
can_buckle = FALSE
|
||||
/// How long it takes to enter the vehice
|
||||
var/enter_delay = 2 SECONDS
|
||||
/// A special curser you may get while driving
|
||||
var/mouse_pointer
|
||||
/// Are the headlights on?
|
||||
var/headlights_toggle = FALSE
|
||||
/// How far do the headlights reach?
|
||||
var/headlight_range = 6
|
||||
/// How powerful are the headlights?
|
||||
var/headlight_power = 2
|
||||
/// Determines which occupants provide access when bumping into doors
|
||||
var/access_provider_flags = VEHICLE_CONTROL_DRIVE
|
||||
|
||||
/obj/tgvehicle/sealed/Initialize(mapload)
|
||||
. = ..()
|
||||
RegisterSignal(src, COMSIG_SUPERMATTER_CONSUMED, PROC_REF(on_entered_supermatter))
|
||||
|
||||
/obj/tgvehicle/sealed/generate_actions()
|
||||
. = ..()
|
||||
initialize_passenger_action_type(/datum/action/vehicle/sealed/climb_out)
|
||||
|
||||
/obj/tgvehicle/sealed/generate_action_type()
|
||||
var/datum/action/vehicle/sealed/E = ..()
|
||||
. = E
|
||||
if(istype(E))
|
||||
E.vehicle_entered_target = src
|
||||
|
||||
/obj/tgvehicle/sealed/MouseDrop_T(mob/living/M, mob/living/user)
|
||||
if(!istype(M))
|
||||
return ..()
|
||||
mob_try_enter(M)
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/sealed/Exited(atom/movable/gone, direction)
|
||||
. = ..()
|
||||
if(ismob(gone))
|
||||
remove_occupant(gone)
|
||||
|
||||
// so that we can check the access of the vehicle's occupants. Ridden vehicles do this in the riding component, but these don't have that
|
||||
/obj/tgvehicle/sealed/Bump(atom/A)
|
||||
. = ..()
|
||||
if(istype(A, /obj/machinery/door))
|
||||
var/obj/machinery/door/conditionalwall = A
|
||||
for(var/mob/occupant as anything in return_controllers_with_flag(access_provider_flags))
|
||||
if(!conditionalwall.allowed(occupant))
|
||||
continue
|
||||
if(conditionalwall.operating || conditionalwall.emagged || conditionalwall.foam_level)
|
||||
return
|
||||
conditionalwall.bumpopen(occupant)
|
||||
|
||||
/obj/tgvehicle/sealed/after_add_occupant(mob/M)
|
||||
. = ..()
|
||||
ADD_TRAIT(M, TRAIT_HANDS_BLOCKED, VEHICLE_TRAIT)
|
||||
|
||||
/obj/tgvehicle/sealed/after_remove_occupant(mob/M)
|
||||
. = ..()
|
||||
REMOVE_TRAIT(M, TRAIT_HANDS_BLOCKED, VEHICLE_TRAIT)
|
||||
|
||||
/obj/tgvehicle/sealed/proc/mob_try_enter(mob/rider)
|
||||
if(!istype(rider))
|
||||
return FALSE
|
||||
var/enter_delay = get_enter_delay(rider)
|
||||
if(enter_delay == 0)
|
||||
if(enter_checks(rider))
|
||||
mob_enter(rider)
|
||||
return TRUE
|
||||
return FALSE
|
||||
if(do_after(rider, enter_delay, target = src))
|
||||
if(!enter_checks(rider))
|
||||
return FALSE
|
||||
mob_enter(rider)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/// returns enter do_after delay for the given mob in ticks
|
||||
/obj/tgvehicle/sealed/proc/get_enter_delay(mob/M)
|
||||
return enter_delay
|
||||
|
||||
/// Extra checks to perform during the do_after to enter the vehicle
|
||||
/obj/tgvehicle/sealed/proc/enter_checks(mob/M)
|
||||
return occupant_amount() < max_occupants
|
||||
|
||||
/obj/tgvehicle/sealed/proc/mob_enter(mob/M, silent = FALSE)
|
||||
if(!istype(M))
|
||||
return FALSE
|
||||
if(!silent)
|
||||
M.visible_message("<span class='notice'>[M] climbs into \the [src]!</span>")
|
||||
M.forceMove(src)
|
||||
add_occupant(M)
|
||||
return TRUE
|
||||
|
||||
/obj/tgvehicle/sealed/proc/mob_try_exit(mob/M, mob/user, silent = FALSE, randomstep = FALSE)
|
||||
mob_exit(M, silent, randomstep)
|
||||
|
||||
/obj/tgvehicle/sealed/proc/mob_exit(mob/M, silent = FALSE, randomstep = FALSE)
|
||||
if(!istype(M))
|
||||
return FALSE
|
||||
remove_occupant(M)
|
||||
M.forceMove(exit_location(M))
|
||||
if(randomstep)
|
||||
var/turf/target_turf = get_step(exit_location(M), pick(GLOB.cardinal))
|
||||
M.throw_at(target_turf, 5, 10)
|
||||
|
||||
if(!silent)
|
||||
M.visible_message("<span class='notice'>[M] drops out of \the [src]!</span>")
|
||||
return TRUE
|
||||
|
||||
/obj/tgvehicle/sealed/proc/exit_location(M)
|
||||
return drop_location()
|
||||
|
||||
/obj/tgvehicle/sealed/item_interaction(mob/living/user, obj/item/I, list/modifiers)
|
||||
if(key_type && !is_key(inserted_key) && is_key(I))
|
||||
if(I.flags & NODROP || !user.drop_item() || !I.forceMove(src))
|
||||
to_chat(user, "<span class='warning'>[I] is stuck to your hand!</span>")
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
to_chat(user, "<span class='notice'>You insert [I] into [src].</span>")
|
||||
if(inserted_key) // just in case there's an invalid key
|
||||
inserted_key.forceMove(drop_location())
|
||||
inserted_key = I
|
||||
inserted_key.forceMove(src)
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/sealed/proc/remove_key(mob/user)
|
||||
if(!inserted_key)
|
||||
to_chat(user, "<span class='warning'>There is no key in [src]!</span>")
|
||||
return
|
||||
if(!is_occupant(user) || !(occupants[user] & VEHICLE_CONTROL_DRIVE))
|
||||
to_chat(user, "<span class='warning'>You must be driving [src] to remove [src]'s key!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You remove [inserted_key] from [src].</span>")
|
||||
user.put_in_hands(inserted_key)
|
||||
inserted_key = null
|
||||
|
||||
/obj/tgvehicle/sealed/Destroy()
|
||||
dump_mobs()
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/sealed/proc/dump_mobs(randomstep = TRUE)
|
||||
for(var/i in occupants)
|
||||
mob_exit(i, randomstep = randomstep)
|
||||
if(iscarbon(i))
|
||||
var/mob/living/carbon/Carbon = i
|
||||
Carbon.Stun(4 SECONDS)
|
||||
|
||||
/obj/tgvehicle/sealed/proc/dump_specific_mobs(flag, randomstep = TRUE)
|
||||
for(var/i in occupants)
|
||||
if(!(occupants[i] & flag))
|
||||
continue
|
||||
mob_exit(i, randomstep = randomstep)
|
||||
if(iscarbon(i))
|
||||
var/mob/living/carbon/C = i
|
||||
C.Stun(4 SECONDS)
|
||||
|
||||
|
||||
/obj/tgvehicle/sealed/AllowDrop()
|
||||
return FALSE
|
||||
|
||||
/obj/tgvehicle/sealed/relaymove(mob/living/user, direction)
|
||||
if(canmove)
|
||||
vehicle_move(direction)
|
||||
return TRUE
|
||||
|
||||
/// Sinced sealed vehicles (cars and mechs) don't have riding components, the actual movement is handled here from [/obj/tgvehicle/sealed/proc/relaymove]
|
||||
/obj/tgvehicle/sealed/proc/vehicle_move(direction)
|
||||
return FALSE
|
||||
|
||||
/// When we touch a crystal, kill everything inside us
|
||||
/obj/tgvehicle/sealed/proc/on_entered_supermatter(atom/movable/vehicle, atom/movable/supermatter)
|
||||
SIGNAL_HANDLER // COMSIG_SUPERMATTER_CONSUMED
|
||||
for(var/mob/passenger as anything in occupants)
|
||||
if(!is_ai(passenger))
|
||||
passenger.Bump(supermatter)
|
||||
@@ -287,3 +287,116 @@
|
||||
animate(board, pixel_y = -6, time = 0.3 SECONDS)
|
||||
board.unbuckle_mob(rider)
|
||||
addtimer(CALLBACK(board, TYPE_PROC_REF(/obj/tgvehicle/scooter/skateboard, pick_up_board), rider), 0.5 SECONDS) // so the board can still handle "picking it up"
|
||||
|
||||
/datum/action/vehicle/sealed
|
||||
check_flags = AB_CHECK_IMMOBILE | AB_CHECK_CONSCIOUS
|
||||
var/obj/tgvehicle/sealed/vehicle_entered_target
|
||||
|
||||
/datum/action/vehicle/sealed/Destroy()
|
||||
vehicle_entered_target = null
|
||||
return ..()
|
||||
|
||||
/datum/action/vehicle/sealed/climb_out
|
||||
name = "Climb Out"
|
||||
desc = "Climb out of your vehicle!"
|
||||
button_icon_state = "car_eject"
|
||||
|
||||
/datum/action/vehicle/sealed/climb_out/Trigger(trigger_flags)
|
||||
if(..() && istype(vehicle_entered_target))
|
||||
vehicle_entered_target.mob_try_exit(owner, owner)
|
||||
|
||||
/datum/action/vehicle/sealed/remove_key
|
||||
name = "Remove key"
|
||||
desc = "Take your key out of the vehicle's ignition."
|
||||
button_icon_state = "car_removekey"
|
||||
|
||||
/datum/action/vehicle/sealed/remove_key/Trigger(trigger_flags)
|
||||
vehicle_entered_target.remove_key(owner)
|
||||
|
||||
// Clown car stuff
|
||||
/datum/action/vehicle/sealed/horn
|
||||
name = "Honk Horn"
|
||||
desc = "Honk your classy horn."
|
||||
button_icon_state = "car_horn"
|
||||
var/horn_sound = 'sound/items/airhorn.ogg'
|
||||
var/horn_cooldown = 60
|
||||
var/last_horn = 0
|
||||
|
||||
/datum/action/vehicle/sealed/horn/Trigger(trigger_flags)
|
||||
if(last_horn + horn_cooldown > world.time)
|
||||
return
|
||||
last_horn = world.time
|
||||
vehicle_entered_target.visible_message("<span class='warning'>[vehicle_entered_target] loudly honks!</span>")
|
||||
to_chat(owner, "<span class=notice'>You press [vehicle_entered_target]'s horn.</span>")
|
||||
if(istype(vehicle_entered_target.inserted_key, /obj/item/bikehorn))
|
||||
playsound(vehicle_entered_target, 'sound/items/bikehorn.ogg', 75) // The bikehorn plays a sound instead
|
||||
return
|
||||
playsound(vehicle_entered_target, horn_sound, 75)
|
||||
|
||||
/datum/action/vehicle/sealed/headlights
|
||||
name = "Toggle Headlights"
|
||||
desc = "Turn on your brights!"
|
||||
button_icon_state = "car_headlights"
|
||||
|
||||
/datum/action/vehicle/sealed/headlights/Trigger(trigger_flags)
|
||||
to_chat(owner, "<span class=notice'>You flip the switch for the vehicle's headlights.</span>")
|
||||
vehicle_entered_target.headlights_toggle = !vehicle_entered_target.headlights_toggle
|
||||
if(vehicle_entered_target.headlights_toggle)
|
||||
vehicle_entered_target.set_light(vehicle_entered_target.headlight_range, vehicle_entered_target.headlight_power)
|
||||
else
|
||||
vehicle_entered_target.remove_light()
|
||||
vehicle_entered_target.update_appearance()
|
||||
playsound(owner, vehicle_entered_target.headlights_toggle ? 'sound/weapons/gun_interactions/pistol_magin.ogg' : 'sound/weapons/gun_interactions/pistol_magout.ogg', 40, TRUE)
|
||||
|
||||
/datum/action/vehicle/sealed/dump_kidnapped_mobs
|
||||
name = "Dump Kidnapped Mobs"
|
||||
desc = "Dump all objects and people in your car on the floor."
|
||||
button_icon_state = "car_dump"
|
||||
|
||||
/datum/action/vehicle/sealed/dump_kidnapped_mobs/Trigger(trigger_flags)
|
||||
vehicle_entered_target.visible_message("<span class='warning'>[vehicle_target] starts dumping the people inside of it.</span>")
|
||||
vehicle_entered_target.dump_specific_mobs(VEHICLE_CONTROL_KIDNAPPED)
|
||||
|
||||
|
||||
/datum/action/vehicle/sealed/roll_the_dice
|
||||
name = "Press Colorful Button"
|
||||
desc = "Press one of those colorful buttons on your display panel!"
|
||||
button_icon_state = "car_rtd"
|
||||
|
||||
/datum/action/vehicle/sealed/roll_the_dice/Trigger(trigger_flags)
|
||||
if(!istype(vehicle_entered_target, /obj/tgvehicle/sealed/car/clowncar))
|
||||
return
|
||||
var/obj/tgvehicle/sealed/car/clowncar/C = vehicle_entered_target
|
||||
C.roll_the_dice(owner)
|
||||
|
||||
/datum/action/vehicle/sealed/cannon
|
||||
name = "Toggle Siege Mode"
|
||||
desc = "Destroy them with their own fodder!"
|
||||
button_icon_state = "car_cannon"
|
||||
|
||||
/datum/action/vehicle/sealed/cannon/Trigger(trigger_flags)
|
||||
if(!istype(vehicle_entered_target, /obj/tgvehicle/sealed/car/clowncar))
|
||||
return
|
||||
var/obj/tgvehicle/sealed/car/clowncar/C = vehicle_entered_target
|
||||
C.toggle_cannon(owner)
|
||||
|
||||
|
||||
/datum/action/vehicle/sealed/thank
|
||||
name = "Thank the Clown Car Driver"
|
||||
desc = "They're just doing their job."
|
||||
button_icon_state = "car_thanktheclown"
|
||||
COOLDOWN_DECLARE(thank_time_cooldown)
|
||||
|
||||
/datum/action/vehicle/sealed/thank/Trigger(trigger_flags)
|
||||
if(!istype(vehicle_entered_target, /obj/tgvehicle/sealed/car/clowncar))
|
||||
return
|
||||
if(!COOLDOWN_FINISHED(src, thank_time_cooldown))
|
||||
return
|
||||
COOLDOWN_START(src, thank_time_cooldown, 6 SECONDS)
|
||||
var/obj/tgvehicle/sealed/car/clowncar/clown_car = vehicle_entered_target
|
||||
var/list/mob/drivers = clown_car.return_drivers()
|
||||
if(!length(drivers))
|
||||
to_chat(owner, "<span class='warning'>You prepare to thank the driver, only to realize that they don't exist.</span>")
|
||||
return
|
||||
var/mob/clown = pick(drivers)
|
||||
owner.say("Thank you for the fun ride, [clown.name]!")
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
var/max_occupants = 1
|
||||
////Maximum amount of drivers
|
||||
var/max_drivers = 1
|
||||
/// Whether the vehicle is currently able to move
|
||||
var/canmove = TRUE
|
||||
/**
|
||||
* If the driver needs a certain item in hand (or inserted, for vehicles) to drive this. For vehicles, this must be duplicated on their riding component subtype
|
||||
* [/datum/component/riding/var/keytype] variable because only a few specific checks are handled here with this var, and the majority of it is on the riding component
|
||||
@@ -26,14 +28,18 @@
|
||||
var/key_type
|
||||
///The inserted key, needed on some vehicles to start the engine
|
||||
var/obj/item/key/inserted_key
|
||||
/// Whether the vehicle is currently able to move
|
||||
var/can_move = TRUE
|
||||
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)
|
||||
///This vehicle will follow us when we move (like atrailer duh)
|
||||
var/obj/tgvehicle/trailer
|
||||
var/are_legs_exposed = FALSE
|
||||
/// Sound played when entering a vehicle
|
||||
var/enter_sound
|
||||
/// Sound played when exiting a vehicle
|
||||
var/exit_sound
|
||||
|
||||
new_attack_chain = TRUE
|
||||
|
||||
/obj/tgvehicle/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -51,6 +57,8 @@
|
||||
/obj/tgvehicle/Exited(atom/movable/gone, direction)
|
||||
if(gone == inserted_key)
|
||||
inserted_key = null
|
||||
if(exit_sound)
|
||||
playsound(src, exit_sound, 70, TRUE)
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/examine(mob/user)
|
||||
@@ -137,7 +145,7 @@
|
||||
return
|
||||
|
||||
/obj/tgvehicle/relaymove(mob/living/user, direction)
|
||||
if(!can_move)
|
||||
if(!canmove)
|
||||
return FALSE
|
||||
if(is_driver(user))
|
||||
return relaydrive(user, direction)
|
||||
@@ -192,7 +200,7 @@
|
||||
add_occupant(M)
|
||||
return ..()
|
||||
|
||||
/obj/tgvehicle/attackby__legacy__attackchain(obj/item/I, mob/user, params)
|
||||
/obj/tgvehicle/item_interaction(mob/living/user, obj/item/I, list/modifiers)
|
||||
if(!key_type || is_key(inserted_key) || !is_key(I))
|
||||
return ..()
|
||||
if(user.drop_item())
|
||||
@@ -203,15 +211,12 @@
|
||||
inserted_key = I
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[I] seems to be stuck to your hand!</span>")
|
||||
if(inserted_key) //just in case there's an invalid key
|
||||
inserted_key.forceMove(drop_location())
|
||||
inserted_key = I
|
||||
|
||||
/obj/tgvehicle/AltClick(mob/user)
|
||||
if(!inserted_key)
|
||||
return ..()
|
||||
if(!is_occupant(user))
|
||||
to_chat(user, "<span class='warning'>You must be riding the [src] to remove [src]'s key!</span>")
|
||||
if(!is_occupant(user) || !(occupants[user] & VEHICLE_CONTROL_DRIVE))
|
||||
to_chat(user, "<span class='warning'>You must be driving the [src] to remove [src]'s key!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You remove [inserted_key] from [src].</span>")
|
||||
inserted_key.forceMove(drop_location())
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 88 KiB |
@@ -3240,8 +3240,11 @@
|
||||
#include "code\modules\vehicle\sportscar.dm"
|
||||
#include "code\modules\vehicle\vehicle.dm"
|
||||
#include "code\modules\vehicle\tg_vehicles\scooter.dm"
|
||||
#include "code\modules\vehicle\tg_vehicles\sealed.dm"
|
||||
#include "code\modules\vehicle\tg_vehicles\tg_vehicle_actions.dm"
|
||||
#include "code\modules\vehicle\tg_vehicles\tg_vehicles.dm"
|
||||
#include "code\modules\vehicle\tg_vehicles\cars\car.dm"
|
||||
#include "code\modules\vehicle\tg_vehicles\cars\clowncar.dm"
|
||||
#include "code\modules\virology\virology_goals.dm"
|
||||
#include "code\modules\vote\vote_datum.dm"
|
||||
#include "code\modules\vote\vote_presets.dm"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user