diff --git a/code/_compile_options.dm b/code/_compile_options.dm index e054e830239..1c1ad2e6924 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -36,6 +36,7 @@ #define AGE_MIN 17 //youngest a character can be #define AGE_MAX 85 //oldest a character can be #define WIZARD_AGE_MIN 30 //youngest a wizard can be +#define APPRENTICE_AGE_MIN 29 //youngest an apprentice can be #define SHOES_SLOWDOWN 0 //How much shoes slow you down by default. Negative values speed you up #define POCKET_STRIP_DELAY 40 //time taken (in deciseconds) to search somebody's pockets #define DOOR_CRUSH_DAMAGE 15 //the amount of damage that airlocks deal when they crush you diff --git a/code/datums/antagonists/antag_datum.dm b/code/datums/antagonists/antag_datum.dm index ad5a9d9e18d..7e500c2e4a8 100644 --- a/code/datums/antagonists/antag_datum.dm +++ b/code/datums/antagonists/antag_datum.dm @@ -1,20 +1,21 @@ +GLOBAL_LIST_EMPTY(antagonists) + /datum/antagonist var/name = "Antagonist" - var/datum/mind/owner //Mind that owns this datum - var/silent = FALSE //Silent will prevent the gain/lose texts to show - var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with - var/delete_on_death = TRUE + var/delete_on_mind_deletion = TRUE /datum/antagonist/New(datum/mind/new_owner) + GLOB.antagonists += src typecache_datum_blacklist = typecacheof(typecache_datum_blacklist) if(new_owner) owner = new_owner /datum/antagonist/Destroy() + GLOB.antagonists -= src if(owner) LAZYREMOVE(owner.antag_datums, src) owner = null diff --git a/code/datums/antagonists/datum_cult.dm b/code/datums/antagonists/datum_cult.dm index 8c05c54b211..37dce88691e 100644 --- a/code/datums/antagonists/datum_cult.dm +++ b/code/datums/antagonists/datum_cult.dm @@ -131,7 +131,7 @@ /datum/antagonist/cult/master/on_gain() . = ..() var/mob/living/current = owner.current - SSticker.mode.set_antag_hud(current, "cultmaster") + set_antag_hud(current, "cultmaster") /datum/antagonist/cult/master/greet() to_chat(owner.current, "You are the cult's Master. As the cult's Master, you have a unique title and loud voice when communicating, are capable of marking \ diff --git a/code/datums/antagonists/devil.dm b/code/datums/antagonists/devil.dm index f66fc5a3b23..76a9e022c60 100644 --- a/code/datums/antagonists/devil.dm +++ b/code/datums/antagonists/devil.dm @@ -86,7 +86,7 @@ GLOBAL_LIST_INIT(devil_syllable, list("hal", "ve", "odr", "neit", "ci", "quon", GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr.")) /datum/antagonist/devil //Don't delete upon mind destruction, otherwise soul re-selling will break. - delete_on_death = FALSE + delete_on_mind_deletion = FALSE var/obligation var/ban var/bane diff --git a/code/datums/antagonists/wizard.dm b/code/datums/antagonists/wizard.dm new file mode 100644 index 00000000000..627b3e88d71 --- /dev/null +++ b/code/datums/antagonists/wizard.dm @@ -0,0 +1,273 @@ +#define APPRENTICE_DESTRUCTION "destruction" +#define APPRENTICE_BLUESPACE "bluespace" +#define APPRENTICE_ROBELESS "robeless" +#define APPRENTICE_HEALING "healing" + +/datum/antagonist/wizard + name = "Space Wizard" + var/give_objectives = TRUE + var/strip = TRUE //strip before equipping + var/allow_rename = TRUE + var/hud_version = "wizard" + var/datum/objective_team/wizard/wiz_team //Only created if wizard summons apprentices + var/list/objectives = list() //this should be base datum antag proc and list, todo make lazy + var/move_to_lair = TRUE + var/outfit_type = /datum/outfit/wizard + var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */ + +/datum/antagonist/wizard/on_gain() + register() + if(give_objectives) + create_objectives() + equip_wizard() + if(move_to_lair) + send_to_lair() + . = ..() + if(allow_rename) + rename_wizard() + +/datum/antagonist/wizard/proc/register() + SSticker.mode.wizards |= owner + +/datum/antagonist/wizard/proc/unregister() + SSticker.mode.wizards -= src + +/datum/objective_team/wizard + name = "wizard team" + +/datum/antagonist/wizard/proc/create_wiz_team() + wiz_team = new(owner) + wiz_team.name = "[owner.current.real_name] team" + update_wiz_icons_added(owner.current) + +/datum/antagonist/wizard/proc/send_to_lair() + if(!owner || !owner.current) + return + if(!GLOB.wizardstart.len) + SSjob.SendToLateJoin(owner.current) + to_chat(owner, "HOT INSERTION, GO GO GO") + owner.current.forceMove(pick(GLOB.wizardstart)) + +/datum/antagonist/wizard/proc/create_objectives() + switch(rand(1,100)) + if(1 to 30) + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = owner + kill_objective.find_target() + objectives += kill_objective + + if (!(locate(/datum/objective/escape) in owner.objectives)) + var/datum/objective/escape/escape_objective = new + escape_objective.owner = owner + objectives += escape_objective + + if(31 to 60) + var/datum/objective/steal/steal_objective = new + steal_objective.owner = owner + steal_objective.find_target() + objectives += steal_objective + + if (!(locate(/datum/objective/escape) in owner.objectives)) + var/datum/objective/escape/escape_objective = new + escape_objective.owner = owner + objectives += escape_objective + + if(61 to 85) + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = owner + kill_objective.find_target() + objectives += kill_objective + + var/datum/objective/steal/steal_objective = new + steal_objective.owner = owner + steal_objective.find_target() + objectives += steal_objective + + if (!(locate(/datum/objective/survive) in owner.objectives)) + var/datum/objective/survive/survive_objective = new + survive_objective.owner = owner + objectives += survive_objective + + else + if (!(locate(/datum/objective/hijack) in owner.objectives)) + var/datum/objective/hijack/hijack_objective = new + hijack_objective.owner = owner + objectives += hijack_objective + + for(var/datum/objective/O in objectives) + owner.objectives += O + +/datum/antagonist/wizard/on_removal() + unregister() + owner.RemoveAllSpells() // TODO keep track which spells are wizard spells which innate stuff + return ..() + +/datum/antagonist/wizard/proc/equip_wizard() + if(!owner) + return + var/mob/living/carbon/human/H = owner.current + if(!istype(H)) + return + if(strip) + H.delete_equipment() + //Wizards are human by default. Use the mirror if you want something else. + H.set_species(/datum/species/human) + if(H.age < wiz_age) + H.age = wiz_age + H.equipOutfit(outfit_type) + +/datum/antagonist/wizard/greet() + to_chat(owner, "You are the Space Wizard!") + to_chat(owner, "The Space Wizards Federation has given you the following tasks:") + owner.announce_objectives() + to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.") + to_chat(owner, "The spellbook is bound to you, and others cannot use it.") + to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.") + to_chat(owner,"Remember: do not forget to prepare your spells.") + +/datum/antagonist/wizard/farewell() + to_chat(owner, "You have been brainwashed! You are no longer a wizard!") + +/datum/antagonist/wizard/proc/rename_wizard() + set waitfor = FALSE + + var/wizard_name_first = pick(GLOB.wizard_first) + var/wizard_name_second = pick(GLOB.wizard_second) + var/randomname = "[wizard_name_first] [wizard_name_second]" + var/mob/living/wiz_mob = owner.current + var/newname = copytext(sanitize(input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN) + + if (!newname) + newname = randomname + + wiz_mob.fully_replace_character_name(wiz_mob.real_name, newname) + +/datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override) + var/mob/living/M = mob_override || owner.current + if(wiz_team) //Don't bother with the icon if you're solo wizard + update_wiz_icons_added(M) + M.faction |= "wizard" + +/datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override) + var/mob/living/M = mob_override || owner.current + update_wiz_icons_removed(M) + M.faction -= "wizard" + +/datum/antagonist/wizard/apprentice + name = "Wizard Apprentice" + hud_version = "apprentice" + var/datum/mind/master + var/school = APPRENTICE_DESTRUCTION + outfit_type = /datum/outfit/wizard/apprentice + wiz_age = APPRENTICE_AGE_MIN + +/datum/antagonist/wizard/apprentice/greet() + to_chat(owner, "You are [master.current.real_name]'s apprentice! You are bound by magic contract to follow their orders and help them in accomplishing their goals.") + owner.announce_objectives() + +/datum/antagonist/wizard/apprentice/register() + SSticker.mode.apprentices |= src + +/datum/antagonist/wizard/apprentice/unregister() + SSticker.mode.apprentices -= src + +/datum/antagonist/wizard/apprentice/equip_wizard() + . = ..() + if(!owner) + return + var/mob/living/carbon/human/H = owner.current + if(!istype(H)) + return + switch(school) + if(APPRENTICE_DESTRUCTION) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball(null)) + to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball.") + if(APPRENTICE_BLUESPACE) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null)) + to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.") + if(APPRENTICE_HEALING) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall(null)) + H.put_in_hands(new /obj/item/gun/magic/staff/healing(H)) + to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.") + if(APPRENTICE_HEALING) + owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null)) + to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.") + +/datum/antagonist/wizard/apprentice/create_objectives() + var/datum/objective/protect/new_objective = new /datum/objective/protect + new_objective.owner = owner + new_objective.target = master + new_objective.explanation_text = "Protect [master.current.real_name], the wizard." + owner.objectives += new_objective + objectives += new_objective + +//Random event wizard +/datum/antagonist/wizard/apprentice/imposter + name = "Wizard Imposter" + allow_rename = FALSE + +/datum/antagonist/wizard/apprentice/imposter/greet() + to_chat(owner, "You are an imposter! Trick and confuse the crew to misdirect malice from your handsome original!") + owner.announce_objectives() + +/datum/antagonist/wizard/apprentice/imposter/equip_wizard() + var/mob/living/carbon/human/master_mob = master.current + var/mob/living/carbon/human/H = owner.current + if(!istype(master_mob) || !istype(H)) + return + if(master_mob.ears) + H.equip_to_slot_or_del(new master_mob.ears.type, slot_ears) + if(master_mob.w_uniform) + H.equip_to_slot_or_del(new master_mob.w_uniform.type, slot_w_uniform) + if(master_mob.shoes) + H.equip_to_slot_or_del(new master_mob.shoes.type, slot_shoes) + if(master_mob.wear_suit) + H.equip_to_slot_or_del(new master_mob.wear_suit.type, slot_wear_suit) + if(master_mob.head) + H.equip_to_slot_or_del(new master_mob.head.type, slot_head) + if(master_mob.back) + H.equip_to_slot_or_del(new master_mob.back.type, slot_back) + + //Operation: Fuck off and scare people + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null)) + +/datum/antagonist/wizard/proc/update_wiz_icons_added(mob/living/wiz) + var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ] + wizhud.join_hud(wiz) + set_antag_hud(wiz, hud_version) + +/datum/antagonist/wizard/proc/update_wiz_icons_removed(mob/living/wiz) + var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ] + wizhud.leave_hud(wiz) + set_antag_hud(wiz, null) + + +/datum/antagonist/wizard/academy + name = "Academy Teacher" + outfit_type = /datum/outfit/wizard/academy + +/datum/antagonist/wizard/academy/equip_wizard() + . = ..() + + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile) + owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball) + + var/mob/living/M = owner.current + if(!istype(M)) + return + + var/obj/item/implant/exile/Implant = new/obj/item/implant/exile(M) + Implant.implant(M) + +/datum/antagonist/wizard/academy/create_objectives() + var/datum/objective/new_objective = new("Protect Wizard Academy from the intruders") + new_objective.owner = owner + owner.objectives += new_objective + objectives += new_objective \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index a5b53773376..31e2c89f576 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -75,7 +75,7 @@ if(islist(antag_datums)) for(var/i in antag_datums) var/datum/antagonist/antag_datum = i - if(antag_datum.delete_on_death) + if(antag_datum.delete_on_mind_deletion) qdel(i) antag_datums = null return ..() @@ -128,10 +128,16 @@ memory = null // Datum antag mind procs -/datum/mind/proc/add_antag_datum(datum_type, team) - if(!datum_type) +/datum/mind/proc/add_antag_datum(datum_type_or_instance, team) + if(!datum_type_or_instance) return - var/datum/antagonist/A = new datum_type(src, team) + var/datum/antagonist/A + if(!ispath(datum_type_or_instance)) + A = datum_type_or_instance + if(!istype(A)) + return + else + A = new datum_type_or_instance(src, team) if(!A.can_be_owned(src)) qdel(A) return @@ -188,12 +194,12 @@ /datum/mind/proc/remove_traitor() if(src in SSticker.mode.traitors) - src.remove_antag_datum(ANTAG_DATUM_TRAITOR) + remove_antag_datum(ANTAG_DATUM_TRAITOR) SSticker.mode.update_traitor_icons_removed(src) /datum/mind/proc/remove_brother() if(src in SSticker.mode.brothers) - src.remove_antag_datum(ANTAG_DATUM_BROTHER) + remove_antag_datum(ANTAG_DATUM_BROTHER) SSticker.mode.update_brother_icons_removed(src) /datum/mind/proc/remove_nukeop() @@ -205,11 +211,8 @@ remove_antag_equip() /datum/mind/proc/remove_wizard() - if(src in SSticker.mode.wizards) - SSticker.mode.wizards -= src - RemoveAllSpells() + remove_antag_datum(/datum/antagonist/wizard) special_role = null - remove_antag_equip() /datum/mind/proc/remove_cultist() if(src in SSticker.mode.cult) @@ -250,7 +253,6 @@ remove_rev() SSticker.mode.update_changeling_icons_removed(src) SSticker.mode.update_traitor_icons_removed(src) - SSticker.mode.update_wiz_icons_removed(src) SSticker.mode.update_cult_icons_removed(src) SSticker.mode.update_rev_icons_removed(src) @@ -526,11 +528,9 @@ if (SSticker.mode.config_tag=="wizard") text = uppertext(text) text = "[text]: " - if ((src in SSticker.mode.wizards) || (src in SSticker.mode.apprentices)) + if (has_antag_datum(/datum/antagonist/wizard)) text += "YES | no" - text += "
To lair, undress, dress up, let choose name." - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" + text += "
To lair, undress" else text += "yes | NO" @@ -1043,27 +1043,15 @@ switch(href_list["wizard"]) if("clear") remove_wizard() - to_chat(current, "You have been brainwashed! You are no longer a wizard!") log_admin("[key_name(usr)] has de-wizard'ed [current].") - SSticker.mode.update_wiz_icons_removed(src) if("wizard") - if(!(src in SSticker.mode.wizards)) - SSticker.mode.wizards += src + if(has_antag_datum(/datum/antagonist/wizard)) special_role = "Wizard" - //SSticker.mode.learn_basic_spells(current) - to_chat(current, "You are the Space Wizard!") + add_antag_datum(/datum/antagonist/wizard) message_admins("[key_name_admin(usr)] has wizard'ed [current].") log_admin("[key_name(usr)] has wizard'ed [current].") - SSticker.mode.update_wiz_icons_added(src) if("lair") current.forceMove(pick(GLOB.wizardstart)) - if("dressup") - SSticker.mode.equip_wizard(current) - if("name") - SSticker.mode.name_wizard(current) - if("autoobjectives") - SSticker.mode.forge_wizard_objectives(src) - to_chat(usr, "The objectives for wizard [key] have been generated. You can edit them and anounce manually.") else if (href_list["changeling"]) switch(href_list["changeling"]) @@ -1434,20 +1422,10 @@ SSticker.mode.update_changeling_icons_added(src) /datum/mind/proc/make_Wizard() - if(!(src in SSticker.mode.wizards)) - SSticker.mode.wizards += src + if(!has_antag_datum(/datum/antagonist/wizard)) special_role = "Wizard" assigned_role = "Wizard" - if(!GLOB.wizardstart.len) - SSjob.SendToLateJoin(current) - to_chat(current, "HOT INSERTION, GO GO GO") - else - current.forceMove(pick(GLOB.wizardstart)) - - SSticker.mode.equip_wizard(current) - SSticker.mode.name_wizard(current) - SSticker.mode.forge_wizard_objectives(src) - SSticker.mode.greet_wizard(src) + add_antag_datum(/datum/antagonist/wizard) /datum/mind/proc/make_Cultist() diff --git a/code/game/gamemodes/antag_hud.dm b/code/game/gamemodes/antag_hud.dm index 7ee02d7e984..447a87ce209 100644 --- a/code/game/gamemodes/antag_hud.dm +++ b/code/game/gamemodes/antag_hud.dm @@ -29,7 +29,7 @@ //GAME_MODE PROCS //called to set a mob's antag icon state -/datum/game_mode/proc/set_antag_hud(mob/M, new_icon_state) +/proc/set_antag_hud(mob/M, new_icon_state) if(!istype(M)) CRASH("set_antag_hud(): [M] ([M.type]) is not a mob!") var/image/holder = M.hud_list[ANTAG_HUD] @@ -43,7 +43,7 @@ //these are called by mind.transfer_to() /datum/mind/proc/transfer_antag_huds(datum/atom_hud/antag/newhud) leave_all_antag_huds() - SSticker.mode.set_antag_hud(current, antag_hud_icon_state) + set_antag_hud(current, antag_hud_icon_state) if(newhud) newhud.join_hud(current) diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm index c529ab546d4..8b33863381f 100644 --- a/code/game/gamemodes/antag_spawner.dm +++ b/code/game/gamemodes/antag_spawner.dm @@ -29,13 +29,13 @@ dat += "Using this contract, you may summon an apprentice to aid you on your mission.
" dat += "If you are unable to establish contact with your apprentice, you can feed the contract back to the spellbook to refund your points.
" dat += "Which school of magic is your apprentice studying?:
" - dat += "Destruction
" + dat += "Destruction
" dat += "Your apprentice is skilled in offensive magic. They know Magic Missile and Fireball.
" - dat += "Bluespace Manipulation
" + dat += "Bluespace Manipulation
" dat += "Your apprentice is able to defy physics, melting through solid objects and travelling great distances in the blink of an eye. They know Teleport and Ethereal Jaunt.
" - dat += "Healing
" + dat += "Healing
" dat += "Your apprentice is training to cast spells that will aid your survival. They know Forcewall and Charge and come with a Staff of Healing.
" - dat += "Robeless
" + dat += "Robeless
" dat += "Your apprentice is training to cast spells without their robes. They know Knock and Mindswap.
" user << browse(dat, "window=radio") onclose(user, "radio") @@ -63,73 +63,31 @@ return used = 1 var/mob/dead/observer/theghost = pick(candidates) - spawn_antag(theghost.client, get_turf(src), href_list["school"]) - if(H && H.mind) - SSticker.mode.update_wiz_icons_added(H.mind) + spawn_antag(theghost.client, get_turf(src), href_list["school"],H.mind) else to_chat(H, "Unable to reach your apprentice! You can either attack the spellbook with the contract to refund your points, or wait and try again later.") -/obj/item/antag_spawner/contract/spawn_antag(client/C, turf/T, type = "") +/obj/item/antag_spawner/contract/spawn_antag(client/C, turf/T, school,datum/mind/wizard) new /obj/effect/particle_effect/smoke(T) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) C.prefs.copy_to(M) M.key = C.key - var/wizard_name = "the wizard" - if(usr) - wizard_name = usr.real_name - to_chat(M, "You are [wizard_name]'s apprentice! You are bound by magic contract to follow their orders and help them in accomplishing their goals.") - switch(type) - if("destruction") - M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null)) - M.mind.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball(null)) - to_chat(M, "Your service has not gone unrewarded, however. Studying under [wizard_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball.") - if("bluespace") - M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null)) - M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null)) - to_chat(M, "Your service has not gone unrewarded, however. Studying under [wizard_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.") - if("healing") - M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null)) - M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall(null)) - M.put_in_hands(new /obj/item/gun/magic/staff/healing(M), TRUE) - to_chat(M, "Your service has not gone unrewarded, however. Studying under [wizard_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.") - if("robeless") - M.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null)) - M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null)) - to_chat(M, "Your service has not gone unrewarded, however. Studying under [wizard_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.") - - equip_antag(M) - var/wizard_name_first = pick(GLOB.wizard_first) - var/wizard_name_second = pick(GLOB.wizard_second) - var/randomname = "[wizard_name_first] [wizard_name_second]" - if(usr) - var/datum/objective/protect/new_objective = new /datum/objective/protect - new_objective.owner = M.mind - new_objective.target = usr.mind - new_objective.explanation_text = "Protect [usr.real_name], the wizard." - M.mind.objectives += new_objective - SSticker.mode.apprentices += M.mind - M.mind.assigned_role = "Apprentice" - M.mind.special_role = "apprentice" - SSticker.mode.update_wiz_icons_added(M.mind) + var/datum/mind/app_mind = M.mind + var/datum/antagonist/wizard/master_antag = wizard.has_antag_datum(/datum/antagonist/wizard) + if(!master_antag.wiz_team) + master_antag.create_wiz_team() + var/datum/antagonist/wizard/apprentice/app = new(app_mind) + app.wiz_team = master_antag.wiz_team + app.master = wizard + app.school = school + master_antag.wiz_team.members += app_mind + app_mind.add_antag_datum(app) + //TODO Kill these if possible + app_mind.assigned_role = "Apprentice" + app_mind.special_role = "apprentice" + // SEND_SOUND(M, sound('sound/effects/magic.ogg')) - var/newname = copytext(sanitize(input(M, "You are [wizard_name]'s apprentice. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN) - if (!newname) - newname = randomname - M.mind.name = newname - M.real_name = newname - M.name = newname - M.age = rand(AGE_MIN, WIZARD_AGE_MIN - 1) - M.dna.update_dna_identity() -/obj/item/antag_spawner/contract/equip_antag(mob/target) - target.equip_to_slot_or_del(new /obj/item/device/radio/headset(target), slot_ears) - target.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(target), slot_w_uniform) - target.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal/magic(target), slot_shoes) - target.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(target), slot_wear_suit) - target.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(target), slot_head) - target.equip_to_slot_or_del(new /obj/item/storage/backpack(target), slot_back) - target.equip_to_slot_or_del(new /obj/item/storage/box(target), slot_in_backpack) - target.equip_to_slot_or_del(new /obj/item/teleportation_scroll/apprentice(target), slot_r_store) ///////////BORGS AND OPERATIVES diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index 5e22aaac421..0838bfeed68 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -737,7 +737,7 @@ for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) if(knownspell.type == S.type) if(user.mind) - if(user.mind.special_role == "apprentice" || user.mind.special_role == "Wizard") + if(iswizard(user)) to_chat(user,"You're already far more versed in this spell than this flimsy how-to book can provide.") else to_chat(user,"You've already read this one.") diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 76c8d0f41cd..ed8f50af41d 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -16,160 +16,34 @@ announce_text = "There is a space wizard attacking the station!\n\ Wizard: Accomplish your objectives and cause mayhem on the station.\n\ Crew: Eliminate the wizard before they can succeed!" - var/use_huds = 0 var/finished = 0 /datum/game_mode/wizard/pre_setup() - var/datum/mind/wizard = pick(antag_candidates) wizards += wizard modePlayer += wizard wizard.assigned_role = "Wizard" wizard.special_role = "Wizard" + log_game("[wizard.key] (ckey) has been selected as a Wizard") //TODO: Move these to base antag datum if(GLOB.wizardstart.len == 0) to_chat(wizard.current, "A starting location for you could not be found, please report this bug!") return 0 for(var/datum/mind/wiz in wizards) wiz.current.forceMove(pick(GLOB.wizardstart)) - return 1 /datum/game_mode/wizard/post_setup() for(var/datum/mind/wizard in wizards) - log_game("[wizard.key] (ckey) has been selected as a Wizard") - equip_wizard(wizard.current) - forge_wizard_objectives(wizard) - if(use_huds) - update_wiz_icons_added(wizard) - greet_wizard(wizard) - name_wizard(wizard.current) - ..() - return + wizard.add_antag_datum(/datum/antagonist/wizard) + return ..() /datum/game_mode/wizard/generate_report() return "A dangerous Wizards' Federation individual by the name of [pick(GLOB.wizard_first)] [pick(GLOB.wizard_second)] has recently escaped confinement from an unlisted prison facility. This \ man is a dangerous mutant with the ability to alter himself and the world around him by what he and his leaders believe to be magic. If this man attempts an attack on your station, \ his execution is highly encouraged, as is the preservation of his body for later study." -/datum/game_mode/proc/forge_wizard_objectives(datum/mind/wizard) - switch(rand(1,100)) - if(1 to 30) - - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = wizard - kill_objective.find_target() - wizard.objectives += kill_objective - - if (!(locate(/datum/objective/escape) in wizard.objectives)) - var/datum/objective/escape/escape_objective = new - escape_objective.owner = wizard - wizard.objectives += escape_objective - if(31 to 60) - var/datum/objective/steal/steal_objective = new - steal_objective.owner = wizard - steal_objective.find_target() - wizard.objectives += steal_objective - - if (!(locate(/datum/objective/escape) in wizard.objectives)) - var/datum/objective/escape/escape_objective = new - escape_objective.owner = wizard - wizard.objectives += escape_objective - - if(61 to 85) - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = wizard - kill_objective.find_target() - wizard.objectives += kill_objective - - var/datum/objective/steal/steal_objective = new - steal_objective.owner = wizard - steal_objective.find_target() - wizard.objectives += steal_objective - - if (!(locate(/datum/objective/survive) in wizard.objectives)) - var/datum/objective/survive/survive_objective = new - survive_objective.owner = wizard - wizard.objectives += survive_objective - - else - if (!(locate(/datum/objective/hijack) in wizard.objectives)) - var/datum/objective/hijack/hijack_objective = new - hijack_objective.owner = wizard - wizard.objectives += hijack_objective - return - - -/datum/game_mode/proc/name_wizard(mob/living/carbon/human/wizard_mob) - //Allows the wizard to choose a custom name or go with a random one. Spawn 0 so it does not lag the round starting. - var/wizard_name_first = pick(GLOB.wizard_first) - var/wizard_name_second = pick(GLOB.wizard_second) - var/randomname = "[wizard_name_first] [wizard_name_second]" - spawn(0) - var/newname = copytext(sanitize(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN) - - if (!newname) - newname = randomname - - wizard_mob.fully_replace_character_name(wizard_mob.real_name, newname) - - /* Wizards by nature cannot be too young. */ - if(wizard_mob.age < WIZARD_AGE_MIN) - wizard_mob.age = WIZARD_AGE_MIN - return - - -/datum/game_mode/proc/greet_wizard(datum/mind/wizard, you_are=1) - if (you_are) - to_chat(wizard.current, "You are the Space Wizard!") - to_chat(wizard.current, "The Space Wizards Federation has given you the following tasks:") - - wizard.announce_objectives() - return - - -/datum/game_mode/proc/learn_basic_spells(mob/living/carbon/human/wizard_mob) - if(!istype(wizard_mob) || !wizard_mob.mind) - return 0 - wizard_mob.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null)) //Wizards get Magic Missile and Ethereal Jaunt by default - wizard_mob.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null)) - - -/datum/game_mode/proc/equip_wizard(mob/living/carbon/human/wizard_mob) - if (!istype(wizard_mob)) - return - - //So zards properly get their items when they are admin-made. - qdel(wizard_mob.wear_suit) - qdel(wizard_mob.head) - qdel(wizard_mob.shoes) - for(var/obj/item/I in wizard_mob.held_items) - qdel(I) - qdel(wizard_mob.r_store) - qdel(wizard_mob.l_store) - - wizard_mob.set_species(/datum/species/human) - wizard_mob.equip_to_slot_or_del(new /obj/item/device/radio/headset(wizard_mob), slot_ears) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(wizard_mob), slot_w_uniform) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal/magic(wizard_mob), slot_shoes) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) - wizard_mob.equip_to_slot_or_del(new /obj/item/storage/backpack(wizard_mob), slot_back) - wizard_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival(wizard_mob), slot_in_backpack) - wizard_mob.equip_to_slot_or_del(new /obj/item/teleportation_scroll(wizard_mob), slot_r_store) - var/obj/item/spellbook/spellbook = new /obj/item/spellbook(wizard_mob) - spellbook.owner = wizard_mob - wizard_mob.put_in_hands(spellbook, TRUE) - - to_chat(wizard_mob, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.") - to_chat(wizard_mob, "The spellbook is bound to you, and others cannot use it.") - to_chat(wizard_mob, "In your pockets you will find a teleport scroll. Use it as needed.") - wizard_mob.mind.store_memory("Remember: do not forget to prepare your spells.") - return 1 - - /datum/game_mode/wizard/check_finished() - for(var/datum/mind/wizard in wizards) if(isliving(wizard.current) && wizard.current.stat!=DEAD) return ..() @@ -186,7 +60,6 @@ to_chat(world, "The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!") SSticker.news_report = WIZARD_KILLED - ..() return 1 @@ -239,20 +112,6 @@ to_chat(world, text) return 1 - -//OTHER PROCS - //returns whether the mob is a wizard (or apprentice) /proc/iswizard(mob/living/M) - return istype(M) && M.mind && SSticker && SSticker.mode && ((M.mind in SSticker.mode.wizards) || (M.mind in SSticker.mode.apprentices)) - - -/datum/game_mode/proc/update_wiz_icons_added(datum/mind/wiz_mind) - var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ] - wizhud.join_hud(wiz_mind.current) - set_antag_hud(wiz_mind.current, ((wiz_mind in wizards) ? "wizard" : "apprentice")) - -/datum/game_mode/proc/update_wiz_icons_removed(datum/mind/wiz_mind) - var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ] - wizhud.leave_hud(wiz_mind.current) - set_antag_hud(wiz_mind.current, null) + return M.mind && M.mind.has_antag_datum(/datum/antagonist/wizard,TRUE) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 141b6e866a5..7531a66c2df 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -382,7 +382,8 @@ Traitors and the like can also be revived with the previous role mostly intact. switch(new_character.mind.special_role) if("Wizard") new_character.forceMove(pick(GLOB.wizardstart)) - SSticker.mode.equip_wizard(new_character) + var/datum/antagonist/wizard/A = new_character.mind.has_antag_datum(/datum/antagonist/wizard,TRUE) + A.equip_wizard() if("Syndicate") new_character.forceMove(pick(GLOB.nukeop_start)) call(/datum/game_mode/proc/equip_syndicate)(new_character) diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index f519e567a64..7412285c222 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -108,27 +108,12 @@ /obj/structure/academy_wizard_spawner/proc/summon_wizard() var/turf/T = src.loc - var/mob/living/carbon/human/wizbody = new(T) - wizbody.equipOutfit(/datum/outfit/wizard/academy) - var/obj/item/implant/exile/Implant = new/obj/item/implant/exile(wizbody) - Implant.implant(wizbody) - wizbody.faction |= "wizard" - wizbody.real_name = "Academy Teacher" - wizbody.name = "Academy Teacher" - - var/datum/mind/wizmind = new /datum/mind() - wizmind.name = "Wizard Defender" + wizbody.fully_replace_character_name("Academy Teacher") + wizbody.mind_initialize() + var/datum/mind/wizmind = wizbody.mind wizmind.special_role = "Academy Defender" - var/datum/objective/O = new("Protect Wizard Academy from the intruders") - wizmind.objectives += O - wizmind.transfer_to(wizbody) - SSticker.mode.wizards |= wizmind - - wizmind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt) - wizmind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile) - wizmind.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball) - + wizmind.add_antag_datum(/datum/antagonist/wizard/academy) current_wizard = wizbody give_control() diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm index b021259d5ce..0552c1b4b31 100644 --- a/code/modules/clothing/outfits/standard.dm +++ b/code/modules/clothing/outfits/standard.dm @@ -265,6 +265,20 @@ back = /obj/item/storage/backpack backpack_contents = list(/obj/item/storage/box=1) +/datum/outfit/wizard/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + if(visualsOnly) + return + + var/obj/item/spellbook/S = locate() in H.held_items + if(S) + S.owner = H + +/datum/outfit/wizard/apprentice + name = "Wizard Apprentice" + r_hand = null + l_hand = null + r_pocket = /obj/item/teleportation_scroll/apprentice + /datum/outfit/wizard/red name = "Red Wizard" diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm index f5b6ae1cc1e..30a4377839e 100644 --- a/code/modules/events/wizard/curseditems.dm +++ b/code/modules/events/wizard/curseditems.dm @@ -40,7 +40,7 @@ for(var/mob/living/carbon/human/H in GLOB.living_mob_list) if(ruins_spaceworthiness && !(H.z in GLOB.station_z_levels) || isspaceturf(H.loc) || isplasmaman(H)) continue //#savetheminers - if(ruins_wizard_loadout && H.mind && ((H.mind in SSticker.mode.wizards) || (H.mind in SSticker.mode.apprentices))) + if(ruins_wizard_loadout && iswizard(H)) continue if(item_set == "catgirls2015") //Wizard code means never having to say you're sorry H.gender = FEMALE diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm index 1ba052c4c0b..54d3d8ed41c 100644 --- a/code/modules/events/wizard/imposter.dm +++ b/code/modules/events/wizard/imposter.dm @@ -24,35 +24,18 @@ I.name = I.dna.real_name I.updateappearance(mutcolor_update=1) I.domutcheck() - if(W.ears) - I.equip_to_slot_or_del(new W.ears.type, slot_ears) - if(W.w_uniform) - I.equip_to_slot_or_del(new W.w_uniform.type , slot_w_uniform) - if(W.shoes) - I.equip_to_slot_or_del(new W.shoes.type, slot_shoes) - if(W.wear_suit) - I.equip_to_slot_or_del(new W.wear_suit.type, slot_wear_suit) - if(W.head) - I.equip_to_slot_or_del(new W.head.type, slot_head) - if(W.back) - I.equip_to_slot_or_del(new W.back.type, slot_back) I.key = C.key - - //Operation: Fuck off and scare people - I.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null)) - I.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null)) - I.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null)) - + var/datum/antagonist/wizard/master = M.has_antag_datum(/datum/antagonist/wizard) + if(!master.wiz_team) + master.create_wiz_team() + var/datum/antagonist/wizard/apprentice/imposter = new(I.mind) + imposter.master = M + imposter.wiz_team = master.wiz_team + master.wiz_team += imposter + I.mind.add_antag_datum(imposter) + //Remove if possible SSticker.mode.apprentices += I.mind I.mind.special_role = "imposter" - - var/datum/objective/protect/protect_objective = new /datum/objective/protect - protect_objective.owner = I.mind - protect_objective.target = W.mind - protect_objective.explanation_text = "Protect [W.real_name], the wizard." - I.mind.objectives += protect_objective - SSticker.mode.update_wiz_icons_added(I.mind) - - I.log_message("Is an imposter!", INDIVIDUAL_ATTACK_LOG) - to_chat(I, "You are an imposter! Trick and confuse the crew to misdirect malice from your handsome original!") + // + I.log_message("Is an imposter!", INDIVIDUAL_ATTACK_LOG) //? SEND_SOUND(I, sound('sound/effects/magic.ogg')) diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm index 568a8fbc72b..2f525d20982 100644 --- a/code/modules/events/wizard/shuffle.dm +++ b/code/modules/events/wizard/shuffle.dm @@ -82,7 +82,7 @@ var/list/mobs = list() for(var/mob/living/carbon/human/H in GLOB.living_mob_list) - if(H.stat || !H.mind || (H.mind in SSticker.mode.wizards) || (H.mind in SSticker.mode.apprentices)) + if(H.stat || !H.mind || iswizard(H)) continue //the wizard(s) are spared on this one mobs += H diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 457376990e6..cb086b71156 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -363,7 +363,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp if(M.mind in SSticker.mode.changelings) return 2 if("wizard") - if(M.mind in SSticker.mode.wizards) + if(iswizard(M)) return 2 if("apprentice") if(M.mind in SSticker.mode.apprentices) diff --git a/code/modules/spells/spell_types/rightandwrong.dm b/code/modules/spells/spell_types/rightandwrong.dm index 54c2d000fe7..0cb7e6c47de 100644 --- a/code/modules/spells/spell_types/rightandwrong.dm +++ b/code/modules/spells/spell_types/rightandwrong.dm @@ -13,7 +13,7 @@ if(H.stat == DEAD || !(H.client)) continue if(H.mind) - if(H.mind.special_role == "Wizard" || H.mind.special_role == "apprentice" || H.mind.special_role == "survivalist") + if(iswizard(H) || H.mind.special_role == "survivalist") continue if(prob(survivor_probability) && !(H.mind in SSticker.mode.traitors)) SSticker.mode.traitors += H.mind diff --git a/tgstation.dme b/tgstation.dme index fec76a0e2f9..924d8de89b4 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -259,6 +259,7 @@ #include "code\datums\antagonists\datum_traitor.dm" #include "code\datums\antagonists\devil.dm" #include "code\datums\antagonists\ninja.dm" +#include "code\datums\antagonists\wizard.dm" #include "code\datums\components\_component.dm" #include "code\datums\components\archaeology.dm" #include "code\datums\components\infective.dm"