diff --git a/_maps/RandomZLevels/away_mission/SnowCabin.dmm b/_maps/RandomZLevels/away_mission/SnowCabin.dmm index 7de2ed53f1..fea4130184 100644 --- a/_maps/RandomZLevels/away_mission/SnowCabin.dmm +++ b/_maps/RandomZLevels/away_mission/SnowCabin.dmm @@ -3166,7 +3166,7 @@ }, /area/awaymission/cabin/caves/sovietcave) "il" = ( -/obj/item/toy/prize/deathripley{ +/obj/item/toy/mecha/deathripley{ desc = "The mining mecha of the exploration team."; name = "exploraton squad Ripley"; pixel_y = 15 diff --git a/_maps/map_files/CogStation/CogStation.dmm b/_maps/map_files/CogStation/CogStation.dmm index 5fbbc1d401..fcd4949a27 100644 --- a/_maps/map_files/CogStation/CogStation.dmm +++ b/_maps/map_files/CogStation/CogStation.dmm @@ -16856,7 +16856,7 @@ /turf/open/floor/plating, /area/command/heads_quarters/hos) "aLb" = ( -/obj/item/toy/prize/honk, +/obj/item/toy/mecha/honk, /obj/structure/disposalpipe/segment{ dir = 4 }, diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index cd48e318bf..d70bb9d9b4 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -47411,7 +47411,7 @@ "dmL" = ( /obj/machinery/light/small, /obj/item/toy/dummy, -/obj/item/toy/prize/honk{ +/obj/item/toy/mecha/honk{ pixel_y = 12 }, /obj/structure/table/wood, diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index e5f5a9c6b7..208bca6705 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -17971,7 +17971,7 @@ /area/syndicate_mothership) "Sb" = ( /obj/structure/table/wood, -/obj/item/toy/prize/mauler{ +/obj/item/toy/mecha/mauler{ pixel_y = 12 }, /turf/open/floor/plasteel/dark, diff --git a/code/datums/elements/series.dm b/code/datums/elements/series.dm new file mode 100644 index 0000000000..0b34b24ae5 --- /dev/null +++ b/code/datums/elements/series.dm @@ -0,0 +1,35 @@ +/** + * ## series element! + * + * bespoke element that assigns a series number to toys on examine, and shows their series name! + * used for mechas and rare collectable hats, should totally be used for way more ;) + */ +/datum/element/series + element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH // Detach for turfs + id_arg_index = 2 + var/list/subtype_list + var/series_name + +/datum/element/series/Attach(datum/target, subtype, series_name) + . = ..() + if(!isatom(target)) + return ELEMENT_INCOMPATIBLE + if(!subtype) + stack_trace("series element without subtype given!") + return ELEMENT_INCOMPATIBLE + subtype_list = subtypesof(subtype) + src.series_name = series_name + var/atom/attached = target + RegisterSignal(attached, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) + +/datum/element/series/Detach(datum/target) + . = ..() + UnregisterSignal(target, COMSIG_PARENT_EXAMINE) + +///signal called examining +/datum/element/series/proc/on_examine(datum/target, mob/user, list/examine_list) + SIGNAL_HANDLER + + var/series_number = subtype_list.Find(target.type) + examine_list += span_boldnotice("[target] is part of the \"[series_name]\" series!") + examine_list += span_notice("Collect them all: [series_number]/[length(subtype_list)].") diff --git a/code/datums/elements/toy_talk.dm b/code/datums/elements/toy_talk.dm new file mode 100644 index 0000000000..8061eafaeb --- /dev/null +++ b/code/datums/elements/toy_talk.dm @@ -0,0 +1,28 @@ +/** + * Allows people to talk via the item with .l or .r + * + * Be sure to override [/atom/movable/proc/GetVoice] if you want the item's "voice" to not default to itself + */ +/datum/element/toy_talk + +/datum/element/toy_talk/Attach(datum/target) + . = ..() + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ITEM_TALK_INTO, PROC_REF(do_talk)) + +/datum/element/toy_talk/Detach(datum/source, ...) + . = ..() + UnregisterSignal(source, COMSIG_ITEM_TALK_INTO) + +/datum/element/toy_talk/proc/do_talk(obj/item/source, mob/speaker, message, channel, list/spans, language, list/message_mods) + SIGNAL_HANDLER + + if(!ismob(speaker) || message_mods[MODE_HEADSET] || message_mods[MODE_RELAY]) + return NONE + + message_mods[MODE_RELAY] = TRUE // Redundant (given NOPASS) but covers our bases + speaker.log_talk(message, LOG_SAY, tag = "toy talk ([source])") + source.say(message, language = language, sanitize = FALSE, message_mods = list(MODE_RELAY = TRUE)) + return NOPASS diff --git a/code/game/machinery/computer/arcade/orion_trail.dm b/code/game/machinery/computer/arcade/orion_trail.dm index 8894d53a21..21481b3755 100644 --- a/code/game/machinery/computer/arcade/orion_trail.dm +++ b/code/game/machinery/computer/arcade/orion_trail.dm @@ -869,7 +869,7 @@ /obj/item/orion_ship name = "model settler ship" desc = "A model spaceship, it looks like those used back in the day when travelling to Orion! It even has a miniature FX-293 reactor, which was renowned for its instability and tendency to explode..." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "ship" w_class = WEIGHT_CLASS_SMALL var/active = 0 //if the ship is on diff --git a/code/game/objects/items/AI_modules.dm b/code/game/objects/items/AI_modules.dm index 7492f5779f..1906f85a6f 100644 --- a/code/game/objects/items/AI_modules.dm +++ b/code/game/objects/items/AI_modules.dm @@ -525,7 +525,7 @@ AI MODULES /obj/item/ai_module/toyAI // -- Incoming //No actual reason to inherit from ion boards here, either. *sigh* ~Miauw name = "toy AI" desc = "A little toy model AI core with real law uploading action!" //Note: subtle tell - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "AI" laws = list("") diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm index ff15511092..a189c1a9ba 100644 --- a/code/game/objects/items/eightball.dm +++ b/code/game/objects/items/eightball.dm @@ -2,7 +2,7 @@ name = "magic eightball" desc = "A black ball with a stenciled number eight in white on the side. It seems full of dark liquid.\nThe instructions state that you should ask your question aloud, and then shake." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "eightball" w_class = WEIGHT_CLASS_TINY diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index a935a55ecf..1fed9a6d4c 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -621,7 +621,7 @@ /obj/item/storage/box/snappops name = "snap pop box" desc = "Eight wrappers of fun! Ages 8 and up. Not suitable for children." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "spbox" illustration = null @@ -870,7 +870,7 @@ /obj/item/storage/box/mechfigures/PopulateContents() for(var/i in 1 to 4) - var/randomFigure = pick(subtypesof(/obj/item/toy/prize/)) + var/randomFigure = pick(subtypesof(/obj/item/toy/mecha)) new randomFigure(src) diff --git a/code/game/objects/items/toy_mechs.dm b/code/game/objects/items/toy_mechs.dm new file mode 100644 index 0000000000..efcd7862ef --- /dev/null +++ b/code/game/objects/items/toy_mechs.dm @@ -0,0 +1,654 @@ +/** + * Mech prizes + MECHA COMBAT!! + */ + +/// Mech battle special attack types. +#define SPECIAL_ATTACK_HEAL 1 +#define SPECIAL_ATTACK_DAMAGE 2 +#define SPECIAL_ATTACK_UTILITY 3 +#define SPECIAL_ATTACK_OTHER 4 + +/// Max length of a mech battle +#define MAX_BATTLE_LENGTH 50 + +/obj/item/toy/mecha + icon = 'icons/obj/toys/toy.dmi' + icon_state = "fivestarstoy" + verb_say = "beeps" + verb_ask = "beeps" + verb_exclaim = "beeps" + verb_yell = "beeps" + w_class = WEIGHT_CLASS_SMALL + /// Timer when it'll be off cooldown + var/timer = 0 + /// Cooldown between play sessions + var/cooldown = 1.5 SECONDS + /// Cooldown multiplier after a battle (by default: battle cooldowns are 30 seconds) + var/cooldown_multiplier = 20 + /// If it makes noise when played with + var/quiet = FALSE + /// TRUE = Offering battle to someone || FALSE = Not offering battle + var/wants_to_battle = FALSE + /// TRUE = in combat currently || FALSE = Not in combat + var/in_combat = FALSE + /// The mech's health in battle + var/combat_health = 0 + /// The mech's max combat health + var/max_combat_health = 0 + /// TRUE = the special attack is charged || FALSE = not charged + var/special_attack_charged = FALSE + /// What type of special attack they use - SPECIAL_ATTACK_DAMAGE, SPECIAL_ATTACK_HEAL, SPECIAL_ATTACK_UTILITY, SPECIAL_ATTACK_OTHER + var/special_attack_type = 0 + /// What message their special move gets on examining + var/special_attack_type_message = "" + /// The battlecry when using the special attack + var/special_attack_cry = "*flip" + /// Current cooldown of their special attack + var/special_attack_cooldown = 0 + /// This mech's win count in combat + var/wins = 0 + /// ...And their loss count in combat + var/losses = 0 + +/obj/item/toy/mecha/Initialize(mapload) + . = ..() + AddElement(/datum/element/series, /obj/item/toy/mecha, "Mini-Mecha action figures") + //AddElement(/datum/element/toy_talk) + combat_health = max_combat_health + switch(special_attack_type) + if(SPECIAL_ATTACK_DAMAGE) + special_attack_type_message = "an aggressive move, which deals bonus damage." + if(SPECIAL_ATTACK_HEAL) + special_attack_type_message = "a defensive move, which grants bonus healing." + if(SPECIAL_ATTACK_UTILITY) + special_attack_type_message = "a utility move, which heals the user and damages the opponent." + if(SPECIAL_ATTACK_OTHER) + special_attack_type_message = "a special move, which [special_attack_type_message]" + else + special_attack_type_message = "a mystery move, even I don't know." + +/** + * this proc combines "sleep" while also checking for if the battle should continue + * + * this goes through some of the checks - the toys need to be next to each other to fight! + * if it's player vs themself: They need to be able to "control" both mechs (either must be adjacent or using TK) + * if it's player vs player: Both players need to be able to "control" their mechs (either must be adjacent or using TK) + * if it's player vs mech (suicide): the mech needs to be in range of the player + * if all the checks are TRUE, it does the sleeps, and returns TRUE. Otherwise, it returns FALSE. + * Arguments: + * * delay - the amount of time the sleep at the end of the check will sleep for + * * attacker - the attacking toy in the battle. + * * attacker_controller - the controller of the attacking toy. there should ALWAYS be an attacker_controller + * * opponent - (optional) the defender controller in the battle, for PvP + */ +/obj/item/toy/mecha/proc/combat_sleep(delay, obj/item/toy/mecha/attacker, mob/living/carbon/attacker_controller, mob/living/carbon/opponent) + if(!attacker_controller) + return FALSE + + if(!attacker) //if there's no attacker, then attacker_controller IS the attacker + if(!in_range(src, attacker_controller)) + attacker_controller.visible_message(span_suicide("[attacker_controller] is running from [src]! The coward!")) + return FALSE + else // if there's an attacker, we can procede as normal + if(!in_range(src, attacker)) //and the two toys aren't next to each other, the battle ends + attacker_controller.visible_message(span_notice("[attacker] and [src] separate, ending the battle."), \ + span_notice("[attacker] and [src] separate, ending the battle.")) + return FALSE + + //dead men tell no tales, incapacitated men fight no fights + if(attacker_controller.incapacitated()) + return FALSE + //if the attacker_controller isn't next to the attacking toy (and doesn't have telekinesis), the battle ends + if(!in_range(attacker, attacker_controller) && !(attacker_controller.dna.check_mutation(/datum/mutation/human/telekinesis))) + attacker_controller.visible_message(span_notice("[attacker_controller.name] separates from [attacker], ending the battle."), \ + span_notice("You separate from [attacker], ending the battle.")) + return FALSE + + //if it's PVP and the opponent is not next to the defending(src) toy (and doesn't have telekinesis), the battle ends + if(opponent) + if(opponent.incapacitated()) + return FALSE + if(!in_range(src, opponent) && !(opponent.dna.check_mutation(/datum/mutation/human/telekinesis))) + opponent.visible_message(span_notice("[opponent.name] separates from [src], ending the battle."), \ + span_notice("You separate from [src], ending the battle.")) + return FALSE + //if it's not PVP and the attacker_controller isn't next to the defending toy (and doesn't have telekinesis), the battle ends + else + if (!in_range(src, attacker_controller) && !(attacker_controller.dna.check_mutation(/datum/mutation/human/telekinesis))) + attacker_controller.visible_message(span_notice("[attacker_controller.name] separates from [src] and [attacker], ending the battle."), \ + span_notice("You separate [attacker] and [src], ending the battle.")) + return FALSE + + //if all that is good, then we can sleep peacefully + sleep(delay) + return TRUE + +//all credit to skasi for toy mech fun ideas +/obj/item/toy/mecha/attack_self(mob/user) + if(timer < world.time) + to_chat(user, span_notice("You play with [src].")) + timer = world.time + cooldown + if(!quiet) + playsound(user, 'sound/mecha/mechstep.ogg', 20, TRUE) + else + . = ..() + +/obj/item/toy/mecha/attack_hand(mob/user, list/modifiers) + . = ..() + if(.) + return + if(loc == user) + attack_self(user) + +/** + * If you attack a mech with a mech, initiate combat between them + */ +/obj/item/toy/mecha/attackby(obj/item/user_toy, mob/living/user) + if(istype(user_toy, /obj/item/toy/mecha)) + var/obj/item/toy/mecha/P = user_toy + if(check_battle_start(user, P)) + mecha_brawl(P, user) + ..() + +/** + * Attack is called from the user's toy, aimed at target(another human), checking for target's toy. + */ +/obj/item/toy/mecha/attack(mob/living/carbon/human/target, mob/living/carbon/human/user) + if(target == user) + to_chat(user, span_notice("Target another toy mech if you want to start a battle with yourself.")) + return + else if(user.a_intent != INTENT_HARM) + if(wants_to_battle) //prevent spamming someone with offers + to_chat(user, span_notice("You already are offering battle to someone!")) + return + if(!check_battle_start(user)) //if the user's mech isn't ready, don't bother checking + return + + for(var/obj/item/I in target.held_items) + if(istype(I, /obj/item/toy/mecha)) //if you attack someone with a mech who's also holding a mech, offer to battle them + var/obj/item/toy/mecha/P = I + if(!P.check_battle_start(target, null, user)) //check if the attacker mech is ready + break + + //slap them with the metaphorical white glove + if(P.wants_to_battle) //if the target mech wants to battle, initiate the battle from their POV + mecha_brawl(P, target, user) //P = defender's mech / SRC = attacker's mech / target = defender / user = attacker + P.wants_to_battle = FALSE + return + + //extend the offer of battle to the other mech + to_chat(user, span_notice("You offer battle to [target.name]!")) + to_chat(target, span_notice("[user.name] wants to battle with [user.p_their()] [name]! Attack them with a toy mech to initiate combat.")) + wants_to_battle = TRUE + addtimer(CALLBACK(src, PROC_REF(withdraw_offer), user), 6 SECONDS) + return + + ..() + +/** + * Overrides attack_tk - Sorry, you have to be face to face to initiate a battle, it's good sportsmanship + */ +/obj/item/toy/mecha/attack_tk(mob/user) + if(timer < world.time) + to_chat(user, span_notice("You telekinetically play with [src].")) + timer = world.time + cooldown + if(!quiet) + playsound(user, 'sound/mecha/mechstep.ogg', 20, TRUE) + return STOP_ATTACK_PROC_CHAIN + + +/** + * Resets the request for battle. + * + * For use in a timer, this proc resets the wants_to_battle variable after a short period. + * Arguments: + * * user - the user wanting to do battle + */ +/obj/item/toy/mecha/proc/withdraw_offer(mob/living/carbon/user) + if(wants_to_battle) + wants_to_battle = FALSE + to_chat(user, span_notice("You get the feeling they don't want to battle.")) +/** + * Starts a battle, toy mech vs player. Player... doesn't win. + */ +/obj/item/toy/mecha/suicide_act(mob/living/carbon/user) + if(in_combat) + to_chat(user, span_notice("[src] is in battle, let it finish first.")) + return + + user.visible_message(span_suicide("[user] begins a fight [user.p_they()] can't win with [src]! It looks like [user.p_theyre()] trying to commit suicide!")) + + in_combat = TRUE + sleep(1.5 SECONDS) + for(var/i in 1 to 4) + switch(i) + if(1, 3) + SpinAnimation(5, 0) + playsound(src, 'sound/mecha/mechstep.ogg', 30, TRUE) + user.adjustBruteLoss(25) + user.adjustStaminaLoss(50) + if(2) + user.SpinAnimation(5, 0) + playsound(user, 'sound/weapons/smash.ogg', 20, TRUE) + combat_health-- //we scratched it! + if(4) + say(special_attack_cry + "!!") + user.adjustStaminaLoss(25) + + if(!combat_sleep(1 SECONDS, null, user)) + say("PATHETIC.") + combat_health = max_combat_health + in_combat = FALSE + return SHAME + + sleep(0.5 SECONDS) + user.adjustBruteLoss(450) + + in_combat = FALSE + say("AN EASY WIN. MY POWER INCREASES.") // steal a soul, become swole + add_atom_colour(rgb(255, 115, 115), ADMIN_COLOUR_PRIORITY) + max_combat_health = round(max_combat_health*1.5 + 0.1) + combat_health = max_combat_health + wins++ + return BRUTELOSS + +/obj/item/toy/mecha/examine() + . = ..() + . += span_notice("This toy's special attack is [special_attack_cry], [special_attack_type_message]") + if(in_combat) + . += span_notice("This toy has a maximum health of [max_combat_health]. Currently, it's [combat_health].") + . += span_notice("Its special move light is [special_attack_cooldown? "flashing red." : "green and is ready!"]") + else + . += span_notice("This toy has a maximum health of [max_combat_health].") + + if(wins || losses) + . += span_notice("This toy has [wins] wins, and [losses] losses.") + +/obj/item/toy/mecha/can_speak(allow_mimes) + return !quiet && ..() + +/** + * The 'master' proc of the mech battle. Processes the entire battle's events and makes sure it start and finishes correctly. + * + * src is the defending toy, and the battle proc is called on it to begin the battle. + * After going through a few checks at the beginning to ensure the battle can start properly, the battle begins a loop that lasts + * until either toy has no more health. During this loop, it also ensures the mechs stay in combat range of each other. + * It will then randomly decide attacks for each toy, occasionally making one or the other use their special attack. + * When either mech has no more health, the loop ends, and it displays the victor and the loser while updating their stats and resetting them. + * Arguments: + * * attacker - the attacking toy, the toy in the attacker_controller's hands + * * attacker_controller - the user, the one who is holding the toys / controlling the fight + * * opponent - optional arg used in Mech PvP battles: the other person who is taking part in the fight (controls src) + */ +/obj/item/toy/mecha/proc/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 brawl!"), \ + span_danger("You collide [attacker] into [src], sparking a fierce battle!"), \ + span_hear("You hear hard plastic smacking into hard plastic."), 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/mecha/mechstep.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 hard plastic smacking hard plastic."), 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 hard plastic rubbing against hard plastic."), 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 hard plastic rubbing against hard plastic."), 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 hard plastic bouncing off hard plastic."), 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 hard plastic bouncing off hard plastic."), 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/mecha/mechstep.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 hard plastic smashing hard plastic."), 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("YOU'RE NOTHING BUT SCRAP!", "I'LL YIELD TO NONE!", "GLORY IS MINE!", "AN EASY FIGHT.", "YOU SHOULD HAVE NEVER FACED ME.", "ROCKED AND SOCKED.") + + 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 destroyed!"), \ + span_boldnotice("Both [src] and [attacker] are destroyed!")) + 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("NEXT TIME.") + //don't want to make this a one sided conversation + quiet? attacker.say("I WENT EASY ON YOU.") : attacker.say("OF COURSE.") + + in_combat = FALSE + attacker.in_combat = FALSE + + combat_health = max_combat_health + attacker.combat_health = attacker.max_combat_health + + return + +/** + * This proc checks if a battle can be initiated between src and attacker. + * + * Both SRC and attacker (if attacker is included) timers are checked if they're on cooldown, and + * both SRC and attacker (if attacker is included) are checked if they are in combat already. + * If any of the above are true, the proc returns FALSE and sends a message to user (and target, if included) otherwise, it returns TRUE + * Arguments: + * * user: the user who is initiating the battle + * * attacker: optional arg for checking two mechs at once + * * target: optional arg used in Mech PvP battles (if used, attacker is target's toy) + */ +/obj/item/toy/mecha/proc/check_battle_start(mob/living/carbon/user, obj/item/toy/mecha/attacker, mob/living/carbon/target) + if(attacker?.in_combat) + to_chat(user, span_notice("[target?target.p_their() : "Your" ] [attacker.name] is in combat.")) + to_chat(target, span_notice("Your [attacker.name] is in combat.")) + return FALSE + if(in_combat) + to_chat(user, span_notice("Your [name] is in combat.")) + to_chat(target, span_notice("[user.p_their()] [name] is in combat.")) + return FALSE + if(attacker && attacker.timer > world.time) + to_chat(user, span_notice("[target?target.p_their() : "Your" ] [attacker.name] isn't ready for battle.")) + to_chat(target, span_notice("Your [attacker.name] isn't ready for battle.")) + return FALSE + if(timer > world.time) + to_chat(user, span_notice("Your [name] isn't ready for battle.")) + to_chat(target, span_notice("[user.p_their()] [name] isn't ready for battle.")) + return FALSE + + return TRUE + +/** + * Processes any special attack moves that happen in the battle (called in the mechaBattle proc). + * + * Makes the toy shout their special attack cry and updates its cooldown. Then, does the special attack. + * Arguments: + * * victim - the toy being hit by the special move + */ +/obj/item/toy/mecha/proc/special_attack_move(obj/item/toy/mecha/victim) + say(special_attack_cry + "!!") + + special_attack_charged = FALSE + special_attack_cooldown = 3 + + switch(special_attack_type) + if(SPECIAL_ATTACK_DAMAGE) //+2 damage + victim.combat_health-=2 + playsound(src, 'sound/weapons/marauder.ogg', 20, TRUE) + if(SPECIAL_ATTACK_HEAL) //+2 healing + combat_health+=2 + playsound(src, 'sound/mecha/mech_shield_raise.ogg', 20, TRUE) + if(SPECIAL_ATTACK_UTILITY) //+1 heal, +1 damage + victim.combat_health-- + combat_health++ + playsound(src, 'sound/mecha/mechmove01.ogg', 30, TRUE) + if(SPECIAL_ATTACK_OTHER) //other + super_special_attack(victim) + else + say("I FORGOT MY SPECIAL ATTACK...") + +/** + * Base proc for 'other' special attack moves. + * + * This one is only for inheritance, each mech with an 'other' type move has their procs below. + * Arguments: + * * victim - the toy being hit by the super special move (doesn't necessarily need to be used) + */ +/obj/item/toy/mecha/proc/super_special_attack(obj/item/toy/mecha/victim) + visible_message(span_notice("[src] does a cool flip.")) + +/obj/item/toy/mecha/ripley + name = "toy Ripley MK-I" + icon_state = "ripleytoy" + max_combat_health = 4 //200 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "CLAMP SMASH" + +/obj/item/toy/mecha/ripleymkii + name = "toy Ripley MK-II" + icon_state = "ripleymkiitoy" + max_combat_health = 5 //250 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "GIGA DRILL BREAK" + +/obj/item/toy/mecha/hauler + name = "toy Hauler" + icon_state = "haulertoy" + max_combat_health = 3 //100 integrity? + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "HAUL AWAY" + +/obj/item/toy/mecha/clarke + name = "toy Clarke" + icon_state = "clarketoy" + max_combat_health = 4 //200 integrity + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "ROLL OUT" + +/obj/item/toy/mecha/odysseus + name = "toy Odysseus" + icon_state = "odysseustoy" + max_combat_health = 4 //120 integrity + special_attack_type = SPECIAL_ATTACK_HEAL + special_attack_cry = "MECHA BEAM" + +/obj/item/toy/mecha/gygax + name = "toy Gygax" + icon_state = "gygaxtoy" + max_combat_health = 5 //250 integrity + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "SUPER SERVOS" + +/obj/item/toy/mecha/durand + name = "toy Durand" + icon_state = "durandtoy" + max_combat_health = 6 //400 integrity + special_attack_type = SPECIAL_ATTACK_HEAL + special_attack_cry = "SHIELD OF PROTECTION" + +/obj/item/toy/mecha/savannahivanov + name = "toy Savannah-Ivanov" + icon_state = "savannahivanovtoy" + max_combat_health = 7 //450 integrity + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "SKYFALL!! IVANOV STRIKE" + +/obj/item/toy/mecha/phazon + name = "toy Phazon" + icon_state = "phazontoy" + max_combat_health = 6 //200 integrity + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "NO-CLIP" + +/obj/item/toy/mecha/honk + name = "toy H.O.N.K." + icon_state = "honktoy" + max_combat_health = 4 //140 integrity + special_attack_type = SPECIAL_ATTACK_OTHER + special_attack_type_message = "puts the opposing mech's special move on cooldown and heals this mech." + special_attack_cry = "MEGA HORN" + +/obj/item/toy/mecha/honk/super_special_attack(obj/item/toy/mecha/victim) + playsound(src, 'sound/machines/honkbot_evil_laugh.ogg', 20, TRUE) + victim.special_attack_cooldown += 3 //Adds cooldown to the other mech and gives a minor self heal + combat_health++ + +/obj/item/toy/mecha/darkgygax + name = "toy Dark Gygax" + icon_state = "darkgygaxtoy" + max_combat_health = 6 //300 integrity + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "ULTRA SERVOS" + +/obj/item/toy/mecha/mauler + name = "toy Mauler" + icon_state = "maulertoy" + max_combat_health = 7 //500 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "BULLET STORM" + +/obj/item/toy/mecha/darkhonk + name = "toy Dark H.O.N.K." + icon_state = "darkhonktoy" + max_combat_health = 5 //300 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "BOMBANANA SPREE" + +/obj/item/toy/mecha/deathripley + name = "toy Death-Ripley" + icon_state = "deathripleytoy" + max_combat_health = 5 //250 integrity + special_attack_type = SPECIAL_ATTACK_OTHER + special_attack_type_message = "instantly destroys the opposing mech if its health is less than this mech's health." + special_attack_cry = "KILLER CLAMP" + +/obj/item/toy/mecha/deathripley/super_special_attack(obj/item/toy/mecha/victim) + playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 20, TRUE) + if(victim.combat_health < combat_health) //Instantly kills the other mech if it's health is below our's. + say("EXECUTE!!") + victim.combat_health = 0 + else //Otherwise, just deal one damage. + victim.combat_health-- + +/obj/item/toy/mecha/reticence + name = "toy Reticence" + icon_state = "reticencetoy" + quiet = TRUE + max_combat_health = 4 //100 integrity + special_attack_type = SPECIAL_ATTACK_OTHER + special_attack_type_message = "has a lower cooldown than normal special moves, increases the opponent's cooldown, and deals damage." + special_attack_cry = "*wave" + +/obj/item/toy/mecha/reticence/super_special_attack(obj/item/toy/mecha/victim) + special_attack_cooldown-- //Has a lower cooldown... + victim.special_attack_cooldown++ //and increases the opponent's cooldown by 1... + victim.combat_health-- //and some free damage. + +/obj/item/toy/mecha/marauder + name = "toy Marauder" + icon_state = "maraudertoy" + max_combat_health = 7 //500 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "BEAM BLAST" + +/obj/item/toy/mecha/seraph + name = "toy Seraph" + icon_state = "seraphtoy" + max_combat_health = 8 //550 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "ROCKET BARRAGE" + +/obj/item/toy/mecha/firefighter //rip + name = "toy Firefighter" + icon_state = "firefightertoy" + max_combat_health = 5 //250 integrity? + special_attack_type = SPECIAL_ATTACK_HEAL + special_attack_cry = "FIRE SHIELD" + +#undef SPECIAL_ATTACK_HEAL +#undef SPECIAL_ATTACK_DAMAGE +#undef SPECIAL_ATTACK_UTILITY +#undef SPECIAL_ATTACK_OTHER +#undef MAX_BATTLE_LENGTH diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index b05abdc49a..3ea05d3fbd 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -7,7 +7,6 @@ * Toy swords * Crayons * Snap pops - * Mech prizes * AI core prizes * Toy codex gigas * Skeleton toys @@ -24,7 +23,6 @@ * Toy Daggers */ - /obj/item/toy throwforce = 0 throw_speed = 3 @@ -39,7 +37,7 @@ /obj/item/toy/balloon name = "water balloon" desc = "A translucent balloon. There's nothing in it." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "waterballoon-e" item_state = "balloon-empty" @@ -379,7 +377,7 @@ /obj/item/toy/foamblade name = "foam armblade" desc = "It says \"Sternside Changs #1 fan\" on it." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "foamblade" item_state = "arm_blade" lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi' @@ -508,7 +506,7 @@ /obj/item/toy/snappop name = "snap pop" desc = "Wow!" - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "snappop" w_class = WEIGHT_CLASS_TINY var/ash_type = /obj/effect/decal/cleanable/ash @@ -544,7 +542,7 @@ ash_type = /obj/effect/decal/cleanable/ash/snappop_phoenix /obj/effect/decal/cleanable/ash/snappop_phoenix - var/respawn_time = 300 + var/respawn_time = 30 SECONDS /obj/effect/decal/cleanable/ash/snappop_phoenix/New() . = ..() @@ -554,96 +552,10 @@ new /obj/item/toy/snappop/phoenix(get_turf(src)) qdel(src) - -/* - * Mech prizes - */ -/obj/item/toy/prize - icon = 'icons/obj/toy.dmi' - icon_state = "ripleytoy" - var/timer = 0 - var/cooldown = 30 - var/quiet = 0 - -//all credit to skasi for toy mech fun ideas -/obj/item/toy/prize/attack_self(mob/user) - if(timer < world.time) - to_chat(user, "You play with [src].") - timer = world.time + cooldown - if(!quiet) - playsound(user, 'sound/mecha/mechstep.ogg', 20, 1) - else - . = ..() - -/obj/item/toy/prize/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) - if(loc == user) - attack_self(user) - -/obj/item/toy/prize/ripley - name = "toy Ripley" - desc = "Mini-Mecha action figure! Collect them all! 1/12." - -/obj/item/toy/prize/fireripley - name = "toy firefighting Ripley" - desc = "Mini-Mecha action figure! Collect them all! 2/12." - icon_state = "fireripleytoy" - -/obj/item/toy/prize/deathripley - name = "toy deathsquad Ripley" - desc = "Mini-Mecha action figure! Collect them all! 3/12." - icon_state = "deathripleytoy" - -/obj/item/toy/prize/gygax - name = "toy Gygax" - desc = "Mini-Mecha action figure! Collect them all! 4/12." - icon_state = "gygaxtoy" - -/obj/item/toy/prize/durand - name = "toy Durand" - desc = "Mini-Mecha action figure! Collect them all! 5/12." - icon_state = "durandprize" - -/obj/item/toy/prize/honk - name = "toy H.O.N.K." - desc = "Mini-Mecha action figure! Collect them all! 6/12." - icon_state = "honkprize" - -/obj/item/toy/prize/marauder - name = "toy Marauder" - desc = "Mini-Mecha action figure! Collect them all! 7/12." - icon_state = "marauderprize" - -/obj/item/toy/prize/seraph - name = "toy Seraph" - desc = "Mini-Mecha action figure! Collect them all! 8/12." - icon_state = "seraphprize" - -/obj/item/toy/prize/mauler - name = "toy Mauler" - desc = "Mini-Mecha action figure! Collect them all! 9/12." - icon_state = "maulerprize" - -/obj/item/toy/prize/odysseus - name = "toy Odysseus" - desc = "Mini-Mecha action figure! Collect them all! 10/12." - icon_state = "odysseusprize" - -/obj/item/toy/prize/phazon - name = "toy Phazon" - desc = "Mini-Mecha action figure! Collect them all! 11/12." - icon_state = "phazonprize" - -/obj/item/toy/prize/reticence - name = "toy Reticence" - desc = "Mini-Mecha action figure! Collect them all! 12/12." - icon_state = "reticenceprize" - quiet = 1 - - /obj/item/toy/talking name = "talking action figure" desc = "A generic action figure modeled after nothing in particular." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "owlprize" w_class = WEIGHT_CLASS_SMALL var/cooldown = FALSE @@ -772,7 +684,7 @@ /obj/item/toy/cards/deck name = "deck of cards" desc = "A deck of space-grade playing cards." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' deckstyle = "nanotrasen" icon_state = "deck_nanotrasen_full" w_class = WEIGHT_CLASS_SMALL @@ -903,7 +815,7 @@ /obj/item/toy/cards/cardhand name = "hand of cards" desc = "A number of cards not in a deck, customarily held in ones hand." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "none" w_class = WEIGHT_CLASS_TINY var/list/currenthand = list() @@ -1000,7 +912,7 @@ /obj/item/toy/cards/singlecard name = "card" desc = "a card" - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "singlecard_down_nanotrasen" w_class = WEIGHT_CLASS_TINY var/cardname = null @@ -1124,7 +1036,7 @@ /obj/item/toy/nuke name = "\improper Nuclear Fission Explosive toy" desc = "A plastic model of a Nuclear Fission Explosive." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "nuketoyidle" w_class = WEIGHT_CLASS_SMALL var/cooldown = 0 @@ -1151,7 +1063,7 @@ /obj/item/toy/minimeteor name = "\improper Mini-Meteor" desc = "Relive the excitement of a meteor shower! SweetMeat-eor. Co is not responsible for any injuries, headaches or hearing loss caused by Mini-Meteor." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "minimeteor" w_class = WEIGHT_CLASS_SMALL @@ -1194,7 +1106,7 @@ /obj/item/toy/snowball name = "snowball" desc = "A compact ball of snow. Good for throwing at people." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "snowball" throwforce = 12 //pelt your enemies to death with lumps of snow damtype = STAMINA @@ -1268,7 +1180,7 @@ */ /obj/item/toy/toy_xeno - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "toy_xeno" name = "xenomorph action figure" desc = "MEGA presents the new Xenos Isolated action figure! Comes complete with realistic sounds! Pull back string to use." @@ -1297,7 +1209,7 @@ /obj/item/toy/cattoy name = "toy mouse" desc = "A colorful toy mouse!" - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "toy_mouse" w_class = WEIGHT_CLASS_SMALL var/cooldown = 0 @@ -1311,7 +1223,7 @@ /obj/item/toy/figure name = "Non-Specific Action Figure action figure" desc = null - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "nuketoy" var/cooldown = 0 var/toysay = "What the fuck did you do?" @@ -1518,7 +1430,7 @@ /obj/item/toy/dummy name = "ventriloquist dummy" desc = "It's a dummy, dummy." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "assistant" item_state = "doll" var/doll_name = "Dummy" diff --git a/code/modules/cargo/packs/costumes_toys.dm b/code/modules/cargo/packs/costumes_toys.dm index 6a37ef80a9..542ba29d14 100644 --- a/code/modules/cargo/packs/costumes_toys.dm +++ b/code/modules/cargo/packs/costumes_toys.dm @@ -150,24 +150,30 @@ /obj/item/toy/talking/AI, /obj/item/toy/talking/codex_gigas, /obj/item/clothing/under/syndicate/tacticool, - /obj/item/toy/sword , + /obj/item/toy/sword, /obj/item/toy/gun, /obj/item/gun/ballistic/shotgun/toy/crossbow, /obj/item/storage/box/fakesyndiesuit, /obj/item/storage/crayons, /obj/item/toy/spinningtoy, - /obj/item/toy/prize/ripley, - /obj/item/toy/prize/fireripley, - /obj/item/toy/prize/deathripley, - /obj/item/toy/prize/gygax, - /obj/item/toy/prize/durand, - /obj/item/toy/prize/honk, - /obj/item/toy/prize/marauder, - /obj/item/toy/prize/seraph, - /obj/item/toy/prize/mauler, - /obj/item/toy/prize/odysseus, - /obj/item/toy/prize/phazon, - /obj/item/toy/prize/reticence, + /obj/item/toy/mecha/ripley, + /obj/item/toy/mecha/ripleymkii, + /obj/item/toy/mecha/hauler, + /obj/item/toy/mecha/clarke, + /obj/item/toy/mecha/odysseus, + /obj/item/toy/mecha/gygax, + /obj/item/toy/mecha/durand, + /obj/item/toy/mecha/savannahivanov, + /obj/item/toy/mecha/phazon, + /obj/item/toy/mecha/honk, + /obj/item/toy/mecha/darkgygax, + /obj/item/toy/mecha/mauler, + /obj/item/toy/mecha/darkhonk, + /obj/item/toy/mecha/deathripley, + /obj/item/toy/mecha/reticence, + /obj/item/toy/mecha/marauder, + /obj/item/toy/mecha/seraph, + /obj/item/toy/mecha/firefighter, /obj/item/toy/cards/deck, /obj/item/toy/nuke, /obj/item/toy/minimeteor, diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index 20cb7cc824..5c63905d5c 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -4,6 +4,11 @@ /obj/item/clothing/head/collectable name = "collectable hat" desc = "A rare collectable hat." + icon_state = null + +/obj/item/clothing/head/collectable/Initialize() + . = ..() + AddElement(/datum/element/series, /obj/item/clothing/head/collectable, "Super duper collectable hats") /obj/item/clothing/head/collectable/petehat name = "ultra rare Pete's hat!" diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm index 16d51337f1..e1a81d6ea6 100644 --- a/code/modules/events/holiday/vday.dm +++ b/code/modules/events/holiday/vday.dm @@ -40,7 +40,7 @@ /obj/item/valentine name = "valentine" desc = "A Valentine's card! Wonder what it says..." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "sc_Ace of Hearts_syndicate" // shut up var/message = "A generic message of love or whatever." resistance_flags = FLAMMABLE diff --git a/code/modules/games/cas.dm b/code/modules/games/cas.dm index 77db8dbe3f..5261887b73 100644 --- a/code/modules/games/cas.dm +++ b/code/modules/games/cas.dm @@ -12,7 +12,7 @@ /obj/item/toy/cards/deck/cas name = "\improper CAS deck (white)" desc = "A deck for the game Cards Against Spess, still popular after all these centuries. Warning: may include traces of broken fourth wall. This is the white deck." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "deck_caswhite_full" deckstyle = "caswhite" var/card_face = "cas_white" diff --git a/code/modules/games/unum.dm b/code/modules/games/unum.dm index 9820fa6754..3cd043e050 100644 --- a/code/modules/games/unum.dm +++ b/code/modules/games/unum.dm @@ -2,7 +2,7 @@ /obj/item/toy/cards/deck/unum name = "\improper UNUM deck" desc = "A deck of unum cards. House rules to argue over not included." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "deck_unum_full" deckstyle = "unum" original_size = 108 diff --git a/code/modules/holiday/easter.dm b/code/modules/holiday/easter.dm index 9c538f4bde..89b1944ae0 100644 --- a/code/modules/holiday/easter.dm +++ b/code/modules/holiday/easter.dm @@ -118,7 +118,7 @@ var/eggcolor = pick("blue","green","mime","orange","purple","rainbow","red","yellow") icon_state = "egg-[eggcolor]" /obj/item/reagent_containers/food/snacks/egg/proc/dispensePrize(turf/where) - var/won = pick(/obj/item/clothing/head/bunnyhead, + var/prize_list = list(/obj/item/clothing/head/bunnyhead, /obj/item/clothing/suit/bunnysuit, /obj/item/reagent_containers/food/snacks/grown/carrot, /obj/item/reagent_containers/food/snacks/chocolateegg, @@ -126,13 +126,12 @@ /obj/item/toy/gun, /obj/item/toy/sword, /obj/item/toy/foamblade, - /obj/item/toy/prize/ripley, - /obj/item/toy/prize/honk, /obj/item/toy/plush/carpplushie, /obj/item/toy/redbutton, - /obj/item/clothing/head/collectable/rabbitears) + /obj/item/clothing/head/collectable/rabbitears) + subtypesof(/obj/item/toy/mecha) + var/won = pick(prize_list) new won(where) - new/obj/item/reagent_containers/food/snacks/chocolateegg(where) + new /obj/item/reagent_containers/food/snacks/chocolateegg(where) /obj/item/reagent_containers/food/snacks/egg/attack_self(mob/user) ..() diff --git a/code/modules/holodeck/holo_effect.dm b/code/modules/holodeck/holo_effect.dm index 5d6259d9c2..6585ca39a5 100644 --- a/code/modules/holodeck/holo_effect.dm +++ b/code/modules/holodeck/holo_effect.dm @@ -26,7 +26,7 @@ // Generates a holodeck-tracked card deck /obj/effect/holodeck_effect/cards - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "deck_nanotrasen_full" var/obj/item/toy/cards/deck/D diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm index 097023e643..906285a5c8 100644 --- a/code/modules/jobs/job_types/roboticist.dm +++ b/code/modules/jobs/job_types/roboticist.dm @@ -27,7 +27,7 @@ threat = 1 family_heirlooms = list( - /obj/item/toy/figure/borg + /obj/item/toy/figure/borg, ) mail_goodies = list( @@ -36,6 +36,10 @@ /obj/item/modular_computer/tablet/preset/advanced = 5 ) +/datum/job/roboticist/New() + . = ..() + family_heirlooms += subtypesof(/obj/item/toy/mecha) + /datum/outfit/job/roboticist name = "Roboticist" jobtype = /datum/job/roboticist diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm index 3f213eb934..24b5ce4052 100644 --- a/code/modules/mining/abandoned_crates.dm +++ b/code/modules/mining/abandoned_crates.dm @@ -61,7 +61,7 @@ if(53 to 54) new /obj/item/toy/balloon(src) if(55 to 56) - var/newitem = pick(subtypesof(/obj/item/toy/prize)) + var/newitem = pick(subtypesof(/obj/item/toy/mecha)) new newitem(src) if(57 to 58) new /obj/item/toy/syndicateballoon(src) diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index 4c8872ffbf..6170dfdb68 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -512,7 +512,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians /obj/item/guardiancreator name = "deck of tarot cards" desc = "An enchanted deck of tarot cards, rumored to be a source of unimaginable power." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "deck_syndicate_full" var/used = FALSE var/theme = "magic" diff --git a/code/modules/pool/pool_noodles.dm b/code/modules/pool/pool_noodles.dm index 6118354792..6ecb6b30b1 100644 --- a/code/modules/pool/pool_noodles.dm +++ b/code/modules/pool/pool_noodles.dm @@ -1,7 +1,7 @@ //Pool noodles /obj/item/toy/poolnoodle - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "noodle" name = "pool noodle" desc = "A strange, bulky, bendable toy that can annoy people." diff --git a/code/modules/projectiles/guns/ballistic/toy.dm b/code/modules/projectiles/guns/ballistic/toy.dm index cd3473f8fc..238e5770c4 100644 --- a/code/modules/projectiles/guns/ballistic/toy.dm +++ b/code/modules/projectiles/guns/ballistic/toy.dm @@ -69,7 +69,7 @@ /obj/item/gun/ballistic/shotgun/toy/crossbow name = "foam force crossbow" desc = "A weapon favored by many overactive children. Ages 8 and up." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "foamcrossbow" item_state = "crossbow" mag_type = /obj/item/ammo_box/magazine/internal/shot/toy/crossbow diff --git a/code/modules/reagents/reagent_containers/rags.dm b/code/modules/reagents/reagent_containers/rags.dm index 4982571a3b..6273088453 100644 --- a/code/modules/reagents/reagent_containers/rags.dm +++ b/code/modules/reagents/reagent_containers/rags.dm @@ -2,7 +2,7 @@ name = "damp rag" desc = "For cleaning up messes, you suppose." w_class = WEIGHT_CLASS_TINY - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "rag" item_flags = NOBLUDGEON reagent_flags = REFILLABLE | DRAINABLE diff --git a/code/modules/research/designs/autoylathe_designs.dm b/code/modules/research/designs/autoylathe_designs.dm index 6db9755585..95eb469102 100644 --- a/code/modules/research/designs/autoylathe_designs.dm +++ b/code/modules/research/designs/autoylathe_designs.dm @@ -81,76 +81,129 @@ category = list("initial", "Toys") /datum/design/autoylathe/mech/model1 - name = "Toy Ripley" + name = "Toy Ripley MK-I" id = "toymech1" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/ripley + build_path = /obj/item/toy/mecha/ripley + category = list("hacked", "Figurines") /datum/design/autoylathe/mech/model2 - name = "Toy Firefighter Ripley" + name = "Toy Ripley MK-II" id = "toymech2" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/fireripley + build_path = /obj/item/toy/mecha/ripleymkii + category = list("hacked", "Figurines") -/datum/design/autoylathe/mech/contraband/model3 - name = "Toy Deathsquad fireripley " +/datum/design/autoylathe/mech/model3 + name = "Toy Hauler" id = "toymech3" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/deathripley + build_path = /obj/item/toy/mecha/hauler + category = list("hacked", "Figurines") /datum/design/autoylathe/mech/model4 - name = "Toy Gygax" + name = "Toy Clarke" id = "toymech4" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/gygax + build_path = /obj/item/toy/mecha/clarke + category = list("hacked", "Figurines") /datum/design/autoylathe/mech/model5 - name = "Toy Durand" + name = "Toy Odysseus" id = "toymech5" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/durand + build_path = /obj/item/toy/mecha/odysseus + category = list("hacked", "Figurines") -/datum/design/autoylathe/mech/contraband/model6 - name = "Toy H.O.N.K." +/datum/design/autoylathe/mech/model6 + name = "Toy Gygax" id = "toymech6" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/honk + build_path = /obj/item/toy/mecha/gygax + category = list("hacked", "Figurines") -/datum/design/autoylathe/mech/contraband/model7 - name = "Toy Marauder" +/datum/design/autoylathe/mech/model7 + name = "Toy Durand" id = "toymech7" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/marauder + build_path = /obj/item/toy/mecha/durand + category = list("hacked", "Figurines") -/datum/design/autoylathe/mech/contraband/model8 - name = "Toy Seraph" +/datum/design/autoylathe/mech/model8 + name = "Toy Savannah-Ivanov" id = "toymech8" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/seraph + build_path = /obj/item/toy/mecha/savannahivanov + category = list("hacked", "Figurines") -/datum/design/autoylathe/mech/contraband/model9 - name = "Toy Mauler" +/datum/design/autoylathe/mech/model9 + name = "Toy Phazon" id = "toymech9" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/mauler + build_path = /obj/item/toy/mecha/phazon + category = list("hacked", "Figurines") -/datum/design/autoylathe/mech/model10 - name = "Toy Odysseus" +/datum/design/autoylathe/mech/contraband/model10 + name = "Toy H.O.N.K" id = "toymech10" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/odysseus + build_path = /obj/item/toy/mecha/honk + category = list("hacked", "Figurines") -/datum/design/autoylathe/mech/model11 - name = "Toy Phazon" +/datum/design/autoylathe/mech/contraband/model11 + name = "Toy Dark Gygax" id = "toymech11" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/phazon + build_path = /obj/item/toy/mecha/darkgygax + category = list("hacked", "Figurines") /datum/design/autoylathe/mech/contraband/model12 - name = "Toy Reticence" + name = "Toy Mauler" id = "toymech12" materials = list(/datum/material/plastic = 250) - build_path = /obj/item/toy/prize/reticence + build_path = /obj/item/toy/mecha/mauler + category = list("hacked", "Figurines") + +/datum/design/autoylathe/mech/contraband/model13 + name = "Toy Dark H.O.N.K" + id = "toymech13" + materials = list(/datum/material/plastic = 250) + build_path = /obj/item/toy/mecha/darkhonk + category = list("hacked", "Figurines") + +/datum/design/autoylathe/mech/contraband/model14 + name = "Toy Death-Ripley" + id = "toymech14" + materials = list(/datum/material/plastic = 250) + build_path = /obj/item/toy/mecha/deathripley + category = list("hacked", "Figurines") + +/datum/design/autoylathe/mech/contraband/model15 + name = "Toy Reticence" + id = "toymech15" + materials = list(/datum/material/plastic = 250) + build_path = /obj/item/toy/mecha/reticence + category = list("hacked", "Figurines") + +/datum/design/autoylathe/mech/contraband/model16 + name = "Toy Marauder" + id = "toymech16" + materials = list(/datum/material/plastic = 250) + build_path = /obj/item/toy/mecha/marauder + category = list("hacked", "Figurines") + +/datum/design/autoylathe/mech/contraband/model17 + name = "Toy Seraph" + id = "toymech17" + materials = list(/datum/material/plastic = 250) + build_path = /obj/item/toy/mecha/seraph + category = list("hacked", "Figurines") + +/datum/design/autoylathe/mech/model18 + name = "Toy Firefighter" + id = "toymech18" + materials = list(/datum/material/plastic = 250) + build_path = /obj/item/toy/mecha/firefighter category = list("hacked", "Figurines") /datum/design/autoylathe/talking/AI diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm index 0be8cb9196..774f1820ea 100644 --- a/code/modules/spells/spell_types/wizard.dm +++ b/code/modules/spells/spell_types/wizard.dm @@ -333,7 +333,7 @@ /obj/item/spellpacket/lightningbolt name = "\improper Lightning bolt Spell Packet" desc = "Some birdseed wrapped in cloth that somehow crackles with electricity." - icon = 'icons/obj/toy.dmi' + icon = 'icons/obj/toys/toy.dmi' icon_state = "snappop" w_class = WEIGHT_CLASS_TINY diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi deleted file mode 100644 index e8106905b7..0000000000 Binary files a/icons/obj/toy.dmi and /dev/null differ diff --git a/icons/obj/toys/toy.dmi b/icons/obj/toys/toy.dmi new file mode 100644 index 0000000000..8a4826d411 Binary files /dev/null and b/icons/obj/toys/toy.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 640bc99969..4a116ff9fd 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -724,6 +724,7 @@ #include "code\datums\elements\photosynthesis.dm" #include "code\datums\elements\polychromic.dm" #include "code\datums\elements\scavenging.dm" +#include "code\datums\elements\series.dm" #include "code\datums\elements\snail_crawl.dm" #include "code\datums\elements\spellcasting.dm" #include "code\datums\elements\squish.dm" @@ -1207,6 +1208,7 @@ #include "code\game\objects\items\teleprod.dm" #include "code\game\objects\items\telescopic_iv.dm" #include "code\game\objects\items\theft_tools.dm" +#include "code\game\objects\items\toy_mechs.dm" #include "code\game\objects\items\toys.dm" #include "code\game\objects\items\trash.dm" #include "code\game\objects\items\vending_items.dm"