mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-29 15:08:02 +01:00
Ape Uprising (#31609)
* Allow monkey-like species to craft * Add signal for mobs entering vents * Implement basic uplifted antag datum & nest * Make phrasing nicer * Allow primitive species to vary & add time requirements * Add spawning functionality to nest * Make primitives unable to be humanised a little less violently * Fix linter complaints * Fix typo which made the linter mad * Add objectives and antag teams * Make nests consider other nests for spawn limit * Add greeting text * Add guaranteed early spawns * Allow deconstruction of nests & recovery of scrap * Allow uplifted mobs to ventcrawl with small items * Clarify nest spawning conditions on examine * Tweak NPC numbers & AI * Add obtain objective and give an extra objective * Give thanks to the linter * Change team objective & make the alert nicer * Make event only spawn monkeys by default * Add examine text explaining deconstruction * Allow ghosts to see nest materials * Handle stacks properly in nest deconstruction * Add on-demand ghost spawn to reduce roll spam * Add nest crafting * Allow lesser mobs to use medical stacks I don't believe this should affect anything else, as every other mob capable of holding these items is an advanced tool user. * Add hint about crafting on examine * Ensure only nest monkeys retaliate to nest damage * Add uplifted primitive nests to the spawners menu
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
// M: Mob to mess with
|
||||
// flags: See below, bitfield.
|
||||
/proc/domutcheck(mob/living/M, flags = 0)
|
||||
if(HAS_TRAIT(M, TRAIT_GENELESS))
|
||||
return
|
||||
|
||||
for(var/mutation_type in GLOB.dna_mutations)
|
||||
var/datum/mutation/mutation = GLOB.dna_mutations[mutation_type]
|
||||
if(!M || !M.dna)
|
||||
|
||||
@@ -116,6 +116,11 @@
|
||||
/// A list of all minds that are infected with the zombie virus, but aren't zombies yet
|
||||
var/list/datum/mind/zombie_infected = list()
|
||||
|
||||
/// An associative list between a species and all the minds that are uplifted primitive of that species.
|
||||
var/list/datum/mind/uplifted_primitives = alist()
|
||||
/// An associative list between the species of the team and the team's datum.
|
||||
var/list/datum/team/uplifted_primitive/uplifted_teams = alist()
|
||||
|
||||
/datum/game_mode/proc/announce() //to be calles when round starts
|
||||
to_chat(world, "<B>Notice</B>: [src] did not define announce()")
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/datum/event/spawn_uplifted_primitive
|
||||
name = "Uplifted Primitive Spawn"
|
||||
nominal_severity = EVENT_LEVEL_MAJOR
|
||||
|
||||
/// The list of species this event can spawn uplifted primitives of.
|
||||
var/list/allowed_species = list(
|
||||
/datum/species/monkey,
|
||||
)
|
||||
|
||||
/datum/event/spawn_uplifted_primitive/start()
|
||||
INVOKE_ASYNC(src, PROC_REF(make_uplifted_primitive))
|
||||
|
||||
/datum/event/spawn_uplifted_primitive/proc/make_uplifted_primitive()
|
||||
// disallow species which have existing players
|
||||
var/list/currently_allowed_species = allowed_species - SSticker.mode.uplifted_primitives
|
||||
if(!length(currently_allowed_species))
|
||||
message_admins("Warning: The uplifted primitives event could not be triggered because all allowed species already have an uplifted team.")
|
||||
kill()
|
||||
return
|
||||
var/datum/species/selected_species = pick(currently_allowed_species)
|
||||
|
||||
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an uplifted primitive?", ROLE_UPLIFTED_PRIMITIVE, TRUE, source = /obj/structure/uplifted_primitive/nest)
|
||||
if(!length(candidates))
|
||||
message_admins("no candidates were found for the uplifted primitive event.")
|
||||
kill()
|
||||
return
|
||||
|
||||
var/mob/C = pick(candidates)
|
||||
var/key = C.key
|
||||
|
||||
if(!key)
|
||||
kill()
|
||||
return
|
||||
|
||||
var/list/vents = get_valid_vent_spawns(exclude_mobs_nearby = TRUE)
|
||||
if(!length(vents))
|
||||
message_admins("Warning: No suitable vents detected for spawning uplifted primitives. Force picking from station vents regardless of state!")
|
||||
vents = get_valid_vent_spawns(unwelded_only = FALSE, min_network_size = 0)
|
||||
if(!length(vents))
|
||||
message_admins("Warning: No vents detected for spawning uplifted primitives at all!")
|
||||
kill()
|
||||
return
|
||||
|
||||
var/obj/selected_vent = pick(vents)
|
||||
|
||||
dust_if_respawnable(C)
|
||||
|
||||
var/datum/mind/player_mind = new /datum/mind(key)
|
||||
var/mob/living/carbon/human/primitive = new(selected_vent, selected_species)
|
||||
|
||||
player_mind.active = TRUE
|
||||
player_mind.transfer_to(primitive)
|
||||
player_mind.add_antag_datum(/datum/antagonist/uplifted_primitive)
|
||||
|
||||
SSticker.mode.uplifted_teams[selected_species].guaranteed_sentient_spawns = 2
|
||||
|
||||
primitive.add_ventcrawl(selected_vent)
|
||||
|
||||
message_admins("[key_name_admin(primitive)] has been made into an uplifted [initial(primitive.name)] by an event.")
|
||||
log_game("[key_name_admin(primitive)] was spawned as an uplifted [initial(primitive.name)] by an event.")
|
||||
@@ -0,0 +1,23 @@
|
||||
/obj/item/uplifted
|
||||
icon = 'icons/obj/uplifted_primitive.dmi'
|
||||
|
||||
/obj/item/uplifted/nest_bundle
|
||||
name = "bundle of junk"
|
||||
desc = "A loosely put together collection of scrap metal and food items."
|
||||
icon_state = "bundle"
|
||||
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
|
||||
var/scrap = 0
|
||||
var/food = 0
|
||||
|
||||
/obj/item/uplifted/nest_bundle/Initialize(mapload, new_scrap, new_food)
|
||||
. = ..()
|
||||
scrap = new_scrap
|
||||
food = new_food
|
||||
|
||||
/obj/item/uplifted/nest_bundle/examine(mob/user)
|
||||
. = ..()
|
||||
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/uplifted_primitive))
|
||||
. += SPAN_NOTICE("It contains [scrap] units of scrap.")
|
||||
. += SPAN_NOTICE("It contains [food] units of food.")
|
||||
@@ -0,0 +1,380 @@
|
||||
/// The amount of integrity to regenerate in exchange for scrap every process().
|
||||
#define INTEGRITY_REGEN_AMOUNT 5
|
||||
/// The amount of scrap to consume to regenerate every process().
|
||||
#define INTEGRITY_REGEN_SCRAP_COST 2000
|
||||
/// The amount of scrap to consume when spawning a new mobs.
|
||||
#define SPAWN_SCRAP_COST 5000
|
||||
/// The amount of food to consume when spawning a new mobs.
|
||||
#define SPAWN_FOOD_COST 20
|
||||
/// The minimum amount of time to wait between spawning new mobs.
|
||||
#define SPAWN_COOLDOWN_TIME (120 SECONDS)
|
||||
/// The number of percentage points to increase the probability of the next spawned mob being sentient by.
|
||||
#define SPAWN_SENTIENT_PROB_INCREASE 25
|
||||
/// The maximum number the spawn count can reach before disabling NPC spawning.
|
||||
#define SPAWN_MAX_LIMIT 2
|
||||
/// The number of additional mobs permitted by each extra nearby nest.
|
||||
#define SPAWN_EXTRA_NEST_MOBS 1
|
||||
|
||||
/obj/structure/uplifted_primitive
|
||||
icon = 'icons/obj/uplifted_primitive.dmi'
|
||||
anchored = TRUE
|
||||
|
||||
/obj/structure/uplifted_primitive/nest
|
||||
icon_state = "nest"
|
||||
desc = "A home for some primitive form of sentient being."
|
||||
|
||||
max_integrity = 200
|
||||
|
||||
/// The species of lesser human to spawn.
|
||||
var/datum/species/nest_species
|
||||
|
||||
/// The amount of scrap (recycled materials) currently stored in the nest.
|
||||
var/available_scrap = 0
|
||||
|
||||
/// The amount of food currently stored in the nest.
|
||||
var/available_food = 0
|
||||
|
||||
/// The cooldown between successfully spawning new mobs.
|
||||
COOLDOWN_DECLARE(spawn_cooldown)
|
||||
|
||||
/// The probability that the next spawned mob will be sentient.
|
||||
var/sentient_probability = 5
|
||||
|
||||
/// The spawner used to let ghosts spawn without a roll. If null, the nest is not in on demand mode.
|
||||
var/obj/effect/mob_spawn/uplifted_primitive/on_demand_spawner
|
||||
|
||||
/// The mapping between the path of a crafting output and a list containing
|
||||
/// its scrap and food costs, and an optional argument to be passed to new().
|
||||
var/list/crafting_items = alist(
|
||||
/obj/item/stack/sheet/wood = list(5000, 0, 5),
|
||||
/obj/item/stack/tile/grass = list(5000, 2, 10),
|
||||
/obj/item/stack/medical/bruise_pack/comfrey = list(2000, 10, 3),
|
||||
/obj/item/stack/medical/ointment/aloe = list(2000, 10, 3),
|
||||
/obj/item/grown/bananapeel = list(0, 3),
|
||||
)
|
||||
var/list/crafting_choices_cache
|
||||
var/list/crafting_mapping_cache
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/Initialize(mapload, datum/species/species = /datum/species/monkey)
|
||||
. = ..(mapload)
|
||||
AddElement(/datum/element/relay_attackers)
|
||||
RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(rally_retaliation))
|
||||
START_PROCESSING(SSobj, src)
|
||||
COOLDOWN_START(src, spawn_cooldown, SPAWN_COOLDOWN_TIME)
|
||||
nest_species = species
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/Destroy()
|
||||
QDEL_NULL(on_demand_spawner)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
UnregisterSignal(src, COMSIG_ATOM_WAS_ATTACKED)
|
||||
RemoveElement(/datum/element/relay_attackers)
|
||||
return ..()
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/deconstruct(disassembled)
|
||||
if(available_scrap > 0 || available_food > 0)
|
||||
new /obj/item/uplifted/nest_bundle(get_turf(src), available_scrap, available_food)
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/examine(mob/user)
|
||||
. = ..()
|
||||
. += SPAN_NOTICE("It appears to have been built by [lowertext(nest_species::name_plural)].")
|
||||
|
||||
var/datum/antagonist/uplifted_primitive/antag = user.mind?.has_antag_datum(/datum/antagonist/uplifted_primitive)
|
||||
if((antag && user.dna?.species.type == nest_species) || isobserver(user))
|
||||
. += SPAN_NOTICE("It contains [available_scrap] units of scrap.")
|
||||
. += SPAN_NOTICE("It contains [available_food] units of food.")
|
||||
. += SPAN_NOTICE("It needs at least [SPAWN_SCRAP_COST] scrap and [SPAWN_FOOD_COST] food to produce another primitive.")
|
||||
. += SPAN_NOTICE("A new primitive can emerge [COOLDOWN_FINISHED(src, spawn_cooldown) \
|
||||
? "soon" \
|
||||
: "in [round(COOLDOWN_TIMELEFT(src, spawn_cooldown) / (1 SECONDS))] seconds"].")
|
||||
|
||||
. += SPAN_NOTICE("<b>Control-Click</b> to open the crafting radial.")
|
||||
if(locateUID(antag?.nest_uid) == src)
|
||||
. += SPAN_NOTICE("<b>Alt-Click</b> to deconstruct the nest.")
|
||||
|
||||
if(isobserver(user))
|
||||
. += SPAN_NOTICE("<b>Click</b> to spawn as a primitive when this nest is ready.")
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/process()
|
||||
if(obj_integrity < max_integrity && available_scrap >= INTEGRITY_REGEN_SCRAP_COST)
|
||||
available_scrap -= INTEGRITY_REGEN_SCRAP_COST
|
||||
obj_integrity = min(obj_integrity + INTEGRITY_REGEN_AMOUNT, max_integrity)
|
||||
|
||||
if(!on_demand_spawner && available_scrap >= SPAWN_SCRAP_COST && available_food >= SPAWN_FOOD_COST && COOLDOWN_FINISHED(src, spawn_cooldown))
|
||||
COOLDOWN_START(src, spawn_cooldown, SPAWN_COOLDOWN_TIME)
|
||||
|
||||
var/rolled = roll_sentience()
|
||||
if(rolled && try_spawn_uplifted())
|
||||
available_scrap -= SPAWN_SCRAP_COST
|
||||
available_food -= SPAWN_FOOD_COST
|
||||
sentient_probability = initial(sentient_probability)
|
||||
|
||||
if(has_guaranteed_spawn())
|
||||
SSticker.mode.uplifted_teams[nest_species].guaranteed_sentient_spawns -= 1
|
||||
else
|
||||
if(count_spawn_limit() < SPAWN_MAX_LIMIT)
|
||||
spawn_npc()
|
||||
available_scrap -= SPAWN_SCRAP_COST
|
||||
available_food -= SPAWN_FOOD_COST
|
||||
else if(rolled)
|
||||
on_demand_spawner = new(src)
|
||||
// accumulating probability ensures a sentient spawn occurs in a finite number of iterations
|
||||
sentient_probability = min(sentient_probability + SPAWN_SENTIENT_PROB_INCREASE, 100)
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/proc/roll_sentience()
|
||||
return has_guaranteed_spawn() || prob(sentient_probability)
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/proc/has_guaranteed_spawn()
|
||||
var/datum/team/uplifted_primitive/team = SSticker.mode.uplifted_teams[nest_species]
|
||||
return team?.guaranteed_sentient_spawns > 0
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/proc/spawn_npc()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
var/mob/living/carbon/human/primitive = new(get_turf(src), nest_species)
|
||||
primitive.ai_controller = new /datum/ai_controller/monkey/uplifted_npc(primitive)
|
||||
visible_message(SPAN_NOTICE("[primitive.name] emerges out of the nest!"))
|
||||
|
||||
/// Returns TRUE if a new player mob was successfully spawned, otherwise FALSE.
|
||||
/obj/structure/uplifted_primitive/nest/proc/try_spawn_uplifted()
|
||||
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an uplifted primitive?", ROLE_UPLIFTED_PRIMITIVE, TRUE, source = /obj/structure/uplifted_primitive/nest)
|
||||
if(!length(candidates) || QDELETED(src))
|
||||
return FALSE
|
||||
|
||||
var/mob/C = pick(candidates)
|
||||
return incarnate_uplifted_mob(C)
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/proc/incarnate_uplifted_mob(mob/target)
|
||||
var/key = target.key
|
||||
if(!key)
|
||||
return FALSE
|
||||
|
||||
dust_if_respawnable(target)
|
||||
|
||||
var/datum/mind/player_mind = new /datum/mind(key)
|
||||
var/mob/living/carbon/human/primitive = new(get_turf(src), nest_species)
|
||||
|
||||
player_mind.active = TRUE
|
||||
player_mind.transfer_to(primitive)
|
||||
player_mind.add_antag_datum(/datum/antagonist/uplifted_primitive)
|
||||
|
||||
visible_message(SPAN_NOTICE("[primitive.name] emerges out of the nest with a curious look!"))
|
||||
return TRUE
|
||||
|
||||
/// Returns the number of mobs currently contributing to the spawn limit.
|
||||
/// Currently, any NPC human with the same species as the nest and a monkey ai controller within range is counted.
|
||||
/obj/structure/uplifted_primitive/nest/proc/count_spawn_limit()
|
||||
var/count = 0
|
||||
|
||||
for(var/atom/movable/A in orange(src, 7))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(istype(H))
|
||||
if(H.dna.species.type != nest_species || H.mind)
|
||||
continue
|
||||
if(!istype(H.ai_controller, /datum/ai_controller/monkey/uplifted_npc))
|
||||
continue
|
||||
count += 1
|
||||
|
||||
var/obj/structure/uplifted_primitive/nest/N = A
|
||||
if(istype(N))
|
||||
if(N.nest_species != nest_species)
|
||||
continue
|
||||
count -= SPAWN_EXTRA_NEST_MOBS
|
||||
|
||||
return count
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/attack_ghost(mob/dead/observer/user)
|
||||
if(..() || !istype(user))
|
||||
return TRUE
|
||||
|
||||
if(!SSghost_spawns.is_eligible(user, role_text = ROLE_UPLIFTED_PRIMITIVE, check_antaghud = TRUE))
|
||||
to_chat(user, SPAN_WARNING("You are not eligible to spawn as an uplifted primitive!"))
|
||||
return TRUE
|
||||
|
||||
if(on_demand_spawner \
|
||||
&& available_scrap >= SPAWN_SCRAP_COST \
|
||||
&& available_food >= SPAWN_FOOD_COST \
|
||||
&& COOLDOWN_FINISHED(src, spawn_cooldown))
|
||||
if(tgui_alert(user, "Are you sure you want to spawn as an uplifted primitive?", "Are you sure?", list("Yes", "No")) != "Yes")
|
||||
return
|
||||
incarnate_uplifted_mob(user)
|
||||
available_scrap -= SPAWN_SCRAP_COST
|
||||
available_food -= SPAWN_FOOD_COST
|
||||
sentient_probability = initial(sentient_probability)
|
||||
QDEL_NULL(on_demand_spawner)
|
||||
COOLDOWN_START(src, spawn_cooldown, SPAWN_COOLDOWN_TIME)
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("This nest is not ready to spawn another primitive!"))
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/AltClick(mob/user, modifiers)
|
||||
var/datum/antagonist/uplifted_primitive/antag = user.mind.has_antag_datum(/datum/antagonist/uplifted_primitive)
|
||||
|
||||
if(!antag || locateUID(antag.nest_uid) != src)
|
||||
return ..()
|
||||
|
||||
to_chat(user, SPAN_NOTICE("You start disassembling the nest."))
|
||||
if(!do_after(user, 7 SECONDS, target = src))
|
||||
return
|
||||
|
||||
to_chat(user, SPAN_NOTICE("You finish disassembling the nest and collect the remaining scrap and food."))
|
||||
deconstruct(TRUE)
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/CtrlClick(mob/user, modifiers)
|
||||
if(!user.mind?.has_antag_datum(/datum/antagonist/uplifted_primitive))
|
||||
return ..()
|
||||
|
||||
if(user.dna?.species.type != nest_species)
|
||||
return ..()
|
||||
|
||||
show_crafting_radial(user)
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/proc/show_crafting_radial(mob/user)
|
||||
if(!crafting_choices_cache || !crafting_mapping_cache)
|
||||
build_crafting_caches()
|
||||
|
||||
var/raw_choice = show_radial_menu(user, src, crafting_choices_cache, radius = 48, require_near = TRUE)
|
||||
if(!raw_choice)
|
||||
return
|
||||
var/path = crafting_mapping_cache[raw_choice]
|
||||
var/list/data = crafting_items[path]
|
||||
var/scrap_cost = data[1]
|
||||
var/food_cost = data[2]
|
||||
|
||||
if(available_scrap < scrap_cost || available_food < food_cost)
|
||||
to_chat(user, SPAN_WARNING("The nest does not have enough resources to make this!"))
|
||||
return
|
||||
|
||||
if(!do_after(user, 5 SECONDS, target = src))
|
||||
return
|
||||
|
||||
available_scrap -= scrap_cost
|
||||
available_food -= food_cost
|
||||
|
||||
if(data.len >= 3)
|
||||
new path(get_turf(src), data[3])
|
||||
else
|
||||
new path(get_turf(src))
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/proc/build_crafting_caches()
|
||||
var/choices = list()
|
||||
var/mapping = list()
|
||||
for(var/path in crafting_items)
|
||||
var/list/data = crafting_items[path]
|
||||
var/list/costs = list()
|
||||
|
||||
if(data[1] > 0)
|
||||
costs += "[data[1]] scrap"
|
||||
if(data[2] > 0)
|
||||
costs += "[data[2]] food"
|
||||
|
||||
var/key = "[path:name] ([costs.Join(", ")])"
|
||||
choices[key] = image(icon = path:icon, icon_state = path:icon_state)
|
||||
mapping[key] = path
|
||||
crafting_choices_cache = choices
|
||||
crafting_mapping_cache = mapping
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/item_interaction(mob/living/user, obj/item/used, list/modifiers)
|
||||
if(!user.mind || !user.mind.has_antag_datum(/datum/antagonist/uplifted_primitive))
|
||||
return NONE
|
||||
|
||||
// mobs of other uplifted species should be able to interact normally
|
||||
if(user.dna.species.type != nest_species)
|
||||
return NONE
|
||||
|
||||
if(user.a_intent != INTENT_HELP)
|
||||
return NONE
|
||||
|
||||
if(used.resistance_flags & INDESTRUCTIBLE)
|
||||
to_chat(user, SPAN_WARNING("You don't think it's a good idea to put [used] in the nest."))
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
|
||||
var/potential_scrap = 0
|
||||
var/potential_food = 0
|
||||
if(used.materials && length(used.materials) > 0)
|
||||
for(var/mat in used.materials)
|
||||
potential_scrap += used.materials[mat]
|
||||
|
||||
if(used.reagents)
|
||||
for(var/datum/reagent/consumable/C in used.reagents.reagent_list)
|
||||
if(nest_species.dietflags & C.diet_flags)
|
||||
potential_food += C.nutriment_factor
|
||||
|
||||
var/obj/item/uplifted/nest_bundle/scrap_ball = used
|
||||
if(istype(scrap_ball))
|
||||
potential_scrap += scrap_ball.scrap
|
||||
potential_food += scrap_ball.food
|
||||
|
||||
var/obj/item/stack/pile = used
|
||||
var/multiplier = istype(pile) ? pile.amount : 1
|
||||
|
||||
if(potential_scrap == 0 && potential_food == 0)
|
||||
to_chat(user, SPAN_WARNING("[used] does not seem suitable to disassemble or place in the nest."))
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
|
||||
user.visible_message(
|
||||
SPAN_WARNING("[user] starts finding a place to put [used] in the nest."),
|
||||
SPAN_NOTICE("You start finding a place to put [used] in the nest.")
|
||||
)
|
||||
|
||||
if(!do_after(user, multiplier SECONDS, target = src))
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
|
||||
if(potential_scrap > 0)
|
||||
to_chat(user, SPAN_NOTICE("You dismantle [used] and place the scrap around the nest."))
|
||||
available_scrap += potential_scrap * multiplier
|
||||
|
||||
if(potential_food > 0)
|
||||
to_chat(user, SPAN_NOTICE("You place the edible parts of [used] in the nest."))
|
||||
available_food += potential_food * multiplier
|
||||
|
||||
qdel(used)
|
||||
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/play_attack_sound(damage_amount, damage_type, damage_flag)
|
||||
if(!damage_amount)
|
||||
return
|
||||
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 80, TRUE)
|
||||
if(BURN)
|
||||
playsound(loc, 'sound/items/welder.ogg', 80, TRUE)
|
||||
|
||||
/obj/structure/uplifted_primitive/nest/proc/rally_retaliation(atom/source, atom/attacker, flags)
|
||||
SIGNAL_HANDLER
|
||||
if(!istype(attacker, /mob/living) || !(flags & ATTACKER_DAMAGING_ATTACK))
|
||||
return
|
||||
for(var/mob/living/carbon/human/defender in oview(src, 7))
|
||||
if(defender.mind || defender.dna.species.type != nest_species)
|
||||
continue
|
||||
if(defender == attacker) // Do not commit suicide attacking yourself
|
||||
continue
|
||||
|
||||
var/datum/ai_controller/monkey/uplifted_npc/M = defender.ai_controller
|
||||
if(istype(M))
|
||||
M.retaliate(attacker)
|
||||
|
||||
/obj/effect/mob_spawn/uplifted_primitive
|
||||
name = "Uplifted Primitive"
|
||||
description = "You are a primitive being who has awoken with sentience. Survive on station with your fellow primitives and complete objectives."
|
||||
roundstart = FALSE
|
||||
|
||||
/obj/effect/mob_spawn/uplifted_primitive/Initialize(mapload)
|
||||
var/obj/structure/uplifted_primitive/nest/N = loc
|
||||
if(istype(N))
|
||||
name = "Uplifted [N.nest_species::name]"
|
||||
return ..()
|
||||
|
||||
/obj/effect/mob_spawn/uplifted_primitive/attack_ghost(mob/user)
|
||||
return loc.attack_ghost(user)
|
||||
|
||||
#undef INTEGRITY_REGEN_AMOUNT
|
||||
#undef INTEGRITY_REGEN_SCRAP_COST
|
||||
#undef SPAWN_SCRAP_COST
|
||||
#undef SPAWN_FOOD_COST
|
||||
#undef SPAWN_COOLDOWN_TIME
|
||||
#undef SPAWN_SENTIENT_PROB_INCREASE
|
||||
#undef SPAWN_MAX_LIMIT
|
||||
#undef SPAWN_EXTRA_NEST_MOBS
|
||||
@@ -0,0 +1,97 @@
|
||||
/datum/objective/uplifted
|
||||
needs_target = FALSE
|
||||
|
||||
/datum/objective/uplifted/propagate
|
||||
explanation_text = "Propagate your kind across the station by filling your nest with food and scrap."
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/uplifted/collect_items
|
||||
explanation_text = "Decorate your nest with whatever interesting items you find around the station."
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/uplifted/collect_animals
|
||||
explanation_text = "Find creatures who do not have the gift of sentience and keep them safe near your nest."
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/uplifted/barter
|
||||
explanation_text = "Trade whatever you can find or steal with anyone willing to give you appropriate payment."
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/uplifted/fortify
|
||||
explanation_text = "Fortify your nest against potential attackers and arm your fellow primitives."
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/uplifted/expand
|
||||
explanation_text = "Expand your territory into nearby rooms and areas to fit your species' growing needs."
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/uplifted/build_nest_in_area
|
||||
/// The area in which the nest must be built to complete the objective.
|
||||
var/area/target_area
|
||||
|
||||
/datum/objective/uplifted/build_nest_in_area/New()
|
||||
..()
|
||||
target_area = select_target_location()
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/uplifted/build_nest_in_area/update_explanation_text()
|
||||
explanation_text = "Build and defend a collective nest in [target_area.name]."
|
||||
|
||||
/datum/objective/uplifted/build_nest_in_area/proc/select_target_location()
|
||||
var/list/potential_areas = list()
|
||||
for(var/area/station/A in SSmapping.existing_station_areas)
|
||||
// using the cult summon validity as a heuristic here should be fine,
|
||||
// since the goal here is similar
|
||||
if(!A.valid_territory)
|
||||
continue
|
||||
|
||||
potential_areas += A
|
||||
return pick(potential_areas)
|
||||
|
||||
/datum/objective/uplifted/build_nest_in_area/check_completion()
|
||||
if(completed)
|
||||
return TRUE
|
||||
for(var/datum/mind/M in get_owners())
|
||||
var/datum/antagonist/uplifted_primitive/U = M.has_antag_datum(/datum/antagonist/uplifted_primitive)
|
||||
var/obj/nest = locateUID(U.nest_uid)
|
||||
if(!QDELETED(nest) && get_area(nest) == target_area)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/objective/uplifted/obtain
|
||||
/// The typepath of the item which must be possessed to complete the objective.
|
||||
var/obj/item/target_type
|
||||
|
||||
/// The possible typepaths that can be selected as a target.
|
||||
var/list/allowed_targets = list(
|
||||
/obj/item/clothing/head/helmet,
|
||||
/obj/item/melee/baton/cattleprod,
|
||||
/obj/item/fireaxe,
|
||||
/obj/item/gun/energy/laser/practice,
|
||||
/obj/item/clothing/gloves/color/yellow,
|
||||
/obj/item/camera,
|
||||
/obj/item/aicard,
|
||||
/obj/item/pda,
|
||||
/obj/item/megaphone,
|
||||
)
|
||||
|
||||
/datum/objective/uplifted/obtain/New()
|
||||
..()
|
||||
target_type = pick(allowed_targets)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/uplifted/obtain/update_explanation_text()
|
||||
explanation_text = "Obtain [target_type::name] through barter, theft, craftiness, or any other means."
|
||||
|
||||
/datum/objective/uplifted/obtain/check_completion()
|
||||
if(completed)
|
||||
return TRUE
|
||||
for(var/datum/mind/M in get_owners())
|
||||
if(locate(target_type) in M.current.contents)
|
||||
return TRUE
|
||||
|
||||
var/datum/antagonist/uplifted_primitive/U = M.has_antag_datum(/datum/antagonist/uplifted_primitive)
|
||||
var/obj/nest = locateUID(U.nest_uid)
|
||||
if(!QDELETED(nest) && locate(target_type) in get_turf(nest))
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -0,0 +1,47 @@
|
||||
/datum/spell/uplifted_make_nest
|
||||
name = "Make Nest"
|
||||
desc = "Build a nest at your current location."
|
||||
|
||||
action_icon = 'icons/obj/uplifted_primitive.dmi'
|
||||
action_icon_state = "nest"
|
||||
action_background_icon_state = "bg_default"
|
||||
|
||||
base_cooldown = 15 SECONDS
|
||||
clothes_req = FALSE
|
||||
antimagic_flags = NONE
|
||||
|
||||
/datum/spell/uplifted_make_nest/create_new_targeting()
|
||||
return new /datum/spell_targeting/self
|
||||
|
||||
/datum/spell/uplifted_make_nest/cast(list/targets, mob/user)
|
||||
var/datum/antagonist/uplifted_primitive/U = user.mind.has_antag_datum(/datum/antagonist/uplifted_primitive)
|
||||
|
||||
user.visible_message(SPAN_NOTICE("[user] starts building a nest!"), SPAN_NOTICE("You start building a nest!"))
|
||||
|
||||
if(!do_after(user, 10 SECONDS, target = user))
|
||||
return
|
||||
|
||||
var/obj/structure/uplifted_primitive/nest/nest = new(get_turf(user), U.initial_species)
|
||||
U.nest_uid = nest.UID()
|
||||
|
||||
/datum/spell/uplifted_make_nest/can_cast(mob/user, charge_check, show_message)
|
||||
if(!..())
|
||||
return FALSE
|
||||
|
||||
var/datum/antagonist/uplifted_primitive/U = user.mind.has_antag_datum(/datum/antagonist/uplifted_primitive)
|
||||
|
||||
if(!U)
|
||||
return FALSE
|
||||
|
||||
var/obj/structure/uplifted_primitive/nest/nest = locateUID(U.nest_uid)
|
||||
if(!QDELETED(nest))
|
||||
if(show_message)
|
||||
to_chat(user, SPAN_WARNING("You already have a nest!"))
|
||||
return FALSE
|
||||
|
||||
if(!isturf(user.loc))
|
||||
if(show_message)
|
||||
to_chat(user, SPAN_WARNING("You cannot make a nest here!"))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
@@ -30,6 +30,7 @@ GLOBAL_LIST_INIT(role_playtime_requirements, list(
|
||||
ROLE_CULTIST = 20,
|
||||
ROLE_ALIEN = 10,
|
||||
ROLE_ABDUCTOR = 20,
|
||||
ROLE_UPLIFTED_PRIMITIVE = 10,
|
||||
))
|
||||
|
||||
// Admin Verbs
|
||||
|
||||
@@ -26,10 +26,6 @@
|
||||
to_chat(user, SPAN_DANGER("[src] cannot be applied to [M]!"))
|
||||
return FALSE
|
||||
|
||||
if(!user.IsAdvancedToolUser())
|
||||
to_chat(user, SPAN_DANGER("You don't have the dexterity to do this!"))
|
||||
return FALSE
|
||||
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected)
|
||||
|
||||
Reference in New Issue
Block a user