diff --git a/code/__DEFINES/role_bans.dm b/code/__DEFINES/role_bans.dm index 465b55d2a81..fe553c6b50d 100644 --- a/code/__DEFINES/role_bans.dm +++ b/code/__DEFINES/role_bans.dm @@ -21,6 +21,7 @@ ROLE_SYNDICATE,\ ROLE_MANIAC,\ ROLE_WEREWOLF,\ + ROLE_VURDALAK,\ ROLE_VAMPIRE,\ ROLE_NBEAST,\ ROLE_BANDIT,\ diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index cf724cd41d1..168a90cc445 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -9,6 +9,7 @@ #define ROLE_MANIAC "Maniac" #define ROLE_WEREWOLF "Werewolf" #define ROLE_VAMPIRE "Vampire" +#define ROLE_VURDALAK "Vurdalak" #define ROLE_NBEAST "Vampire Lord" #define ROLE_BANDIT "Bandit" #define ROLE_DELF "Dark Elf" @@ -64,6 +65,7 @@ GLOBAL_LIST_INIT(special_roles_rogue, list( ROLE_BANDIT = /datum/antagonist/bandit, ROLE_ASPIRANT = /datum/antagonist/aspirant, ROLE_WEREWOLF = /datum/antagonist/werewolf, + ROLE_VURDALAK = /datum/antagonist/vurdalak, ROLE_ZIZOIDCULTIST = /datum/antagonist/zizocultist, ROLE_NBEAST = /datum/antagonist/vampirelord, ROLE_LICH = /datum/antagonist/lich diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index edcd6c3e05e..61364c1859b 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -183,7 +183,8 @@ GLOBAL_LIST_INIT(roguetraits, list( TRAIT_SHARPER_BLADES = span_info("My weapons lose their sharpness slower."), // REDMOON ADD TRAIT_BLACKLEG = span_info("I can cheat by rigging coin and dice, and peek at cards."), // REDMOON ADD TRAIT_INQUISITION = span_info("I am a member of the Inquisition. I can easily identify others of my cadre at a glance."), // REDMOON ADD - TRAIT_BARDIC_TRAINING = "Xylixian inspiration grants my songs boons and ailments.", + TRAIT_BARDIC_TRAINING = "Xylixian inspiration grants my songs boons and ailments.", // REDMOON ADD + TRAIT_GROUNDSLEEPER = span_info("I can sleep on floor or even bare soil with ease."), // REDMOON ADD TRAIT_STUDENT = span_info("The scholar has taught me new skills!"), TRAIT_DARKLING = span_info("I am a creature of the dark and am sensitive to the light. The hateful sun is especially harsh on my eyes, and I prefer to sleep during the dae."), TRAIT_BOG_TREKKING = "Expert in navigating these lands.", diff --git a/code/__REDFINES/DNA.dm b/code/___REDFINES/DNA.dm similarity index 100% rename from code/__REDFINES/DNA.dm rename to code/___REDFINES/DNA.dm diff --git a/code/__REDFINES/colors.dm b/code/___REDFINES/colors.dm similarity index 100% rename from code/__REDFINES/colors.dm rename to code/___REDFINES/colors.dm diff --git a/code/__REDFINES/mobs.dm b/code/___REDFINES/mobs.dm similarity index 100% rename from code/__REDFINES/mobs.dm rename to code/___REDFINES/mobs.dm diff --git a/code/__REDFINES/prefereces.dm b/code/___REDFINES/prefereces.dm similarity index 100% rename from code/__REDFINES/prefereces.dm rename to code/___REDFINES/prefereces.dm diff --git a/code/__REDFINES/stats.dm b/code/___REDFINES/stats.dm similarity index 100% rename from code/__REDFINES/stats.dm rename to code/___REDFINES/stats.dm diff --git a/code/__REDFINES/stress.dm b/code/___REDFINES/stress.dm similarity index 100% rename from code/__REDFINES/stress.dm rename to code/___REDFINES/stress.dm diff --git a/code/__REDFINES/traits.dm b/code/___REDFINES/traits.dm similarity index 85% rename from code/__REDFINES/traits.dm rename to code/___REDFINES/traits.dm index 18a0a59c791..33d869033c9 100644 --- a/code/__REDFINES/traits.dm +++ b/code/___REDFINES/traits.dm @@ -1,5 +1,6 @@ #define TRAIT_INQUISITION "Member of the Inquisition" #define TRAIT_BARDIC_TRAINING "Bardic Training" +#define TRAIT_GROUNDSLEEPER "Ground Sleeper" #define TRAIT_SOFTHEARTED "softhearted" //no bad mood from dismembering or seeing this #define BODYTYPE_FEMALE_PLUMP "female_plump" #define TRAIT_EORA "Eora" diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index c8f6e46a17a..ed899600a32 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -74,6 +74,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SEEPRICES" = TRAIT_SEEPRICES, "Bardic Training" = TRAIT_BARDIC_TRAINING, // REDMOON ADD "Blackleg" = TRAIT_BLACKLEG, // REDMOON ADD + "TRAIT_GROUNDSLEEPER" = TRAIT_GROUNDSLEEPER, // REDMOON ADD "TRAIT_STUDENT" = TRAIT_STUDENT, "TRAIT_BOG_TREKKING" = TRAIT_BOG_TREKKING, "TRAIT_DEATHBYSNUSNU" = TRAIT_DEATHBYSNUSNU, diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 386266b9bfd..73a3396e7b7 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -755,7 +755,7 @@ var/mob/living/carbon/human/H = hud.mymob if(H.mind && H.mind.antag_datums) for(var/datum/antagonist/D in H.mind.antag_datums) - if(istype(D, /datum/antagonist/vampirelord) || istype(D, /datum/antagonist/vampire) || istype(D, /datum/antagonist/bandit) || istype(D, /datum/antagonist/lich)) + if(istype(D, /datum/antagonist/vampirelord) || istype(D, /datum/antagonist/vampire) || istype(D, /datum/antagonist/bandit) || istype(D, /datum/antagonist/lich) || istype(D, /datum/antagonist/vurdalak)) qdel(src) return if(H.advsetup) diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index f8fa4a0b063..f47c892d762 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -253,6 +253,11 @@ caused_wound?.werewolf_infect_attempt() if(prob(60)) user.werewolf_feed(src) + if(user.mind.has_antag_datum(/datum/antagonist/vurdalak)) + if(prob(20)) + visible_message(span_danger("[user] откусывает кусок от [src]!"), \ + span_userdanger("ОНО ЕСТ МЕНЯ ЖИВЬЁМ!!")) + user.vurdalak_feed(src) // both player and npc deadites can infect if(user.mind.has_antag_datum(/datum/antagonist/zombie) || istype(user, /mob/living/carbon/human/species/deadite)) var/datum/antagonist/zombie/existing_zomble = mind?.has_antag_datum(/datum/antagonist/zombie) diff --git a/code/datums/character_flaw/addiction.dm b/code/datums/character_flaw/addiction.dm index 5b1ce3e4cfe..c08ec34cfb5 100644 --- a/code/datums/character_flaw/addiction.dm +++ b/code/datums/character_flaw/addiction.dm @@ -37,7 +37,7 @@ return if(user.mind.antag_datums) for(var/datum/antagonist/D in user.mind.antag_datums) - if(istype(D, /datum/antagonist/vampirelord) || istype(D, /datum/antagonist/werewolf) || istype(D, /datum/antagonist/skeleton) || istype(D, /datum/antagonist/zombie) || istype(D, /datum/antagonist/lich)) + if(istype(D, /datum/antagonist/vampirelord) || istype(D, /datum/antagonist/werewolf) || istype(D, /datum/antagonist/skeleton) || istype(D, /datum/antagonist/zombie) || istype(D, /datum/antagonist/lich) || istype(D, /datum/antagonist/vurdalak)) return var/mob/living/carbon/human/H = user var/oldsated = sated diff --git a/code/game/gamemodes/roguetown/roguetown.dm b/code/game/gamemodes/roguetown/roguetown.dm index dd839af3923..ea833ee17c0 100644 --- a/code/game/gamemodes/roguetown/roguetown.dm +++ b/code/game/gamemodes/roguetown/roguetown.dm @@ -131,7 +131,8 @@ var/global/list/roguegamemodes = list("Rebellion", "Vampires and Werewolves", "N return TRUE if(SSticker.manualmodes) forcedmodes |= SSticker.manualmodes - + if(num_players() < 20) + return TRUE // Убираем рол антагонистов, если низкий онлайн if(forcedmodes.len) message_admins("Manual gamemodes selected.") for(var/G in forcedmodes) @@ -162,7 +163,7 @@ var/global/list/roguegamemodes = list("Rebellion", "Vampires and Werewolves", "N log_game("Major Antagonist: None") return TRUE - if(num_players() >= 64) + if(num_players() >= 50) var/major_roll_highpop = pick(1,2,3,4) switch(major_roll_highpop) if(1) @@ -178,7 +179,7 @@ var/global/list/roguegamemodes = list("Rebellion", "Vampires and Werewolves", "N if(4) pick_lich() log_game("Major Antagonist: Lich") - else if(num_players() >= 52) + else if(num_players() >= 40) var/major_roll_midpop = pick(1,2,3) switch(major_roll_midpop) if(1) @@ -208,6 +209,11 @@ var/global/list/roguegamemodes = list("Rebellion", "Vampires and Werewolves", "N pick_bandits() log_game("Minor Antagonist: Bandit") + if(num_players() >= 30) + if(prob(100)) + pick_vurdalaks() + log_game("Minor Antagonist: Vurdalak") + if(prob(45)) pick_aspirants() log_game("Minor Antagonist: Aspirant") @@ -691,6 +697,14 @@ var/global/list/roguegamemodes = list("Rebellion", "Vampires and Werewolves", "N rebelguy.add_antag_datum(new_antag) GLOB.pre_setup_antags -= rebelguy +///////////////// VURDALAK + for(var/datum/mind/vurdalak in pre_vurdalaks) + var/datum/antagonist/new_antag = new /datum/antagonist/vurdalak() + //addtimer(CALLBACK(werewolf, TYPE_PROC_REF(/datum/mind, add_antag_datum), new_antag), rand(10,100)) + vurdalak.add_antag_datum(new_antag) + GLOB.pre_setup_antags -= vurdalak + vurdalaks += vurdalak + ..() //We're not actually ready until all traitors are assigned. gamemode_ready = FALSE diff --git a/code/modules/antagonists/roguetown/roleobjs/skeleton.dm b/code/modules/antagonists/roguetown/roleobjs/skeleton.dm index 92c2b3bc092..9f6f0b6d932 100644 --- a/code/modules/antagonists/roguetown/roleobjs/skeleton.dm +++ b/code/modules/antagonists/roguetown/roleobjs/skeleton.dm @@ -12,6 +12,8 @@ return span_boldnotice("Another deadite.") if(istype(examined_datum, /datum/antagonist/skeleton)) return span_boldnotice("Another deadite. My ally.") + if(istype(examined_datum, /datum/antagonist/vurdalak)) + return span_boldwarning("Another deadite... May be hostile. Better not provoke.") /datum/antagonist/skeleton/on_gain() diff --git a/code/modules/antagonists/roguetown/villain/vampirelord.dm b/code/modules/antagonists/roguetown/villain/vampirelord.dm index 36b9b3f95a4..2b59f6fc6a3 100644 --- a/code/modules/antagonists/roguetown/villain/vampirelord.dm +++ b/code/modules/antagonists/roguetown/villain/vampirelord.dm @@ -46,6 +46,8 @@ GLOBAL_LIST_EMPTY(vampire_objects) return span_boldnotice("Another deadite.") if(istype(examined_datum, /datum/antagonist/skeleton)) return span_boldnotice("Another deadite.") + if(istype(examined_datum, /datum/antagonist/vurdalak)) + return span_boldwarning("Another deadite... A competitor for blood. Hunter of the terror bog.") /datum/antagonist/vampirelord/apply_innate_effects(mob/living/mob_override) var/mob/living/M = mob_override || owner.current diff --git a/code/modules/antagonists/roguetown/villain/werewolf/werewolf_spells.dm b/code/modules/antagonists/roguetown/villain/werewolf/werewolf_spells.dm index 030c1a0dab2..1eaf3e1b1bd 100644 --- a/code/modules/antagonists/roguetown/villain/werewolf/werewolf_spells.dm +++ b/code/modules/antagonists/roguetown/villain/werewolf/werewolf_spells.dm @@ -102,6 +102,9 @@ var/sound_cooldown = 10 * 60 * 10 // cooldown in deciseconds (15 minutes) if(target.mind.has_antag_datum(/datum/antagonist/vampirelord)) to_chat(src, span_warning("... What? Its an elder vampire!")) return + if(target.mind.has_antag_datum(/datum/antagonist/vurdalak)) + to_chat(src, span_warning("... What? Its an undead!")) + return (!do_after(user, 7 SECONDS, target = target)) var/ramount = 20 var/rid = /datum/reagent/medicine/healthpot diff --git a/code/modules/antagonists/roguetown/villain/zomble.dm b/code/modules/antagonists/roguetown/villain/zomble.dm index 889b676ccb6..32c89f129a5 100644 --- a/code/modules/antagonists/roguetown/villain/zomble.dm +++ b/code/modules/antagonists/roguetown/villain/zomble.dm @@ -39,6 +39,8 @@ return span_boldnotice("Another deadite. [fellow_zombie.has_turned ? "My ally." : span_warning("Hasn't turned yet.")]") if(istype(examined_datum, /datum/antagonist/skeleton)) return span_boldnotice("Another deadite.") + if(istype(examined_datum, /datum/antagonist/vurdalak)) + return span_boldwarning("Another deadite. May be agressive...") /datum/antagonist/zombie/on_gain() var/mob/living/carbon/human/zombie = owner?.current diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 836275a9611..556d70a0ea9 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -178,7 +178,8 @@ else if(mind && mind.special_role == "Vampire Lord") . += "A MONSTER!" - + else if(mind?.special_role == ROLE_VURDALAK) + . += span_userdanger("A MONSTER!") var/commie_text if(mind) @@ -194,6 +195,9 @@ if(mind.special_role == "Vampire Lord") . += span_userdanger("A MONSTER!") + if(mind.special_role == ROLE_VURDALAK) + . += span_userdanger("A MONSTER!") + if(mind.assigned_role == "Lunatic") . += span_userdanger("LUNATIC!") diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 4d113a9ee51..9e14360d4ac 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -130,6 +130,8 @@ if(VD) if(statpanel("Stats")) stat("Vitae:",VD.vitae) + if(skin_armor) + stat("Skin Armor Integrity:", skin_armor.obj_integrity) if((mind.assigned_role == "Confessor") || (mind.assigned_role == "Inquisitor")) if(statpanel("Status")) stat("Confessions sent: [GLOB.confessors.len]") diff --git a/code/modules/mob/living/carbon/human/species_types/roguetown/other/werewolf.dm b/code/modules/mob/living/carbon/human/species_types/roguetown/other/werewolf.dm index 5f609657258..514fdee30ba 100644 --- a/code/modules/mob/living/carbon/human/species_types/roguetown/other/werewolf.dm +++ b/code/modules/mob/living/carbon/human/species_types/roguetown/other/werewolf.dm @@ -30,6 +30,7 @@ TRAIT_HARDDISMEMBER, //Decapping Volfs causes them to bug out, badly, and need admin intervention to fix. Bandaid fix. TRAIT_PIERCEIMMUNE, //Prevents weapon dusting and caltrop effects due to them transforming when killed/stepping on shards. TRAIT_BOG_TREKKING, + TRAIT_GROUNDSLEEPER, // REDMOON ADD TRAIT_IGNORESLOWDOWN ) inherent_biotypes = MOB_HUMANOID diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 16a641310ba..8821566ddb1 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -727,5 +727,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put recovery_amt = max_energy * 0.005 // 7.5 минут на полный отдых за сон на земле else recovery_amt = max_energy * 0.0035 // REDMOON ADD END - 10 минут на полный отдых за лежание на земле без сна, чтобы был смысл искать кровати + if(HAS_TRAIT(src, TRAIT_GROUNDSLEEPER)) + recovery_amt = max_energy * 0.01 // 3 минуты на полный отдых за сон на земле energy_add(recovery_amt) diff --git a/code/modules/mob/living/grabbing.dm b/code/modules/mob/living/grabbing.dm index c2dc96c0445..62314f70178 100644 --- a/code/modules/mob/living/grabbing.dm +++ b/code/modules/mob/living/grabbing.dm @@ -536,6 +536,7 @@ if(user.mind && C.mind) var/datum/antagonist/vampirelord/VDrinker = user.mind.has_antag_datum(/datum/antagonist/vampirelord) + var/datum/antagonist/vurdalak/vurdalak_drinker = user.mind.has_antag_datum(/datum/antagonist/vurdalak) var/datum/antagonist/vampirelord/VVictim = C.mind.has_antag_datum(/datum/antagonist/vampirelord) var/zomwerewolf = C.mind.has_antag_datum(/datum/antagonist/werewolf) if(!zomwerewolf) @@ -565,6 +566,16 @@ VDrinker.handle_vitae(500) else to_chat(user, span_warning("No more vitae from this blood...")) + else if(vurdalak_drinker) + if(C.vitae_bank > 500) + C.blood_volume = max(C.blood_volume-45, 0) + C.vitae_bank -= 1500 + if(!C.vitae_bank) + if(!(C.real_name in vurdalak_drinker.unique_victims)) + vurdalak_drinker.unique_victims += C.real_name + vurdalak_drinker.handle_power_up() + else + to_chat(user, span_warning("No more vitae from this blood...")) else if(VVictim) to_chat(user, "A strange, sweet taste tickles my throat.") @@ -581,6 +592,8 @@ VDrinker.handle_vitae(300, 300) else VDrinker.handle_vitae(300) + else if(user.mind.has_antag_datum(/datum/antagonist/vurdalak)) + to_chat(user, span_warning("I feel no lifeforce in this blood... It's useless.")) C.blood_volume = max(C.blood_volume-5, 0) C.handle_blood() diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index e2692c7586d..bf6fdf1f5f5 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -656,6 +656,9 @@ return if(L.energy <= 0) return + if(HAS_TRAIT(L, TRAIT_NORUN)) + to_chat(L, span_warning("My joints have decayed too much for running!")) + return if(ishuman(L)) var/mob/living/carbon/human/H = L if(!H.check_armor_skill() || H.legcuffed) diff --git a/code/modules/roguetown/roguejobs/gravedigger/hole.dm b/code/modules/roguetown/roguejobs/gravedigger/hole.dm index 784a1809bc1..a48a9752c15 100644 --- a/code/modules/roguetown/roguejobs/gravedigger/hole.dm +++ b/code/modules/roguetown/roguejobs/gravedigger/hole.dm @@ -24,6 +24,11 @@ faildirt = 3 icon_state = "grave" +/obj/structure/closet/dirthole/grave/Initialize() + ..() + if(is_valid_hunting_area(get_area(src))) + GLOB.vurdalak_spawn_locations += get_turf(src) + /obj/structure/closet/dirthole/closed desc = "A mound of dirt with something below." stage = 4 diff --git a/code/modules/spells/roguetown/acolyte/pestra.dm b/code/modules/spells/roguetown/acolyte/pestra.dm index 40b822dc11a..a7ba84ff7ae 100644 --- a/code/modules/spells/roguetown/acolyte/pestra.dm +++ b/code/modules/spells/roguetown/acolyte/pestra.dm @@ -230,7 +230,7 @@ revert_cast() return FALSE // If, for whatever reason, someone manages to capture a vampire with (somehow) rot??? This prevents them from losing their undead biotype. - if(target.mind?.has_antag_datum(/datum/antagonist/vampire) || target.mind?.has_antag_datum(/datum/antagonist/vampire/lesser) || target.mind?.has_antag_datum(/datum/antagonist/vampirelord)) + if(target.mind?.has_antag_datum(/datum/antagonist/vampire) || target.mind?.has_antag_datum(/datum/antagonist/vampire/lesser) || target.mind?.has_antag_datum(/datum/antagonist/vampirelord) || target.mind?.has_antag_datum(/datum/antagonist/vurdalak)) to_chat(user, span_warning("It's of an incurable evil, I can't.")) revert_cast() return FALSE diff --git a/code/modules/spells/roguetown/acolyte/sawbones.dm b/code/modules/spells/roguetown/acolyte/sawbones.dm index b110e59f139..24148dbae54 100644 --- a/code/modules/spells/roguetown/acolyte/sawbones.dm +++ b/code/modules/spells/roguetown/acolyte/sawbones.dm @@ -175,7 +175,7 @@ return FALSE // If, for whatever reason, someone manages to capture a vampire with (somehow) rot??? This prevents them from losing their undead biotype. - if(target.mind?.has_antag_datum(/datum/antagonist/vampire) || target.mind?.has_antag_datum(/datum/antagonist/vampire/lesser) || target.mind?.has_antag_datum(/datum/antagonist/vampirelord)) + if(target.mind?.has_antag_datum(/datum/antagonist/vampire) || target.mind?.has_antag_datum(/datum/antagonist/vampire/lesser) || target.mind?.has_antag_datum(/datum/antagonist/vampirelord) || target.mind?.has_antag_datum(/datum/antagonist/vurdalak)) to_chat(user, span_warning("It's of an incurable evil, I can't.")) revert_cast() return FALSE diff --git a/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak.dm b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak.dm new file mode 100644 index 00000000000..ac77f7205d6 --- /dev/null +++ b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak.dm @@ -0,0 +1,131 @@ +/mob/living/carbon/human/proc/vurdalak_transform() + if(!mind) + log_runtime("NO MIND ON [src.name] WHEN TRANSFORMING") + Paralyze(1, ignore_canstun = TRUE) + for(var/obj/item/W in src) + qdel(W) + set_cmode(FALSE) + if(client) + SSdroning.play_area_sound(get_area(src), client) +// stop_cmusic() + + var/ww_path + if(gender == MALE) + ww_path = /mob/living/carbon/human/species/vurdalak/male + else + ww_path = /mob/living/carbon/human/species/vurdalak/female + + var/mob/living/carbon/human/species/vurdalak/W = new ww_path(loc) + + W.set_patron(/datum/patron/godless) + W.gender = gender + W.regenerate_icons() + W.limb_destroyer = TRUE + + W.ambushable = FALSE + W.cmode_music = 'modular_redmoon/sounds/music/combat_vurdalak.ogg' + W.skin_armor = new /obj/item/clothing/suit/roguetown/armor/skin_armor/vurdalak_skin(W) + + W.after_creation() + mind.transfer_to(W) + W.mind.known_skills = list() + W.mind.skill_experience = list() + W.real_name = W.dna.species.random_name() + W.name = W.real_name + W.mob_biotypes |= MOB_UNDEAD + W.faction = list("undead") + + W.base_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB) + W.update_a_intents() + W.can_do_sex = FALSE + W.grant_all_languages(omnitongue = FALSE) + + W.possible_rmb_intents = list(/datum/rmb_intent/feint,\ + /datum/rmb_intent/riposte,\ + /datum/rmb_intent/weak) + + W.mind.adjust_skillrank(/datum/skill/combat/wrestling, 4, TRUE) + W.mind.adjust_skillrank(/datum/skill/combat/unarmed, 3, TRUE) + W.mind.adjust_skillrank(/datum/skill/misc/climbing, 6, TRUE) + W.mind.adjust_skillrank(/datum/skill/misc/sneaking, 5, TRUE) + W.mind.adjust_skillrank(/datum/skill/misc/swimming, 5, TRUE) + + W.mind.RemoveAllSpells() + W.mind.AddSpell(new /obj/effect/proc_holder/spell/self/claws_vurdalak) + W.mind.AddSpell(new /obj/effect/proc_holder/spell/self/vurdalak_targets_search) + W.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/vurdalak_rejuv) + W.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/vurdalak_feast) + + qdel(src) + +/mob/living/carbon/human/proc/vurdalak_feed(mob/living/carbon/human/target) + if(!istype(target)) + return + + to_chat(src, span_warning("I feed on succulent flesh. I feel reinvigorated.")) + adjust_hydration(50) + adjust_nutrition(50) + return src.reagents.add_reagent(/datum/reagent/medicine/healthpot, 2) + +/obj/item/clothing/suit/roguetown/armor/skin_armor/vurdalak_skin + slot_flags = null + name = "vurdalak's skin" + desc = "" + icon_state = null + body_parts_covered = FULL_BODY + armor = list("blunt" = 100, "slash" = 100, "stab" = 100, "bullet" = 150, "laser" = 0,"energy" = 0, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + prevent_crits = list(BCLASS_CUT, BCLASS_CHOP, BCLASS_STAB, BCLASS_BLUNT, BCLASS_SMASH, BCLASS_TWIST) + blocksound = SOFTHIT + blade_dulling = DULLING_BASHCHOP + sewrepair = FALSE + max_integrity = 200 + item_flags = DROPDEL + +/datum/intent/simple/vurdalak + name = "claw" + icon_state = "inchop" + blade_class = BCLASS_CHOP + attack_verb = list("claws", "mauls", "eviscerates") + animname = "chop" + hitsound = "genslash" + candodge = TRUE + canparry = TRUE + miss_text = "slashes the air!" + miss_sound = "bluntwooshlarge" + item_d_type = "slash" + +/obj/item/rogueweapon/vurdalak_claw + name = "claws" + desc = "" + item_state = null + lefthand_file = null + righthand_file = null + icon = 'icons/roguetown/weapons/32.dmi' + max_blade_int = 999999 + max_integrity = 999999 + force = 20 // Penetration + strength + block_chance = 0 + wdefense = 4 + armor_penetration = 30 + associated_skill = /datum/skill/combat/unarmed + wlength = WLENGTH_NORMAL + w_class = WEIGHT_CLASS_BULKY + can_parry = TRUE + sharpness = IS_SHARP + parrysound = "bladedmedium" + swingsound = BLADEWOOSH_MED + possible_item_intents = list(/datum/intent/simple/vurdalak) + parrysound = list('sound/combat/parry/parrygen.ogg') + embedding = list("embedded_pain_multiplier" = 0, "embed_chance" = 0, "embedded_fall_chance" = 0) + item_flags = DROPDEL + +/obj/item/rogueweapon/vurdalak_claw/Initialize() + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, TRAIT_GENERIC) + ADD_TRAIT(src, TRAIT_NOEMBED, TRAIT_GENERIC) + +/obj/item/rogueweapon/vurdalak_claw/right + icon_state = "claw_r" + +/obj/item/rogueweapon/vurdalak_claw/left + icon_state = "claw_l" diff --git a/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_datum.dm b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_datum.dm new file mode 100644 index 00000000000..1f7d2731b3b --- /dev/null +++ b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_datum.dm @@ -0,0 +1,277 @@ +GLOBAL_LIST_EMPTY(vurdalak_spawn_locations) + +/* + * ДАТУМ АНТАГОНИСТА + */ + +/datum/antagonist/vurdalak + name = ROLE_VURDALAK + roundend_category = "Vurdalaks" + antagpanel_category = "Vurdalak" + job_rank = ROLE_VURDALAK + confess_lines = list( + "GHRAAA!", + "AAAGH!", + "SSSUN WILL MAKE ME CRY!!" + ) + rogue_enabled = TRUE + var/day_debuff = TRUE + var/bog_bounded = TRUE + var/list/unique_victims = list() + var/last_sound = 0 + var/hide_strength = 200 + var/claws_armor_penetration = 30 + var/last_tod = "day" + +/datum/antagonist/vurdalak/roundend_report() + var/traitorwin = TRUE + + printplayer(owner) + + var/count = 0 + if(objectives.len)//If the traitor had no objectives, don't need to process this. + for(var/datum/objective/objective in objectives) + objective.update_explanation_text() + to_chat(world, "Times they have feasted: [unique_victims.len]") + if(objective.check_completion()) + to_chat(world, "Goal #[count]: [objective.explanation_text] TRIUMPH!") + else + to_chat(world, "Goal #[count]: [objective.explanation_text] Failure.") + traitorwin = FALSE + count += objective.triumph_count + + var/special_role_text = lowertext(name) + if(traitorwin) + if(count) + if(owner) + owner.adjust_triumphs(count) + to_chat(world, span_greentext("The [special_role_text] has TRIUMPHED!")) + if(owner?.current) + owner.current.playsound_local(get_turf(owner.current), 'sound/misc/triumph.ogg', 100, FALSE, pressure_affected = FALSE) + else + to_chat(world, span_redtext("The [special_role_text] has FAILED!")) + if(owner?.current) + owner.current.playsound_local(get_turf(owner.current), 'sound/misc/fail.ogg', 100, FALSE, pressure_affected = FALSE) + + + +/datum/antagonist/vurdalak/examine_friendorfoe(datum/antagonist/examined_datum,mob/examiner,mob/examined) + if(istype(examined_datum, /datum/antagonist/werewolf/lesser)) + return span_boldwarning("I see Dendor's threads... Must be lupine. Dangerous foe.") + if(istype(examined_datum, /datum/antagonist/werewolf)) + return span_boldwarning("I see strong Dendor's threads... Have to be old lupine. Dangerous foe.") + if(istype(examined_datum, /datum/antagonist/vampirelord/lesser)) + return span_boldwarning("A lesser Vampire spawn... Another, weaker deadite.") + if(istype(examined_datum, /datum/antagonist/vampirelord)) + return span_boldwarning("An Ancient Vampire... A powerful city predator.") + if(istype(examined_datum, /datum/antagonist/vurdalak)) + return span_boldwarning("A cursed creature of Night... Just like me.") + +/datum/antagonist/vurdalak/on_gain() + owner.special_role = name + owner.assigned_role = name + move_to_spawn_location() + + var/mob/living/carbon/human/H = owner.current + H.vurdalak_transform() + owner.name = H.real_name + + forge_vurdalak_objectives() + greet() + return ..() + +/datum/antagonist/vurdalak/proc/move_to_spawn_location() + owner.current.forceMove(pick(GLOB.vurdalak_spawn_locations)) + +/datum/antagonist/vurdalak/on_life(mob/user) + if(!user) + return + if(owner.current.stat == DEAD) + return + var/mob/living/carbon/human/vurdalak = owner.current + if(vurdalak.advsetup) + return + if(world.time % 5) + if(GLOB.tod != "night") + if(day_debuff) + if(isturf(vurdalak.loc)) + var/turf/T = vurdalak.loc + if(T.can_see_sky()) + if(T.get_lumcount() > 0.05) + vurdalak.apply_status_effect(/datum/status_effect/debuff/vurdalak_sunlight_exposed) + last_tod = "" + if(GLOB.tod == "night") + if(last_tod != "night") + last_tod = "night" + for(var/client/client in GLOB.clients - vurdalak.client) + if(!client.mob) + continue + if(!is_station_level(client.mob.z)) + continue + if(client.mob.y > 298) // Довольно проклято + continue + var/distance = 20 + if(client.mob.z == vurdalak.z) + distance = CLAMP(100 - get_dist(vurdalak, client.mob), 5, 80) + client.mob.playsound_local(client.mob, 'modular_redmoon/sounds/misc/vurdalak_far.ogg', distance, FALSE) + to_chat(client.mob, span_warning("Я слышу завывания болотного чудища[distance >= 50 ? " в далеке..." : "..."]")) + vurdalak.playsound_local(vurdalak, 'modular_redmoon/sounds/misc/vurdalak_far.ogg', 80, FALSE) + to_chat(vurdalak, span_notice("Я вою в ночь, чтобы показать, кому принадлежит это болото... Или же от бессилия сделать что-то с тем, кем я стал?")) + + if(vurdalak.m_intent != MOVE_INTENT_SNEAK) + if(last_sound + 10 SECONDS <= world.time) + for(var/mob/living/carbon/human/target in range(4, user) - vurdalak) + if(target.stat) + continue + if(target.m_intent == MOVE_INTENT_SNEAK) + continue + + var/sound_to_pick = pick('sound/vo/mobs/wwolf/pain (1).ogg', 'sound/vo/mobs/wwolf/pain (2).ogg', 'sound/vo/mobs/wwolf/pain (3).ogg', 'sound/vo/mobs/wwolf/roar.ogg') + playsound(vurdalak.loc, sound_to_pick, 80, 1) + last_sound = world.time + break + + if(last_sound + 10 SECONDS <= world.time) + for(var/mob/living/carbon/human/target in range(7, user) - vurdalak) + if(target.stat) + continue + if(target.m_intent == MOVE_INTENT_SNEAK) + continue + + playsound(vurdalak.loc, 'sound/vo/mobs/wwolf/sniff.ogg', 60, 1) + last_sound = world.time + break + + if(bog_bounded) + if(vurdalak.y >= 194) // Супер-проклято. Нужны нормальные зоны для помещений в болоте, потому что они одни и те же, что и в деревне. + vurdalak.apply_status_effect(/datum/status_effect/debuff/vurdalak_church_exposed) + + if(vurdalak.on_fire) + vurdalak.freak_out() + +/datum/antagonist/vurdalak/on_removal() + if(!silent && owner.current) + to_chat(owner.current, span_danger("I am no longer a [name]!")) + owner.special_role = null + owner.current = null + return ..() + +/datum/antagonist/vurdalak/proc/add_objective(datum/objective/O) + objectives += O + +/datum/antagonist/vurdalak/proc/remove_objective(datum/objective/O) + objectives -= O + +/datum/antagonist/vurdalak/proc/forge_vurdalak_objectives() + if(!(locate(/datum/objective/vurdalak) in objectives)) + var/datum/objective/vurdalak/vurdalak_objective = new + vurdalak_objective.owner = owner + add_objective(vurdalak_objective) + return + +/datum/antagonist/vurdalak/greet() + to_chat(owner.current, span_biginfo("I had been cursed for ages ago...")) + to_chat(owner.current, span_biginfo("Astrata, the unforgiving ascended witch, had been dull to my pleads, while dark-minded folks mutilated my body for the knowledges I have tried to spread.")) + to_chat(owner.current, span_biginfo("The mind of mine had been shaken, but not lost to time. I already cannot remember my name nor family, but can recognise what I have become.")) + to_chat(owner.current, span_biginfo("With each night, I feel less and less disgust to myself, doing what I need to survive another dae.")) + + owner.current.playsound_local(get_turf(owner.current), 'modular_redmoon/sounds/music/intro_vurdalak.ogg', 80, FALSE, pressure_affected = FALSE) + + owner.announce_objectives() + + return ..() + +/datum/antagonist/vurdalak/proc/handle_power_up() + if(unique_victims.len <= 10) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Моя шкура станет плотнее, когда я регенерирую.") : span_cult("My hide will be more resilent after I regenerate myself.")) + hide_strength += 20 + switch(unique_victims.len) + if(1) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Я чувствую себя сильнее.") : span_cult("I feel myself more powerful.")) + owner.current.change_stat(STAT_STRENGTH, 1) + if(2) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Я чувствую себя крепче.") : span_cult("I feel myself more resilent.")) + owner.current.change_stat(STAT_CONSTITUTION, 1) + owner.current.adjust_triumphs(1) + if(3) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Необычная сила распространяется в моих ногах. Я могу двигаться быстрее.") : span_cult("Unusual power spreads in leg. I can move a bit faster now.")) + owner.current.change_stat(STAT_SPEED, 1) + if(4) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Я чувствую себя сильнее.") : span_cult("I feel myself more powerful.")) + owner.current.change_stat(STAT_STRENGTH, 1) + owner.current.adjust_triumphs(1) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Я ощущаю, что куда лучше орудую своими когтями.") : span_cult("I feel myself much more confident in claws combat.")) + owner.adjust_skillrank(/datum/skill/combat/unarmed, 1, TRUE) + if(5) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Я чувствую себя крепче.") : span_cult("I feel myself more resilent.")) + owner.current.change_stat(STAT_CONSTITUTION, 1) + if(6) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Необычная сила распространяется в моих ногах. Я могу двигаться быстрее!") : span_cult("Unusual power spreads in leg. I can move a bit faster now!")) + owner.current.change_stat(STAT_SPEED, 1) + owner.current.adjust_triumphs(1) + if(7) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Моя кровь... Моя кровь кипит! Жажда убивать становится сильнее... Я ощущаю, как думать становится сложнее.") : span_cult("My blood... My blood boils! The urge to kill is more than before... Do I lose myself?")) + owner.current.change_stat(STAT_ENDURANCE, 1) + owner.current.change_stat(STAT_INTELLIGENCE, -1) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Из моей хватки не вырваться!") : span_cult("My grasp is much stronger now!")) + owner.adjust_skillrank(/datum/skill/combat/wrestling, 1, TRUE) + if(8) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Я ЧУВСТВУЮ СЕБЯ ВЫНОСЛИВЕЕ, ЧЕМ КОГДА ЛИБО! Я МОГУ УБИВАТЬ БОЛЬШЕ! ЦЕРКОВЬ УМОЕТСЯ В КРОВИ!") : span_cult("I FEEL MYSELF LESS FATIGUE THAN EVER BEFORE! I CAN KILL MORE! THE CHURCH WILL BATH IN BLOOD!")) + owner.current.change_stat(STAT_ENDURANCE, 1) + owner.current.change_stat(STAT_INTELLIGENCE, -1) + owner.current.adjust_triumphs(1) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("В БОЮ КОГТЯМИ МНЕ НЕТ РАВНЫХ!") : span_cult("IN COMBAT WITH CLAWS NO ONE CAN MATCH ME!")) + owner.adjust_skillrank(/datum/skill/combat/unarmed, 1, TRUE) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("МОИ КОГТИ СОЗДАНЫ, ЧТОБЫ РАЗРЫВАТЬ ЖЕЛЕЗО!") : span_cult("MY CLAWS ARE FORMED TO BREAK THROUGH IRON!")) + claws_armor_penetration = 50 + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("И я стал весить больше... Какое счастье, что мне теперь не так и сильно нужно прыгать по деревьям. Корм мне не угроза.") : span_cult("And I have become heavier... Lucky me I don't need to jump on trees anymore. A prey is not a threat to me.")) + REMOVE_TRAIT(owner.current, TRAIT_ZJUMP, SPECIES_TRAIT) + if(9) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cult("Я ЧУВСТВУЮ... СОЛНЦЕ... ОНА НЕ ИМЕЕТ БОЛЕЕ ВЛАСТИ НАДО МНОЙ...") : span_cult("I FEEL... THE SUN... SHE HAS NO POWER OVER ME ANYMORE...")) + owner.current.change_stat(STAT_INTELLIGENCE, -2) + day_debuff = FALSE + if(10) + to_chat(owner.current, owner.current.client.prefs.be_russian ? span_cultbigbold("СИЛА ЦЕРКВИ БОЛЬШЕ НЕ СМОЖЕТ СДЕРЖАТЬ МЕНЯ! ЦЕРКОВЬ... НЕ СМОЖЕТ... СДЕРЖИВАТЬ МЕНЯ! КРОВЬ! СМЕРТЬ! Я САМА СМЕРТЬ!!!") : span_cultbigbold("THEIR LIFEFORCE DID LET ME TO PASS THE TERROR BOG! THE CHURCH... NO LONGER... HOLDS ME OFF... BLOOD! DEATH! AM THE DEATH ITSELF!!!")) + owner.current.change_stat(STAT_INTELLIGENCE, -4) + bog_bounded = FALSE + +/* + * ЗАДАЧИ + */ + +/datum/objective/vurdalak + name = "drink_blood" + explanation_text = "With each and every dae, my lifeforce betrays me. I have to drink blood or feast on 10 victims, to sustain myself for a bit more." + triumph_count = 5 + +/datum/objective/vurdalak/check_completion() + var/datum/antagonist/vurdalak/vurdalak_datum = owner.has_antag_datum(/datum/antagonist/vurdalak) + if(vurdalak_datum) + if(vurdalak_datum.unique_victims.len >= 10) + return TRUE + +/* + * СОСТОЯНИЯ + */ + +/datum/status_effect/debuff/vurdalak_sunlight_exposed + id = "vurdalak_sunglight" + alert_type = /atom/movable/screen/alert/status_effect/debuff/vurdalak_sunlight_exposed + effectedstats = list("strength" = -5, "constitution" = -5, "speed" = -5) + duration = 10 SECONDS + +/datum/status_effect/debuff/vurdalak_church_exposed + id = "vurdalak_church" + alert_type = /atom/movable/screen/alert/status_effect/debuff/vurdalak_church_exposed + effectedstats = list("strength" = -8, "constitution" = -8, "speed" = -8) // Братан НЕ ДОЛЖЕН суваться в деревню + duration = 10 SECONDS + +/atom/movable/screen/alert/status_effect/debuff/vurdalak_sunlight_exposed + name = "SUNLIGHT!" + desc = "I feel myself sick of it!\n" + icon_state = "stressb" + +/atom/movable/screen/alert/status_effect/debuff/vurdalak_church_exposed + name = "Church!" + desc = "Their church weakens me... I need to get back to the bog.\n" + icon_state = "stressvb" diff --git a/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_globals.dm b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_globals.dm new file mode 100644 index 00000000000..eadb9fb5636 --- /dev/null +++ b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_globals.dm @@ -0,0 +1,60 @@ +/datum/game_mode + var/list/datum/mind/vurdalaks = list() + var/list/datum/mind/pre_vurdalaks = list() + +/datum/game_mode/chaosmode/proc/pick_vurdalaks() + // Ideally we want Refugees/Towners to roll it + restricted_jobs = list( + "Duke", + "Duke Consort", + "Dungeoneer", + "Inquisitor", + "Confessor", + "Watchman", + "Man at Arms", + "Priest", + "Acolyte", + "Cleric", + "Retinue Captain", + "Court Magos", + "Templar", + "Vanguard", + "Warden", + "Knight", + "Mortician", + "Mercenary", + "Bandit", + "Goblin Chief", + "Goblin Cook", + "Goblin Guard", + "Goblin Rabble", + "Goblin Smith", + "Goblin Shaman" + ) + +// var/num_werewolves = rand(1) + antag_candidates = get_players_for_role(ROLE_VURDALAK) + antag_candidates = shuffle(antag_candidates) + + for(var/datum/mind/vurdalak in antag_candidates) + var/blockme = FALSE + if(!(vurdalak in allantags)) + blockme = TRUE + if(vurdalak.assigned_role in GLOB.noble_positions) + continue + if(vurdalak.assigned_role in GLOB.youngfolk_positions) + blockme = TRUE + if(blockme) + continue + allantags -= vurdalak + pre_vurdalaks += vurdalak + vurdalak.special_role = ROLE_VURDALAK + //vurdalak.assigned_role = ROLE_WEREWOLF + vurdalak.restricted_roles = restricted_jobs.Copy() + testing("[key_name(vurdalak)] has been selected as a VURDALAK") + log_game("[key_name(vurdalak)] has been selected as a [vurdalak.special_role]") + antag_candidates.Remove(vurdalak) + break + for(var/antag in pre_vurdalaks) + GLOB.pre_setup_antags |= antag + restricted_jobs = list() diff --git a/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_species.dm b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_species.dm new file mode 100644 index 00000000000..a9bcff8016e --- /dev/null +++ b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_species.dm @@ -0,0 +1,127 @@ +/mob/living/carbon/human/species/vurdalak + race = /datum/species/vurdalak + +/mob/living/carbon/human/species/vurdalak/male + gender = MALE + +/mob/living/carbon/human/species/vurdalak/female + gender = FEMALE + +/datum/species/vurdalak + name = "vurdalak" + id = "vurdalak" + species_traits = list(NO_UNDERWEAR, NO_ORGAN_FEATURES, NO_BODYPART_FEATURES) + inherent_traits = list( + TRAIT_STRONGBITE, + TRAIT_ZJUMP, + TRAIT_NOFALLDAMAGE1, + TRAIT_STEELHEARTED, + TRAIT_TOXIMMUNE, + TRAIT_ORGAN_EATER, + TRAIT_NASTY_EATER, + TRAIT_NOSTINK, + TRAIT_CRITICAL_RESISTANCE, + TRAIT_HARDDISMEMBER, //Decapping Volfs causes them to bug out, badly, and need admin intervention to fix. Bandaid fix. +// TRAIT_PIERCEIMMUNE, //Prevents weapon dusting and caltrop effects due to them transforming when killed/stepping on shards. + TRAIT_BOG_TREKKING, + TRAIT_IGNORESLOWDOWN, + TRAIT_NOPAINSTUN, + TRAIT_LIMPDICK, + TRAIT_NOMOOD, + TRAIT_NOSLEEP, + TRAIT_ZOMBIE_IMMUNE, + TRAIT_DODGEEXPERT, + TRAIT_GROUNDSLEEPER, + TRAIT_NORUN + ) + inherent_biotypes = MOB_HUMANOID + armor = 30 + no_equip = list(SLOT_SHIRT, SLOT_HEAD, SLOT_WEAR_MASK, SLOT_ARMOR, SLOT_GLOVES, SLOT_SHOES, SLOT_PANTS, SLOT_CLOAK, SLOT_BELT, SLOT_BACK_R, SLOT_BACK_L, SLOT_S_STORE) + nojumpsuit = 1 + sexes = 0 + offset_features = list(OFFSET_HANDS = list(0,2), OFFSET_HANDS_F = list(0,2)) + soundpack_m = /datum/voicepack/werewolf + soundpack_f = /datum/voicepack/werewolf + specstats = list( + "strength" = 5, + "perception" = -2, + "intelligence" = -2, + "constitution" = 6, + "endurance" = 6, + "speed" = 2, + "fortune" = 0 + ) + enflamed_icon = "widefire" + organs = list( + ORGAN_SLOT_BRAIN = /obj/item/organ/brain, + ORGAN_SLOT_HEART = /obj/item/organ/heart, + ORGAN_SLOT_LUNGS = /obj/item/organ/lungs, + ORGAN_SLOT_EYES = /obj/item/organ/eyes/night_vision/zombie, + ORGAN_SLOT_EARS = /obj/item/organ/ears, + ORGAN_SLOT_LIVER = /obj/item/organ/liver, + ORGAN_SLOT_STOMACH = /obj/item/organ/stomach, + ORGAN_SLOT_APPENDIX = /obj/item/organ/appendix, + ) + languages = list( + /datum/language/hellspeak, + ) + +/datum/species/vurdalak/send_voice(mob/living/carbon/human/H) + playsound(get_turf(H), pick('sound/vo/mobs/wwolf/wolftalk1.ogg','sound/vo/mobs/wwolf/wolftalk2.ogg'), 100, TRUE, -1) + +/datum/species/vurdalak/regenerate_icons(mob/living/carbon/human/H) + H.icon = 'modular_redmoon/icons/mobs/vurdalak.dmi' + H.base_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB) + H.icon_state = "vurdalack" + H.update_damage_overlays() + return TRUE + +/datum/species/vurdalak/spec_life(mob/living/carbon/human/H) + ..() + H.blood_volume = min(H.blood_volume + 5, BLOOD_VOLUME_NORMAL) + H.heal_wounds(2) + H.adjustBruteLoss(-2, 0) + H.adjustFireLoss(-2, 0) + H.adjustOxyLoss(-0.5, 0) + H.adjustCloneLoss(-0.5, 0) //heals as half as health + H.update_damage_overlays() + +/datum/species/vurdalak/on_species_gain(mob/living/carbon/C, datum/species/old_species) + . = ..() + RegisterSignal(C, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + +/datum/species/vurdalak/update_damage_overlays(mob/living/carbon/human/H) + return TRUE // Временно отключено, до спрайтов дамага + H.remove_overlay(DAMAGE_LAYER) + var/list/hands = list() + var/mutable_appearance/inhand_overlay = mutable_appearance("[H.icon_state]-dam", layer=-DAMAGE_LAYER) + var/burnhead = 0 + var/brutehead = 0 + var/burnch = 0 + var/brutech = 0 + var/obj/item/bodypart/affecting = H.get_bodypart(BODY_ZONE_HEAD) + if(affecting) + burnhead = (affecting.burn_dam / affecting.max_damage) + brutehead = (affecting.brute_dam / affecting.max_damage) + affecting = H.get_bodypart(BODY_ZONE_CHEST) + if(affecting) + burnch = (affecting.burn_dam / affecting.max_damage) + brutech = (affecting.brute_dam / affecting.max_damage) + var/usedloss = 0 + if(burnhead > usedloss) + usedloss = burnhead + if(brutehead > usedloss) + usedloss = brutehead + if(burnch > usedloss) + usedloss = burnch + if(brutech > usedloss) + usedloss = brutech + inhand_overlay.alpha = 255 * usedloss + testing("damalpha [inhand_overlay.alpha]") + hands += inhand_overlay + H.overlays_standing[DAMAGE_LAYER] = hands + H.apply_overlay(DAMAGE_LAYER) + return TRUE + +/datum/species/vurdalak/random_name(gender,unique,lastname) + return pick("Vurdalak", "Nightcurse", "Night Creature", "Abomination", "Creature", "Monster", "Beast", "Terrorbog Beast", "Rockhill Beast", "Nighthunter", "Nightshade", "Dreadclaw", "Nightstalker") diff --git a/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_spells.dm b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_spells.dm new file mode 100644 index 00000000000..ef4331bb68b --- /dev/null +++ b/modular_redmoon/code/modules/antagonists/roguetown/villian/vurdalak/vurdalak_spells.dm @@ -0,0 +1,224 @@ +/obj/effect/proc_holder/spell/self/claws_vurdalak + name = "Extend Claws" + desc = "!" + overlay_state = "claws" + antimagic_allowed = TRUE + charge_max = 20 // 2 seconds + var/extended = FALSE + +/obj/effect/proc_holder/spell/self/claws_vurdalak/cast(mob/user = usr) + ..() + var/obj/item/rogueweapon/vurdalak_claw/left/l + var/obj/item/rogueweapon/vurdalak_claw/right/r + l = user.get_active_held_item() + r = user.get_inactive_held_item() + + if (extended) + if (istype(user.get_active_held_item(), /obj/item/rogueweapon/vurdalak_claw)) + user.dropItemToGround(l, TRUE) + user.dropItemToGround(r, TRUE) + qdel(l) + qdel(r) + // user.visible_message("Your claws retract.", "You feel your claws retracting.", "You hear a sound of claws retracting.") + extended = FALSE + else + var/obj + for(var/i = 0, i<2, i++) + switch(i) + if(0) + obj = user.get_active_held_item() + if(1) + obj = user.get_inactive_held_item() + user.dropItemToGround(obj, TRUE) + var/datum/antagonist/vurdalak/vurdalak_antag_datum = user.mind.has_antag_datum(/datum/antagonist/vurdalak) + + l = new(user, 1) + r = new(user, 2) + user.put_in_hands(l, TRUE, FALSE, TRUE) + user.put_in_hands(r, TRUE, FALSE, TRUE) + l.armor_penetration = vurdalak_antag_datum.claws_armor_penetration + r.armor_penetration = vurdalak_antag_datum.claws_armor_penetration + // user.visible_message("Your claws extend.", "You feel your claws extending.", "You hear a sound of claws extending.") + extended = TRUE + +/obj/effect/proc_holder/spell/self/vurdalak_targets_search + name = "Seek for Prey" + desc = "!" + overlay_state = "transfix" + antimagic_allowed = TRUE + charge_max = 10 SECONDS + +/obj/effect/proc_holder/spell/self/vurdalak_targets_search/cast(mob/user = usr) + ..() + if(GLOB.tod != "night") + to_chat(user, user.client.prefs.be_russian ? span_warning("Ночь ещё не пришла... Мне нужно ждать, чтобы почувствовать добычу.") : span_warning("The night has not come... I need wait to look for the prey.")) + revert_cast() + return + var/area/user_area = get_area(user) + if(!user_area.outdoors) + to_chat(user, user.client.prefs.be_russian ? span_warning("Для этого, мне сначала нужно видеть луну.") : span_warning("I need to be under the sky to see the moon for it.")) + revert_cast() + return + var/mob/target = null + var/last_best_distance = 1000 + for(var/mob/living/carbon/human/potential_victim in GLOB.human_list - user) + if(!is_valid_hunting_area(get_area(potential_victim))) + continue + if(potential_victim.stat) + continue + if(potential_victim.m_intent == MOVE_INTENT_SNEAK) + continue + if(!potential_victim.mind) + continue + if("undead" in potential_victim.faction) + continue + if(HAS_TRAIT(potential_victim, HUGBOX_TRAIT)) + continue + var/distance_to_potential_victim = get_dist_euclidian(user, potential_victim) + if(distance_to_potential_victim < last_best_distance) + last_best_distance = distance_to_potential_victim + target = potential_victim + + if(!target) + to_chat(user, user.client.prefs.be_russian ? span_warning("Нет живых на болоте... Кто-то может прятаться в подземелье, зданиях или красться.") : span_warning("There are no living beings in the wilderness... Some may hide in the underground, in huts or sneak around.")) + revert_cast() + return + + var/arrowpart + switch(get_dir(user, target)) + if(NORTH) + arrowpart = " ⇑" + if(SOUTH) + arrowpart = " ⇓" + if(EAST) + arrowpart = " ⇒" + if(WEST) + arrowpart = " ⇐" + if(NORTHWEST) + arrowpart = " ⇖" + if(NORTHEAST) + arrowpart = " ⇗" + if(SOUTHWEST) + arrowpart = " ⇙" + if(SOUTHEAST) + arrowpart = " ⇘" + + to_chat(user, user.client.prefs.be_russian ? span_cult("Чувствую... Бьется сердце в [FLOOR(last_best_distance*1.5, 1)] шагах от меня. ([arrowpart])") : span_cult("I feel it... A heart is beating [FLOOR(last_best_distance*1.5, 1)] steps away. ([arrowpart])")) + playsound(user.loc, 'sound/vo/mobs/wwolf/sniff.ogg', 60, 1) + +// Spells +/obj/effect/proc_holder/spell/targeted/vurdalak_rejuv + name = "Undead Regeneration" + desc = "Regenerates my targeted limb and replenishes half my stamina. Recharges every 60 seconds. I must stand still." + overlay_state = "unrot" + action_icon = 'icons/mob/actions/roguespells.dmi' + releasedrain = 0 + chargedrain = 0 + chargetime = 0 + range = -1 + warnie = "sydwarning" + movement_interrupt = TRUE + chargedloop = null + invocation_type = "whisper" + associated_skill = /datum/skill/magic/druidic + antimagic_allowed = FALSE + charge_max = 3.5 MINUTES + cooldown_min = 3.5 MINUTES + include_user = TRUE + max_targets = 1 + +/obj/effect/proc_holder/spell/targeted/vurdalak_rejuv/cast(list/targets, mob/user = usr) +// if(GLOB.tod != "night") +// to_chat(user, user.client.prefs.be_russian ? span_warning("Ночь ещё не пришла... Мне нужно ждать, чтобы регенерировать.") : span_warning("The night has not come... I need wait to regenerate myself.")) +// revert_cast() +// return FALSE + + if(user && ishuman(user)) + var/mob/living/carbon/human/vurdalak = user + var/datum/antagonist/vurdalak/vurdalak_antag_datum = user.mind.has_antag_datum(/datum/antagonist/vurdalak) + + var/silver_curse_status = FALSE // Fail to cast condition. + for(var/datum/status_effect/debuff/silver_curse/silver_curse in vurdalak.status_effects) + silver_curse_status = TRUE + break + if(silver_curse_status) + to_chat(vurdalak, user.client.prefs.be_russian ? span_danger("Моё ПРОКЛЯТИЕ не даёт мне восстановиться!") : span_danger("My BANE is not letting me rejuvenate!")) + revert_cast() + return FALSE + + if(vurdalak_antag_datum) + to_chat(user, user.client.prefs.be_russian ? span_cult("Я чувствую, как моё тело быстро восстанавливается... Ровно как и моя шкура. Чем больше я загублю, тем сильнее стану.") : span_cult("I feel how my body regenerates quickly... As well as my hide. More I will feed myself, more I will restore.")) + vurdalak.reagents.add_reagent(/datum/reagent/medicine/greaterhealthpot, length(vurdalak_antag_datum.unique_victims)) + vurdalak.heal_wounds(vurdalak_antag_datum.unique_victims.len * 20) + vurdalak.blood_volume += BLOOD_VOLUME_SURVIVE + vurdalak.update_damage_overlays() + vurdalak.playsound_local(get_turf(vurdalak), 'sound/misc/vampirespell.ogg', 100, FALSE, pressure_affected = FALSE) + + if(vurdalak.skin_armor) + qdel(vurdalak.skin_armor) + vurdalak.skin_armor = new /obj/item/clothing/suit/roguetown/armor/skin_armor/vurdalak_skin(vurdalak) + vurdalak.skin_armor.max_integrity = vurdalak_antag_datum.hide_strength + vurdalak.skin_armor.obj_integrity = vurdalak_antag_datum.hide_strength + +// Spells +/obj/effect/proc_holder/spell/targeted/vurdalak_feast + name = "Feast" + desc = "" + overlay_state = "tamebeast" + action_icon = 'icons/mob/actions/roguespells.dmi' + releasedrain = 0 + chargedrain = 0 + chargetime = 30 + range = 1 + warnie = "sydwarning" + movement_interrupt = TRUE + chargedloop = null + invocation_type = "whisper" + associated_skill = /datum/skill/magic/druidic + antimagic_allowed = TRUE + charge_max = 15 SECONDS + cooldown_min = 15 SECONDS + include_user = FALSE + max_targets = 1 + +/obj/effect/proc_holder/spell/targeted/vurdalak_feast/cast(list/targets, mob/user = usr) + if(iscarbon(targets[1])) + var/mob/living/carbon/target = targets[1] + if(ishuman(user)) + var/mob/living/carbon/human/vurdalak = user + var/datum/antagonist/vurdalak/vurdalak_antag_datum = user.mind.has_antag_datum(/datum/antagonist/vurdalak) + if(target.vitae_bank <= 500) + to_chat(vurdalak, user.client.prefs.be_russian ? span_warning("Это тело уже лишилось своей жизненной силы...") : span_warning("This body has already lost its lifeforce...")) + revert_cast() + return + if(!target.mind) + to_chat(vurdalak, user.client.prefs.be_russian ? span_warning("Я не чувствую достаточно жизненной силы в этом теле... Это бесполезно.") : span_warning("I don't feel enought lifeforce in this body... It will be useless.")) + revert_cast() + return + + while(target.vitae_bank > 500) + var/obj/item = vurdalak.get_active_held_item() + if(!item || !(item.type in typesof(/obj/item/rogueweapon/vurdalak_claw))) + to_chat(vurdalak, user.client.prefs.be_russian ? span_warning("Мне нужно выпустить когти, чтобы приступить к трапезе.") : span_warning("I need to extend my claws to feast on them.")) + revert_cast() + break + if(!do_after(user, 1.4 SECONDS, target = target)) + break + if(prob(10)) + user.emote("rage") + if(!target.stat) + target.emote("painscream") + vurdalak.ClickOn(target, list()) + if(prob(10)) + var/obj/O = new /obj/effect/decal/cleanable/blood/gibs (get_turf(target)) + var/turf/target_turf = get_step(target, pick(alldirs)) + O.forceMove(target_turf) + target.vitae_bank -= 500 + if(target.vitae_bank <= 500) + vurdalak.visible_message(span_dead("[vurdalak] finishes with [target]..."), vurdalak.client.prefs.be_russian ? span_dead("Я закончил поглощать плоть... Жизненной силы достаточно.") : span_dead("I have consumed their flesh... The lifeforce is enough.")) + if(!(target.real_name in vurdalak_antag_datum.unique_victims)) + vurdalak_antag_datum.unique_victims += target.real_name + vurdalak_antag_datum.handle_power_up() + else + return FALSE + return TRUE diff --git a/modular_redmoon/icons/mobs/ghoul.dmi b/modular_redmoon/icons/mobs/ghoul.dmi new file mode 100644 index 00000000000..faab80181b4 Binary files /dev/null and b/modular_redmoon/icons/mobs/ghoul.dmi differ diff --git a/modular_redmoon/icons/mobs/vurdalak.dmi b/modular_redmoon/icons/mobs/vurdalak.dmi new file mode 100644 index 00000000000..58106b8e179 Binary files /dev/null and b/modular_redmoon/icons/mobs/vurdalak.dmi differ diff --git a/modular_redmoon/sounds/misc/vurdalak_far.ogg b/modular_redmoon/sounds/misc/vurdalak_far.ogg new file mode 100644 index 00000000000..8c5b6398e67 Binary files /dev/null and b/modular_redmoon/sounds/misc/vurdalak_far.ogg differ diff --git a/modular_redmoon/sounds/music/combat_vurdalak.ogg b/modular_redmoon/sounds/music/combat_vurdalak.ogg new file mode 100644 index 00000000000..c2454adf490 Binary files /dev/null and b/modular_redmoon/sounds/music/combat_vurdalak.ogg differ diff --git a/modular_redmoon/sounds/music/intro_vurdalak.ogg b/modular_redmoon/sounds/music/intro_vurdalak.ogg new file mode 100644 index 00000000000..43cbb4c6e5e Binary files /dev/null and b/modular_redmoon/sounds/music/intro_vurdalak.ogg differ diff --git a/roguetown.dme b/roguetown.dme index b5f3284f481..342e2892c6e 100644 --- a/roguetown.dme +++ b/roguetown.dme @@ -18,6 +18,13 @@ #include "code\rt.dm" #include "code\unit_tests.dm" #include "code\world.dm" +#include "code\___REDFINES\colors.dm" +#include "code\___REDFINES\DNA.dm" +#include "code\___REDFINES\mobs.dm" +#include "code\___REDFINES\prefereces.dm" +#include "code\___REDFINES\stats.dm" +#include "code\___REDFINES\stress.dm" +#include "code\___REDFINES\traits.dm" #include "code\__DEFINES\_globals.dm" #include "code\__DEFINES\_protect.dm" #include "code\__DEFINES\_tick.dm" @@ -199,13 +206,6 @@ #include "code\__HELPERS\sorts\InsertSort.dm" #include "code\__HELPERS\sorts\MergeSort.dm" #include "code\__HELPERS\sorts\TimSort.dm" -#include "code\__REDFINES\colors.dm" -#include "code\__REDFINES\DNA.dm" -#include "code\__REDFINES\mobs.dm" -#include "code\__REDFINES\prefereces.dm" -#include "code\__REDFINES\stats.dm" -#include "code\__REDFINES\stress.dm" -#include "code\__REDFINES\traits.dm" #include "code\_globalvars\bitfields.dm" #include "code\_globalvars\configuration.dm" #include "code\_globalvars\game_modes.dm" @@ -2183,6 +2183,11 @@ #include "modular_redmoon\code\modules\antagonists\purishep.dm" #include "modular_redmoon\code\modules\antagonists\_common\antag_datum.dm" #include "modular_redmoon\code\modules\antagonists\roguetown\villian\vampirelord.dm" +#include "modular_redmoon\code\modules\antagonists\roguetown\villian\vurdalak\vurdalak.dm" +#include "modular_redmoon\code\modules\antagonists\roguetown\villian\vurdalak\vurdalak_datum.dm" +#include "modular_redmoon\code\modules\antagonists\roguetown\villian\vurdalak\vurdalak_globals.dm" +#include "modular_redmoon\code\modules\antagonists\roguetown\villian\vurdalak\vurdalak_species.dm" +#include "modular_redmoon\code\modules\antagonists\roguetown\villian\vurdalak\vurdalak_spells.dm" #include "modular_redmoon\code\modules\armor\rogueclothes\armor.dm" #include "modular_redmoon\code\modules\cargo\packsrogue\_packs.dm" #include "modular_redmoon\code\modules\cargo\packsrogue\bandit\brigand.dm"