diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index ac7589364c6..a16d120bb4d 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -30,8 +30,7 @@ var/newscaster_announcements = null var/ert_disabled = 0 var/uplink_welcome = "Syndicate Uplink Console:" - var/uplink_uses = 10 - var/datum/cult_info/cultdat = new /datum/cult_info() //here instead of cult for adminbus purposes + var/uplink_uses = 20 var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) @@ -54,16 +53,11 @@ //pre_pre_setup() For when you really don't want certain jobs ingame. /datum/game_mode/proc/pre_pre_setup() - return 1 ///pre_setup() ///Attempts to select players for special roles the mode might have. /datum/game_mode/proc/pre_setup() - cultdat = pick(all_cults) - if(!cultdat) - to_chat(world, "Failed to select a cult datum, Shank a coder.") - return 0 return 1 diff --git a/code/game/gamemodes/game_mode2.dm b/code/game/gamemodes/game_mode2.dm new file mode 100644 index 00000000000..030b9a08012 --- /dev/null +++ b/code/game/gamemodes/game_mode2.dm @@ -0,0 +1,477 @@ +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/* + * GAMEMODES (by Rastaf0) + * + * In the new mode system all special roles are fully supported. + * You can have proper wizards/traitors/changelings/cultists during any mode. + * Only two things really depends on gamemode: + * 1. Starting roles, equipment and preparations + * 2. Conditions of finishing the round. + * + */ + + +/datum/game_mode + var/name = "invalid" + var/config_tag = null + var/intercept_hacked = 0 + var/votable = 1 + var/probability = 0 + var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm + var/explosion_in_progress = 0 //sit back and relax + var/list/datum/mind/modePlayer = new + var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist + var/list/protected_jobs = list() // Jobs that can't be traitors + var/list/protected_species = list() // Species that can't be traitors + var/required_players = 0 + var/required_enemies = 0 + var/recommended_enemies = 0 + var/newscaster_announcements = null + var/ert_disabled = 0 + var/uplink_welcome = "Syndicate Uplink Console:" + var/uplink_uses = 10 + var/datum/cult_info/cultdat = new /datum/cult_info() //here instead of cult for adminbus purposes + + var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) + var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) + +/datum/game_mode/proc/announce() //to be calles when round starts + to_chat(world, "Notice: [src] did not define announce()") + + +///can_start() +///Checks to see if the game can be setup and ran with the current number of players or whatnot. +/datum/game_mode/proc/can_start() + var/playerC = 0 + for(var/mob/new_player/player in player_list) + if((player.client)&&(player.ready)) + playerC++ + + if(playerC >= required_players) + return 1 + return 0 + +//pre_pre_setup() For when you really don't want certain jobs ingame. +/datum/game_mode/proc/pre_pre_setup() + + return 1 + +///pre_setup() +///Attempts to select players for special roles the mode might have. +/datum/game_mode/proc/pre_setup() + cultdat = pick(all_cults) + if(!cultdat) + to_chat(world, "Failed to select a cult datum, Shank a coder.") + return 0 + return 1 + + +///post_setup() +///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things +/datum/game_mode/proc/post_setup() + spawn (ROUNDSTART_LOGOUT_REPORT_TIME) + display_roundstart_logout_report() + + feedback_set_details("round_start","[time2text(world.realtime)]") + if(ticker && ticker.mode) + feedback_set_details("game_mode","[ticker.mode]") +// if(revdata) +// feedback_set_details("revision","[revdata.revision]") + feedback_set_details("server_ip","[world.internet_address]:[world.port]") + start_state = new /datum/station_state() + start_state.count() + return 1 + + +///process() +///Called by the gameticker +/datum/game_mode/proc/process() + return 0 + +//Called by the gameticker +/datum/game_mode/proc/process_job_tasks() + var/obj/machinery/message_server/useMS = null + if(message_servers) + for (var/obj/machinery/message_server/MS in message_servers) + if(MS.active) + useMS = MS + break + for(var/mob/M in player_list) + if(M.mind) + var/obj/item/device/pda/P=null + for(var/obj/item/device/pda/check_pda in PDAs) + if (check_pda.owner==M.name) + P=check_pda + break + var/count=0 + for(var/datum/job_objective/objective in M.mind.job_objectives) + count++ + var/msg="" + var/pay=0 + if(objective.per_unit && objective.units_compensated0) + if(M.mind.initial_account) + M.mind.initial_account.money += pay + var/datum/transaction/T = new() + T.target_name = "[command_name()] Payroll" + T.purpose = "Payment" + T.amount = pay + T.date = current_date_string + T.time = worldtime2text() + T.source_terminal = "\[CLASSIFIED\] Terminal #[rand(111,333)]" + M.mind.initial_account.transaction_log.Add(T) + msg += "You have been sent the $[pay], as agreed." + else + msg += "However, we were unable to send you the $[pay] you're entitled." + if(useMS && P) + useMS.send_pda_message("[P.owner]", "[command_name()] Payroll", msg) + + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + PM.notify("Message from [command_name()] (Payroll), \"[msg]\" (Unable to Reply)", 0) + break + +/datum/game_mode/proc/check_finished() //to be called by ticker + if(shuttle_master.emergency.mode >= SHUTTLE_ENDGAME || station_was_nuked) + return 1 + return 0 + +/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. + return + +/datum/game_mode/proc/declare_completion() + var/clients = 0 + var/surviving_humans = 0 + var/surviving_total = 0 + var/ghosts = 0 + var/escaped_humans = 0 + var/escaped_total = 0 + var/escaped_on_pod_1 = 0 + var/escaped_on_pod_2 = 0 + var/escaped_on_pod_3 = 0 + var/escaped_on_pod_5 = 0 + var/escaped_on_shuttle = 0 + + var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) + + if(shuttle_master.emergency.mode < SHUTTLE_ENDGAME) //shuttle didn't get to centcom + escape_locations -= /area/shuttle/escape + + for(var/mob/M in player_list) + if(M.client) + clients++ + if(ishuman(M)) + if(!M.stat) + surviving_humans++ + if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) + escaped_humans++ + if(!M.stat) + surviving_total++ + if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) + escaped_total++ + + if(M.loc && M.loc.loc && M.loc.loc.type == shuttle_master.emergency.areaInstance.type && shuttle_master.emergency.mode >= SHUTTLE_ENDGAME) + escaped_on_shuttle++ + + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom) + escaped_on_pod_1++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom) + escaped_on_pod_2++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom) + escaped_on_pod_3++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod5/centcom) + escaped_on_pod_5++ + + if(isobserver(M)) + ghosts++ + + if(clients > 0) + feedback_set("round_end_clients",clients) + if(ghosts > 0) + feedback_set("round_end_ghosts",ghosts) + if(surviving_humans > 0) + feedback_set("survived_human",surviving_humans) + if(surviving_total > 0) + feedback_set("survived_total",surviving_total) + if(escaped_humans > 0) + feedback_set("escaped_human",escaped_humans) + if(escaped_total > 0) + feedback_set("escaped_total",escaped_total) + if(escaped_on_shuttle > 0) + feedback_set("escaped_on_shuttle",escaped_on_shuttle) + if(escaped_on_pod_1 > 0) + feedback_set("escaped_on_pod_1",escaped_on_pod_1) + if(escaped_on_pod_2 > 0) + feedback_set("escaped_on_pod_2",escaped_on_pod_2) + if(escaped_on_pod_3 > 0) + feedback_set("escaped_on_pod_3",escaped_on_pod_3) + if(escaped_on_pod_5 > 0) + feedback_set("escaped_on_pod_5",escaped_on_pod_5) + + send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") + return 0 + + +/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. + return 0 + +/datum/game_mode/proc/get_players_for_role(var/role, override_jobbans=0) + var/list/players = list() + var/list/candidates = list() + //var/list/drafted = list() + //var/datum/mind/applicant = null + + var/roletext = get_roletext(role) + + // Assemble a list of active players without jobbans. + for(var/mob/new_player/player in player_list) + if(player.client && player.ready) + if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, roletext)) + if(player_old_enough_antag(player.client,role)) + players += player + + // Shuffle the players list so that it becomes ping-independent. + players = shuffle(players) + + // Get a list of all the people who want to be the antagonist for this round, except those with incompatible species + for(var/mob/new_player/player in players) + if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species)) + log_debug("[player.key] had [roletext] enabled, so we are drafting them.") + candidates += player.mind + players -= player + + // If we don't have enough antags, draft people who voted for the round. + if(candidates.len < recommended_enemies) + for(var/key in round_voters) + for(var/mob/new_player/player in players) + if(player.ckey == key) + log_debug("[player.key] voted for this round, so we are drafting them.") + candidates += player.mind + players -= player + break + + // Remove candidates who want to be antagonist but have a job that precludes it + if(restricted_jobs) + for(var/datum/mind/player in candidates) + for(var/job in restricted_jobs) + if(player.assigned_role == job) + candidates -= player + + + return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies + // recommended_enemies if the number of people with that role set to yes is less than recomended_enemies, + // Less if there are not enough valid players in the game entirely to make recommended_enemies. + + +/datum/game_mode/proc/latespawn(var/mob) + +/* +/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/player) + if(player.preferences.be_special & role) + return 1 + return 0 +*/ + +/datum/game_mode/proc/num_players() + . = 0 + for(var/mob/new_player/P in player_list) + if(P.client && P.ready) + .++ + +/datum/game_mode/proc/num_players_started() + . = 0 + for(var/mob/living/carbon/human/H in player_list) + if(H.client) + .++ + +/////////////////////////////////// +//Keeps track of all living heads// +/////////////////////////////////// +/datum/game_mode/proc/get_living_heads() + . = list() + for(var/mob/living/carbon/human/player in mob_list) + if(player.stat != DEAD && player.mind && (player.mind.assigned_role in command_positions)) + . |= player.mind + + +//////////////////////////// +//Keeps track of all heads// +//////////////////////////// +/datum/game_mode/proc/get_all_heads() + . = list() + for(var/mob/player in mob_list) + if(player.mind && (player.mind.assigned_role in command_positions)) + . |= player.mind + +////////////////////////////////////////////// +//Keeps track of all living security members// +////////////////////////////////////////////// +/datum/game_mode/proc/get_living_sec() + . = list() + for(var/mob/living/carbon/human/player in mob_list) + if(player.stat != DEAD && player.mind && (player.mind.assigned_role in security_positions)) + . |= player.mind + +//////////////////////////////////////// +//Keeps track of all security members// +//////////////////////////////////////// +/datum/game_mode/proc/get_all_sec() + . = list() + for(var/mob/living/carbon/human/player in mob_list) + if(player.mind && (player.mind.assigned_role in security_positions)) + . |= player.mind + +/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) + return 0 + +/datum/game_mode/New() + newscaster_announcements = pick(newscaster_standard_feeds) + +////////////////////////// +//Reports player logouts// +////////////////////////// +proc/display_roundstart_logout_report() + var/msg = "\blue Roundstart logout report\n\n" + for(var/mob/living/L in mob_list) + + if(L.ckey) + var/found = 0 + for(var/client/C in clients) + if(C.ckey == L.ckey) + found = 1 + break + if(!found) + msg += "[L.name] ([L.ckey]), the [L.job] (Disconnected)\n" + + + if(L.ckey && L.client) + if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) + msg += "[L.name] ([L.ckey]), the [L.job] (Connected, Inactive)\n" + continue //AFK client + if(L.stat) + if(L.suiciding) //Suicider + msg += "[L.name] ([L.ckey]), the [L.job] (Suicide)\n" + job_master.FreeRole(L.job) + message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Suicide)\n") + continue //Disconnected client + if(L.stat == UNCONSCIOUS) + msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" + continue //Unconscious + if(L.stat == DEAD) + msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" + continue //Dead + + continue //Happy connected client + for(var/mob/dead/observer/D in mob_list) + if(D.mind && (D.mind.original == L || D.mind.current == L)) + if(L.stat == DEAD) + if(L.suiciding) //Suicider + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Suicide)\n" + continue //Disconnected client + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" + continue //Dead mob, ghost abandoned + else + if(D.can_reenter_corpse) + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (This shouldn't appear.)\n" + continue //Lolwhat + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Ghosted)\n" + job_master.FreeRole(L.job) + message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Ghosted While Alive)\n") + continue //Ghosted while alive + + + + for(var/mob/M in mob_list) + if(M.client && M.client.holder) + to_chat(M, msg) + + +proc/get_nt_opposed() + var/list/dudes = list() + for(var/mob/living/carbon/human/man in player_list) + if(man.client) + if(man.client.prefs.nanotrasen_relation == "Opposed") + dudes += man + else if(man.client.prefs.nanotrasen_relation == "Skeptical" && prob(50)) + dudes += man + if(dudes.len == 0) return null + return pick(dudes) + +//Announces objectives/generic antag text. +/proc/show_generic_antag_text(var/datum/mind/player) + if(player.current) + to_chat(player.current, "You are an antagonist! Within the rules, \ + try to act as an opposing force to the crew. Further RP and try to make sure \ + other players have fun! If you are confused or at a loss, always adminhelp, \ + and before taking extreme actions, please try to also contact the administration! \ + Think through your actions and make the roleplay immersive! Please remember all \ + rules aside from those without explicit exceptions apply to antagonists.") + +/proc/show_objectives(var/datum/mind/player) + if(!player || !player.current) return + + var/obj_count = 1 + to_chat(player.current, "\blue Your current objectives:") + for(var/datum/objective/objective in player.objectives) + to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + +/proc/get_roletext(var/role) + return role + +/proc/get_nuke_code() + var/nukecode = "ERROR" + for(var/obj/machinery/nuclearbomb/bomb in world) + if(bomb && bomb.r_code && bomb.z == ZLEVEL_STATION) + nukecode = bomb.r_code + return nukecode + +/datum/game_mode/proc/replace_jobbaned_player(mob/living/M, role_type) + var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100) + var/mob/dead/observer/theghost = null + if(candidates.len) + theghost = pick(candidates) + to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") + message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.") + M.ghostize() + M.key = theghost.key + else + message_admins("[M] ([M.key] has been converted into [role_type] with an active antagonist jobban for said role since no ghost has volunteered to take their place.") + to_chat(M, "You have been converted into [role_type] with an active jobban. Any further violations of the rules on your part are likely to result in a permanent ban.") + +/datum/game_mode/proc/printplayer(datum/mind/ply, fleecheck) + var/text = "
[ply.key] was [ply.name] the [ply.assigned_role] and" + if(ply.current) + if(ply.current.stat == DEAD) + text += " died" + else + text += " survived" + if(fleecheck && ply.current.z > ZLEVEL_STATION) + text += " while fleeing the station" + if(ply.current.real_name != ply.name) + text += " as [ply.current.real_name]" + else + text += " had their body destroyed" + return text + +/datum/game_mode/proc/printobjectives(datum/mind/ply) + var/text = "" + var/count = 1 + for(var/datum/objective/objective in ply.objectives) + if(objective.check_completion()) + text += "
Objective #[count]: [objective.explanation_text] Success!" + else + text += "
Objective #[count]: [objective.explanation_text] Fail." + count++ + return text \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index f0e2944df04..c894b014a87 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -1,5 +1,5 @@ -/mob/living/simple_animal/hostile/construct +/mob/living/simple_animal/construct name = "Construct" real_name = "Construct" desc = "" @@ -19,11 +19,10 @@ faction = list("cult") flying = 1 universal_speak = 1 - AIStatus = AI_OFF //normal constructs don't have AI var/list/construct_spells = list() - var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." + loot = list(/obj/item/weapon/reagent_containers/food/snacks/ectoplasm) -/mob/living/simple_animal/hostile/construct/New() +/mob/living/simple_animal/construct/New() ..() name = text("[initial(name)] ([rand(1, 1000)])") real_name = name @@ -31,17 +30,16 @@ AddSpell(new spell(src)) updateglow() -/mob/living/simple_animal/hostile/construct/death() +/mob/living/simple_animal/construct/death() ..() - new /obj/item/weapon/reagent_containers/food/snacks/ectoplasm (src.loc) for(var/mob/M in viewers(src, null)) if((M.client && !( M.blinded ))) - M.show_message("[src] collapses in a shattered heap.") + M.show_message("\red [src] collapses in a shattered heap. ") ghostize() qdel(src) return -/mob/living/simple_animal/hostile/construct/examine(mob/user) +/mob/living/simple_animal/construct/examine(mob/user) ..(user) var/msg = "" @@ -56,34 +54,32 @@ to_chat(user, msg) -/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M) - if(istype(M, /mob/living/simple_animal/hostile/construct/builder)) - if(health < maxHealth) - adjustBruteLoss(-5) - if(src != M) - Beam(M,icon_state="sendbeam",icon='icons/effects/effects.dmi',time=4) - M.visible_message("[M] repairs some of \the [src]'s dents.", \ - "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.") - else - M.visible_message("[M] repairs some of its own dents.", \ - "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.") +/mob/living/simple_animal/construct/attack_animal(mob/living/simple_animal/M as mob) + if(istype(M, /mob/living/simple_animal/construct/builder)) + health += 5 + M.custom_emote(1,"mends some of \the [src]'s wounds.") + else + if(M.melee_damage_upper <= 0) + M.custom_emote(1, "[M.friendly] \the [src]") else - if(src != M) - to_chat(M, "You cannot repair [src]'s dents, as it has none!") - else - to_chat(M, "You cannot repair your own dents, as you have none!") - else if(src != M) - ..() + M.do_attack_animation(src) + if(M.attack_sound) + playsound(loc, M.attack_sound, 50, 1, 1) + for(var/mob/O in viewers(src, null)) + O.show_message("\The [M] [M.attacktext] \the [src]!", 1) + add_logs(M, src, "attacked") + var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + adjustBruteLoss(damage) -/mob/living/simple_animal/hostile/construct/narsie_act() +/mob/living/simple_animal/construct/narsie_act() return /////////////////Juggernaut/////////////// -/mob/living/simple_animal/hostile/construct/armoured +/mob/living/simple_animal/construct/armoured name = "Juggernaut" real_name = "Juggernaut" desc = "A possessed suit of armour driven by the will of the restless dead" @@ -103,19 +99,13 @@ status_flags = 0 construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) force_threshold = 11 - playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ - create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." -/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -/mob/living/simple_animal/hostile/construct/armoured/Life() +/mob/living/simple_animal/construct/armoured/Life() weakened = 0 return ..() -/mob/living/simple_animal/hostile/construct/armoured/bullet_act(var/obj/item/projectile/P) +/mob/living/simple_animal/construct/armoured/bullet_act(var/obj/item/projectile/P) if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) var/reflectchance = 80 - round(P.damage/3) if(prob(reflectchance)) @@ -148,7 +138,7 @@ -/mob/living/simple_animal/hostile/construct/wraith +/mob/living/simple_animal/construct/wraith name = "Wraith" real_name = "Wraith" desc = "A wicked bladed shell contraption piloted by a bound spirit" @@ -163,17 +153,14 @@ see_in_dark = 7 attack_sound = 'sound/weapons/bladeslice.ogg' construct_spells = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) - retreat_distance = 2 //AI wraiths will move in and out of combat - playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." -/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON + /////////////////////////////Artificer///////////////////////// -/mob/living/simple_animal/hostile/construct/builder +/mob/living/simple_animal/construct/builder name = "Artificer" real_name = "Artificer" desc = "A bulbous construct dedicated to building and maintaining The Cult of Nar-Sie's armies" @@ -188,69 +175,18 @@ melee_damage_upper = 5 attacktext = "rams" environment_smash = 2 - retreat_distance = 10 - minimum_distance = 10 //AI artificers will flee like fuck attack_sound = 'sound/weapons/punch2.ogg' construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser, /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, - /obj/effect/proc_holder/spell/aoe_turf/conjure/pylon, /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone, /obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser) - playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \ - use magic missile, repair allied constructs (by clicking on them), \ - and, most important of all, create new constructs by producing soulstones to capture souls, \ - and shells to place those soulstones into." - - -/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? - if(istype(A, /mob/living/simple_animal/hostile/construct)) //is it a construct? - var/mob/living/simple_animal/hostile/construct/C = A - if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is - return 1 - else - return 0 - else - return 0 - -/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it - return 0 - if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible - return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs - -/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets) - ..() - if(isliving(target)) - var/mob/living/L = target - if(istype(L, /mob/living/simple_animal/hostile/construct) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it - LoseTarget() - return 0 - if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/builder/Aggro() - ..() - if(istype(target, /mob/living/simple_animal/hostile/construct)) //oh the target is a construct no need to flee - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/builder/LoseAggro() - ..() - retreat_distance = initial(retreat_distance) - minimum_distance = initial(minimum_distance) - -/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - /////////////////////////////Behemoth///////////////////////// -/mob/living/simple_animal/hostile/construct/behemoth +/mob/living/simple_animal/construct/behemoth name = "Behemoth" real_name = "Behemoth" desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal." @@ -272,18 +208,10 @@ var/energy = 0 var/max_energy = 1000 -/mob/living/simple_animal/hostile/construct/behemoth/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -/mob/living/simple_animal/hostile/construct/behemoth/Life() - weakened = 0 - return ..() - /////////////////////////////Harvester///////////////////////// -/mob/living/simple_animal/hostile/construct/harvester +/mob/living/simple_animal/construct/harvester name = "Harvester" real_name = "Harvester" desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon." @@ -297,24 +225,16 @@ attacktext = "prods" speed = 0 environment_smash = 1 - see_in_dark = 8 + see_in_dark = 7 attack_sound = 'sound/weapons/tap.ogg' construct_spells = list(/obj/effect/proc_holder/spell/targeted/smoke/disable) - retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier - playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ - Bring those who still cling to this world of illusion back to the master so they may know Truth." - -/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0) +/mob/living/simple_animal/construct/harvester/Process_Spacemove(var/movement_dir = 0) return 1 -/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - - ////////////////Glow//////////////////// -/mob/living/simple_animal/hostile/construct/proc/updateglow() +/mob/living/simple_animal/construct/proc/updateglow() overlays = 0 var/overlay_layer = LIGHTING_LAYER + 1 if(layer != MOB_LAYER) @@ -331,7 +251,7 @@ set category = "Behemoth" set name = "Summon Cultist (300)" set desc = "Teleport a cultist to your location" - if (istype(usr,/mob/living/simple_animal/hostile/constructbehemoth)) + if (istype(usr,/mob/living/simple_animal/constructbehemoth)) if(usr.energy<300) to_chat(usr, "\red You do not have enough power stored!") @@ -352,77 +272,3 @@ return cultist.loc = usr.loc usr.visible_message("/red [cultist] appears in a flash of red light as [usr] glows with power")*/ - - -///ui stuff - -/mob/living/simple_animal/hostile/construct/armoured/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(250 to INFINITY) healths.icon_state = "juggernaut_health0" - if(208 to 249) healths.icon_state = "juggernaut_health1" - if(167 to 207) healths.icon_state = "juggernaut_health2" - if(125 to 166) healths.icon_state = "juggernaut_health3" - if(84 to 124) healths.icon_state = "juggernaut_health4" - if(42 to 83) healths.icon_state = "juggernaut_health5" - if(1 to 41) healths.icon_state = "juggernaut_health6" - else healths.icon_state = "juggernaut_health7" - - -/mob/living/simple_animal/hostile/construct/behemoth/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(750 to INFINITY) healths.icon_state = "juggernaut_health0" - if(625 to 749) healths.icon_state = "juggernaut_health1" - if(500 to 624) healths.icon_state = "juggernaut_health2" - if(375 to 499) healths.icon_state = "juggernaut_health3" - if(250 to 374) healths.icon_state = "juggernaut_health4" - if(125 to 249) healths.icon_state = "juggernaut_health5" - if(1 to 124) healths.icon_state = "juggernaut_health6" - else healths.icon_state = "juggernaut_health7" - -/mob/living/simple_animal/hostile/construct/builder/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(50 to INFINITY) healths.icon_state = "artificer_health0" - if(42 to 49) healths.icon_state = "artificer_health1" - if(34 to 41) healths.icon_state = "artificer_health2" - if(26 to 33) healths.icon_state = "artificer_health3" - if(18 to 25) healths.icon_state = "artificer_health4" - if(10 to 17) healths.icon_state = "artificer_health5" - if(1 to 9) healths.icon_state = "artificer_health6" - else healths.icon_state = "artificer_health7" - - - -/mob/living/simple_animal/hostile/construct/wraith/handle_hud_icons_health() - - ..() - if(healths) - switch(health) - if(75 to INFINITY) healths.icon_state = "wraith_health0" - if(62 to 74) healths.icon_state = "wraith_health1" - if(50 to 61) healths.icon_state = "wraith_health2" - if(37 to 49) healths.icon_state = "wraith_health3" - if(25 to 36) healths.icon_state = "wraith_health4" - if(12 to 24) healths.icon_state = "wraith_health5" - if(1 to 11) healths.icon_state = "wraith_health6" - else healths.icon_state = "wraith_health7" - - -/mob/living/simple_animal/hostile/construct/harvester/handle_hud_icons_health() - - ..() - if(healths) - switch(health) - if(150 to INFINITY) healths.icon_state = "harvester_health0" - if(125 to 149) healths.icon_state = "harvester_health1" - if(100 to 124) healths.icon_state = "harvester_health2" - if(75 to 99) healths.icon_state = "harvester_health3" - if(50 to 74) healths.icon_state = "harvester_health4" - if(25 to 49) healths.icon_state = "harvester_health5" - if(1 to 24) healths.icon_state = "harvester_health6" - else healths.icon_state = "harvester_health7" \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/constructs2.dm b/code/modules/mob/living/simple_animal/constructs2.dm new file mode 100644 index 00000000000..67aea80b539 --- /dev/null +++ b/code/modules/mob/living/simple_animal/constructs2.dm @@ -0,0 +1,428 @@ + +/mob/living/simple_animal/hostile/construct + name = "Construct" + real_name = "Construct" + desc = "" + speak_emote = list("hisses") + emote_hear = list("wails","screeches") + response_help = "thinks better of touching" + response_disarm = "flails at" + response_harm = "punches" + icon_dead = "shade_dead" + speed = 0 + a_intent = I_HARM + stop_automated_movement = 1 + status_flags = CANPUSH + attack_sound = 'sound/weapons/punch1.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + faction = list("cult") + flying = 1 + universal_speak = 1 + AIStatus = AI_OFF //normal constructs don't have AI + var/list/construct_spells = list() + var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." + +/mob/living/simple_animal/hostile/construct/New() + ..() + name = text("[initial(name)] ([rand(1, 1000)])") + real_name = name + for(var/spell in construct_spells) + AddSpell(new spell(src)) + updateglow() + +/mob/living/simple_animal/hostile/construct/death() + ..() + new /obj/item/weapon/reagent_containers/food/snacks/ectoplasm (src.loc) + for(var/mob/M in viewers(src, null)) + if((M.client && !( M.blinded ))) + M.show_message("[src] collapses in a shattered heap.") + ghostize() + qdel(src) + return + +/mob/living/simple_animal/hostile/construct/examine(mob/user) + ..(user) + + var/msg = "" + if (src.health < src.maxHealth) + msg += "" + if (src.health >= src.maxHealth/2) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + msg += "" + msg += "*---------*" + + to_chat(user, msg) + +/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M) + if(istype(M, /mob/living/simple_animal/hostile/construct/builder)) + if(health < maxHealth) + adjustBruteLoss(-5) + if(src != M) + Beam(M,icon_state="sendbeam",icon='icons/effects/effects.dmi',time=4) + M.visible_message("[M] repairs some of \the [src]'s dents.", \ + "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.") + else + M.visible_message("[M] repairs some of its own dents.", \ + "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.") + else + if(src != M) + to_chat(M, "You cannot repair [src]'s dents, as it has none!") + else + to_chat(M, "You cannot repair your own dents, as you have none!") + else if(src != M) + ..() + + +/mob/living/simple_animal/hostile/construct/narsie_act() + return + +/////////////////Juggernaut/////////////// + + + +/mob/living/simple_animal/hostile/construct/armoured + name = "Juggernaut" + real_name = "Juggernaut" + desc = "A possessed suit of armour driven by the will of the restless dead" + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 250 + health = 250 + response_harm = "harmlessly punches" + harm_intent_damage = 0 + melee_damage_lower = 30 + melee_damage_upper = 30 + attacktext = "smashes their armoured gauntlet into" + speed = 3 + environment_smash = 2 + attack_sound = 'sound/weapons/punch3.ogg' + status_flags = 0 + construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) + force_threshold = 11 + playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ + create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." + + +/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +/mob/living/simple_animal/hostile/construct/armoured/Life() + weakened = 0 + return ..() + +/mob/living/simple_animal/hostile/construct/armoured/bullet_act(var/obj/item/projectile/P) + if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) + var/reflectchance = 80 - round(P.damage/3) + if(prob(reflectchance)) + if((P.damage_type == BRUTE || P.damage_type == BURN)) + adjustBruteLoss(P.damage * 0.5) + visible_message("The [P.name] gets reflected by [src]'s shell!", \ + "The [P.name] gets reflected by [src]'s shell!") + + // Find a turf near or on the original location to bounce to + if(P.starting) + var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.original = locate(new_x, new_y, P.z) + P.starting = curloc + P.current = curloc + P.firer = src + P.yo = new_y - curloc.y + P.xo = new_x - curloc.x + + return -1 // complete projectile permutation + + return (..(P)) + + + +////////////////////////Wraith///////////////////////////////////////////// + + + +/mob/living/simple_animal/hostile/construct/wraith + name = "Wraith" + real_name = "Wraith" + desc = "A wicked bladed shell contraption piloted by a bound spirit" + icon = 'icons/mob/mob.dmi' + icon_state = "floating" + icon_living = "floating" + maxHealth = 75 + health = 75 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "slashes" + see_in_dark = 7 + attack_sound = 'sound/weapons/bladeslice.ogg' + construct_spells = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) + retreat_distance = 2 //AI wraiths will move in and out of combat + playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." + +/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + +/////////////////////////////Artificer///////////////////////// + + + +/mob/living/simple_animal/hostile/construct/builder + name = "Artificer" + real_name = "Artificer" + desc = "A bulbous construct dedicated to building and maintaining The Cult of Nar-Sie's armies" + icon = 'icons/mob/mob.dmi' + icon_state = "artificer" + icon_living = "artificer" + maxHealth = 50 + health = 50 + response_harm = "viciously beats" + harm_intent_damage = 5 + melee_damage_lower = 5 + melee_damage_upper = 5 + attacktext = "rams" + environment_smash = 2 + retreat_distance = 10 + minimum_distance = 10 //AI artificers will flee like fuck + attack_sound = 'sound/weapons/punch2.ogg' + construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser, + /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, + /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, + /obj/effect/proc_holder/spell/aoe_turf/conjure/pylon, + /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone, + /obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser) + + playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \ + use magic missile, repair allied constructs (by clicking on them), \ + and, most important of all, create new constructs by producing soulstones to capture souls, \ + and shells to place those soulstones into." + + +/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? + if(istype(A, /mob/living/simple_animal/hostile/construct)) //is it a construct? + var/mob/living/simple_animal/hostile/construct/C = A + if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is + return 1 + else + return 0 + else + return 0 + +/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it + return 0 + if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible + return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs + +/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets) + ..() + if(isliving(target)) + var/mob/living/L = target + if(istype(L, /mob/living/simple_animal/hostile/construct) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it + LoseTarget() + return 0 + if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you + retreat_distance = null + minimum_distance = 1 + +/mob/living/simple_animal/hostile/construct/builder/Aggro() + ..() + if(istype(target, /mob/living/simple_animal/hostile/construct)) //oh the target is a construct no need to flee + retreat_distance = null + minimum_distance = 1 + +/mob/living/simple_animal/hostile/construct/builder/LoseAggro() + ..() + retreat_distance = initial(retreat_distance) + minimum_distance = initial(minimum_distance) + +/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + + +/////////////////////////////Behemoth///////////////////////// + + +/mob/living/simple_animal/hostile/construct/behemoth + name = "Behemoth" + real_name = "Behemoth" + desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal." + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 750 + health = 750 + speak_emote = list("rumbles") + response_harm = "harmlessly punches" + harm_intent_damage = 0 + melee_damage_lower = 50 + melee_damage_upper = 50 + attacktext = "brutally crushes" + speed = 5 + environment_smash = 2 + attack_sound = 'sound/weapons/punch4.ogg' + force_threshold = 11 + var/energy = 0 + var/max_energy = 1000 + +/mob/living/simple_animal/hostile/construct/behemoth/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +/mob/living/simple_animal/hostile/construct/behemoth/Life() + weakened = 0 + return ..() + + +/////////////////////////////Harvester///////////////////////// + +/mob/living/simple_animal/hostile/construct/harvester + name = "Harvester" + real_name = "Harvester" + desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon." + icon = 'icons/mob/mob.dmi' + icon_state = "harvester" + icon_living = "harvester" + maxHealth = 60 + health = 60 + melee_damage_lower = 1 + melee_damage_upper = 5 + attacktext = "prods" + speed = 0 + environment_smash = 1 + see_in_dark = 8 + attack_sound = 'sound/weapons/tap.ogg' + construct_spells = list(/obj/effect/proc_holder/spell/targeted/smoke/disable) + retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier + playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ + Bring those who still cling to this world of illusion back to the master so they may know Truth." + + +/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0) + return 1 + + +/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + + +////////////////Glow//////////////////// +/mob/living/simple_animal/hostile/construct/proc/updateglow() + overlays = 0 + var/overlay_layer = LIGHTING_LAYER + 1 + if(layer != MOB_LAYER) + overlay_layer=TURF_LAYER+0.2 + + overlays += image(icon,"glow-[icon_state]",overlay_layer) + set_light(2, -2, l_color = "#FFFFFF") + +////////////////Powers////////////////// + + +/* +/client/proc/summon_cultist() + set category = "Behemoth" + set name = "Summon Cultist (300)" + set desc = "Teleport a cultist to your location" + if (istype(usr,/mob/living/simple_animal/hostile/constructbehemoth)) + + if(usr.energy<300) + to_chat(usr, "\red You do not have enough power stored!") + return + + if(usr.stat) + return + + usr.energy -= 300 + var/list/mob/living/cultists = new + for(var/datum/mind/H in ticker.mode.cult) + if (istype(H.current,/mob/living)) + cultists+=H.current + var/mob/cultist = input("Choose the one who you want to summon", "Followers of Geometer") as null|anything in (cultists - usr) + if(!cultist) + return + if (cultist == usr) //just to be sure. + return + cultist.loc = usr.loc + usr.visible_message("/red [cultist] appears in a flash of red light as [usr] glows with power")*/ + + +///ui stuff + +/mob/living/simple_animal/hostile/construct/armoured/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(250 to INFINITY) healths.icon_state = "juggernaut_health0" + if(208 to 249) healths.icon_state = "juggernaut_health1" + if(167 to 207) healths.icon_state = "juggernaut_health2" + if(125 to 166) healths.icon_state = "juggernaut_health3" + if(84 to 124) healths.icon_state = "juggernaut_health4" + if(42 to 83) healths.icon_state = "juggernaut_health5" + if(1 to 41) healths.icon_state = "juggernaut_health6" + else healths.icon_state = "juggernaut_health7" + + +/mob/living/simple_animal/hostile/construct/behemoth/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(750 to INFINITY) healths.icon_state = "juggernaut_health0" + if(625 to 749) healths.icon_state = "juggernaut_health1" + if(500 to 624) healths.icon_state = "juggernaut_health2" + if(375 to 499) healths.icon_state = "juggernaut_health3" + if(250 to 374) healths.icon_state = "juggernaut_health4" + if(125 to 249) healths.icon_state = "juggernaut_health5" + if(1 to 124) healths.icon_state = "juggernaut_health6" + else healths.icon_state = "juggernaut_health7" + +/mob/living/simple_animal/hostile/construct/builder/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(50 to INFINITY) healths.icon_state = "artificer_health0" + if(42 to 49) healths.icon_state = "artificer_health1" + if(34 to 41) healths.icon_state = "artificer_health2" + if(26 to 33) healths.icon_state = "artificer_health3" + if(18 to 25) healths.icon_state = "artificer_health4" + if(10 to 17) healths.icon_state = "artificer_health5" + if(1 to 9) healths.icon_state = "artificer_health6" + else healths.icon_state = "artificer_health7" + + + +/mob/living/simple_animal/hostile/construct/wraith/handle_hud_icons_health() + + ..() + if(healths) + switch(health) + if(75 to INFINITY) healths.icon_state = "wraith_health0" + if(62 to 74) healths.icon_state = "wraith_health1" + if(50 to 61) healths.icon_state = "wraith_health2" + if(37 to 49) healths.icon_state = "wraith_health3" + if(25 to 36) healths.icon_state = "wraith_health4" + if(12 to 24) healths.icon_state = "wraith_health5" + if(1 to 11) healths.icon_state = "wraith_health6" + else healths.icon_state = "wraith_health7" + + +/mob/living/simple_animal/hostile/construct/harvester/handle_hud_icons_health() + + ..() + if(healths) + switch(health) + if(150 to INFINITY) healths.icon_state = "harvester_health0" + if(125 to 149) healths.icon_state = "harvester_health1" + if(100 to 124) healths.icon_state = "harvester_health2" + if(75 to 99) healths.icon_state = "harvester_health3" + if(50 to 74) healths.icon_state = "harvester_health4" + if(25 to 49) healths.icon_state = "harvester_health5" + if(1 to 24) healths.icon_state = "harvester_health6" + else healths.icon_state = "harvester_health7" \ No newline at end of file diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi index 412a3606bd8..a22bb625198 100644 Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ diff --git a/icons/obj/items2.dmi b/icons/obj/items2.dmi new file mode 100644 index 00000000000..412a3606bd8 Binary files /dev/null and b/icons/obj/items2.dmi differ