diff --git a/maplestation.dme b/maplestation.dme index 16445d510ef8..cd15fd68665e 100644 --- a/maplestation.dme +++ b/maplestation.dme @@ -6820,7 +6820,9 @@ #include "maplestation_modules\story_content\shiro_equipment\code\shiroclothing.dm" #include "maplestation_modules\story_content\story_posters\code\contraband.dm" #include "maplestation_modules\story_content\stranger_equipment\code\strangerclothing.dm" +#include "maplestation_modules\story_content\volkan_equipment\code\clockworktoys.dm" #include "maplestation_modules\story_content\volkan_equipment\code\councilgifts.dm" +#include "maplestation_modules\story_content\volkan_equipment\code\mini_forklift.dm" #include "maplestation_modules\story_content\volkan_equipment\code\sunitems.dm" #include "maplestation_modules\story_content\volkan_equipment\code\volkancomponents.dm" #include "maplestation_modules\story_content\volkan_equipment\code\volkanitems.dm" diff --git a/maplestation_modules/story_content/volkan_equipment/code/clockworktoys.dm b/maplestation_modules/story_content/volkan_equipment/code/clockworktoys.dm new file mode 100644 index 000000000000..5492809d0a16 --- /dev/null +++ b/maplestation_modules/story_content/volkan_equipment/code/clockworktoys.dm @@ -0,0 +1,205 @@ +#define SPECIAL_ATTACK_HEAL 1 +#define SPECIAL_ATTACK_DAMAGE 2 +#define SPECIAL_ATTACK_UTILITY 3 +#define SPECIAL_ATTACK_OTHER 4 + +#define MAX_BATTLE_LENGTH 50 + +/obj/item/toy/mecha/clockwork + name = "clockwork toy" + icon = 'maplestation_modules/story_content/volkan_equipment/icons/clockworkgifts.dmi' + desc = "A metal clockwork toy with a small windup key underneath." + icon_state = "bug" + verb_say = "clicks" + verb_ask = "ticks" + verb_exclaim = "clacks" + verb_yell = "clacks" + max_combat_health = 4 + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "Bzz!!!" + +/obj/item/toy/mecha/clockwork/Initialize(mapload) + . = ..() + RemoveElement(/datum/element/series, /obj/item/toy/mecha, "Mini-Mecha action figures") + AddElement(/datum/element/series, /obj/item/toy/mecha/clockwork, "CaLE's Handmade Clockwork Toys") + +/** +* A Modular redo of this proc. It is the exact same thing as the original but with different verbs, etc. I want this cool thing but they are not plastic! +*/ +/obj/item/toy/mecha/clockwork/mecha_brawl(obj/item/toy/mecha/attacker, mob/living/carbon/attacker_controller, mob/living/carbon/opponent) + //A GOOD DAY FOR A SWELL BATTLE! + attacker_controller.visible_message(span_danger("[attacker_controller.name] collides [attacker] with [src]! Looks like they're preparing for a little battle!"), \ + span_danger("You collide [attacker] into [src], sparking a battle!"), \ + span_hear("You hear metal clinking onto metal!"), COMBAT_MESSAGE_RANGE) + + /// Who's in control of the defender (src)? + var/mob/living/carbon/src_controller = (opponent)? opponent : attacker_controller + /// How long has the battle been going? + var/battle_length = 0 + + in_combat = TRUE + attacker.in_combat = TRUE + + //1.5 second cooldown * 20 = 30 second cooldown after a fight + timer = world.time + cooldown*cooldown_multiplier + attacker.timer = world.time + attacker.cooldown*attacker.cooldown_multiplier + + sleep(1 SECONDS) + //--THE BATTLE BEGINS-- + while(combat_health > 0 && attacker.combat_health > 0 && battle_length < MAX_BATTLE_LENGTH) + if(!combat_sleep(0.5 SECONDS, attacker, attacker_controller, opponent)) //combat_sleep checks everything we need to have checked for combat to continue + break + + //before we do anything - deal with charged attacks + if(special_attack_charged) + src_controller.visible_message(span_danger("[src] unleashes its special attack!!"), \ + span_danger("You unleash [src]'s special attack!")) + special_attack_move(attacker) + else if(attacker.special_attack_charged) + + attacker_controller.visible_message(span_danger("[attacker] unleashes its special attack!!"), \ + span_danger("You unleash [attacker]'s special attack!")) + attacker.special_attack_move(src) + else + //process the cooldowns + if(special_attack_cooldown > 0) + special_attack_cooldown-- + if(attacker.special_attack_cooldown > 0) + attacker.special_attack_cooldown-- + + //combat commences + switch(rand(1,8)) + if(1 to 3) //attacker wins + if(attacker.special_attack_cooldown == 0 && attacker.combat_health <= round(attacker.max_combat_health/3)) //if health is less than 1/3 and special off CD, use it + attacker.special_attack_charged = TRUE + attacker_controller.visible_message(span_danger("[attacker] begins charging its special attack!!"), \ + span_danger("You begin charging [attacker]'s special attack!")) + else //just attack + attacker.SpinAnimation(5, 0) + playsound(attacker, 'sound/effects/footstep/rustystep1.ogg', 30, TRUE) + combat_health-- + attacker_controller.visible_message(span_danger("[attacker] devastates [src]!"), \ + span_danger("You ram [attacker] into [src]!"), \ + span_hear("You hear metal clinking!"), COMBAT_MESSAGE_RANGE) + if(prob(5)) + combat_health-- + playsound(src, 'sound/effects/meteorimpact.ogg', 20, TRUE) + attacker_controller.visible_message(span_boldwarning("...and lands a CRIPPLING BLOW!"), \ + span_boldwarning("...and you land a CRIPPLING blow on [src]!"), null, COMBAT_MESSAGE_RANGE) + + if(4) //both lose + attacker.SpinAnimation(5, 0) + SpinAnimation(5, 0) + combat_health-- + attacker.combat_health-- + do_sparks(2, FALSE, src) + do_sparks(2, FALSE, attacker) + if(prob(50)) + attacker_controller.visible_message(span_danger("[attacker] and [src] clash dramatically, causing sparks to fly!"), \ + span_danger("[attacker] and [src] clash dramatically, causing sparks to fly!"), \ + span_hear("You hear metal clinking!"), COMBAT_MESSAGE_RANGE) + else + src_controller.visible_message(span_danger("[src] and [attacker] clash dramatically, causing sparks to fly!"), \ + span_danger("[src] and [attacker] clash dramatically, causing sparks to fly!"), \ + span_hear("You hear metal clinking!"), COMBAT_MESSAGE_RANGE) + if(5) //both win + playsound(attacker, 'sound/weapons/parry.ogg', 20, TRUE) + if(prob(50)) + attacker_controller.visible_message(span_danger("[src]'s attack deflects off of [attacker]."), \ + span_danger("[src]'s attack deflects off of [attacker]."), \ + span_hear("You hear metal clinking!"), COMBAT_MESSAGE_RANGE) + else + src_controller.visible_message(span_danger("[attacker]'s attack deflects off of [src]."), \ + span_danger("[attacker]'s attack deflects off of [src]."), \ + span_hear("You hear metal clinking!"), COMBAT_MESSAGE_RANGE) + + if(6 to 8) //defender wins + if(special_attack_cooldown == 0 && combat_health <= round(max_combat_health/3)) //if health is less than 1/3 and special off CD, use it + special_attack_charged = TRUE + src_controller.visible_message(span_danger("[src] begins charging its special attack!!"), \ + span_danger("You begin charging [src]'s special attack!")) + else //just attack + SpinAnimation(5, 0) + playsound(src, 'sound/effects/footstep/rustystep1.ogg', 30, TRUE) + attacker.combat_health-- + src_controller.visible_message(span_danger("[src] smashes [attacker]!"), \ + span_danger("You smash [src] into [attacker]!"), \ + span_hear("You hear metal clinking!"), COMBAT_MESSAGE_RANGE) + if(prob(5)) + attacker.combat_health-- + playsound(attacker, 'sound/effects/meteorimpact.ogg', 20, TRUE) + src_controller.visible_message(span_boldwarning("...and lands a CRIPPLING BLOW!"), \ + span_boldwarning("...and you land a CRIPPLING blow on [attacker]!"), null, COMBAT_MESSAGE_RANGE) + else + attacker_controller.visible_message(span_notice("[src] and [attacker] stand around awkwardly."), \ + span_notice("You don't know what to do next.")) + + battle_length++ + sleep(0.5 SECONDS) + + /// Lines chosen for the winning mech + var/list/winlines = list("Tktktktk!", "You hear a happy buzz!*") + + if(attacker.combat_health <= 0 && combat_health <= 0) //both lose + playsound(src, 'sound/machines/warning-buzzer.ogg', 20, TRUE) + attacker_controller.visible_message(span_boldnotice("MUTUALLY ASSURED DESTRUCTION!! [src] and [attacker] both end up losing!"), \ + span_boldnotice("Both [src] and [attacker] have lost!")) + else if(attacker.combat_health <= 0) //src wins + wins++ + attacker.losses++ + playsound(attacker, 'sound/effects/light_flicker.ogg', 20, TRUE) + attacker_controller.visible_message(span_notice("[attacker] falls apart!"), \ + span_notice("[attacker] falls apart!"), null, COMBAT_MESSAGE_RANGE) + say("[pick(winlines)]") + src_controller.visible_message(span_notice("[src] destroys [attacker] and walks away victorious!"), \ + span_notice("You raise up [src] victoriously over [attacker]!")) + else if (combat_health <= 0) //attacker wins + attacker.wins++ + losses++ + playsound(src, 'sound/effects/light_flicker.ogg', 20, TRUE) + src_controller.visible_message(span_notice("[src] collapses!"), \ + span_notice("[src] collapses!"), null, COMBAT_MESSAGE_RANGE) + attacker.say("[pick(winlines)]") + attacker_controller.visible_message(span_notice("[attacker] demolishes [src] and walks away victorious!"), \ + "[span_notice("You raise up [attacker] proudly over [src]")]!") + else //both win? + say("CLkclkclk!") + //don't want to make this a one sided conversation + quiet? attacker.say("CLkclkclk!") : attacker.say("Buzzes!*") + + in_combat = FALSE + attacker.in_combat = FALSE + + combat_health = max_combat_health + attacker.combat_health = attacker.max_combat_health + + return + + +/obj/item/toy/mecha/clockwork/snake + name = "clockwork snake" + icon_state = "snake" + max_combat_health = 4 + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "Sssssss..." + +/obj/item/toy/mecha/clockwork/mothroach + name = "clockwork mothroach" + icon_state = "mothroach" + max_combat_health = 5 + special_attack_type = SPECIAL_ATTACK_HEAL + special_attack_cry = "Bzzzz!" + +/obj/item/toy/mecha/clockwork/bird + name = "clockwork parakeet" + icon_state = "bird" + max_combat_health = 3 + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "Keeek!" + +/obj/item/toy/mecha/clockwork/cat + name = "clockwork cat" + icon_state = "cat" + max_combat_health = 3 + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "Meow!" diff --git a/maplestation_modules/story_content/volkan_equipment/code/mini_forklift.dm b/maplestation_modules/story_content/volkan_equipment/code/mini_forklift.dm new file mode 100644 index 000000000000..036261904925 --- /dev/null +++ b/maplestation_modules/story_content/volkan_equipment/code/mini_forklift.dm @@ -0,0 +1,127 @@ +/obj/vehicle/ridden/mini_forklift + name = "mini forklift" + icon = 'maplestation_modules/story_content/volkan_equipment/icons/forklift.dmi' + desc = "A mini novelty but still usable electric forklift built by CaLE. It is a tight fit! It is too small to hold crates, but it is the perfect size to hold flatpacks." + icon_state = "mini_forklift" + max_integrity = 150 + armor_type = /datum/armor/mini_forklift + integrity_failure = 0.5 + are_legs_exposed = FALSE + var/cover_iconstate = "mini_forklift_cover" + layer = OBJ_LAYER + +#define MAX_FLAT_PACKS 4 + +/datum/armor/mini_forklift + melee = 60 + bullet = 25 + laser = 20 + bomb = 50 + fire = 60 + acid = 60 + +/obj/vehicle/ridden/mini_forklift/atom_deconstruct(disassembled) + for(var/atom/movable/content as anything in contents) + content.forceMove(drop_location()) + +/obj/vehicle/ridden/mini_forklift/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = NONE + if(isnull(held_item)) + return + + if(istype(held_item, /obj/item/flatpack)) + context[SCREENTIP_CONTEXT_LMB] = "Load pack" + return CONTEXTUAL_SCREENTIP_SET + +/obj/vehicle/ridden/mini_forklift/examine(mob/user) + . = ..() + if(!in_range(user, src) && !isobserver(user)) + return + + . += "From bottom to top, this forklift is holding:" + for(var/obj/item/flatpack as anything in contents) + . += flatpack.name + +/obj/vehicle/ridden/mini_forklift/attack_hand(mob/user, list/modifiers) + . = ..() + if(.) + return + user.put_in_hands(contents[length(contents)]) //topmost box + update_appearance(UPDATE_OVERLAYS) + +/obj/vehicle/ridden/mini_forklift/item_interaction(mob/living/user, obj/item/attacking_item, params) + if(!istype(attacking_item, /obj/item/flatpack) || user.combat_mode || attacking_item.flags_1 & HOLOGRAM_1 || attacking_item.item_flags & ABSTRACT) + return ITEM_INTERACT_SKIP_TO_ATTACK + + if (length(contents) >= MAX_FLAT_PACKS) + balloon_alert(user, "full!") + return ITEM_INTERACT_BLOCKING + if (!user.transferItemToLoc(attacking_item, src)) + return ITEM_INTERACT_BLOCKING + update_appearance(UPDATE_OVERLAYS) + return ITEM_INTERACT_SUCCESS + +/obj/vehicle/ridden/mini_forklift/Initialize(mapload) + . = ..() + AddElement(/datum/element/ridable, /datum/component/riding/vehicle/forklift) + +/obj/vehicle/ridden/mini_forklift/post_buckle_mob(mob/living/user) + . = ..() + update_appearance() + +/obj/vehicle/ridden/mini_forklift/post_unbuckle_mob() + . = ..() + update_appearance() + +/obj/vehicle/ridden/mini_forklift/generate_actions() + . = ..() + initialize_controller_action_type(/datum/action/vehicle/ridden/horn, VEHICLE_CONTROL_DRIVE) + +/obj/vehicle/ridden/mini_forklift/update_overlays() + . = ..() + if(has_buckled_mobs()) + . += mutable_appearance(icon, cover_iconstate, ABOVE_MOB_LAYER, appearance_flags = KEEP_APART) + + var/offset = 0 + for(var/item in contents) + var/mutable_appearance/flatpack_overlay = mutable_appearance(icon, "flatcart_flat", layer = ABOVE_MOB_LAYER + (offset * 0.01)) + flatpack_overlay.pixel_y = offset + offset += 5 + . += flatpack_overlay + +/datum/component/riding/vehicle/forklift + vehicle_move_delay = 1.75 + ride_check_flags = RIDER_NEEDS_LEGS | RIDER_NEEDS_ARMS | UNBUCKLE_DISABLED_RIDER + +/datum/component/riding/vehicle/forklift/get_parent_offsets_and_layers() + . = ..() + return list( + TEXT_NORTH = list(0, 0, BELOW_MOB_LAYER), + TEXT_SOUTH = list(0, -3, BELOW_MOB_LAYER), + TEXT_EAST = list(0, 0, BELOW_MOB_LAYER), + TEXT_WEST = list(0, 0, BELOW_MOB_LAYER), + ) + +/datum/component/riding/vehicle/forklift/get_rider_offsets_and_layers(pass_index, mob/offsetter) + return list( + TEXT_NORTH = list( 1, 0), + TEXT_SOUTH = list( 0, 2), + TEXT_EAST = list(-5, 0), + TEXT_WEST = list( 5, 0), + ) + +#undef MAX_FLAT_PACKS + +//Honk honk!! +/datum/action/vehicle/ridden/horn + check_flags = AB_CHECK_HANDS_BLOCKED | AB_CHECK_CONSCIOUS + name = "Honk Horn" + desc = "Honk your vehicle's horn." + button_icon_state = "car_horn" + var/hornsound = 'sound/items/carhorn.ogg' + +/datum/action/vehicle/ridden/horn/Trigger(trigger_flags) + //Yes, I did put no cooldown. Yes, it was on purpose. Horns must be spammable. + vehicle_target.visible_message(span_danger("The forklift loudly honks!")) + to_chat(owner, span_notice("You press the horn.")) + playsound(vehicle_target, hornsound, 75) diff --git a/maplestation_modules/story_content/volkan_equipment/icons/clockworkgifts.dmi b/maplestation_modules/story_content/volkan_equipment/icons/clockworkgifts.dmi new file mode 100644 index 000000000000..efd261a9d1cc Binary files /dev/null and b/maplestation_modules/story_content/volkan_equipment/icons/clockworkgifts.dmi differ diff --git a/maplestation_modules/story_content/volkan_equipment/icons/forklift.dmi b/maplestation_modules/story_content/volkan_equipment/icons/forklift.dmi new file mode 100644 index 000000000000..a2eb5770db63 Binary files /dev/null and b/maplestation_modules/story_content/volkan_equipment/icons/forklift.dmi differ