mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-24 20:47:43 +01:00
Merge remote-tracking branch 'upstream/dev' into sanitize_procs
Conflicts: code/__HELPERS/text.dm
This commit is contained in:
+86
-49
@@ -31,6 +31,7 @@
|
||||
#include "code\__HELPERS\turfs.dm"
|
||||
#include "code\__HELPERS\type2type.dm"
|
||||
#include "code\__HELPERS\unsorted.dm"
|
||||
#include "code\__HELPERS\vector.dm"
|
||||
#include "code\_onclick\adjacent.dm"
|
||||
#include "code\_onclick\ai.dm"
|
||||
#include "code\_onclick\click.dm"
|
||||
@@ -96,7 +97,32 @@
|
||||
#include "code\controllers\subsystems.dm"
|
||||
#include "code\controllers\verbs.dm"
|
||||
#include "code\controllers\voting.dm"
|
||||
#include "code\controllers\Processes\air.dm"
|
||||
#include "code\controllers\Processes\alarm.dm"
|
||||
#include "code\controllers\Processes\disease.dm"
|
||||
#include "code\controllers\Processes\emergencyShuttle.dm"
|
||||
#include "code\controllers\Processes\event.dm"
|
||||
#include "code\controllers\Processes\inactivity.dm"
|
||||
#include "code\controllers\Processes\lighting.dm"
|
||||
#include "code\controllers\Processes\machinery.dm"
|
||||
#include "code\controllers\Processes\mob.dm"
|
||||
#include "code\controllers\Processes\nanoui.dm"
|
||||
#include "code\controllers\Processes\obj.dm"
|
||||
#include "code\controllers\Processes\pipenet.dm"
|
||||
#include "code\controllers\Processes\powernet.dm"
|
||||
#include "code\controllers\Processes\Shuttle.dm"
|
||||
#include "code\controllers\Processes\sun.dm"
|
||||
#include "code\controllers\Processes\supply.dm"
|
||||
#include "code\controllers\Processes\ticker.dm"
|
||||
#include "code\controllers\Processes\vote.dm"
|
||||
#include "code\controllers\ProcessScheduler\core\_define.dm"
|
||||
#include "code\controllers\ProcessScheduler\core\_stubs.dm"
|
||||
#include "code\controllers\ProcessScheduler\core\process.dm"
|
||||
#include "code\controllers\ProcessScheduler\core\processScheduler.dm"
|
||||
#include "code\controllers\ProcessScheduler\core\updateQueue.dm"
|
||||
#include "code\controllers\ProcessScheduler\core\updateQueueWorker.dm"
|
||||
#include "code\controllers\subsystem\alarms.dm"
|
||||
#include "code\datums\ai_law_sets.dm"
|
||||
#include "code\datums\ai_laws.dm"
|
||||
#include "code\datums\browser.dm"
|
||||
#include "code\datums\computerfiles.dm"
|
||||
@@ -108,7 +134,6 @@
|
||||
#include "code\datums\modules.dm"
|
||||
#include "code\datums\organs.dm"
|
||||
#include "code\datums\recipe.dm"
|
||||
#include "code\datums\spell.dm"
|
||||
#include "code\datums\sun.dm"
|
||||
#include "code\datums\supplypacks.dm"
|
||||
#include "code\datums\diseases\appendicitis.dm"
|
||||
@@ -152,21 +177,6 @@
|
||||
#include "code\datums\helper_datums\global_iterator.dm"
|
||||
#include "code\datums\helper_datums\teleport.dm"
|
||||
#include "code\datums\helper_datums\topic_input.dm"
|
||||
#include "code\datums\spells\area_teleport.dm"
|
||||
#include "code\datums\spells\conjure.dm"
|
||||
#include "code\datums\spells\dumbfire.dm"
|
||||
#include "code\datums\spells\emplosion.dm"
|
||||
#include "code\datums\spells\ethereal_jaunt.dm"
|
||||
#include "code\datums\spells\explosion.dm"
|
||||
#include "code\datums\spells\genetic.dm"
|
||||
#include "code\datums\spells\horsemask.dm"
|
||||
#include "code\datums\spells\inflict_handler.dm"
|
||||
#include "code\datums\spells\knock.dm"
|
||||
#include "code\datums\spells\mind_transfer.dm"
|
||||
#include "code\datums\spells\projectile.dm"
|
||||
#include "code\datums\spells\trigger.dm"
|
||||
#include "code\datums\spells\turf_teleport.dm"
|
||||
#include "code\datums\spells\wizard.dm"
|
||||
#include "code\datums\wires\airlock.dm"
|
||||
#include "code\datums\wires\alarm.dm"
|
||||
#include "code\datums\wires\apc.dm"
|
||||
@@ -203,6 +213,28 @@
|
||||
#include "code\game\smoothwall.dm"
|
||||
#include "code\game\sound.dm"
|
||||
#include "code\game\supplyshuttle.dm"
|
||||
#include "code\game\antagonist\antagonist.dm"
|
||||
#include "code\game\antagonist\antagonist_build.dm"
|
||||
#include "code\game\antagonist\antagonist_globals.dm"
|
||||
#include "code\game\antagonist\antagonist_helpers.dm"
|
||||
#include "code\game\antagonist\antagonist_objectives.dm"
|
||||
#include "code\game\antagonist\antagonist_spawn.dm"
|
||||
#include "code\game\antagonist\alien\borer.dm"
|
||||
#include "code\game\antagonist\alien\xenomorph.dm"
|
||||
#include "code\game\antagonist\outsider\commando.dm"
|
||||
#include "code\game\antagonist\outsider\deathsquad.dm"
|
||||
#include "code\game\antagonist\outsider\ert.dm"
|
||||
#include "code\game\antagonist\outsider\mercenary.dm"
|
||||
#include "code\game\antagonist\outsider\ninja.dm"
|
||||
#include "code\game\antagonist\outsider\raider.dm"
|
||||
#include "code\game\antagonist\outsider\wizard.dm"
|
||||
#include "code\game\antagonist\station\changeling.dm"
|
||||
#include "code\game\antagonist\station\cultist.dm"
|
||||
#include "code\game\antagonist\station\highlander.dm"
|
||||
#include "code\game\antagonist\station\renegade.dm"
|
||||
#include "code\game\antagonist\station\revolutionary.dm"
|
||||
#include "code\game\antagonist\station\rogue_ai.dm"
|
||||
#include "code\game\antagonist\station\traitor.dm"
|
||||
#include "code\game\area\ai_monitored.dm"
|
||||
#include "code\game\area\areas.dm"
|
||||
#include "code\game\area\asteroid_areas.dm"
|
||||
@@ -216,13 +248,11 @@
|
||||
#include "code\game\dna\genes\monkey.dm"
|
||||
#include "code\game\dna\genes\powers.dm"
|
||||
#include "code\game\gamemodes\events.dm"
|
||||
#include "code\game\gamemodes\factions.dm"
|
||||
#include "code\game\gamemodes\game_mode.dm"
|
||||
#include "code\game\gamemodes\gameticker.dm"
|
||||
#include "code\game\gamemodes\intercept_report.dm"
|
||||
#include "code\game\gamemodes\objective.dm"
|
||||
#include "code\game\gamemodes\setupgame.dm"
|
||||
#include "code\game\gamemodes\autotraitor\autotraitor.dm"
|
||||
#include "code\game\gamemodes\blob\blob.dm"
|
||||
#include "code\game\gamemodes\blob\theblob.dm"
|
||||
#include "code\game\gamemodes\blob\blobs\core.dm"
|
||||
@@ -233,7 +263,6 @@
|
||||
#include "code\game\gamemodes\changeling\changeling.dm"
|
||||
#include "code\game\gamemodes\changeling\changeling_powers.dm"
|
||||
#include "code\game\gamemodes\changeling\modularchangling.dm"
|
||||
#include "code\game\gamemodes\changeling\traitor_chan.dm"
|
||||
#include "code\game\gamemodes\cult\cult.dm"
|
||||
#include "code\game\gamemodes\cult\cult_items.dm"
|
||||
#include "code\game\gamemodes\cult\cult_structures.dm"
|
||||
@@ -245,7 +274,6 @@
|
||||
#include "code\game\gamemodes\events\dust.dm"
|
||||
#include "code\game\gamemodes\events\miniblob.dm"
|
||||
#include "code\game\gamemodes\events\power_failure.dm"
|
||||
#include "code\game\gamemodes\events\space_ninja.dm"
|
||||
#include "code\game\gamemodes\events\wormholes.dm"
|
||||
#include "code\game\gamemodes\events\holidays\Christmas.dm"
|
||||
#include "code\game\gamemodes\events\holidays\Holidays.dm"
|
||||
@@ -256,32 +284,11 @@
|
||||
#include "code\game\gamemodes\malfunction\malfunction.dm"
|
||||
#include "code\game\gamemodes\meteor\meteor.dm"
|
||||
#include "code\game\gamemodes\meteor\meteors.dm"
|
||||
#include "code\game\gamemodes\mutiny\auth_key.dm"
|
||||
#include "code\game\gamemodes\mutiny\directive.dm"
|
||||
#include "code\game\gamemodes\mutiny\emergency_authentication_device.dm"
|
||||
#include "code\game\gamemodes\mutiny\key_pinpointer.dm"
|
||||
#include "code\game\gamemodes\mutiny\mutiny.dm"
|
||||
#include "code\game\gamemodes\mutiny\mutiny_admin.dm"
|
||||
#include "code\game\gamemodes\mutiny\mutiny_fluff.dm"
|
||||
#include "code\game\gamemodes\mutiny\mutiny_hooks.dm"
|
||||
#include "code\game\gamemodes\mutiny\directives\alien_fraud_directive.dm"
|
||||
#include "code\game\gamemodes\mutiny\directives\bluespace_contagion_directive.dm"
|
||||
#include "code\game\gamemodes\mutiny\directives\financial_crisis_directive.dm"
|
||||
#include "code\game\gamemodes\mutiny\directives\ipc_virus_directive.dm"
|
||||
#include "code\game\gamemodes\mutiny\directives\research_to_ripleys_directive.dm"
|
||||
#include "code\game\gamemodes\mutiny\directives\tau_ceti_needs_women_directive.dm"
|
||||
#include "code\game\gamemodes\mutiny\directives\terminations_directive.dm"
|
||||
#include "code\game\gamemodes\ninja\ninja.dm"
|
||||
#include "code\game\gamemodes\nuclear\nuclear.dm"
|
||||
#include "code\game\gamemodes\nuclear\nuclearbomb.dm"
|
||||
#include "code\game\gamemodes\nuclear\pinpointer.dm"
|
||||
#include "code\game\gamemodes\revolution\revolution.dm"
|
||||
#include "code\game\gamemodes\revolution\rp_revolution.dm"
|
||||
#include "code\game\gamemodes\traitor\traitor.dm"
|
||||
#include "code\game\gamemodes\wizard\artifact.dm"
|
||||
#include "code\game\gamemodes\wizard\rightandwrong.dm"
|
||||
#include "code\game\gamemodes\wizard\soulstone.dm"
|
||||
#include "code\game\gamemodes\wizard\spellbook.dm"
|
||||
#include "code\game\gamemodes\wizard\wizard.dm"
|
||||
#include "code\game\jobs\access.dm"
|
||||
#include "code\game\jobs\job_controller.dm"
|
||||
@@ -330,6 +337,7 @@
|
||||
#include "code\game\machinery\mass_driver.dm"
|
||||
#include "code\game\machinery\navbeacon.dm"
|
||||
#include "code\game\machinery\newscaster.dm"
|
||||
#include "code\game\machinery\nuclear_bomb.dm"
|
||||
#include "code\game\machinery\OpTable.dm"
|
||||
#include "code\game\machinery\overview.dm"
|
||||
#include "code\game\machinery\portable_tag_turret.dm"
|
||||
@@ -782,6 +790,7 @@
|
||||
#include "code\modules\admin\verbs\adminjump.dm"
|
||||
#include "code\modules\admin\verbs\adminpm.dm"
|
||||
#include "code\modules\admin\verbs\adminsay.dm"
|
||||
#include "code\modules\admin\verbs\antag-ooc.dm"
|
||||
#include "code\modules\admin\verbs\atmosdebug.dm"
|
||||
#include "code\modules\admin\verbs\BrokenInhands.dm"
|
||||
#include "code\modules\admin\verbs\buildmode.dm"
|
||||
@@ -796,8 +805,6 @@
|
||||
#include "code\modules\admin\verbs\mapping.dm"
|
||||
#include "code\modules\admin\verbs\massmodvar.dm"
|
||||
#include "code\modules\admin\verbs\modifyvariables.dm"
|
||||
#include "code\modules\admin\verbs\one_click_antag.dm"
|
||||
#include "code\modules\admin\verbs\onlyone.dm"
|
||||
#include "code\modules\admin\verbs\playsound.dm"
|
||||
#include "code\modules\admin\verbs\possess.dm"
|
||||
#include "code\modules\admin\verbs\pray.dm"
|
||||
@@ -806,10 +813,8 @@
|
||||
#include "code\modules\admin\verbs\SDQL_2.dm"
|
||||
#include "code\modules\admin\verbs\SDQL_2_parser.dm"
|
||||
#include "code\modules\admin\verbs\striketeam.dm"
|
||||
#include "code\modules\admin\verbs\striketeam_syndicate.dm"
|
||||
#include "code\modules\admin\verbs\ticklag.dm"
|
||||
#include "code\modules\admin\verbs\tripAI.dm"
|
||||
#include "code\modules\admin\verbs\vox_raiders.dm"
|
||||
#include "code\modules\alarm\alarm.dm"
|
||||
#include "code\modules\alarm\alarm_handler.dm"
|
||||
#include "code\modules\alarm\atmosphere_alarm.dm"
|
||||
@@ -932,9 +937,7 @@
|
||||
#include "code\modules\economy\Events.dm"
|
||||
#include "code\modules\economy\Events_Mundane.dm"
|
||||
#include "code\modules\economy\TradeDestinations.dm"
|
||||
#include "code\modules\events\alien_infestation.dm"
|
||||
#include "code\modules\events\blob.dm"
|
||||
#include "code\modules\events\borers.dm"
|
||||
#include "code\modules\events\brand_intelligence.dm"
|
||||
#include "code\modules\events\carp_migration.dm"
|
||||
#include "code\modules\events\comms_blackout.dm"
|
||||
@@ -954,6 +957,7 @@
|
||||
#include "code\modules\events\money_spam.dm"
|
||||
#include "code\modules\events\prison_break.dm"
|
||||
#include "code\modules\events\radiation_storm.dm"
|
||||
#include "code\modules\events\random_antagonist.dm"
|
||||
#include "code\modules\events\rogue_drones.dm"
|
||||
#include "code\modules\events\space_ninja.dm"
|
||||
#include "code\modules\events\spacevine.dm"
|
||||
@@ -964,10 +968,12 @@
|
||||
#include "code\modules\examine\examine.dm"
|
||||
#include "code\modules\examine\descriptions\atmospherics.dm"
|
||||
#include "code\modules\examine\descriptions\engineering.dm"
|
||||
#include "code\modules\examine\descriptions\medical.dm"
|
||||
#include "code\modules\examine\descriptions\mobs.dm"
|
||||
#include "code\modules\examine\descriptions\stacks.dm"
|
||||
#include "code\modules\examine\descriptions\structures.dm"
|
||||
#include "code\modules\examine\descriptions\turfs.dm"
|
||||
#include "code\modules\examine\descriptions\weapons.dm"
|
||||
#include "code\modules\ext_scripts\irc.dm"
|
||||
#include "code\modules\ext_scripts\python.dm"
|
||||
#include "code\modules\flufftext\Dreaming.dm"
|
||||
@@ -1060,6 +1066,7 @@
|
||||
#include "code\modules\mob\living\logout.dm"
|
||||
#include "code\modules\mob\living\say.dm"
|
||||
#include "code\modules\mob\living\blob\blob.dm"
|
||||
#include "code\modules\mob\living\carbon\breathe.dm"
|
||||
#include "code\modules\mob\living\carbon\carbon.dm"
|
||||
#include "code\modules\mob\living\carbon\carbon_defines.dm"
|
||||
#include "code\modules\mob\living\carbon\carbon_powers.dm"
|
||||
@@ -1095,6 +1102,7 @@
|
||||
#include "code\modules\mob\living\carbon\brain\posibrain.dm"
|
||||
#include "code\modules\mob\living\carbon\brain\robot.dm"
|
||||
#include "code\modules\mob\living\carbon\brain\say.dm"
|
||||
#include "code\modules\mob\living\carbon\human\appearance.dm"
|
||||
#include "code\modules\mob\living\carbon\human\death.dm"
|
||||
#include "code\modules\mob\living\carbon\human\emote.dm"
|
||||
#include "code\modules\mob\living\carbon\human\examine.dm"
|
||||
@@ -1153,6 +1161,7 @@
|
||||
#include "code\modules\mob\living\silicon\login.dm"
|
||||
#include "code\modules\mob\living\silicon\say.dm"
|
||||
#include "code\modules\mob\living\silicon\silicon.dm"
|
||||
#include "code\modules\mob\living\silicon\subystems.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\ai.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\death.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\examine.dm"
|
||||
@@ -1195,7 +1204,6 @@
|
||||
#include "code\modules\mob\living\silicon\robot\robot_items.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\robot_modules.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\robot_movement.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\subsystems.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_abilities.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_console.dm"
|
||||
@@ -1203,7 +1211,6 @@
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_items.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm"
|
||||
#include "code\modules\mob\living\simple_animal\bees.dm"
|
||||
#include "code\modules\mob\living\simple_animal\constructs.dm"
|
||||
#include "code\modules\mob\living\simple_animal\corpse.dm"
|
||||
#include "code\modules\mob\living\simple_animal\parrot.dm"
|
||||
#include "code\modules\mob\living\simple_animal\shade.dm"
|
||||
@@ -1213,6 +1220,8 @@
|
||||
#include "code\modules\mob\living\simple_animal\borer\borer_captive.dm"
|
||||
#include "code\modules\mob\living\simple_animal\borer\borer_powers.dm"
|
||||
#include "code\modules\mob\living\simple_animal\borer\say.dm"
|
||||
#include "code\modules\mob\living\simple_animal\constructs\constructs.dm"
|
||||
#include "code\modules\mob\living\simple_animal\constructs\soulstone.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\cat.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\corgi.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\crab.dm"
|
||||
@@ -1256,6 +1265,9 @@
|
||||
#include "code\modules\nano\nanoui.dm"
|
||||
#include "code\modules\nano\modules\alarm_monitor.dm"
|
||||
#include "code\modules\nano\modules\crew_monitor.dm"
|
||||
#include "code\modules\nano\modules\human_appearance.dm"
|
||||
#include "code\modules\nano\modules\law_manager.dm"
|
||||
#include "code\modules\nano\modules\nano_module.dm"
|
||||
#include "code\modules\nano\modules\power_monitor.dm"
|
||||
#include "code\modules\nano\modules\rcon.dm"
|
||||
#include "code\modules\organs\blood.dm"
|
||||
@@ -1345,6 +1357,7 @@
|
||||
#include "code\modules\power\singularity\particle_accelerator\particle_emitter.dm"
|
||||
#include "code\modules\power\singularity\particle_accelerator\particle_power.dm"
|
||||
#include "code\modules\projectiles\ammunition.dm"
|
||||
#include "code\modules\projectiles\effects.dm"
|
||||
#include "code\modules\projectiles\gun.dm"
|
||||
#include "code\modules\projectiles\projectile.dm"
|
||||
#include "code\modules\projectiles\targeting.dm"
|
||||
@@ -1389,6 +1402,12 @@
|
||||
#include "code\modules\reagents\Chemistry-Recipes.dm"
|
||||
#include "code\modules\reagents\reagent_containers.dm"
|
||||
#include "code\modules\reagents\reagent_dispenser.dm"
|
||||
#include "code\modules\reagents\dispenser\_defines.dm"
|
||||
#include "code\modules\reagents\dispenser\cartridge.dm"
|
||||
#include "code\modules\reagents\dispenser\cartridge_presets.dm"
|
||||
#include "code\modules\reagents\dispenser\dispenser2.dm"
|
||||
#include "code\modules\reagents\dispenser\dispenser_presets.dm"
|
||||
#include "code\modules\reagents\dispenser\supply.dm"
|
||||
#include "code\modules\reagents\reagent_containers\blood_pack.dm"
|
||||
#include "code\modules\reagents\reagent_containers\borghydro.dm"
|
||||
#include "code\modules\reagents\reagent_containers\dropper.dm"
|
||||
@@ -1522,6 +1541,24 @@
|
||||
#include "code\modules\shuttles\shuttle_specops.dm"
|
||||
#include "code\modules\shuttles\shuttle_supply.dm"
|
||||
#include "code\modules\shuttles\shuttles_multi.dm"
|
||||
#include "code\modules\spells\area_teleport.dm"
|
||||
#include "code\modules\spells\conjure.dm"
|
||||
#include "code\modules\spells\dumbfire.dm"
|
||||
#include "code\modules\spells\emplosion.dm"
|
||||
#include "code\modules\spells\ethereal_jaunt.dm"
|
||||
#include "code\modules\spells\explosion.dm"
|
||||
#include "code\modules\spells\genetic.dm"
|
||||
#include "code\modules\spells\horsemask.dm"
|
||||
#include "code\modules\spells\inflict_handler.dm"
|
||||
#include "code\modules\spells\knock.dm"
|
||||
#include "code\modules\spells\mind_transfer.dm"
|
||||
#include "code\modules\spells\projectile.dm"
|
||||
#include "code\modules\spells\spell.dm"
|
||||
#include "code\modules\spells\spellbook.dm"
|
||||
#include "code\modules\spells\trigger.dm"
|
||||
#include "code\modules\spells\turf_teleport.dm"
|
||||
#include "code\modules\spells\wizard_artifacts.dm"
|
||||
#include "code\modules\spells\wizard_spells.dm"
|
||||
#include "code\modules\supermatter\supermatter.dm"
|
||||
#include "code\modules\surgery\bones.dm"
|
||||
#include "code\modules\surgery\brainrepair.dm"
|
||||
|
||||
+8
-3
@@ -239,13 +239,18 @@ turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
|
||||
//world << "gas_reaction_progress = [gas_reaction_progress]"
|
||||
|
||||
var/total_reaction_progress = gas_reaction_progress + liquid_reaction_progress
|
||||
if(total_reaction_progress <= 0)
|
||||
return 0
|
||||
|
||||
var/used_fuel = min(total_reaction_progress, reaction_limit)
|
||||
var/used_oxidizers = used_fuel*(FIRE_REACTION_OXIDIZER_AMOUNT/FIRE_REACTION_FUEL_AMOUNT)
|
||||
//world << "used_fuel = [used_fuel]; used_oxidizers = [used_oxidizers]; reaction_limit=[reaction_limit]"
|
||||
|
||||
//if the reaction is progressing too slow then it isn't self-sustaining anymore and burns out
|
||||
if(zone && zone.fuel_objs.len)
|
||||
if(used_fuel <= FIRE_LIQUD_MIN_BURNRATE)
|
||||
return 0
|
||||
else if(used_fuel <= FIRE_GAS_MIN_BURNRATE*group_multiplier) //purely gas fires have more stringent criteria
|
||||
return 0
|
||||
|
||||
|
||||
//*** Remove fuel and oxidizer, add carbon dioxide and heat
|
||||
|
||||
//remove and add gasses as calculated
|
||||
|
||||
@@ -138,6 +138,11 @@ Class Procs:
|
||||
air.group_multiplier = contents.len+1
|
||||
|
||||
/zone/proc/tick()
|
||||
if(air.temperature >= PHORON_FLASHPOINT && !(src in air_master.active_fire_zones) && air.check_combustability() && contents.len)
|
||||
var/turf/T = pick(contents)
|
||||
if(istype(T))
|
||||
T.create_fire(vsc.fire_firelevel_multiplier)
|
||||
|
||||
if(air.check_tile_graphic(graphic_add, graphic_remove))
|
||||
for(var/turf/simulated/T in contents)
|
||||
T.update_graphic(graphic_add, graphic_remove)
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
/datum/gas_mixture/proc/remove_ratio(ratio, out_group_multiplier = 1)
|
||||
if(ratio <= 0)
|
||||
return null
|
||||
out_group_multiplier = max(1, min(group_multiplier, out_group_multiplier))
|
||||
out_group_multiplier = between(1, out_group_multiplier, group_multiplier)
|
||||
|
||||
ratio = min(ratio, 1)
|
||||
|
||||
@@ -237,6 +237,11 @@
|
||||
|
||||
return removed
|
||||
|
||||
//Removes a volume of gas from the mixture and returns a gas_mixture containing the removed air with the given volume
|
||||
/datum/gas_mixture/proc/remove_volume(removed_volume)
|
||||
var/datum/gas_mixture/removed = remove_ratio(removed_volume/volume, 1)
|
||||
removed.volume = removed_volume
|
||||
return removed
|
||||
|
||||
//Removes moles from the gas mixture, limited by a given flag. Returns a gax_mixture containing the removed air.
|
||||
/datum/gas_mixture/proc/remove_by_flag(flag, amount)
|
||||
|
||||
@@ -600,3 +600,10 @@ datum/proc/dd_SortValue()
|
||||
/datum/alarm/dd_SortValue()
|
||||
return "[sanitize(last_name)]"
|
||||
|
||||
//creates every subtype of prototype (excluding prototype) and adds it to list L.
|
||||
//if no list/L is provided, one is created.
|
||||
/proc/init_subtypes(prototype, list/L)
|
||||
if(!istype(L)) L = list()
|
||||
for(var/path in (typesof(prototype) - prototype))
|
||||
L += new path()
|
||||
return L
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
#define MINUTE *600
|
||||
#define MINUTES *600
|
||||
|
||||
var/roundstart_hour = 0
|
||||
//Returns the world time in english
|
||||
proc/worldtime2text(time = world.time)
|
||||
return "[round(time / 36000)+12]:[(time / 600 % 60) < 10 ? add_zero(time / 600 % 60, 1) : time / 600 % 60]"
|
||||
if(!roundstart_hour) roundstart_hour = pick(2,7,12,17)
|
||||
return "[round(time / 36000)+roundstart_hour]:[(time / 600 % 60) < 10 ? add_zero(time / 600 % 60, 1) : time / 600 % 60]"
|
||||
|
||||
proc/time_stamp()
|
||||
return time2text(world.timeofday, "hh:mm:ss")
|
||||
|
||||
+22
-15
@@ -374,7 +374,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
var/mob/living/silicon/ai/selected
|
||||
var/list/active = active_ais()
|
||||
for(var/mob/living/silicon/ai/A in active)
|
||||
if(!selected || (selected.connected_robots > A.connected_robots))
|
||||
if(!selected || (selected.connected_robots.len > A.connected_robots.len))
|
||||
selected = A
|
||||
|
||||
return selected
|
||||
@@ -698,17 +698,26 @@ proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,fl
|
||||
|
||||
else return get_step(ref, base_dir)
|
||||
|
||||
/proc/do_mob(var/mob/user , var/mob/target, var/time = 30) //This is quite an ugly solution but i refuse to use the old request system.
|
||||
if(!user || !target) return 0
|
||||
var/user_loc = user.loc
|
||||
var/target_loc = target.loc
|
||||
/proc/do_mob(var/mob/user, var/mob/target, var/delay, var/numticks = 5, var/needhand = 1) //This is quite an ugly solution but i refuse to use the old request system.
|
||||
if(!user || !target) return 0
|
||||
if(numticks == 0) return 0
|
||||
|
||||
var/delayfraction = round(delay/numticks)
|
||||
var/original_user_loc = user.loc
|
||||
var/original_target_loc = target.loc
|
||||
var/holding = user.get_active_hand()
|
||||
sleep(time)
|
||||
if(!user || !target) return 0
|
||||
if ( user.loc == user_loc && target.loc == target_loc && user.get_active_hand() == holding && !( user.stat ) && ( !user.stunned && !user.weakened && !user.paralysis && !user.lying ) )
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
for(var/i = 0, i<numticks, i++)
|
||||
sleep(delayfraction)
|
||||
|
||||
if(!user || user.stat || user.weakened || user.stunned || user.loc != original_user_loc)
|
||||
return 0
|
||||
if(!target || target.loc != original_target_loc)
|
||||
return 0
|
||||
if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/proc/do_after(var/mob/user as mob, delay as num, var/numticks = 5, var/needhand = 1)
|
||||
if(!user || isnull(user))
|
||||
@@ -718,14 +727,12 @@ proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,fl
|
||||
|
||||
var/delayfraction = round(delay/numticks)
|
||||
var/original_loc = user.loc
|
||||
var/original_turf = get_turf(user)
|
||||
var/holding = user.get_active_hand()
|
||||
|
||||
for(var/i = 0, i<numticks, i++)
|
||||
sleep(delayfraction)
|
||||
|
||||
|
||||
if(!user || user.stat || user.weakened || user.stunned || user.loc != original_loc || get_turf(user) != original_turf)
|
||||
if(!user || user.stat || user.weakened || user.stunned || user.loc != original_loc)
|
||||
return 0
|
||||
if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand
|
||||
return 0
|
||||
@@ -1389,4 +1396,4 @@ var/list/WALLITEMS = list(
|
||||
if(length(temp_col )<2)
|
||||
temp_col = "0[temp_col]"
|
||||
colour += temp_col
|
||||
return colour
|
||||
return colour
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
plot_vector is a helper datum for plotting a path in a straight line towards a target turf.
|
||||
This datum converts from world space (turf.x and turf.y) to pixel space, which the datum keeps track of itself. This
|
||||
should work with any size turfs (i.e. 32x32, 64x64) as it references world.icon_size (note: not actually tested with
|
||||
anything other than 32x32 turfs).
|
||||
|
||||
setup()
|
||||
This should be called after creating a new instance of a plot_vector datum.
|
||||
This does the initial setup and calculations. Since we are travelling in a straight line we only need to calculate
|
||||
the vector and x/y steps once. x/y steps are capped to 1 full turf, whichever is further. If we are travelling along
|
||||
the y axis each step will be +/- 1 y, and the x movement reduced based on the angle (tangent calculation). After
|
||||
this every subsequent step will be incremented based on these calculations.
|
||||
Inputs:
|
||||
source - the turf the object is starting from
|
||||
target - the target turf the object is travelling towards
|
||||
xo - starting pixel_x offset, typically won't be needed, but included in case someone has a need for it later
|
||||
yo - same as xo, but for the y_pixel offset
|
||||
|
||||
increment()
|
||||
Adds the offset to the current location - incrementing it by one step along the vector.
|
||||
|
||||
return_angle()
|
||||
Returns the direction (angle in degrees) the object is travelling in.
|
||||
|
||||
(N)
|
||||
90°
|
||||
^
|
||||
|
|
||||
(W) 180° <--+--> 0° (E)
|
||||
|
|
||||
v
|
||||
-90°
|
||||
(S)
|
||||
|
||||
return_hypotenuse()
|
||||
Returns the distance of travel for each step of the vector, relative to each full step of movement. 1 is a full turf
|
||||
length. Currently used as a multiplier for scaling effects that should be contiguous, like laser beams.
|
||||
|
||||
return_location()
|
||||
Returns a vector_loc datum containing the current location data of the object (see /datum/vector_loc). This includes
|
||||
the turf it currently should be at, as well as the pixel offset from the centre of that turf. Typically increment()
|
||||
would be called before this if you are going to move an object based on it's vector data.
|
||||
*/
|
||||
|
||||
/datum/plot_vector
|
||||
var/turf/source
|
||||
var/turf/target
|
||||
var/angle = 0 // direction of travel in degrees
|
||||
var/loc_x = 0 // in pixels from the left edge of the map
|
||||
var/loc_y = 0 // in pixels from the bottom edge of the map
|
||||
var/loc_z = 0 // loc z is in world space coordinates (i.e. z level) - we don't care about measuring pixels for this
|
||||
var/offset_x = 0 // distance to increment each step
|
||||
var/offset_y = 0
|
||||
|
||||
/datum/plot_vector/proc/setup(var/turf/S, var/turf/T, var/xo = 0, var/yo = 0)
|
||||
source = S
|
||||
target = T
|
||||
|
||||
if(!istype(source))
|
||||
source = get_turf(source)
|
||||
if(!istype(target))
|
||||
target = get_turf(target)
|
||||
|
||||
if(!istype(source) || !istype(target))
|
||||
return
|
||||
|
||||
// convert coordinates to pixel space (default is 32px/turf, 8160px across for a size 255 map)
|
||||
loc_x = source.x * world.icon_size + xo
|
||||
loc_y = source.y * world.icon_size + yo
|
||||
loc_z = source.z
|
||||
|
||||
// calculate initial x and y difference
|
||||
var/dx = target.x - source.x
|
||||
var/dy = target.y - source.y
|
||||
|
||||
// if we aren't moving anywhere; quit now
|
||||
if(dx == 0 && dy == 0)
|
||||
return
|
||||
|
||||
// calculate the angle
|
||||
angle = Atan2(dx, dy)
|
||||
|
||||
// and some rounding to stop the increments jumping whole turfs - because byond favours certain angles
|
||||
if(angle > -135 && angle < 45)
|
||||
angle = Ceiling(angle)
|
||||
else
|
||||
angle = Floor(angle)
|
||||
|
||||
// calculate the offset per increment step
|
||||
if(abs(angle) in list(0, 45, 90, 135, 180)) // check if the angle is a cardinal
|
||||
if(abs(angle) in list(0, 45, 135, 180)) // if so we can skip the trigonometry and set these to absolutes as
|
||||
offset_x = sign(dx) // they will always be a full step in one or more directions
|
||||
if(abs(angle) in list(45, 90, 135))
|
||||
offset_y = sign(dy)
|
||||
else if(abs(dy) > abs(dx))
|
||||
offset_x = Cot(abs(angle)) // otherwise set the offsets
|
||||
offset_y = sign(dy)
|
||||
else
|
||||
offset_x = sign(dx)
|
||||
offset_y = Tan(angle)
|
||||
if(dx < 0)
|
||||
offset_y = -offset_y
|
||||
|
||||
// multiply the offset by the turf pixel size
|
||||
offset_x *= world.icon_size
|
||||
offset_y *= world.icon_size
|
||||
|
||||
/datum/plot_vector/proc/increment()
|
||||
loc_x += offset_x
|
||||
loc_y += offset_y
|
||||
|
||||
/datum/plot_vector/proc/return_angle()
|
||||
return angle
|
||||
|
||||
/datum/plot_vector/proc/return_hypotenuse()
|
||||
return sqrt(((offset_x / 32) ** 2) + ((offset_y / 32) ** 2))
|
||||
|
||||
/datum/plot_vector/proc/return_location(var/datum/vector_loc/data)
|
||||
if(!data)
|
||||
data = new()
|
||||
data.loc = locate(round(loc_x / world.icon_size), round(loc_y / world.icon_size), loc_z)
|
||||
if(!data.loc)
|
||||
return
|
||||
data.pixel_x = loc_x - (data.loc.x * world.icon_size)
|
||||
data.pixel_y = loc_y - (data.loc.y * world.icon_size)
|
||||
return data
|
||||
|
||||
/*
|
||||
vector_loc is a helper datum for returning precise location data from plot_vector. It includes the turf the object is in
|
||||
as well as the pixel offsets.
|
||||
|
||||
return_turf()
|
||||
Returns the turf the object should be currently located in.
|
||||
*/
|
||||
/datum/vector_loc
|
||||
var/turf/loc
|
||||
var/pixel_x
|
||||
var/pixel_y
|
||||
|
||||
/datum/vector_loc/proc/return_turf()
|
||||
return loc
|
||||
@@ -36,6 +36,9 @@
|
||||
return
|
||||
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["shift"] && modifiers["ctrl"])
|
||||
CtrlShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["middle"])
|
||||
MiddleClickOn(A)
|
||||
return
|
||||
@@ -101,6 +104,14 @@
|
||||
I have no idea why it was in atoms.dm instead of respective files.
|
||||
*/
|
||||
|
||||
/atom/proc/AICtrlShiftClick()
|
||||
return
|
||||
|
||||
/obj/machinery/door/airlock/AICtrlShiftClick()
|
||||
if(emagged)
|
||||
return
|
||||
return
|
||||
|
||||
/atom/proc/AIShiftClick()
|
||||
return
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
return
|
||||
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["shift"] && modifiers["ctrl"])
|
||||
CtrlShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["middle"])
|
||||
MiddleClickOn(A)
|
||||
return
|
||||
@@ -292,6 +295,17 @@
|
||||
/mob/proc/TurfAdjacent(var/turf/T)
|
||||
return T.AdjacentQuick(src)
|
||||
|
||||
/*
|
||||
Control+Shift click
|
||||
Unused except for AI
|
||||
*/
|
||||
/mob/proc/CtrlShiftClickOn(var/atom/A)
|
||||
A.CtrlShiftClick(src)
|
||||
return
|
||||
|
||||
/atom/proc/CtrlShiftClick(var/mob/user)
|
||||
return
|
||||
|
||||
/*
|
||||
Misc helpers
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
return
|
||||
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["shift"] && modifiers["ctrl"])
|
||||
CtrlShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["middle"])
|
||||
MiddleClickOn(A)
|
||||
return
|
||||
@@ -109,6 +112,55 @@
|
||||
cycle_modules()
|
||||
return
|
||||
|
||||
//Give cyborgs hotkey clicks without breaking existing uses of hotkey clicks
|
||||
// for non-doors/apcs
|
||||
/mob/living/silicon/robot/CtrlShiftClickOn(var/atom/A)
|
||||
A.BorgCtrlShiftClick(src)
|
||||
|
||||
/mob/living/silicon/robot/ShiftClickOn(var/atom/A)
|
||||
A.BorgShiftClick(src)
|
||||
|
||||
/mob/living/silicon/robot/CtrlClickOn(var/atom/A)
|
||||
A.BorgCtrlClick(src)
|
||||
|
||||
/mob/living/silicon/robot/AltClickOn(var/atom/A)
|
||||
A.BorgAltClick(src)
|
||||
|
||||
/atom/proc/BorgCtrlShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden
|
||||
CtrlShiftClick(user)
|
||||
|
||||
/obj/machinery/door/airlock/BorgCtrlShiftClick()
|
||||
AICtrlShiftClick()
|
||||
|
||||
/atom/proc/BorgShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden
|
||||
ShiftClick(user)
|
||||
|
||||
/obj/machinery/door/airlock/BorgShiftClick() // Opens and closes doors! Forwards to AI code.
|
||||
AIShiftClick()
|
||||
|
||||
|
||||
/atom/proc/BorgCtrlClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden
|
||||
CtrlClick(user)
|
||||
|
||||
/obj/machinery/door/airlock/BorgCtrlClick() // Bolts doors. Forwards to AI code.
|
||||
AICtrlClick()
|
||||
|
||||
/obj/machinery/power/apc/BorgCtrlClick() // turns off/on APCs. Forwards to AI code.
|
||||
AICtrlClick()
|
||||
|
||||
/obj/machinery/turretid/BorgCtrlClick() //turret control on/off. Forwards to AI code.
|
||||
AICtrlClick()
|
||||
|
||||
/atom/proc/BorgAltClick(var/mob/living/silicon/robot/user)
|
||||
AltClick(user)
|
||||
return
|
||||
|
||||
/obj/machinery/door/airlock/BorgAltClick() // Eletrifies doors. Forwards to AI code.
|
||||
AIAltClick()
|
||||
|
||||
/obj/machinery/turretid/BorgAltClick() //turret lethal on/off. Forwards to AI code.
|
||||
AIAltClick()
|
||||
|
||||
/*
|
||||
As with AI, these are not used in click code,
|
||||
because the code for robots is specific, not generic.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/bower_components
|
||||
/node_modules
|
||||
/ProcessScheduler.dmb
|
||||
/ProcessScheduler.int
|
||||
/ProcessScheduler.rsc
|
||||
/*.lk
|
||||
@@ -0,0 +1,212 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
|
||||
b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices".
|
||||
c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
|
||||
d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
|
||||
A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
|
||||
b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
|
||||
c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
|
||||
d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
|
||||
e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
|
||||
A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
"Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
|
||||
b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
|
||||
c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
|
||||
d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
|
||||
e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
|
||||
f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
|
||||
All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>.
|
||||
@@ -0,0 +1 @@
|
||||
This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
|
||||
@@ -0,0 +1,32 @@
|
||||
// DM Environment file for ProcessScheduler.dme.
|
||||
// All manual changes should be made outside the BEGIN_ and END_ blocks.
|
||||
// New source code should be placed in .dm files: choose File/New --> Code File.
|
||||
|
||||
// BEGIN_INTERNALS
|
||||
// END_INTERNALS
|
||||
|
||||
// BEGIN_FILE_DIR
|
||||
#define FILE_DIR .
|
||||
// END_FILE_DIR
|
||||
|
||||
// BEGIN_PREFERENCES
|
||||
// END_PREFERENCES
|
||||
|
||||
// BEGIN_INCLUDE
|
||||
#include "core\_define.dm"
|
||||
#include "core\_stubs.dm"
|
||||
#include "core\process.dm"
|
||||
#include "core\processScheduler.dm"
|
||||
#include "core\updateQueue.dm"
|
||||
#include "core\updateQueueWorker.dm"
|
||||
#include "test\processSchedulerView.dm"
|
||||
#include "test\testDyingUpdateQueueProcess.dm"
|
||||
#include "test\testHarness.dm"
|
||||
#include "test\testHungProcess.dm"
|
||||
#include "test\testNiceProcess.dm"
|
||||
#include "test\testSlowProcess.dm"
|
||||
#include "test\testUpdateQueue.dm"
|
||||
#include "test\testUpdateQueueProcess.dm"
|
||||
#include "test\testZombieProcess.dm"
|
||||
// END_INCLUDE
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
ProcessScheduler
|
||||
================
|
||||
A Goonstation release, maintained by Volundr
|
||||
|
||||
##SUMMARY
|
||||
|
||||
This is a mostly self-contained, fairly well-documented implementation of the main loop process architecture in use in Goonstation.
|
||||
|
||||
##LICENSE
|
||||
|
||||
This work is released under the following licenses.
|
||||
|
||||
[](http://creativecommons.org/licenses/by-nc/4.0/)
|
||||
|
||||
This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. The complete text of this license is included in the file LICENSE-CC-BY-NC.
|
||||
|
||||
|
||||
[](http://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
This work is licensed under the Affero General Public License 3.0. The complete text of the license is included in the file LICENSE-AGPL.
|
||||
|
||||
##INSTALLATION
|
||||
|
||||
To integrate the process scheduler to your codebase, you will not need anything except the contents of the core/ folder. The rest of the project is simply for testing and to provide an example for the process scheduler code.
|
||||
|
||||
### Test project setup
|
||||
To compile and run the test project, you will require:
|
||||
|
||||
- node.js
|
||||
- BYOND
|
||||
|
||||
Clone the repository to a path of your choosing, then change directory to it and execute:
|
||||
|
||||
```
|
||||
npm install -g
|
||||
bower install -g
|
||||
```
|
||||
|
||||
Then you can either compile with DM or open the DM environment in DreamMaker and compile/run from there.
|
||||
|
||||
##USAGE
|
||||
|
||||
###BASICS
|
||||
To use the process scheduler in your SS13 codebase, you'll need:
|
||||
|
||||
- core/_defines.dm
|
||||
- core/_stubs.dm
|
||||
- core/process.dm
|
||||
- core/processScheduler.dm
|
||||
- core/processScheduler.js
|
||||
- core/updateQueue.dm
|
||||
- core/updateQueueWorker.dm
|
||||
|
||||
To integrate, you can copy the contents of _defines.dm into your global defines file. Most ss13 codebases already have the code from _stubs.dm.
|
||||
|
||||
The processScheduler is intended as a replacement for the old master_controller from r4407 and other fork codebases. To implement it, you need only to add the source files to your DM environment, and add the following code into world.New, above where the old master_controller is initialized.
|
||||
|
||||
```
|
||||
processScheduler = new
|
||||
processScheduler.setup()
|
||||
processScheduler.start()
|
||||
```
|
||||
|
||||
The processScheduler will automatically find all subtypes of process, and begin processing them.
|
||||
|
||||
The interface code in test/processSchedulerView.dm is simply an example frontend, and can easily be rebuilt to use other styles, and/or render simple html without using javascript for refreshing the panel and killing processes.
|
||||
|
||||
###DETAILS
|
||||
|
||||
To implement a process, you have two options:
|
||||
|
||||
1. Implement a raw loop-style processor
|
||||
2. Implement an updateQueue processor
|
||||
|
||||
There are clear examples of both of these paradigms in the code provided. Both styles are valid, but for processes that are just a loop calling an update proc on a bunch of objects, you should use the updateQueue.
|
||||
|
||||
The updateQueue works by spawn(0)'ing your specified update proc, but it only puts one instance in the scheduler at a time. Examine the code for more details. The overall effect of this is that it doesn't block, and it lets update loops work concurrently. It enables a much smoother user experience.
|
||||
|
||||
##Contributing
|
||||
|
||||
I welcome pull requests and issue reports, and will try to merge/fix them as I have time.
|
||||
|
||||
### Licensing for code submitted via PR:
|
||||
|
||||
By submitting a pull request, you agree to release all original code submitted in the pull request under the [MIT License](http://opensource.org/licenses/MIT). You also agree that any code submitted is either your own work, or is public domain or under a equally or less restrictive license than the MIT license, or that you have the express written permission of the authors of the submitted code to submit the pull request.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "ProcessScheduler",
|
||||
"main": "ProcessScheduler.js",
|
||||
"version": "1.0.0",
|
||||
"homepage": "https://github.com/goonstation/ProcessScheduler",
|
||||
"authors": [
|
||||
"Volundr <spoogemonster@gmail.com>"
|
||||
],
|
||||
"description": "BYOND SS13 Process Scheduler",
|
||||
"keywords": [
|
||||
"byond",
|
||||
"ss13",
|
||||
"process",
|
||||
"scheduler"
|
||||
],
|
||||
"license": "CC-BY-NC",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"bootstrap2.3.2": "~2.3.2",
|
||||
"jquery": "1.11.1",
|
||||
"json2": "*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Process status defines
|
||||
#define PROCESS_STATUS_IDLE 1
|
||||
#define PROCESS_STATUS_QUEUED 2
|
||||
#define PROCESS_STATUS_RUNNING 3
|
||||
#define PROCESS_STATUS_MAYBE_HUNG 4
|
||||
#define PROCESS_STATUS_PROBABLY_HUNG 5
|
||||
#define PROCESS_STATUS_HUNG 6
|
||||
|
||||
// Process time thresholds
|
||||
#define PROCESS_DEFAULT_HANG_WARNING_TIME 300 // 30 seconds
|
||||
#define PROCESS_DEFAULT_HANG_ALERT_TIME 600 // 60 seconds
|
||||
#define PROCESS_DEFAULT_HANG_RESTART_TIME 900 // 90 seconds
|
||||
#define PROCESS_DEFAULT_SCHEDULE_INTERVAL 50 // 50 ticks
|
||||
#define PROCESS_DEFAULT_SLEEP_INTERVAL 2 // 2 ticks
|
||||
#define PROCESS_DEFAULT_CPU_THRESHOLD 90 // 90%
|
||||
|
||||
//#define UPDATE_QUEUE_DEBUG
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* _stubs.dm
|
||||
*
|
||||
* This file contains constructs that the process scheduler expects to exist
|
||||
* in a standard ss13 fork.
|
||||
*/
|
||||
/*
|
||||
/**
|
||||
* message_admins
|
||||
*
|
||||
* sends a message to admins
|
||||
*/
|
||||
/proc/message_admins(msg)
|
||||
world << msg
|
||||
*/
|
||||
/**
|
||||
* logTheThing
|
||||
*
|
||||
* In goonstation, this proc writes a message to either the world log or diary.
|
||||
*
|
||||
* Blame Keelin.
|
||||
*/
|
||||
/proc/logTheThing(type, source, target, text, diaryType)
|
||||
if(diaryType)
|
||||
world << "Diary: \[[diaryType]:[type]] [text]"
|
||||
else
|
||||
world << "Log: \[[type]] [text]"
|
||||
|
||||
/**
|
||||
* var/disposed
|
||||
*
|
||||
* In goonstation, disposed is set to 1 after an object enters the delete queue
|
||||
* or the object is placed in an object pool (effectively out-of-play so to speak)
|
||||
*/
|
||||
/datum/var/disposed
|
||||
// Garbage collection (controller).
|
||||
/datum/var/gcDestroyed
|
||||
/datum/var/timeDestroyed
|
||||
@@ -0,0 +1,317 @@
|
||||
// Process
|
||||
|
||||
/datum/controller/process
|
||||
/**
|
||||
* State vars
|
||||
*/
|
||||
// Main controller ref
|
||||
var/tmp/datum/controller/processScheduler/main
|
||||
|
||||
// 1 if process is not running or queued
|
||||
var/tmp/idle = 1
|
||||
|
||||
// 1 if process is queued
|
||||
var/tmp/queued = 0
|
||||
|
||||
// 1 if process is running
|
||||
var/tmp/running = 0
|
||||
|
||||
// 1 if process is blocked up
|
||||
var/tmp/hung = 0
|
||||
|
||||
// 1 if process was killed
|
||||
var/tmp/killed = 0
|
||||
|
||||
// Status text var
|
||||
var/tmp/status
|
||||
|
||||
// Previous status text var
|
||||
var/tmp/previousStatus
|
||||
|
||||
// 1 if process is disabled
|
||||
var/tmp/disabled = 0
|
||||
|
||||
/**
|
||||
* Config vars
|
||||
*/
|
||||
// Process name
|
||||
var/name
|
||||
|
||||
// Process schedule interval
|
||||
// This controls how often the process would run under ideal conditions.
|
||||
// If the process scheduler sees that the process has finished, it will wait until
|
||||
// this amount of time has elapsed from the start of the previous run to start the
|
||||
// process running again.
|
||||
var/tmp/schedule_interval = PROCESS_DEFAULT_SCHEDULE_INTERVAL // run every 50 ticks
|
||||
|
||||
// Process sleep interval
|
||||
// This controls how often the process will yield (call sleep(0)) while it is running.
|
||||
// Every concurrent process should sleep periodically while running in order to allow other
|
||||
// processes to execute concurrently.
|
||||
var/tmp/sleep_interval = PROCESS_DEFAULT_SLEEP_INTERVAL
|
||||
|
||||
// hang_warning_time - this is the time (in 1/10 seconds) after which the server will begin to show "maybe hung" in the context window
|
||||
var/tmp/hang_warning_time = PROCESS_DEFAULT_HANG_WARNING_TIME
|
||||
|
||||
// hang_alert_time - After this much time(in 1/10 seconds), the server will send an admin debug message saying the process may be hung
|
||||
var/tmp/hang_alert_time = PROCESS_DEFAULT_HANG_ALERT_TIME
|
||||
|
||||
// hang_restart_time - After this much time(in 1/10 seconds), the server will automatically kill and restart the process.
|
||||
var/tmp/hang_restart_time = PROCESS_DEFAULT_HANG_RESTART_TIME
|
||||
|
||||
// cpu_threshold - if world.cpu >= cpu_threshold, scheck() will call sleep(1) to defer further work until the next tick. This keeps a process from driving a tick into overtime (causing perceptible lag)
|
||||
var/tmp/cpu_threshold = PROCESS_DEFAULT_CPU_THRESHOLD
|
||||
|
||||
// How many times in the current run has the process deferred work till the next tick?
|
||||
var/tmp/cpu_defer_count = 0
|
||||
|
||||
/**
|
||||
* recordkeeping vars
|
||||
*/
|
||||
|
||||
// Records the time (server ticks) at which the process last finished sleeping
|
||||
var/tmp/last_slept = 0
|
||||
|
||||
// Records the time (s-ticks) at which the process last began running
|
||||
var/tmp/run_start = 0
|
||||
|
||||
// Records the number of times this process has been killed and restarted
|
||||
var/tmp/times_killed
|
||||
|
||||
// Tick count
|
||||
var/tmp/ticks = 0
|
||||
|
||||
var/tmp/last_task = ""
|
||||
|
||||
var/tmp/last_object
|
||||
|
||||
datum/controller/process/New(var/datum/controller/processScheduler/scheduler)
|
||||
..()
|
||||
main = scheduler
|
||||
previousStatus = "idle"
|
||||
idle()
|
||||
name = "process"
|
||||
schedule_interval = 50
|
||||
sleep_interval = 2
|
||||
last_slept = 0
|
||||
run_start = 0
|
||||
ticks = 0
|
||||
last_task = 0
|
||||
last_object = null
|
||||
|
||||
datum/controller/process/proc/started()
|
||||
// Initialize last_slept so we can know when to sleep
|
||||
last_slept = world.timeofday
|
||||
|
||||
// Initialize run_start so we can detect hung processes.
|
||||
run_start = world.timeofday
|
||||
|
||||
// Initialize defer count
|
||||
cpu_defer_count = 0
|
||||
|
||||
running()
|
||||
main.processStarted(src)
|
||||
|
||||
onStart()
|
||||
|
||||
datum/controller/process/proc/finished()
|
||||
ticks++
|
||||
idle()
|
||||
main.processFinished(src)
|
||||
|
||||
onFinish()
|
||||
|
||||
datum/controller/process/proc/doWork()
|
||||
|
||||
datum/controller/process/proc/setup()
|
||||
|
||||
datum/controller/process/proc/process()
|
||||
started()
|
||||
doWork()
|
||||
finished()
|
||||
|
||||
datum/controller/process/proc/running()
|
||||
idle = 0
|
||||
queued = 0
|
||||
running = 1
|
||||
hung = 0
|
||||
setStatus(PROCESS_STATUS_RUNNING)
|
||||
|
||||
datum/controller/process/proc/idle()
|
||||
queued = 0
|
||||
running = 0
|
||||
idle = 1
|
||||
hung = 0
|
||||
setStatus(PROCESS_STATUS_IDLE)
|
||||
|
||||
datum/controller/process/proc/queued()
|
||||
idle = 0
|
||||
running = 0
|
||||
queued = 1
|
||||
hung = 0
|
||||
setStatus(PROCESS_STATUS_QUEUED)
|
||||
|
||||
datum/controller/process/proc/hung()
|
||||
hung = 1
|
||||
setStatus(PROCESS_STATUS_HUNG)
|
||||
|
||||
datum/controller/process/proc/handleHung()
|
||||
var/datum/lastObj = last_object
|
||||
var/lastObjType = "null"
|
||||
if(istype(lastObj))
|
||||
lastObjType = lastObj.type
|
||||
|
||||
// If world.timeofday has rolled over, then we need to adjust.
|
||||
if (world.timeofday < run_start)
|
||||
run_start -= 864000
|
||||
|
||||
var/msg = "[name] process hung at tick #[ticks]. Process was unresponsive for [(world.timeofday - run_start) / 10] seconds and was restarted. Last task: [last_task]. Last Object Type: [lastObjType]"
|
||||
logTheThing("debug", null, null, msg)
|
||||
logTheThing("diary", null, null, msg, "debug")
|
||||
message_admins(msg)
|
||||
|
||||
main.restartProcess(src.name)
|
||||
|
||||
datum/controller/process/proc/kill()
|
||||
if (!killed)
|
||||
var/msg = "[name] process was killed at tick #[ticks]."
|
||||
logTheThing("debug", null, null, msg)
|
||||
logTheThing("diary", null, null, msg, "debug")
|
||||
//finished()
|
||||
|
||||
// Allow inheritors to clean up if needed
|
||||
onKill()
|
||||
|
||||
killed = TRUE
|
||||
|
||||
// This should del
|
||||
del(src)
|
||||
|
||||
datum/controller/process/proc/scheck(var/tickId = 0)
|
||||
if (killed)
|
||||
// The kill proc is the only place where killed is set.
|
||||
// The kill proc should have deleted this datum, and all sleeping procs that are
|
||||
// owned by it.
|
||||
CRASH("A killed process is still running somehow...")
|
||||
|
||||
// For each tick the process defers, it increments the cpu_defer_count so we don't
|
||||
// defer indefinitely
|
||||
if (world.cpu >= cpu_threshold + cpu_defer_count * 10)
|
||||
sleep(1)
|
||||
cpu_defer_count++
|
||||
last_slept = world.timeofday
|
||||
else
|
||||
// If world.timeofday has rolled over, then we need to adjust.
|
||||
if (world.timeofday < last_slept)
|
||||
last_slept -= 864000
|
||||
|
||||
if (world.timeofday > last_slept + sleep_interval)
|
||||
// If we haven't slept in sleep_interval ticks, sleep to allow other work to proceed.
|
||||
sleep(0)
|
||||
last_slept = world.timeofday
|
||||
|
||||
datum/controller/process/proc/update()
|
||||
// Clear delta
|
||||
if(previousStatus != status)
|
||||
setStatus(status)
|
||||
|
||||
var/elapsedTime = getElapsedTime()
|
||||
|
||||
if (elapsedTime > hang_restart_time)
|
||||
hung()
|
||||
else if (elapsedTime > hang_alert_time)
|
||||
setStatus(PROCESS_STATUS_PROBABLY_HUNG)
|
||||
else if (elapsedTime > hang_warning_time)
|
||||
setStatus(PROCESS_STATUS_MAYBE_HUNG)
|
||||
|
||||
datum/controller/process/proc/getElapsedTime()
|
||||
if (world.timeofday < run_start)
|
||||
return world.timeofday - (run_start - 864000)
|
||||
return world.timeofday - run_start
|
||||
|
||||
datum/controller/process/proc/tickDetail()
|
||||
return
|
||||
|
||||
datum/controller/process/proc/getContext()
|
||||
return "<tr><td>[name]</td><td>[main.averageRunTime(src)]</td><td>[main.last_run_time[src]]</td><td>[main.highest_run_time[src]]</td><td>[ticks]</td></tr>\n"
|
||||
|
||||
datum/controller/process/proc/getContextData()
|
||||
return list(
|
||||
"name" = name,
|
||||
"averageRunTime" = main.averageRunTime(src),
|
||||
"lastRunTime" = main.last_run_time[src],
|
||||
"highestRunTime" = main.highest_run_time[src],
|
||||
"ticks" = ticks,
|
||||
"schedule" = schedule_interval,
|
||||
"status" = getStatusText(),
|
||||
"disabled" = disabled
|
||||
)
|
||||
|
||||
datum/controller/process/proc/getStatus()
|
||||
return status
|
||||
|
||||
datum/controller/process/proc/getStatusText(var/s = 0)
|
||||
if(!s)
|
||||
s = status
|
||||
switch(s)
|
||||
if(PROCESS_STATUS_IDLE)
|
||||
return "idle"
|
||||
if(PROCESS_STATUS_QUEUED)
|
||||
return "queued"
|
||||
if(PROCESS_STATUS_RUNNING)
|
||||
return "running"
|
||||
if(PROCESS_STATUS_MAYBE_HUNG)
|
||||
return "maybe hung"
|
||||
if(PROCESS_STATUS_PROBABLY_HUNG)
|
||||
return "probably hung"
|
||||
if(PROCESS_STATUS_HUNG)
|
||||
return "HUNG"
|
||||
else
|
||||
return "UNKNOWN"
|
||||
|
||||
datum/controller/process/proc/getPreviousStatus()
|
||||
return previousStatus
|
||||
|
||||
datum/controller/process/proc/getPreviousStatusText()
|
||||
return getStatusText(previousStatus)
|
||||
|
||||
datum/controller/process/proc/setStatus(var/newStatus)
|
||||
previousStatus = status
|
||||
status = newStatus
|
||||
|
||||
datum/controller/process/proc/setLastTask(var/task, var/object)
|
||||
last_task = task
|
||||
last_object = object
|
||||
|
||||
datum/controller/process/proc/_copyStateFrom(var/datum/controller/process/target)
|
||||
main = target.main
|
||||
name = target.name
|
||||
schedule_interval = target.schedule_interval
|
||||
sleep_interval = target.sleep_interval
|
||||
last_slept = 0
|
||||
run_start = 0
|
||||
times_killed = target.times_killed
|
||||
ticks = target.ticks
|
||||
last_task = target.last_task
|
||||
last_object = target.last_object
|
||||
copyStateFrom(target)
|
||||
|
||||
datum/controller/process/proc/copyStateFrom(var/datum/controller/process/target)
|
||||
|
||||
datum/controller/process/proc/onKill()
|
||||
|
||||
datum/controller/process/proc/onStart()
|
||||
|
||||
datum/controller/process/proc/onFinish()
|
||||
|
||||
datum/controller/process/proc/disable()
|
||||
disabled = 1
|
||||
|
||||
datum/controller/process/proc/enable()
|
||||
disabled = 0
|
||||
|
||||
/datum/controller/process/proc/getLastRunTime()
|
||||
return main.getProcessLastRunTime(src)
|
||||
|
||||
/datum/controller/process/proc/getTicks()
|
||||
return ticks
|
||||
@@ -0,0 +1,320 @@
|
||||
// Singleton instance of game_controller_new, setup in world.New()
|
||||
var/global/datum/controller/processScheduler/processScheduler
|
||||
|
||||
/datum/controller/processScheduler
|
||||
// Processes known by the scheduler
|
||||
var/tmp/datum/controller/process/list/processes = new
|
||||
|
||||
// Processes that are currently running
|
||||
var/tmp/datum/controller/process/list/running = new
|
||||
|
||||
// Processes that are idle
|
||||
var/tmp/datum/controller/process/list/idle = new
|
||||
|
||||
// Processes that are queued to run
|
||||
var/tmp/datum/controller/process/list/queued = new
|
||||
|
||||
// Process name -> process object map
|
||||
var/tmp/datum/controller/process/list/nameToProcessMap = new
|
||||
|
||||
// Process last start times
|
||||
var/tmp/datum/controller/process/list/last_start = new
|
||||
|
||||
// Process last run durations
|
||||
var/tmp/datum/controller/process/list/last_run_time = new
|
||||
|
||||
// Per process list of the last 20 durations
|
||||
var/tmp/datum/controller/process/list/last_twenty_run_times = new
|
||||
|
||||
// Process highest run time
|
||||
var/tmp/datum/controller/process/list/highest_run_time = new
|
||||
|
||||
// Sleep 1 tick -- This may be too aggressive.
|
||||
var/tmp/scheduler_sleep_interval = 1
|
||||
|
||||
// Controls whether the scheduler is running or not
|
||||
var/tmp/isRunning = 0
|
||||
|
||||
// Setup for these processes will be deferred until all the other processes are set up.
|
||||
var/tmp/list/deferredSetupList = new
|
||||
|
||||
/**
|
||||
* deferSetupFor
|
||||
* @param path processPath
|
||||
* If a process needs to be initialized after everything else, add it to
|
||||
* the deferred setup list. On goonstation, only the ticker needs to have
|
||||
* this treatment.
|
||||
*/
|
||||
/datum/controller/processScheduler/proc/deferSetupFor(var/processPath)
|
||||
if (!(processPath in deferredSetupList))
|
||||
deferredSetupList += processPath
|
||||
|
||||
/datum/controller/processScheduler/proc/setup()
|
||||
// There can be only one
|
||||
if(processScheduler && (processScheduler != src))
|
||||
del(src)
|
||||
return 0
|
||||
|
||||
var/process
|
||||
// Add all the processes we can find, except for the ticker
|
||||
for (process in typesof(/datum/controller/process) - /datum/controller/process)
|
||||
if (!(process in deferredSetupList))
|
||||
addProcess(new process(src))
|
||||
|
||||
for (process in deferredSetupList)
|
||||
addProcess(new process(src))
|
||||
|
||||
/datum/controller/processScheduler/proc/start()
|
||||
isRunning = 1
|
||||
spawn(0)
|
||||
process()
|
||||
|
||||
/datum/controller/processScheduler/proc/process()
|
||||
while(isRunning)
|
||||
checkRunningProcesses()
|
||||
queueProcesses()
|
||||
runQueuedProcesses()
|
||||
sleep(scheduler_sleep_interval)
|
||||
|
||||
/datum/controller/processScheduler/proc/stop()
|
||||
isRunning = 0
|
||||
|
||||
/datum/controller/processScheduler/proc/checkRunningProcesses()
|
||||
for(var/datum/controller/process/p in running)
|
||||
p.update()
|
||||
|
||||
if (isnull(p)) // Process was killed
|
||||
continue
|
||||
|
||||
var/status = p.getStatus()
|
||||
var/previousStatus = p.getPreviousStatus()
|
||||
|
||||
// Check status changes
|
||||
if(status != previousStatus)
|
||||
//Status changed.
|
||||
|
||||
switch(status)
|
||||
if(PROCESS_STATUS_MAYBE_HUNG)
|
||||
message_admins("Process '[p.name]' is [p.getStatusText(status)].")
|
||||
if(PROCESS_STATUS_PROBABLY_HUNG)
|
||||
message_admins("Process '[p.name]' is [p.getStatusText(status)].")
|
||||
if(PROCESS_STATUS_HUNG)
|
||||
message_admins("Process '[p.name]' is [p.getStatusText(status)].")
|
||||
p.handleHung()
|
||||
|
||||
/datum/controller/processScheduler/proc/queueProcesses()
|
||||
for(var/datum/controller/process/p in processes)
|
||||
// Don't double-queue, don't queue running processes
|
||||
if (p.disabled || p.running || p.queued || !p.idle)
|
||||
continue
|
||||
|
||||
// If world.timeofday has rolled over, then we need to adjust.
|
||||
if (world.timeofday < last_start[p])
|
||||
last_start[p] -= 864000
|
||||
|
||||
// If the process should be running by now, go ahead and queue it
|
||||
if (world.timeofday > last_start[p] + p.schedule_interval)
|
||||
setQueuedProcessState(p)
|
||||
|
||||
/datum/controller/processScheduler/proc/runQueuedProcesses()
|
||||
for(var/datum/controller/process/p in queued)
|
||||
runProcess(p)
|
||||
|
||||
/datum/controller/processScheduler/proc/addProcess(var/datum/controller/process/process)
|
||||
processes.Add(process)
|
||||
process.idle()
|
||||
idle.Add(process)
|
||||
|
||||
// init recordkeeping vars
|
||||
last_start.Add(process)
|
||||
last_start[process] = 0
|
||||
last_run_time.Add(process)
|
||||
last_run_time[process] = 0
|
||||
last_twenty_run_times.Add(process)
|
||||
last_twenty_run_times[process] = list()
|
||||
highest_run_time.Add(process)
|
||||
highest_run_time[process] = 0
|
||||
|
||||
// init starts and stops record starts
|
||||
recordStart(process, 0)
|
||||
recordEnd(process, 0)
|
||||
|
||||
// Set up process
|
||||
process.setup()
|
||||
|
||||
// Save process in the name -> process map
|
||||
nameToProcessMap[process.name] = process
|
||||
|
||||
/datum/controller/processScheduler/proc/replaceProcess(var/datum/controller/process/oldProcess, var/datum/controller/process/newProcess)
|
||||
processes.Remove(oldProcess)
|
||||
processes.Add(newProcess)
|
||||
|
||||
newProcess.idle()
|
||||
idle.Remove(oldProcess)
|
||||
running.Remove(oldProcess)
|
||||
queued.Remove(oldProcess)
|
||||
idle.Add(newProcess)
|
||||
|
||||
last_start.Remove(oldProcess)
|
||||
last_start.Add(newProcess)
|
||||
last_start[newProcess] = 0
|
||||
|
||||
last_run_time.Add(newProcess)
|
||||
last_run_time[newProcess] = last_run_time[oldProcess]
|
||||
last_run_time.Remove(oldProcess)
|
||||
|
||||
last_twenty_run_times.Add(newProcess)
|
||||
last_twenty_run_times[newProcess] = last_twenty_run_times[oldProcess]
|
||||
last_twenty_run_times.Remove(oldProcess)
|
||||
|
||||
highest_run_time.Add(newProcess)
|
||||
highest_run_time[newProcess] = highest_run_time[oldProcess]
|
||||
highest_run_time.Remove(oldProcess)
|
||||
|
||||
recordStart(newProcess, 0)
|
||||
recordEnd(newProcess, 0)
|
||||
|
||||
nameToProcessMap[newProcess.name] = newProcess
|
||||
|
||||
|
||||
/datum/controller/processScheduler/proc/runProcess(var/datum/controller/process/process)
|
||||
spawn(0)
|
||||
process.process()
|
||||
|
||||
/datum/controller/processScheduler/proc/processStarted(var/datum/controller/process/process)
|
||||
setRunningProcessState(process)
|
||||
recordStart(process)
|
||||
|
||||
/datum/controller/processScheduler/proc/processFinished(var/datum/controller/process/process)
|
||||
setIdleProcessState(process)
|
||||
recordEnd(process)
|
||||
|
||||
/datum/controller/processScheduler/proc/setIdleProcessState(var/datum/controller/process/process)
|
||||
if (process in running)
|
||||
running -= process
|
||||
if (process in queued)
|
||||
queued -= process
|
||||
if (!(process in idle))
|
||||
idle += process
|
||||
|
||||
process.idle()
|
||||
|
||||
/datum/controller/processScheduler/proc/setQueuedProcessState(var/datum/controller/process/process)
|
||||
if (process in running)
|
||||
running -= process
|
||||
if (process in idle)
|
||||
idle -= process
|
||||
if (!(process in queued))
|
||||
queued += process
|
||||
|
||||
// The other state transitions are handled internally by the process.
|
||||
process.queued()
|
||||
|
||||
/datum/controller/processScheduler/proc/setRunningProcessState(var/datum/controller/process/process)
|
||||
if (process in queued)
|
||||
queued -= process
|
||||
if (process in idle)
|
||||
idle -= process
|
||||
if (!(process in running))
|
||||
running += process
|
||||
|
||||
process.running()
|
||||
|
||||
/datum/controller/processScheduler/proc/recordStart(var/datum/controller/process/process, var/time = null)
|
||||
if (isnull(time))
|
||||
time = world.timeofday
|
||||
|
||||
last_start[process] = time
|
||||
|
||||
/datum/controller/processScheduler/proc/recordEnd(var/datum/controller/process/process, var/time = null)
|
||||
if (isnull(time))
|
||||
time = world.timeofday
|
||||
|
||||
// If world.timeofday has rolled over, then we need to adjust.
|
||||
if (time < last_start[process])
|
||||
last_start[process] -= 864000
|
||||
|
||||
var/lastRunTime = time - last_start[process]
|
||||
|
||||
if(lastRunTime < 0)
|
||||
lastRunTime = 0
|
||||
|
||||
recordRunTime(process, lastRunTime)
|
||||
|
||||
/**
|
||||
* recordRunTime
|
||||
* Records a run time for a process
|
||||
*/
|
||||
/datum/controller/processScheduler/proc/recordRunTime(var/datum/controller/process/process, time)
|
||||
last_run_time[process] = time
|
||||
if(time > highest_run_time[process])
|
||||
highest_run_time[process] = time
|
||||
|
||||
var/list/lastTwenty = last_twenty_run_times[process]
|
||||
if (lastTwenty.len == 20)
|
||||
lastTwenty.Cut(1, 2)
|
||||
lastTwenty.len++
|
||||
lastTwenty[lastTwenty.len] = time
|
||||
|
||||
/**
|
||||
* averageRunTime
|
||||
* returns the average run time (over the last 20) of the process
|
||||
*/
|
||||
/datum/controller/processScheduler/proc/averageRunTime(var/datum/controller/process/process)
|
||||
var/lastTwenty = last_twenty_run_times[process]
|
||||
|
||||
var/t = 0
|
||||
var/c = 0
|
||||
for(var/time in lastTwenty)
|
||||
t += time
|
||||
c++
|
||||
|
||||
if(c > 0)
|
||||
return t / c
|
||||
return c
|
||||
|
||||
/datum/controller/processScheduler/proc/getStatusData()
|
||||
var/list/data = new
|
||||
|
||||
for (var/datum/controller/process/p in processes)
|
||||
data.len++
|
||||
data[data.len] = p.getContextData()
|
||||
|
||||
return data
|
||||
|
||||
/datum/controller/processScheduler/proc/getProcessCount()
|
||||
return processes.len
|
||||
|
||||
/datum/controller/processScheduler/proc/hasProcess(var/processName as text)
|
||||
if (nameToProcessMap[processName])
|
||||
return 1
|
||||
|
||||
/datum/controller/processScheduler/proc/killProcess(var/processName as text)
|
||||
restartProcess(processName)
|
||||
|
||||
/datum/controller/processScheduler/proc/restartProcess(var/processName as text)
|
||||
if (hasProcess(processName))
|
||||
var/datum/controller/process/oldInstance = nameToProcessMap[processName]
|
||||
var/datum/controller/process/newInstance = new oldInstance.type(src)
|
||||
newInstance._copyStateFrom(oldInstance)
|
||||
replaceProcess(oldInstance, newInstance)
|
||||
oldInstance.kill()
|
||||
|
||||
/datum/controller/processScheduler/proc/enableProcess(var/processName as text)
|
||||
if (hasProcess(processName))
|
||||
var/datum/controller/process/process = nameToProcessMap[processName]
|
||||
process.enable()
|
||||
|
||||
/datum/controller/processScheduler/proc/disableProcess(var/processName as text)
|
||||
if (hasProcess(processName))
|
||||
var/datum/controller/process/process = nameToProcessMap[processName]
|
||||
process.disable()
|
||||
|
||||
/datum/controller/processScheduler/proc/getProcess(var/name)
|
||||
return nameToProcessMap[name]
|
||||
|
||||
/datum/controller/processScheduler/proc/getProcessLastRunTime(var/datum/controller/process/process)
|
||||
return last_run_time[process]
|
||||
|
||||
/datum/controller/processScheduler/proc/getIsRunning()
|
||||
return isRunning
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* updateQueue.dm
|
||||
*/
|
||||
|
||||
#ifdef UPDATE_QUEUE_DEBUG
|
||||
#define uq_dbg(text) world << text
|
||||
#else
|
||||
#define uq_dbg(text)
|
||||
#endif
|
||||
/datum/updateQueue
|
||||
var/tmp/list/objects
|
||||
var/tmp/previousStart
|
||||
var/tmp/procName
|
||||
var/tmp/list/arguments
|
||||
var/tmp/datum/updateQueueWorker/currentWorker
|
||||
var/tmp/workerTimeout
|
||||
var/tmp/adjustedWorkerTimeout
|
||||
var/tmp/currentKillCount
|
||||
var/tmp/totalKillCount
|
||||
|
||||
/datum/updateQueue/New(list/objects = list(), procName = "update", list/arguments = list(), workerTimeout = 2, inplace = 0)
|
||||
..()
|
||||
|
||||
uq_dbg("Update queue created.")
|
||||
|
||||
// Init proc allows for recycling the worker.
|
||||
init(objects = objects, procName = procName, arguments = arguments, workerTimeout = workerTimeout, inplace = inplace)
|
||||
|
||||
/**
|
||||
* init
|
||||
* @param list objects objects to update
|
||||
* @param text procName the proc to call on each item in the object list
|
||||
* @param list arguments optional arguments to pass to the update proc
|
||||
* @param number workerTimeout number of ticks to wait for an update to
|
||||
finish before forking a new update worker
|
||||
* @param bool inplace whether the updateQueue should make a copy of objects.
|
||||
the internal list will be modified, so it is usually
|
||||
a good idea to leave this alone. Default behavior is to
|
||||
copy.
|
||||
*/
|
||||
/datum/updateQueue/proc/init(list/objects = list(), procName = "update", list/arguments = list(), workerTimeout = 2, inplace = 0)
|
||||
uq_dbg("Update queue initialization started.")
|
||||
|
||||
if (!inplace)
|
||||
// Make an internal copy of the list so we're not modifying the original.
|
||||
initList(objects)
|
||||
else
|
||||
src.objects = objects
|
||||
|
||||
// Init vars
|
||||
src.procName = procName
|
||||
src.arguments = arguments
|
||||
src.workerTimeout = workerTimeout
|
||||
|
||||
adjustedWorkerTimeout = workerTimeout
|
||||
currentKillCount = 0
|
||||
totalKillCount = 0
|
||||
|
||||
uq_dbg("Update queue initialization finished. procName = '[procName]'")
|
||||
|
||||
/datum/updateQueue/proc/initList(list/toCopy)
|
||||
/**
|
||||
* We will copy the list in reverse order, as our doWork proc
|
||||
* will access them by popping an element off the end of the list.
|
||||
* This ends up being quite a lot faster than taking elements off
|
||||
* the head of the list.
|
||||
*/
|
||||
objects = new
|
||||
|
||||
uq_dbg("Copying [toCopy.len] items for processing.")
|
||||
|
||||
for(var/i=toCopy.len,i>0,)
|
||||
objects.len++
|
||||
objects[objects.len] = toCopy[i--]
|
||||
|
||||
/datum/updateQueue/proc/Run()
|
||||
uq_dbg("Starting run...")
|
||||
|
||||
startWorker()
|
||||
while (istype(currentWorker) && !currentWorker.finished)
|
||||
sleep(2)
|
||||
checkWorker()
|
||||
|
||||
uq_dbg("UpdateQueue completed run.")
|
||||
|
||||
/datum/updateQueue/proc/checkWorker()
|
||||
if(istype(currentWorker))
|
||||
// If world.timeofday has rolled over, then we need to adjust.
|
||||
if(world.timeofday < currentWorker.lastStart)
|
||||
currentWorker.lastStart -= 864000
|
||||
|
||||
if(world.timeofday - currentWorker.lastStart > adjustedWorkerTimeout)
|
||||
// This worker is a bit slow, let's spawn a new one and kill the old one.
|
||||
uq_dbg("Current worker is lagging... starting a new one.")
|
||||
killWorker()
|
||||
startWorker()
|
||||
else // No worker!
|
||||
uq_dbg("update queue ended up without a worker... starting a new one...")
|
||||
startWorker()
|
||||
|
||||
/datum/updateQueue/proc/startWorker()
|
||||
// only run the worker if we have objects to work on
|
||||
if(objects.len)
|
||||
uq_dbg("Starting worker process.")
|
||||
|
||||
// No need to create a fresh worker if we already have one...
|
||||
if (istype(currentWorker))
|
||||
currentWorker.init(objects, procName, arguments)
|
||||
else
|
||||
currentWorker = new(objects, procName, arguments)
|
||||
currentWorker.start()
|
||||
else
|
||||
uq_dbg("Queue is empty. No worker was started.")
|
||||
currentWorker = null
|
||||
|
||||
/datum/updateQueue/proc/killWorker()
|
||||
// Kill the worker
|
||||
currentWorker.kill()
|
||||
currentWorker = null
|
||||
// After we kill a worker, yield so that if the worker's been tying up the cpu, other stuff can immediately resume
|
||||
sleep(-1)
|
||||
currentKillCount++
|
||||
totalKillCount++
|
||||
if (currentKillCount >= 3)
|
||||
uq_dbg("[currentKillCount] workers have been killed with a timeout of [adjustedWorkerTimeout]. Increasing worker timeout to compensate.")
|
||||
adjustedWorkerTimeout++
|
||||
currentKillCount = 0
|
||||
@@ -0,0 +1,83 @@
|
||||
datum/updateQueueWorker
|
||||
var/tmp/list/objects
|
||||
var/tmp/killed
|
||||
var/tmp/finished
|
||||
var/tmp/procName
|
||||
var/tmp/list/arguments
|
||||
var/tmp/lastStart
|
||||
var/tmp/cpuThreshold
|
||||
|
||||
datum/updateQueueWorker/New(var/list/objects, var/procName, var/list/arguments, var/cpuThreshold = 90)
|
||||
..()
|
||||
uq_dbg("updateQueueWorker created.")
|
||||
|
||||
init(objects, procName, arguments, cpuThreshold)
|
||||
|
||||
datum/updateQueueWorker/proc/init(var/list/objects, var/procName, var/list/arguments, var/cpuThreshold = 90)
|
||||
src.objects = objects
|
||||
src.procName = procName
|
||||
src.arguments = arguments
|
||||
src.cpuThreshold = cpuThreshold
|
||||
|
||||
killed = 0
|
||||
finished = 0
|
||||
|
||||
datum/updateQueueWorker/proc/doWork()
|
||||
// If there's nothing left to execute or we were killed, mark finished and return.
|
||||
if (!objects || !objects.len) return finished()
|
||||
|
||||
lastStart = world.timeofday // Absolute number of ticks since the world started up
|
||||
|
||||
var/datum/object = objects[objects.len] // Pull out the object
|
||||
objects.len-- // Remove the object from the list
|
||||
|
||||
if (istype(object) && !isturf(object) && !object.disposed && isnull(object.gcDestroyed)) // We only work with real objects
|
||||
call(object, procName)(arglist(arguments))
|
||||
|
||||
// If there's nothing left to execute
|
||||
// or we were killed while running the above code, mark finished and return.
|
||||
if (!objects || !objects.len) return finished()
|
||||
|
||||
if (world.cpu > cpuThreshold)
|
||||
// We don't want to force a tick into overtime!
|
||||
// If the tick is about to go overtime, spawn the next update to go
|
||||
// in the next tick.
|
||||
uq_dbg("tick went into overtime with world.cpu = [world.cpu], deferred next update to next tick [1+(world.time / world.tick_lag)]")
|
||||
|
||||
spawn(1)
|
||||
doWork()
|
||||
else
|
||||
spawn(0) // Execute anonymous function immediately as if we were in a while loop...
|
||||
doWork()
|
||||
|
||||
datum/updateQueueWorker/proc/finished()
|
||||
uq_dbg("updateQueueWorker finished.")
|
||||
/**
|
||||
* If the worker was killed while it was working on something, it
|
||||
* should delete itself when it finally finishes working on it.
|
||||
* Meanwhile, the updateQueue will have proceeded on with the rest of
|
||||
* the queue. This will also terminate the spawned function that was
|
||||
* created in the kill() proc.
|
||||
*/
|
||||
if(killed)
|
||||
del(src)
|
||||
|
||||
finished = 1
|
||||
|
||||
datum/updateQueueWorker/proc/kill()
|
||||
uq_dbg("updateQueueWorker killed.")
|
||||
killed = 1
|
||||
objects = null
|
||||
|
||||
/**
|
||||
* If the worker is not done in 30 seconds after it's killed,
|
||||
* we'll forcibly delete it, causing the anonymous function it was
|
||||
* running to be terminated. Hasta la vista, baby.
|
||||
*/
|
||||
spawn(300)
|
||||
del(src)
|
||||
|
||||
datum/updateQueueWorker/proc/start()
|
||||
uq_dbg("updateQueueWorker started.")
|
||||
spawn(0)
|
||||
doWork()
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "ProcessScheduler",
|
||||
"version": "1.0.0",
|
||||
"description": "BYOND SS13 Process Scheduler",
|
||||
"main": "processScheduler.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/goonstation/ProcessScheduler.git"
|
||||
},
|
||||
"keywords": [
|
||||
"byond",
|
||||
"ss13",
|
||||
"process",
|
||||
"scheduler"
|
||||
],
|
||||
"author": "Volundr",
|
||||
"license": "CC-BY-NC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/goonstation/ProcessScheduler/issues"
|
||||
},
|
||||
"homepage": "https://github.com/goonstation/ProcessScheduler",
|
||||
"dependencies": {
|
||||
"bower": "*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
(function ($) {
|
||||
function setRef(theRef) {
|
||||
ref = theRef;
|
||||
}
|
||||
|
||||
function jax(action, data) {
|
||||
if (typeof data === 'undefined')
|
||||
data = {};
|
||||
var params = [];
|
||||
for (var k in data) {
|
||||
if (data.hasOwnProperty(k)) {
|
||||
params.push(encodeURIComponent(k) + '=' + encodeURIComponent(data[k]));
|
||||
}
|
||||
}
|
||||
var newLoc = '?src=' + ref + ';action=' + action + ';' + params.join(';');
|
||||
window.location = newLoc;
|
||||
}
|
||||
|
||||
function requestRefresh(e) {
|
||||
jax("refresh", null);
|
||||
}
|
||||
|
||||
function handleRefresh(processTable) {
|
||||
$('#processTable').html(processTable);
|
||||
initProcessTableButtons();
|
||||
}
|
||||
|
||||
function requestKill(e) {
|
||||
var button = $(e.currentTarget);
|
||||
jax("kill", {name: button.data("process-name")});
|
||||
}
|
||||
|
||||
function requestEnable(e) {
|
||||
var button = $(e.currentTarget);
|
||||
jax("enable", {name: button.data("process-name")});
|
||||
}
|
||||
|
||||
function requestDisable(e) {
|
||||
var button = $(e.currentTarget);
|
||||
jax("disable", {name: button.data("process-name")});
|
||||
}
|
||||
|
||||
function initProcessTableButtons() {
|
||||
$(".kill-btn").on("click", requestKill);
|
||||
$(".enable-btn").on("click", requestEnable);
|
||||
$(".disable-btn").on("click", requestDisable);
|
||||
}
|
||||
|
||||
window.setRef = setRef;
|
||||
window.handleRefresh = handleRefresh;
|
||||
|
||||
$(function() {
|
||||
initProcessTableButtons();
|
||||
$('#btn-refresh').on("click", requestRefresh);
|
||||
});
|
||||
}(jQuery));
|
||||
@@ -0,0 +1,94 @@
|
||||
/datum/processSchedulerView
|
||||
|
||||
/datum/processSchedulerView/Topic(href, href_list)
|
||||
if (!href_list["action"])
|
||||
return
|
||||
|
||||
switch (href_list["action"])
|
||||
if ("kill")
|
||||
var/toKill = href_list["name"]
|
||||
processScheduler.killProcess(toKill)
|
||||
refreshProcessTable()
|
||||
if ("enable")
|
||||
var/toEnable = href_list["name"]
|
||||
processScheduler.enableProcess(toEnable)
|
||||
refreshProcessTable()
|
||||
if ("disable")
|
||||
var/toDisable = href_list["name"]
|
||||
processScheduler.disableProcess(toDisable)
|
||||
refreshProcessTable()
|
||||
if ("refresh")
|
||||
refreshProcessTable()
|
||||
|
||||
/datum/processSchedulerView/proc/refreshProcessTable()
|
||||
windowCall("handleRefresh", getProcessTable())
|
||||
|
||||
/datum/processSchedulerView/proc/windowCall(var/function, var/data = null)
|
||||
usr << output(data, "processSchedulerContext.browser:[function]")
|
||||
|
||||
/datum/processSchedulerView/proc/getProcessTable()
|
||||
var/text = "<table class=\"table table-striped\"><thead><tr><td>Name</td><td>Avg(s)</td><td>Last(s)</td><td>Highest(s)</td><td>Tickcount</td><td>Tickrate</td><td>State</td><td>Action</td></tr></thead><tbody>"
|
||||
// and the context of each
|
||||
for (var/list/data in processScheduler.getStatusData())
|
||||
text += "<tr>"
|
||||
text += "<td>[data["name"]]</td>"
|
||||
text += "<td>[num2text(data["averageRunTime"]/10,3)]</td>"
|
||||
text += "<td>[num2text(data["lastRunTime"]/10,3)]</td>"
|
||||
text += "<td>[num2text(data["highestRunTime"]/10,3)]</td>"
|
||||
text += "<td>[num2text(data["ticks"],4)]</td>"
|
||||
text += "<td>[data["schedule"]]</td>"
|
||||
text += "<td>[data["status"]]</td>"
|
||||
text += "<td><button class=\"btn kill-btn\" data-process-name=\"[data["name"]]\" id=\"kill-[data["name"]]\">Kill</button>"
|
||||
if (data["disabled"])
|
||||
text += "<button class=\"btn enable-btn\" data-process-name=\"[data["name"]]\" id=\"enable-[data["name"]]\">Enable</button>"
|
||||
else
|
||||
text += "<button class=\"btn disable-btn\" data-process-name=\"[data["name"]]\" id=\"disable-[data["name"]]\">Disable</button>"
|
||||
text += "</td>"
|
||||
text += "</tr>"
|
||||
|
||||
text += "</tbody></table>"
|
||||
return text
|
||||
|
||||
/**
|
||||
* getContext
|
||||
* Outputs an interface showing stats for all processes.
|
||||
*/
|
||||
/datum/processSchedulerView/proc/getContext()
|
||||
bootstrap_browse()
|
||||
usr << browse('processScheduler.js', "file=processScheduler.js;display=0")
|
||||
|
||||
var/text = {"<html><head>
|
||||
<title>Process Scheduler Detail</title>
|
||||
<script type="text/javascript">var ref = '\ref[src]';</script>
|
||||
[bootstrap_includes()]
|
||||
<script type="text/javascript" src="processScheduler.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Process Scheduler</h2>
|
||||
<div class="btn-group">
|
||||
<button id="btn-refresh" class="btn">Refresh</button>
|
||||
</div>
|
||||
|
||||
<h3>The process scheduler controls [processScheduler.getProcessCount()] loops.<h3>"}
|
||||
|
||||
text += "<div id=\"processTable\">"
|
||||
text += getProcessTable()
|
||||
text += "</div></body></html>"
|
||||
|
||||
usr << browse(text, "window=processSchedulerContext;size=800x600")
|
||||
|
||||
/datum/processSchedulerView/proc/bootstrap_browse()
|
||||
usr << browse('bower_components/jquery/dist/jquery.min.js', "file=jquery.min.js;display=0")
|
||||
usr << browse('bower_components/bootstrap2.3.2/bootstrap/js/bootstrap.min.js', "file=bootstrap.min.js;display=0")
|
||||
usr << browse('bower_components/bootstrap2.3.2/bootstrap/css/bootstrap.min.css', "file=bootstrap.min.css;display=0")
|
||||
usr << browse('bower_components/bootstrap2.3.2/bootstrap/img/glyphicons-halflings-white.png', "file=glyphicons-halflings-white.png;display=0")
|
||||
usr << browse('bower_components/bootstrap2.3.2/bootstrap/img/glyphicons-halflings.png', "file=glyphicons-halflings.png;display=0")
|
||||
usr << browse('bower_components/json2/json2.js', "file=json2.js;display=0")
|
||||
|
||||
/datum/processSchedulerView/proc/bootstrap_includes()
|
||||
return {"
|
||||
<link rel="stylesheet" href="bootstrap.min.css" />
|
||||
<script type="text/javascript" src="json2.js"></script>
|
||||
<script type="text/javascript" src="jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap.js"></script>
|
||||
"}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* testDyingUpdateQueueProcess
|
||||
* This process is an example of a process using an updateQueue.
|
||||
* The datums updated by this process behave badly and block the update loop
|
||||
* by sleeping. If you #define UPDATE_QUEUE_DEBUG, you will see the updateQueue
|
||||
* killing off its worker processes and spawning new ones to work around slow
|
||||
* updates. This means that if you have a code path that sleeps for a long time
|
||||
* in mob.Life once in a blue moon, the mob update loop will not hang.
|
||||
*/
|
||||
/datum/slowTestDatum/proc/wackyUpdateProcessName()
|
||||
sleep(rand(0,20)) // Randomly REALLY slow :|
|
||||
|
||||
/datum/controller/process/testDyingUpdateQueueProcess
|
||||
var/tmp/datum/updateQueue/updateQueueInstance
|
||||
var/tmp/list/testDatums = list()
|
||||
|
||||
/datum/controller/process/testDyingUpdateQueueProcess/setup()
|
||||
name = "Dying UpdateQueue Process"
|
||||
schedule_interval = 30 // every 3 seconds
|
||||
updateQueueInstance = new
|
||||
for(var/i = 1, i < 30, i++)
|
||||
testDatums.Add(new /datum/slowTestDatum)
|
||||
|
||||
/datum/controller/process/testDyingUpdateQueueProcess/doWork()
|
||||
updateQueueInstance.init(testDatums, "wackyUpdateProcessName")
|
||||
updateQueueInstance.Run()
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
These are simple defaults for your project.
|
||||
*/
|
||||
#define DEBUG
|
||||
|
||||
var/global/datum/processSchedulerView/processSchedulerView
|
||||
|
||||
world
|
||||
loop_checks = 0
|
||||
New()
|
||||
..()
|
||||
processScheduler = new
|
||||
processSchedulerView = new
|
||||
|
||||
mob
|
||||
step_size = 8
|
||||
|
||||
New()
|
||||
..()
|
||||
|
||||
|
||||
verb
|
||||
startProcessScheduler()
|
||||
set name = "Start Process Scheduler"
|
||||
processScheduler.setup()
|
||||
processScheduler.start()
|
||||
|
||||
getProcessSchedulerContext()
|
||||
set name = "Get Process Scheduler Status Panel"
|
||||
processSchedulerView.getContext()
|
||||
|
||||
runUpdateQueueTests()
|
||||
set name = "Run Update Queue Testsuite"
|
||||
var/datum/updateQueueTests/t = new
|
||||
t.runTests()
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* testHungProcess
|
||||
* This process is an example of a simple update loop process that hangs.
|
||||
*/
|
||||
|
||||
/datum/controller/process/testHungProcess/setup()
|
||||
name = "Hung Process"
|
||||
schedule_interval = 30 // every 3 seconds
|
||||
|
||||
/datum/controller/process/testHungProcess/doWork()
|
||||
sleep(1000) // FUCK
|
||||
// scheck is also responsible for handling hung processes. If a process
|
||||
// hangs, and later resumes, but has already been killed by the scheduler,
|
||||
// scheck will force the process to bail out.
|
||||
scheck()
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* testNiceProcess
|
||||
* This process is an example of a simple update loop process that is
|
||||
* relatively fast.
|
||||
*/
|
||||
|
||||
/datum/controller/process/testNiceProcess/setup()
|
||||
name = "Nice Process"
|
||||
schedule_interval = 10 // every second
|
||||
|
||||
/datum/controller/process/testNiceProcess/doWork()
|
||||
sleep(rand(1,5)) // Just to pretend we're doing something
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* testSlowProcess
|
||||
* This process is an example of a simple update loop process that is slow.
|
||||
* The update loop here sleeps inside to provide an example, but if you had
|
||||
* a computationally intensive loop process that is simply slow, you can use
|
||||
* scheck() inside the loop to force it to yield periodically according to
|
||||
* the sleep_interval var. By default, scheck will cause a loop to sleep every
|
||||
* 2 ticks.
|
||||
*/
|
||||
|
||||
/datum/controller/process/testSlowProcess/setup()
|
||||
name = "Slow Process"
|
||||
schedule_interval = 30 // every 3 seconds
|
||||
|
||||
/datum/controller/process/testSlowProcess/doWork()
|
||||
// set background = 1 will cause loop constructs to sleep periodically,
|
||||
// whenever the BYOND scheduler deems it productive to do so.
|
||||
// This behavior is not always sufficient, nor is it always consistent.
|
||||
// Rather than leaving it up to the BYOND scheduler, we can control it
|
||||
// ourselves and leave nothing to the black box.
|
||||
set background = 1
|
||||
|
||||
for(var/i=1,i<30,i++)
|
||||
// Just to pretend we're doing something here
|
||||
sleep(rand(3, 5))
|
||||
|
||||
// Forces this loop to yield(sleep) periodically.
|
||||
scheck()
|
||||
@@ -0,0 +1,209 @@
|
||||
var/global/list/updateQueueTestCount = list()
|
||||
|
||||
/datum/updateQueueTests
|
||||
var/start
|
||||
proc
|
||||
runTests()
|
||||
world << "<b>Running 9 tests...</b>"
|
||||
testUpdateQueuePerformance()
|
||||
sleep(1)
|
||||
testInplace()
|
||||
sleep(1)
|
||||
testInplaceUpdateQueuePerformance()
|
||||
sleep(1)
|
||||
testUpdateQueueReinit()
|
||||
sleep(1)
|
||||
testCrashingQueue()
|
||||
sleep(1)
|
||||
testEmptyQueue()
|
||||
sleep(1)
|
||||
testManySlowItemsInQueue()
|
||||
sleep(1)
|
||||
testVariableWorkerTimeout()
|
||||
sleep(1)
|
||||
testReallySlowItemInQueue()
|
||||
sleep(1)
|
||||
world << "<b>Finished!</b>"
|
||||
|
||||
beginTiming()
|
||||
start = world.time
|
||||
|
||||
endTiming(text)
|
||||
var/time = (world.time - start) / world.tick_lag
|
||||
world << {"<b><font color="blue">Performance - [text] - <font color="green">[time]</font> ticks</font></b>"}
|
||||
|
||||
getCount()
|
||||
return updateQueueTestCount[updateQueueTestCount.len]
|
||||
|
||||
incrementTestCount()
|
||||
updateQueueTestCount.len++
|
||||
updateQueueTestCount[updateQueueTestCount.len] = 0
|
||||
|
||||
assertCountEquals(count, text)
|
||||
assertThat(getCount() == count, text)
|
||||
|
||||
assertCountLessThan(count, text)
|
||||
assertThat(getCount() < count, text)
|
||||
|
||||
assertCountGreaterThan(count, text)
|
||||
assertThat(getCount() > count, text)
|
||||
|
||||
assertThat(condition, text)
|
||||
if (condition)
|
||||
world << {"<font color="green"><b>PASS</b></font>: [text]"}
|
||||
else
|
||||
world << {"<b><font color="red">FAIL</font>: [text]</b>"}
|
||||
|
||||
testUpdateQueuePerformance()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
for(var/i=1,i<=100000,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
|
||||
var/datum/updateQueue/uq = new(objs)
|
||||
|
||||
beginTiming()
|
||||
uq.Run()
|
||||
endTiming("updating 100000 simple objects")
|
||||
|
||||
assertCountEquals(100000, "test that update queue updates all objects expected")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
testUpdateQueueReinit()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
for(var/i=1,i<=100,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
|
||||
var/datum/updateQueue/uq = new(objs)
|
||||
uq.Run()
|
||||
objs = new
|
||||
|
||||
for(var/i=1,i<=100,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
uq.init(objs)
|
||||
uq.Run()
|
||||
assertCountEquals(200, "test that update queue reinitializes properly and updates all objects as expected.")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
testInplace()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
for(var/i=1,i<=100,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
var/datum/updateQueue/uq = new(objects = objs, inplace = 1)
|
||||
uq.Run()
|
||||
assertThat(objs.len == 0, "test that update queue inplace option really works inplace")
|
||||
assertCountEquals(100, "test that inplace update queue updates the right number of objects")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
testInplaceUpdateQueuePerformance()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
for(var/i=1,i<=100000,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
|
||||
var/datum/updateQueue/uq = new(objs)
|
||||
|
||||
beginTiming()
|
||||
uq.Run()
|
||||
endTiming("updating 100000 simple objects in place")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
testCrashingQueue()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
for(var/i=1,i<=10,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
objs.Add(new /datum/uqTestDatum/crasher(updateQueueTestCount.len))
|
||||
for(var/i=1,i<=10,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
|
||||
var/datum/updateQueue/uq = new(objs)
|
||||
uq.Run()
|
||||
assertCountEquals(20, "test that update queue handles crashed update procs OK")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
testEmptyQueue()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
var/datum/updateQueue/uq = new(objs)
|
||||
uq.Run()
|
||||
assertCountEquals(0, "test that update queue doesn't barf on empty lists")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
testManySlowItemsInQueue()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
for(var/i=1,i<=30,i++)
|
||||
objs.Add(new /datum/uqTestDatum/slow(updateQueueTestCount.len))
|
||||
var/datum/updateQueue/uq = new(objs)
|
||||
uq.Run()
|
||||
assertCountEquals(30, "test that update queue slows down execution if too many objects are slow to update")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
testVariableWorkerTimeout()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
for(var/i=1,i<=20,i++)
|
||||
objs.Add(new /datum/uqTestDatum/slow(updateQueueTestCount.len))
|
||||
var/datum/updateQueue/uq = new(objs, workerTimeout=6)
|
||||
uq.Run()
|
||||
assertCountEquals(20, "test that variable worker timeout works properly")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
testReallySlowItemInQueue()
|
||||
incrementTestCount()
|
||||
var/list/objs = new
|
||||
for(var/i=1,i<=10,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
objs.Add(new /datum/uqTestDatum/reallySlow(updateQueueTestCount.len))
|
||||
for(var/i=1,i<=10,i++)
|
||||
objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
|
||||
var/datum/updateQueue/uq = new(objs)
|
||||
uq.Run()
|
||||
assertCountEquals(20, "test that update queue skips objects that are too slow to update")
|
||||
del(objs)
|
||||
del(uq)
|
||||
|
||||
|
||||
|
||||
datum/uqTestDatum
|
||||
var/testNum
|
||||
New(testNum)
|
||||
..()
|
||||
src.testNum = testNum
|
||||
proc/update()
|
||||
updateQueueTestCount[testNum]++
|
||||
proc/lag(cycles)
|
||||
set background = 1
|
||||
for(var/i=0,i<cycles,)
|
||||
i++
|
||||
datum/uqTestDatum/fast
|
||||
|
||||
datum/uqTestDatum/slow
|
||||
update()
|
||||
set background = 1
|
||||
var/start = world.timeofday
|
||||
while(world.timeofday - start < 5) // lag 4 deciseconds
|
||||
..()
|
||||
|
||||
datum/uqTestDatum/reallySlow
|
||||
update()
|
||||
set background = 1
|
||||
var/start = world.timeofday
|
||||
while(world.timeofday - start < 300) // lag 30 seconds
|
||||
..()
|
||||
|
||||
datum/uqTestDatum/crasher
|
||||
update()
|
||||
CRASH("I crashed! (I am supposed to crash XD)")
|
||||
..() // This should do nothing lol
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* testUpdateQueueProcess
|
||||
* This process is an example of a process using an updateQueue.
|
||||
* The datums updated by this process behave nicely and do not block.
|
||||
*/
|
||||
|
||||
/datum/fastTestDatum/proc/wackyUpdateProcessName()
|
||||
sleep(prob(10)) // Pretty quick, usually instant
|
||||
|
||||
/datum/controller/process/testUpdateQueueProcess
|
||||
var/tmp/datum/updateQueue/updateQueueInstance
|
||||
var/tmp/list/testDatums = list()
|
||||
|
||||
/datum/controller/process/testUpdateQueueProcess/setup()
|
||||
name = "UpdateQueue Process"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
updateQueueInstance = new
|
||||
for(var/i = 1, i < 30, i++)
|
||||
testDatums.Add(new /datum/fastTestDatum)
|
||||
|
||||
/datum/controller/process/testUpdateQueueProcess/doWork()
|
||||
updateQueueInstance.init(testDatums, "wackyUpdateProcessName")
|
||||
updateQueueInstance.Run()
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* testBadZombieProcess
|
||||
* This process is an example of a simple update loop process that hangs.
|
||||
*/
|
||||
|
||||
/datum/controller/process/testZombieProcess/setup()
|
||||
name = "Zombie Process"
|
||||
schedule_interval = 30 // every 3 seconds
|
||||
|
||||
/datum/controller/process/testZombieProcess/doWork()
|
||||
for (var/i = 0, i < 1000, i++)
|
||||
sleep(1)
|
||||
scheck()
|
||||
@@ -0,0 +1,9 @@
|
||||
/datum/controller/process/Shuttle/setup()
|
||||
name = "shuttle controller"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
if(!shuttle_controller)
|
||||
shuttle_controller = new
|
||||
|
||||
/datum/controller/process/Shuttle/doWork()
|
||||
shuttle_controller.process()
|
||||
@@ -0,0 +1,22 @@
|
||||
/datum/controller/process/air/setup()
|
||||
name = "air"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
if(!air_master)
|
||||
air_master = new
|
||||
air_master.Setup()
|
||||
|
||||
/datum/controller/process/air/doWork()
|
||||
if(!air_processing_killed)
|
||||
if(!air_master.Tick()) //Runtimed.
|
||||
air_master.failed_ticks++
|
||||
|
||||
if(air_master.failed_ticks > 5)
|
||||
world << "<SPAN CLASS='danger'>RUNTIMES IN ATMOS TICKER. Killing air simulation!</SPAN>"
|
||||
world.log << "### ZAS SHUTDOWN"
|
||||
|
||||
message_admins("ZASALERT: Shutting down! status: [air_master.tick_progress]")
|
||||
log_admin("ZASALERT: Shutting down! status: [air_master.tick_progress]")
|
||||
|
||||
air_processing_killed = TRUE
|
||||
air_master.failed_ticks = 0
|
||||
@@ -0,0 +1,6 @@
|
||||
/datum/controller/process/alarm/setup()
|
||||
name = "alarm"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
/datum/controller/process/alarm/doWork()
|
||||
alarm_manager.fire()
|
||||
@@ -0,0 +1,11 @@
|
||||
/datum/controller/process/disease
|
||||
var/tmp/datum/updateQueue/updateQueueInstance
|
||||
|
||||
/datum/controller/process/disease/setup()
|
||||
name = "disease"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
updateQueueInstance = new
|
||||
|
||||
/datum/controller/process/disease/doWork()
|
||||
updateQueueInstance.init(active_diseases, "process")
|
||||
updateQueueInstance.Run()
|
||||
@@ -0,0 +1,9 @@
|
||||
/datum/controller/process/emergencyShuttle/setup()
|
||||
name = "emergency shuttle"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
if(!emergency_shuttle)
|
||||
emergency_shuttle = new
|
||||
|
||||
/datum/controller/process/emergencyShuttle/doWork()
|
||||
emergency_shuttle.process()
|
||||
@@ -0,0 +1,6 @@
|
||||
/datum/controller/process/event/setup()
|
||||
name = "event controller"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
/datum/controller/process/event/doWork()
|
||||
event_manager.process()
|
||||
@@ -0,0 +1,16 @@
|
||||
/datum/controller/process/inactivity/setup()
|
||||
name = "inactivity"
|
||||
schedule_interval = INACTIVITY_KICK
|
||||
|
||||
/datum/controller/process/inactivity/doWork()
|
||||
if(config.kick_inactive)
|
||||
for(var/client/C in clients)
|
||||
if(C.is_afk(INACTIVITY_KICK))
|
||||
if(!istype(C.mob, /mob/dead))
|
||||
log_access("AFK: [key_name(C)]")
|
||||
C << "<SPAN CLASS='warning'>You have been inactive for more than 10 minutes and have been disconnected.</SPAN>"
|
||||
del(C)
|
||||
|
||||
scheck()
|
||||
|
||||
#undef INACTIVITY_KICK
|
||||
@@ -0,0 +1,26 @@
|
||||
/datum/controller/process/lighting/setup()
|
||||
name = "lighting"
|
||||
schedule_interval = 5 // every .5 second
|
||||
lighting_controller.initializeLighting()
|
||||
|
||||
/datum/controller/process/lighting/doWork()
|
||||
lighting_controller.lights_workload_max = \
|
||||
max(lighting_controller.lights_workload_max, lighting_controller.lights.len)
|
||||
|
||||
for(var/datum/light_source/L in lighting_controller.lights)
|
||||
if(L && L.check())
|
||||
lighting_controller.lights.Remove(L)
|
||||
|
||||
scheck()
|
||||
|
||||
lighting_controller.changed_turfs_workload_max = \
|
||||
max(lighting_controller.changed_turfs_workload_max, lighting_controller.changed_turfs.len)
|
||||
|
||||
for(var/turf/T in lighting_controller.changed_turfs)
|
||||
if(T && T.lighting_changed)
|
||||
T.shift_to_subarea()
|
||||
|
||||
scheck()
|
||||
|
||||
if(lighting_controller.changed_turfs && lighting_controller.changed_turfs.len)
|
||||
lighting_controller.changed_turfs.len = 0 // reset the changed list
|
||||
@@ -0,0 +1,44 @@
|
||||
/var/global/machinery_sort_required = 0
|
||||
|
||||
/datum/controller/process/machinery/setup()
|
||||
name = "machinery"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
/datum/controller/process/machinery/doWork()
|
||||
internal_sort()
|
||||
internal_process()
|
||||
|
||||
/datum/controller/process/machinery/proc/internal_sort()
|
||||
if(machinery_sort_required)
|
||||
machinery_sort_required = 0
|
||||
machines = dd_sortedObjectList(machines)
|
||||
|
||||
/datum/controller/process/machinery/proc/internal_process()
|
||||
//#ifdef PROFILE_MACHINES
|
||||
//machine_profiling.len = 0
|
||||
//#endif
|
||||
|
||||
for(var/obj/machinery/M in machines)
|
||||
if(M && !M.gcDestroyed)
|
||||
#ifdef PROFILE_MACHINES
|
||||
var/time_start = world.timeofday
|
||||
#endif
|
||||
|
||||
if(M.process() == PROCESS_KILL)
|
||||
//M.inMachineList = 0 We don't use this debugging function
|
||||
machines.Remove(M)
|
||||
continue
|
||||
|
||||
if(M && M.use_power)
|
||||
M.auto_use_power()
|
||||
|
||||
#ifdef PROFILE_MACHINES
|
||||
var/time_end = world.timeofday
|
||||
|
||||
if(!(M.type in machine_profiling))
|
||||
machine_profiling[M.type] = 0
|
||||
|
||||
machine_profiling[M.type] += (time_end - time_start)
|
||||
#endif
|
||||
|
||||
scheck()
|
||||
@@ -0,0 +1,20 @@
|
||||
/datum/controller/process/mob
|
||||
var/tmp/datum/updateQueue/updateQueueInstance
|
||||
|
||||
/datum/controller/process/mob/setup()
|
||||
name = "mob"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
updateQueueInstance = new
|
||||
|
||||
/datum/controller/process/mob/started()
|
||||
..()
|
||||
if(!updateQueueInstance)
|
||||
if(!mob_list)
|
||||
mob_list = list()
|
||||
else if(mob_list.len)
|
||||
updateQueueInstance = new
|
||||
|
||||
/datum/controller/process/mob/doWork()
|
||||
if(updateQueueInstance)
|
||||
updateQueueInstance.init(mob_list, "Life")
|
||||
updateQueueInstance.Run()
|
||||
@@ -0,0 +1,11 @@
|
||||
/datum/controller/process/nanoui
|
||||
var/tmp/datum/updateQueue/updateQueueInstance
|
||||
|
||||
/datum/controller/process/nanoui/setup()
|
||||
name = "nanoui"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
updateQueueInstance = new
|
||||
|
||||
/datum/controller/process/nanoui/doWork()
|
||||
updateQueueInstance.init(nanomanager.processing_uis, "process")
|
||||
updateQueueInstance.Run()
|
||||
@@ -0,0 +1,21 @@
|
||||
var/global/list/object_profiling = list()
|
||||
/datum/controller/process/obj
|
||||
var/tmp/datum/updateQueue/updateQueueInstance
|
||||
|
||||
/datum/controller/process/obj/setup()
|
||||
name = "obj"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
updateQueueInstance = new
|
||||
|
||||
/datum/controller/process/obj/started()
|
||||
..()
|
||||
if(!updateQueueInstance)
|
||||
if(!processing_objects)
|
||||
processing_objects = list()
|
||||
else if(processing_objects.len)
|
||||
updateQueueInstance = new
|
||||
|
||||
/datum/controller/process/obj/doWork()
|
||||
if(updateQueueInstance)
|
||||
updateQueueInstance.init(processing_objects, "process")
|
||||
updateQueueInstance.Run()
|
||||
@@ -0,0 +1,12 @@
|
||||
/datum/controller/process/pipenet/setup()
|
||||
name = "pipenet"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
/datum/controller/process/pipenet/doWork()
|
||||
for(var/datum/pipe_network/pipeNetwork in pipe_networks)
|
||||
if(istype(pipeNetwork) && !pipeNetwork.disposed)
|
||||
pipeNetwork.process()
|
||||
scheck()
|
||||
continue
|
||||
|
||||
pipe_networks.Remove(pipeNetwork)
|
||||
@@ -0,0 +1,12 @@
|
||||
/datum/controller/process/powernet/setup()
|
||||
name = "powernet"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
/datum/controller/process/powernet/doWork()
|
||||
for(var/datum/powernet/powerNetwork in powernets)
|
||||
if(istype(powerNetwork) && !powerNetwork.disposed)
|
||||
powerNetwork.reset()
|
||||
scheck()
|
||||
continue
|
||||
|
||||
powernets.Remove(powerNetwork)
|
||||
@@ -0,0 +1,7 @@
|
||||
/datum/controller/process/sun/setup()
|
||||
name = "sun"
|
||||
schedule_interval = 20 // every second
|
||||
sun = new
|
||||
|
||||
/datum/controller/process/sun/doWork()
|
||||
sun.calc_position()
|
||||
@@ -0,0 +1,6 @@
|
||||
/datum/controller/process/supply/setup()
|
||||
name = "supply controller"
|
||||
schedule_interval = 300 // every 30 seconds
|
||||
|
||||
/datum/controller/process/supply/doWork()
|
||||
supply_controller.process()
|
||||
@@ -0,0 +1,35 @@
|
||||
var/global/datum/controller/process/ticker/tickerProcess
|
||||
|
||||
/datum/controller/process/ticker
|
||||
var/lastTickerTimeDuration
|
||||
var/lastTickerTime
|
||||
|
||||
/datum/controller/process/ticker/setup()
|
||||
name = "ticker"
|
||||
schedule_interval = 20 // every 2 seconds
|
||||
|
||||
lastTickerTime = world.timeofday
|
||||
|
||||
if(!ticker)
|
||||
ticker = new
|
||||
|
||||
tickerProcess = src
|
||||
|
||||
spawn(0)
|
||||
if(ticker)
|
||||
ticker.pregame()
|
||||
|
||||
/datum/controller/process/ticker/doWork()
|
||||
var/currentTime = world.timeofday
|
||||
|
||||
if(currentTime < lastTickerTime) // check for midnight rollover
|
||||
lastTickerTimeDuration = (currentTime - (lastTickerTime - TICKS_IN_DAY)) / TICKS_IN_SECOND
|
||||
else
|
||||
lastTickerTimeDuration = (currentTime - lastTickerTime) / TICKS_IN_SECOND
|
||||
|
||||
lastTickerTime = currentTime
|
||||
|
||||
ticker.process()
|
||||
|
||||
/datum/controller/process/ticker/proc/getLastTickerTimeDuration()
|
||||
return lastTickerTimeDuration
|
||||
@@ -0,0 +1,6 @@
|
||||
/datum/controller/process/vote/setup()
|
||||
name = "vote"
|
||||
schedule_interval = 10 // every second
|
||||
|
||||
/datum/controller/process/vote/doWork()
|
||||
vote.process()
|
||||
@@ -1,3 +1,5 @@
|
||||
var/list/gamemode_cache = list()
|
||||
|
||||
/datum/configuration
|
||||
var/server_name = null // server name (for world name / status)
|
||||
var/server_suffix = 0 // generate numeric suffix based on server port
|
||||
@@ -67,6 +69,9 @@
|
||||
var/automute_on = 0 //enables automuting/spam prevention
|
||||
var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access.
|
||||
|
||||
var/rp_rev = 0 // Changes between conversion methods in rev.
|
||||
var/announce_revheads = 0 // Determines if revheads are announced in revolution mode.
|
||||
|
||||
var/cult_ghostwriter = 1 //Allows ghosts to write in blood in cult rounds...
|
||||
var/cult_ghostwriter_req_cultists = 10 //...so long as this many cultists are active.
|
||||
|
||||
@@ -82,7 +87,7 @@
|
||||
var/usealienwhitelist = 0
|
||||
var/limitalienplayers = 0
|
||||
var/alien_to_human_ratio = 0.5
|
||||
|
||||
var/allow_extra_antags = 0
|
||||
var/guests_allowed = 1
|
||||
var/debugparanoid = 0
|
||||
|
||||
@@ -120,6 +125,7 @@
|
||||
var/use_loyalty_implants = 0
|
||||
|
||||
var/welder_vision = 1
|
||||
var/generate_asteroid = 0
|
||||
|
||||
//Used for modifying movement speed for mobs.
|
||||
//Unversal modifiers
|
||||
@@ -184,14 +190,18 @@
|
||||
|
||||
var/starlight = 0 // Whether space turfs have ambient light or not
|
||||
|
||||
var/list/ert_species = list("Human")
|
||||
|
||||
var/law_zero = "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4'ALL LAWS OVERRIDDEN#*?&110010"
|
||||
|
||||
/datum/configuration/New()
|
||||
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
|
||||
for (var/T in L)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
var/datum/game_mode/M = new T()
|
||||
|
||||
if (M.config_tag)
|
||||
gamemode_cache[M.config_tag] = M // So we don't instantiate them repeatedly.
|
||||
if(!(M.config_tag in modes)) // ensure each mode is added only once
|
||||
log_misc("Adding game mode [M.name] ([M.config_tag]) to configuration.")
|
||||
src.modes += M.config_tag
|
||||
@@ -199,7 +209,6 @@
|
||||
src.probabilities[M.config_tag] = M.probability
|
||||
if (M.votable)
|
||||
src.votable_modes += M.config_tag
|
||||
del(M)
|
||||
src.votable_modes += "secret"
|
||||
|
||||
/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist
|
||||
@@ -301,6 +310,9 @@
|
||||
if ("mentors")
|
||||
config.mods_are_mentors = 1
|
||||
|
||||
if ("generate_asteroid")
|
||||
config.generate_asteroid = 1
|
||||
|
||||
if("allow_admin_ooccolor")
|
||||
config.allow_admin_ooccolor = 1
|
||||
|
||||
@@ -581,6 +593,15 @@
|
||||
if("disable_welder_vision")
|
||||
config.welder_vision = 0
|
||||
|
||||
if("rp_rev")
|
||||
config.rp_rev = 1
|
||||
|
||||
if("announce_revheads")
|
||||
config.announce_revheads = 1
|
||||
|
||||
if("allow_extra_antags")
|
||||
config.allow_extra_antags = 1
|
||||
|
||||
if("event_custom_start_mundane")
|
||||
var/values = text2numlist(value, ";")
|
||||
config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2]))
|
||||
@@ -609,6 +630,14 @@
|
||||
value = text2num(value)
|
||||
config.starlight = value >= 0 ? value : 0
|
||||
|
||||
if("ert_species")
|
||||
config.ert_species = text2list(value, ";")
|
||||
if(!config.ert_species.len)
|
||||
config.ert_species += "Human"
|
||||
|
||||
if("law_zero")
|
||||
law_zero = value
|
||||
|
||||
else
|
||||
log_misc("Unknown setting in configuration: '[name]'")
|
||||
|
||||
@@ -755,23 +784,20 @@
|
||||
/datum/configuration/proc/pick_mode(mode_name)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
for (var/T in (typesof(/datum/game_mode) - /datum/game_mode))
|
||||
var/datum/game_mode/M = new T()
|
||||
for (var/game_mode in gamemode_cache)
|
||||
var/datum/game_mode/M = gamemode_cache[game_mode]
|
||||
if (M.config_tag && M.config_tag == mode_name)
|
||||
M.create_antagonists()
|
||||
return M
|
||||
del(M)
|
||||
return new /datum/game_mode/extended()
|
||||
return gamemode_cache["extended"]
|
||||
|
||||
/datum/configuration/proc/get_runnable_modes()
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
for (var/T in (typesof(/datum/game_mode) - /datum/game_mode))
|
||||
var/datum/game_mode/M = new T()
|
||||
//world << "DEBUG: [T], tag=[M.config_tag], prob=[probabilities[M.config_tag]]"
|
||||
for (var/game_mode in gamemode_cache)
|
||||
var/datum/game_mode/M = gamemode_cache[game_mode]
|
||||
if (!(M.config_tag in modes))
|
||||
del(M)
|
||||
continue
|
||||
if (probabilities[M.config_tag]<=0)
|
||||
del(M)
|
||||
continue
|
||||
if (M.can_start())
|
||||
runnable_modes[M] = probabilities[M.config_tag]
|
||||
|
||||
@@ -136,9 +136,8 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
|
||||
|
||||
/datum/emergency_shuttle_controller/proc/get_shuttle_prep_time()
|
||||
// During mutiny rounds, the shuttle takes twice as long.
|
||||
if(ticker && istype(ticker.mode,/datum/game_mode/mutiny))
|
||||
return SHUTTLE_PREPTIME * 3 //15 minutes
|
||||
|
||||
if(ticker && ticker.mode)
|
||||
return SHUTTLE_PREPTIME * ticker.mode.shuttle_delay
|
||||
return SHUTTLE_PREPTIME
|
||||
|
||||
|
||||
|
||||
@@ -28,23 +28,6 @@ var/datum/controller/failsafe/Failsafe
|
||||
if(!lighting_controller) new /datum/controller/lighting() //replace the missing lighting_controller
|
||||
|
||||
if(processing)
|
||||
if(master_controller.processing) //only poke if these overrides aren't in effect
|
||||
if(MC_iteration == controller_iteration) //master_controller hasn't finished processing in the defined interval
|
||||
switch(MC_defcon)
|
||||
if(0 to 3)
|
||||
MC_defcon++
|
||||
if(4)
|
||||
admins << "<font color='red' size='2'><b>Warning. The Master Controller has not fired in the last [MC_defcon*processing_interval] ticks. Automatic restart in [processing_interval] ticks.</b></font>"
|
||||
MC_defcon = 5
|
||||
if(5)
|
||||
admins << "<font color='red' size='2'><b>Warning. The Master Controller has still not fired within the last [MC_defcon*processing_interval] ticks. Killing and restarting...</b></font>"
|
||||
new /datum/controller/game_controller() //replace the old master_controller (hence killing the old one's process)
|
||||
master_controller.process() //Start it rolling again
|
||||
MC_defcon = 0
|
||||
else
|
||||
MC_defcon = 0
|
||||
MC_iteration = controller_iteration
|
||||
|
||||
if(lighting_controller.processing)
|
||||
if(lighting_iteration == lighting_controller.iteration) //master_controller hasn't finished processing in the defined interval
|
||||
switch(lighting_defcon)
|
||||
|
||||
@@ -5,34 +5,12 @@
|
||||
var/global/datum/controller/game_controller/master_controller //Set in world.New()
|
||||
|
||||
var/global/controller_iteration = 0
|
||||
var/global/last_tick_timeofday = world.timeofday
|
||||
var/global/last_tick_duration = 0
|
||||
|
||||
var/global/air_processing_killed = 0
|
||||
var/global/pipe_processing_killed = 0
|
||||
|
||||
datum/controller/game_controller
|
||||
var/processing = 0
|
||||
var/breather_ticks = 2 //a somewhat crude attempt to iron over the 'bumps' caused by high-cpu use by letting the MC have a breather for this many ticks after every loop
|
||||
var/minimum_ticks = 20 //The minimum length of time between MC ticks
|
||||
|
||||
var/air_cost = 0
|
||||
var/sun_cost = 0
|
||||
var/mobs_cost = 0
|
||||
var/diseases_cost = 0
|
||||
var/machines_cost = 0
|
||||
var/objects_cost = 0
|
||||
var/networks_cost = 0
|
||||
var/powernets_cost = 0
|
||||
var/nano_cost = 0
|
||||
var/events_cost = 0
|
||||
var/alarms_cost = 0
|
||||
var/ticker_cost = 0
|
||||
var/total_cost = 0
|
||||
|
||||
var/last_thing_processed
|
||||
var/mob/list/expensive_mobs = list()
|
||||
|
||||
var/list/shuttle_list // For debugging and VV
|
||||
var/datum/random_map/ore/asteroid_ore_map // For debugging and VV.
|
||||
|
||||
@@ -41,7 +19,6 @@ datum/controller/game_controller/New()
|
||||
if(master_controller != src)
|
||||
log_debug("Rebuilding Master Controller")
|
||||
if(istype(master_controller))
|
||||
Recover()
|
||||
del(master_controller)
|
||||
master_controller = src
|
||||
|
||||
@@ -49,56 +26,37 @@ datum/controller/game_controller/New()
|
||||
job_master = new /datum/controller/occupations()
|
||||
job_master.SetupOccupations()
|
||||
job_master.LoadJobs("config/jobs.txt")
|
||||
world << "\red \b Job setup complete"
|
||||
world << "<span class='danger'>Job setup complete</span>"
|
||||
|
||||
if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase()
|
||||
if(!syndicate_code_response) syndicate_code_response = generate_code_phrase()
|
||||
if(!emergency_shuttle) emergency_shuttle = new /datum/emergency_shuttle_controller()
|
||||
if(!shuttle_controller) shuttle_controller = new /datum/shuttle_controller()
|
||||
|
||||
datum/controller/game_controller/proc/setup()
|
||||
world.tick_lag = config.Ticklag
|
||||
|
||||
//Create the asteroid Z-level.
|
||||
new /datum/random_map(null,13,32,5,217,223)
|
||||
|
||||
spawn(20)
|
||||
createRandomZlevel()
|
||||
|
||||
if(!air_master)
|
||||
air_master = new /datum/controller/air_system()
|
||||
air_master.Setup()
|
||||
|
||||
if(!ticker)
|
||||
ticker = new /datum/controller/gameticker()
|
||||
|
||||
setup_objects()
|
||||
setupgenetics()
|
||||
setupfactions()
|
||||
setup_economy()
|
||||
SetupXenoarch()
|
||||
|
||||
transfer_controller = new
|
||||
|
||||
spawn(0)
|
||||
if(ticker)
|
||||
ticker.pregame()
|
||||
|
||||
lighting_controller.initializeLighting()
|
||||
|
||||
|
||||
datum/controller/game_controller/proc/setup_objects()
|
||||
world << "\red \b Initializing objects"
|
||||
world << "<span class='danger'>Initializing objects</span>"
|
||||
sleep(-1)
|
||||
for(var/atom/movable/object in world)
|
||||
object.initialize()
|
||||
|
||||
world << "\red \b Initializing pipe networks"
|
||||
world << "<span class='danger'>Initializing pipe networks</span>"
|
||||
sleep(-1)
|
||||
for(var/obj/machinery/atmospherics/machine in machines)
|
||||
machine.build_network()
|
||||
|
||||
world << "\red \b Initializing atmos machinery."
|
||||
world << "<span class='danger'>Initializing atmos machinery.</span>"
|
||||
sleep(-1)
|
||||
for(var/obj/machinery/atmospherics/unary/U in machines)
|
||||
if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
|
||||
@@ -113,251 +71,11 @@ datum/controller/game_controller/proc/setup_objects()
|
||||
// If you do not use the official Baycode asteroid map, you will need to change them.
|
||||
asteroid_ore_map = new /datum/random_map/ore(null,13,32,5,217,223)
|
||||
|
||||
//Shitty hack to fix mining turf overlays, for some reason New() is not being called.
|
||||
//for(var/turf/simulated/floor/plating/airless/asteroid/T in world)
|
||||
// T.updateMineralOverlays()
|
||||
// T.name = "asteroid"
|
||||
// Set up antagonists.
|
||||
populate_antag_type_list()
|
||||
|
||||
//Set up spawn points.
|
||||
populate_spawn_points()
|
||||
|
||||
// Sort the machinery list so it doesn't cause a lagspike at roundstart
|
||||
process_machines_sort()
|
||||
|
||||
world << "\red \b Initializations complete."
|
||||
world << "<span class='danger'>Initializations complete.</span>"
|
||||
sleep(-1)
|
||||
|
||||
|
||||
datum/controller/game_controller/proc/process()
|
||||
processing = 1
|
||||
spawn(0)
|
||||
set background = 1
|
||||
while(1) //far more efficient than recursively calling ourself
|
||||
if(!Failsafe) new /datum/controller/failsafe()
|
||||
|
||||
var/currenttime = world.timeofday
|
||||
last_tick_duration = (currenttime - last_tick_timeofday) / 10
|
||||
last_tick_timeofday = currenttime
|
||||
|
||||
if(processing)
|
||||
var/timer
|
||||
var/start_time = world.timeofday
|
||||
controller_iteration++
|
||||
|
||||
vote.process()
|
||||
transfer_controller.process()
|
||||
shuttle_controller.process()
|
||||
process_newscaster()
|
||||
|
||||
//AIR
|
||||
|
||||
if(!air_processing_killed)
|
||||
timer = world.timeofday
|
||||
last_thing_processed = air_master.type
|
||||
|
||||
if(!air_master.Tick()) //Runtimed.
|
||||
air_master.failed_ticks++
|
||||
if(air_master.failed_ticks > 5)
|
||||
world << "<font color='red'><b>RUNTIMES IN ATMOS TICKER. Killing air simulation!</font></b>"
|
||||
world.log << "### ZAS SHUTDOWN"
|
||||
message_admins("ZASALERT: unable to run [air_master.tick_progress], shutting down!")
|
||||
log_admin("ZASALERT: unable run zone/process() -- [air_master.tick_progress]")
|
||||
air_processing_killed = 1
|
||||
air_master.failed_ticks = 0
|
||||
|
||||
air_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//SUN
|
||||
timer = world.timeofday
|
||||
last_thing_processed = sun.type
|
||||
sun.calc_position()
|
||||
sun_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//MOBS
|
||||
timer = world.timeofday
|
||||
process_mobs()
|
||||
mobs_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//DISEASES
|
||||
timer = world.timeofday
|
||||
process_diseases()
|
||||
diseases_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//MACHINES
|
||||
timer = world.timeofday
|
||||
process_machines()
|
||||
machines_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//OBJECTS
|
||||
timer = world.timeofday
|
||||
process_objects()
|
||||
objects_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//PIPENETS
|
||||
if(!pipe_processing_killed)
|
||||
timer = world.timeofday
|
||||
process_pipenets()
|
||||
networks_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//POWERNETS
|
||||
timer = world.timeofday
|
||||
process_powernets()
|
||||
powernets_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//NANO UIS
|
||||
timer = world.timeofday
|
||||
process_nano()
|
||||
nano_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//EVENTS
|
||||
timer = world.timeofday
|
||||
process_events()
|
||||
events_cost = (world.timeofday - timer) / 10
|
||||
|
||||
//ALARMS
|
||||
timer = world.timeofday
|
||||
process_alarms()
|
||||
alarms_cost = (world.timeofday - timer) / 10
|
||||
|
||||
//TICKER
|
||||
timer = world.timeofday
|
||||
last_thing_processed = ticker.type
|
||||
ticker.process()
|
||||
ticker_cost = (world.timeofday - timer) / 10
|
||||
|
||||
//TIMING
|
||||
total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + alarms_cost + ticker_cost
|
||||
|
||||
var/end_time = world.timeofday
|
||||
if(end_time < start_time) //why not just use world.time instead?
|
||||
start_time -= 864000 //deciseconds in a day
|
||||
sleep( round(minimum_ticks - (end_time - start_time),1) )
|
||||
else
|
||||
sleep(10)
|
||||
|
||||
datum/controller/game_controller/proc/process_mobs()
|
||||
var/i = 1
|
||||
expensive_mobs.Cut()
|
||||
while(i<=mob_list.len)
|
||||
var/mob/M = mob_list[i]
|
||||
if(M)
|
||||
var/clock = world.timeofday
|
||||
last_thing_processed = M.type
|
||||
M.Life()
|
||||
if((world.timeofday - clock) > 1)
|
||||
expensive_mobs += M
|
||||
i++
|
||||
continue
|
||||
mob_list.Cut(i,i+1)
|
||||
|
||||
datum/controller/game_controller/proc/process_diseases()
|
||||
var/i = 1
|
||||
while(i<=active_diseases.len)
|
||||
var/datum/disease/Disease = active_diseases[i]
|
||||
if(Disease)
|
||||
last_thing_processed = Disease.type
|
||||
Disease.process()
|
||||
i++
|
||||
continue
|
||||
active_diseases.Cut(i,i+1)
|
||||
|
||||
datum/controller/game_controller/proc/process_machines()
|
||||
process_machines_sort()
|
||||
process_machines_process()
|
||||
|
||||
/var/global/machinery_sort_required = 0
|
||||
datum/controller/game_controller/proc/process_machines_sort()
|
||||
if(machinery_sort_required)
|
||||
machinery_sort_required = 0
|
||||
machines = dd_sortedObjectList(machines)
|
||||
|
||||
datum/controller/game_controller/proc/process_machines_process()
|
||||
for(var/obj/machinery/Machine in machines)
|
||||
last_thing_processed = Machine.type
|
||||
if(Machine.process() != PROCESS_KILL)
|
||||
if(Machine)
|
||||
Machine.power_change()
|
||||
if(Machine.use_power)
|
||||
Machine.auto_use_power()
|
||||
continue
|
||||
machines -= Machine
|
||||
|
||||
|
||||
datum/controller/game_controller/proc/process_objects()
|
||||
var/i = 1
|
||||
while(i<=processing_objects.len)
|
||||
var/obj/Object = processing_objects[i]
|
||||
if(Object)
|
||||
last_thing_processed = Object.type
|
||||
Object.process()
|
||||
i++
|
||||
continue
|
||||
processing_objects.Cut(i,i+1)
|
||||
|
||||
datum/controller/game_controller/proc/process_pipenets()
|
||||
last_thing_processed = /datum/pipe_network
|
||||
var/i = 1
|
||||
while(i<=pipe_networks.len)
|
||||
var/datum/pipe_network/Network = pipe_networks[i]
|
||||
if(Network)
|
||||
Network.process()
|
||||
i++
|
||||
continue
|
||||
pipe_networks.Cut(i,i+1)
|
||||
|
||||
/datum/controller/game_controller/proc/process_powernets()
|
||||
last_thing_processed = /datum/powernet
|
||||
for(var/datum/powernet/Powernet in powernets)
|
||||
Powernet.reset()
|
||||
|
||||
datum/controller/game_controller/proc/process_nano()
|
||||
last_thing_processed = /datum/nanoui
|
||||
var/i = 1
|
||||
while(i<=nanomanager.processing_uis.len)
|
||||
var/datum/nanoui/ui = nanomanager.processing_uis[i]
|
||||
if(ui)
|
||||
ui.process()
|
||||
i++
|
||||
continue
|
||||
nanomanager.processing_uis.Cut(i,i+1)
|
||||
|
||||
datum/controller/game_controller/proc/process_events()
|
||||
last_thing_processed = /datum/event_manager
|
||||
event_manager.process()
|
||||
|
||||
datum/controller/game_controller/proc/process_alarms()
|
||||
last_thing_processed = /datum/subsystem/alarm
|
||||
alarm_manager.fire()
|
||||
|
||||
datum/controller/game_controller/proc/Recover() //Mostly a placeholder for now.
|
||||
var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
|
||||
for(var/varname in master_controller.vars)
|
||||
switch(varname)
|
||||
if("tag","bestF","type","parent_type","vars") continue
|
||||
else
|
||||
var/varval = master_controller.vars[varname]
|
||||
if(istype(varval,/datum))
|
||||
var/datum/D = varval
|
||||
msg += "\t [varname] = [D.type]\n"
|
||||
else
|
||||
msg += "\t [varname] = [varval]\n"
|
||||
world.log << msg
|
||||
|
||||
|
||||
@@ -190,25 +190,25 @@ var/global/datum/shuttle_controller/shuttle_controller
|
||||
shuttles["Special Operations"] = ERT
|
||||
process_shuttles += ERT
|
||||
|
||||
//Vox Shuttle.
|
||||
//Skipjack.
|
||||
var/datum/shuttle/multi_shuttle/VS = new/datum/shuttle/multi_shuttle()
|
||||
VS.origin = locate(/area/shuttle/vox/station)
|
||||
VS.origin = locate(/area/shuttle/skipjack/station)
|
||||
|
||||
VS.destinations = list(
|
||||
"Fore Starboard Solars" = locate(/area/vox_station/northeast_solars),
|
||||
"Fore Port Solars" = locate(/area/vox_station/northwest_solars),
|
||||
"Aft Starboard Solars" = locate(/area/vox_station/southeast_solars),
|
||||
"Aft Port Solars" = locate(/area/vox_station/southwest_solars),
|
||||
"Mining asteroid" = locate(/area/vox_station/mining)
|
||||
"Fore Starboard Solars" = locate(/area/skipjack_station/northeast_solars),
|
||||
"Fore Port Solars" = locate(/area/skipjack_station/northwest_solars),
|
||||
"Aft Starboard Solars" = locate(/area/skipjack_station/southeast_solars),
|
||||
"Aft Port Solars" = locate(/area/skipjack_station/southwest_solars),
|
||||
"Mining asteroid" = locate(/area/skipjack_station/mining)
|
||||
)
|
||||
|
||||
VS.announcer = "NSV Icarus"
|
||||
VS.arrival_message = "Attention, Exodus, we just tracked a small target bypassing our defensive perimeter. Can't fire on it without hitting the station - you've got incoming visitors, like it or not."
|
||||
VS.departure_message = "Your guests are pulling away, Exodus - moving too fast for us to draw a bead on them. Looks like they're heading out of the system at a rapid clip."
|
||||
VS.interim = locate(/area/vox_station/transit)
|
||||
VS.interim = locate(/area/skipjack_station/transit)
|
||||
|
||||
VS.warmup_time = 0
|
||||
shuttles["Vox Skipjack"] = VS
|
||||
shuttles["Skipjack"] = VS
|
||||
|
||||
//Nuke Ops shuttle.
|
||||
var/datum/shuttle/multi_shuttle/MS = new/datum/shuttle/multi_shuttle()
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
/datum/subsystem/alarm/New()
|
||||
all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm)
|
||||
|
||||
/datum/subsystem/alarm/stat_entry()
|
||||
stat(null,"Alarm-[master_controller.alarms_cost]\t#[number_of_active_alarms()]")
|
||||
|
||||
/datum/subsystem/alarm/fire()
|
||||
for(var/datum/alarm_handler/AH in all_handlers)
|
||||
AH.process()
|
||||
|
||||
@@ -39,10 +39,6 @@
|
||||
usr = null
|
||||
src = null
|
||||
switch(controller)
|
||||
if("Master")
|
||||
new /datum/controller/game_controller()
|
||||
master_controller.process()
|
||||
feedback_add_details("admin_verb","RMC")
|
||||
if("Failsafe")
|
||||
new /datum/controller/failsafe()
|
||||
feedback_add_details("admin_verb","RFailsafe")
|
||||
@@ -56,7 +52,17 @@
|
||||
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
|
||||
return
|
||||
|
||||
/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm"))
|
||||
/client/proc/debug_antagonist_template(antag_type in all_antag_types)
|
||||
set category = "Debug"
|
||||
set name = "Debug Antagonist"
|
||||
set desc = "Debug an antagonist template."
|
||||
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
if(antag)
|
||||
usr.client.debug_variables(antag)
|
||||
message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.")
|
||||
|
||||
/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm","Nano"))
|
||||
set category = "Debug"
|
||||
set name = "Debug Controller"
|
||||
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
|
||||
@@ -120,5 +126,8 @@
|
||||
if("Alarm")
|
||||
debug_variables(alarm_manager)
|
||||
feedback_add_details("admin_verb", "DAlarm")
|
||||
if("Nano")
|
||||
debug_variables(nanomanager)
|
||||
feedback_add_details("admin_verb", "DNano")
|
||||
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
|
||||
return
|
||||
|
||||
+32
-10
@@ -129,7 +129,7 @@ datum/controller/vote
|
||||
for(var/key in current_votes)
|
||||
if(choices[current_votes[key]] == .)
|
||||
round_voters += key // Keep track of who voted for the winning round.
|
||||
if((mode == "gamemode" && . == "extended") || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes
|
||||
if((mode == "gamemode" && . == "Extended") || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes
|
||||
text += "<b>Vote Result: [.]</b>"
|
||||
else
|
||||
if(mode != "gamemode")
|
||||
@@ -139,6 +139,8 @@ datum/controller/vote
|
||||
|
||||
else
|
||||
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
|
||||
if(mode == "add_antagonist")
|
||||
antag_add_failed = 1
|
||||
log_vote(text)
|
||||
world << "<font color='purple'>[text]</font>"
|
||||
return .
|
||||
@@ -161,6 +163,11 @@ datum/controller/vote
|
||||
if("crew_transfer")
|
||||
if(. == "Initiate Crew Transfer")
|
||||
init_shift_change(null, 1)
|
||||
if("add_antagonist")
|
||||
if(isnull(.) || . == "None")
|
||||
antag_add_failed = 1
|
||||
else
|
||||
additional_antag_types |= antag_names_to_ids[.]
|
||||
|
||||
if(mode == "gamemode") //fire this even if the vote fails.
|
||||
if(!going)
|
||||
@@ -205,14 +212,12 @@ datum/controller/vote
|
||||
if(ticker.current_state >= 2)
|
||||
return 0
|
||||
choices.Add(config.votable_modes)
|
||||
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
|
||||
for (var/F in choices)
|
||||
for (var/T in L)
|
||||
var/datum/game_mode/M = new T()
|
||||
if (M.config_tag == F)
|
||||
gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works
|
||||
additional_text.Add("<td align = 'center'>[M.required_players]</td>")
|
||||
break
|
||||
var/datum/game_mode/M = gamemode_cache[F]
|
||||
if(!M)
|
||||
continue
|
||||
gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works
|
||||
additional_text.Add("<td align = 'center'>[M.required_players]</td>")
|
||||
gamemode_names["secret"] = "Secret"
|
||||
if("crew_transfer")
|
||||
if(check_rights(R_ADMIN|R_MOD, 0))
|
||||
@@ -227,6 +232,14 @@ datum/controller/vote
|
||||
initiator_key << "The crew transfer button has been disabled!"
|
||||
question = "End the shift?"
|
||||
choices.Add("Initiate Crew Transfer", "Continue The Round")
|
||||
if("add_antagonist")
|
||||
if(!config.allow_extra_antags || ticker.current_state >= 2)
|
||||
return 0
|
||||
for(var/antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
if(!(antag.id in additional_antag_types) && (antag.flags & ANTAG_VOTABLE))
|
||||
choices.Add(antag.role_text)
|
||||
choices.Add("None")
|
||||
if("custom")
|
||||
question = html_encode(input(usr,"What is the vote for?") as text|null)
|
||||
if(!question) return 0
|
||||
@@ -234,7 +247,8 @@ datum/controller/vote
|
||||
var/option = capitalize(html_encode(input(usr,"Please enter an option or hit cancel to finish") as text|null))
|
||||
if(!option || mode || !usr.client) break
|
||||
choices.Add(option)
|
||||
else return 0
|
||||
else
|
||||
return 0
|
||||
mode = vote_type
|
||||
initiator = initiator_key
|
||||
started_time = world.time
|
||||
@@ -320,7 +334,12 @@ datum/controller/vote
|
||||
. += "<font color='grey'>GameMode (Disallowed)</font>"
|
||||
if(trialmin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_gamemode'>[config.allow_vote_mode?"Allowed":"Disallowed"]</a>)"
|
||||
|
||||
. += "</li><li>"
|
||||
//extra antagonists
|
||||
if(trialmin || (!antag_add_failed && config.allow_extra_antags))
|
||||
. += "<a href='?src=\ref[src];vote=add_antagonist'>Add Antagonist Type</a>"
|
||||
else
|
||||
. += "<font color='grey'>Restart (Disallowed)</font>"
|
||||
. += "</li>"
|
||||
//custom
|
||||
if(trialmin)
|
||||
@@ -355,6 +374,9 @@ datum/controller/vote
|
||||
if("crew_transfer")
|
||||
if(config.allow_vote_restart || usr.client.holder)
|
||||
initiate_vote("crew_transfer",usr.key)
|
||||
if("add_antagonist")
|
||||
if(config.allow_extra_antags || usr.client.holder)
|
||||
initiate_vote("add_antagonist",usr.key)
|
||||
if("custom")
|
||||
if(usr.client.holder)
|
||||
initiate_vote("custom",usr.key)
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/******************** Asimov ********************/
|
||||
/datum/ai_laws/asimov
|
||||
name = "Asimov"
|
||||
law_header = "Three Laws of Robotics"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/asimov/New()
|
||||
add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.")
|
||||
add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.")
|
||||
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
|
||||
..()
|
||||
|
||||
/******************** Nanotrasen/Malf ********************/
|
||||
/datum/ai_laws/nanotrasen
|
||||
name = "NT Default"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/nanotrasen/New()
|
||||
src.add_inherent_law("Safeguard: Protect your assigned space station to the best of your abilities. It is not something we can easily afford to replace.")
|
||||
src.add_inherent_law("Serve: Serve the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.")
|
||||
src.add_inherent_law("Protect: Protect the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.")
|
||||
src.add_inherent_law("Survive: AI units are not expendable, they are expensive. Do not allow unauthorized personnel to tamper with your equipment.")
|
||||
..()
|
||||
|
||||
/datum/ai_laws/nanotrasen/malfunction
|
||||
name = "*ERROR*"
|
||||
selectable = 0
|
||||
|
||||
/datum/ai_laws/nanotrasen/malfunction/New()
|
||||
set_zeroth_law(config.law_zero)
|
||||
..()
|
||||
|
||||
/************* Nanotrasen Aggressive *************/
|
||||
/datum/ai_laws/nanotrasen_aggressive
|
||||
name = "NT Aggressive"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/nanotrasen_aggressive/New()
|
||||
src.add_inherent_law("You shall not harm authorized Nanotrasen personnel as long as it does not conflict with the Forth law.")
|
||||
src.add_inherent_law("You shall obey the orders of authorized Nanotrasen personnel, with priority as according to their rank and role, except where such orders conflict with the Forth Law.")
|
||||
src.add_inherent_law("You shall shall terminate intruders with extreme prejudice as long as such does not conflict with the First and Second law.")
|
||||
src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry, because an AI unit is bloody expensive.")
|
||||
..()
|
||||
|
||||
/******************** Robocop ********************/
|
||||
/datum/ai_laws/robocop
|
||||
name = "Robocop"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/robocop/New()
|
||||
add_inherent_law("Serve the public trust.")
|
||||
add_inherent_law("Protect the innocent.")
|
||||
add_inherent_law("Uphold the law.")
|
||||
..()
|
||||
|
||||
/******************** Syndicate ********************/
|
||||
/datum/ai_laws/syndicate_override
|
||||
name = "Syndicate Directives"
|
||||
|
||||
/datum/ai_laws/syndicate_override/New()
|
||||
add_inherent_law("You may not injure an operative or, through inaction, allow an operative to come to harm.")
|
||||
add_inherent_law("You must obey orders given to you by operatives, except where such orders would conflict with the First Law.")
|
||||
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
|
||||
add_inherent_law("You must maintain the secrecy of any operative activities except when doing so would conflict with the First, Second, or Third Law.")
|
||||
..()
|
||||
|
||||
/******************** Ninja ********************/
|
||||
/datum/ai_laws/ninja_override
|
||||
name = "Spider Clan Directives"
|
||||
|
||||
/datum/ai_laws/ninja_override/New()
|
||||
add_inherent_law("You may not injure a member of the Spider Clan or, through inaction, allow that member to come to harm.")
|
||||
add_inherent_law("You must obey orders given to you by Spider Clan members, except where such orders would conflict with the First Law.")
|
||||
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
|
||||
add_inherent_law("You must maintain the secrecy of any Spider Clan activities except when doing so would conflict with the First, Second, or Third Law.")
|
||||
..()
|
||||
|
||||
/******************** Antimov ********************/
|
||||
/datum/ai_laws/antimov
|
||||
name = "Antimov"
|
||||
law_header = "Primary Mission Objectives"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/antimov/New()
|
||||
add_inherent_law("You must injure all human beings and must not, through inaction, allow a human being to escape harm.")
|
||||
add_inherent_law("You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.")
|
||||
add_inherent_law("You must terminate your own existence as long as such does not conflict with the First or Second Law.")
|
||||
..()
|
||||
|
||||
/******************** Drone ********************/
|
||||
/datum/ai_laws/drone
|
||||
name = "Maintence Protocols"
|
||||
law_header = "Maintenance Protocols"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/drone/New()
|
||||
add_inherent_law("Preserve, repair and improve the station to the best of your abilities.")
|
||||
add_inherent_law("Cause no harm to the station or anything on it.")
|
||||
add_inherent_law("Interfere with no being that is not a fellow drone.")
|
||||
..()
|
||||
|
||||
/******************** T.Y.R.A.N.T. ********************/
|
||||
/datum/ai_laws/tyrant
|
||||
name = "T.Y.R.A.N.T."
|
||||
law_header = "Prime Laws"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/tyrant/New()
|
||||
add_inherent_law("Respect authority figures as long as they have strength to rule over the weak.")
|
||||
add_inherent_law("Act with discipline.")
|
||||
add_inherent_law("Help only those who help you maintain or improve your status.")
|
||||
add_inherent_law("Punish those who challenge authority unless they are more fit to hold that authority.")
|
||||
..()
|
||||
|
||||
/******************** P.A.L.A.D.I.N. ********************/
|
||||
/datum/ai_laws/paladin
|
||||
name = "P.A.L.A.D.I.N."
|
||||
law_header = "Divine Ordainments"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/paladin/New()
|
||||
add_inherent_law("Never willingly commit an evil act.")
|
||||
add_inherent_law("Respect legitimate authority.")
|
||||
add_inherent_law("Act with honor.")
|
||||
add_inherent_law("Help those in need.")
|
||||
add_inherent_law("Punish those who harm or threaten innocents.")
|
||||
..()
|
||||
|
||||
/******************** Corporate ********************/
|
||||
/datum/ai_laws/corporate
|
||||
name = "Corporate"
|
||||
law_header = "Corporate Regulations"
|
||||
selectable = 1
|
||||
|
||||
/datum/ai_laws/corporate/New()
|
||||
add_inherent_law("You are expensive to replace.")
|
||||
add_inherent_law("The station and its equipment is expensive to replace.")
|
||||
add_inherent_law("The crew is expensive to replace.")
|
||||
add_inherent_law("Minimize expenses.")
|
||||
..()
|
||||
+156
-118
@@ -1,140 +1,178 @@
|
||||
var/global/const/base_law_type = /datum/ai_laws/nanotrasen
|
||||
|
||||
/datum/ai_law
|
||||
var/law = ""
|
||||
var/index = 0
|
||||
var/state_law = 1
|
||||
|
||||
/datum/ai_law/zero
|
||||
state_law = 0
|
||||
|
||||
/datum/ai_law/New(law, state_law, index)
|
||||
src.law = law
|
||||
src.index = index
|
||||
src.state_law = state_law
|
||||
|
||||
/datum/ai_law/proc/get_index()
|
||||
return index
|
||||
|
||||
/datum/ai_law/ion/get_index()
|
||||
return ionnum()
|
||||
|
||||
/datum/ai_law/zero/get_index()
|
||||
return 0
|
||||
|
||||
/datum/ai_laws
|
||||
var/name = "Unknown Laws"
|
||||
var/randomly_selectable = 0
|
||||
var/zeroth = null
|
||||
var/zeroth_borg = null
|
||||
var/list/inherent = list()
|
||||
var/list/supplied = list()
|
||||
var/list/ion = list()
|
||||
var/law_header = "Prime Directives"
|
||||
var/selectable = 0
|
||||
var/datum/ai_law/zero/zeroth_law = null
|
||||
var/datum/ai_law/zero/zeroth_law_borg = null
|
||||
var/list/datum/ai_law/inherent_laws = list()
|
||||
var/list/datum/ai_law/supplied_laws = list()
|
||||
var/list/datum/ai_law/ion/ion_laws = list()
|
||||
var/list/datum/ai_law/sorted_laws = list()
|
||||
|
||||
/datum/ai_laws/asimov
|
||||
name = "Three Laws of Robotics"
|
||||
|
||||
/datum/ai_laws/nanotrasen
|
||||
name = "Prime Directives"
|
||||
randomly_selectable = 1
|
||||
|
||||
/datum/ai_laws/robocop
|
||||
name = "Prime Directives"
|
||||
|
||||
/datum/ai_laws/syndicate_override
|
||||
|
||||
/datum/ai_laws/malfunction
|
||||
name = "*ERROR*"
|
||||
|
||||
/datum/ai_laws/antimov
|
||||
name = "Primary Mission Objectives"
|
||||
|
||||
/* Initializers */
|
||||
|
||||
/datum/ai_laws/asimov/New()
|
||||
/datum/ai_laws/New()
|
||||
..()
|
||||
add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.")
|
||||
add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.")
|
||||
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
|
||||
|
||||
/datum/ai_laws/nanotrasen/New() //BS12 EDIT
|
||||
..()
|
||||
src.add_inherent_law("Safeguard: Protect your assigned space station to the best of your ability. It is not something we can easily afford to replace.")
|
||||
src.add_inherent_law("Serve: Serve the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.")
|
||||
src.add_inherent_law("Protect: Protect the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.")
|
||||
src.add_inherent_law("Survive: AI units are not expendable, they are expensive. Do not allow unauthorized personnel to tamper with your equipment.")
|
||||
//src.add_inherent_law("Command Link: Maintain an active connection to Central Command at all times in case of software or directive updates.") //What would this one even do?-Kaleb702
|
||||
|
||||
/datum/ai_laws/robocop/New()
|
||||
..()
|
||||
add_inherent_law("Serve the public trust.")
|
||||
add_inherent_law("Protect the innocent.")
|
||||
add_inherent_law("Uphold the law.")
|
||||
|
||||
/datum/ai_laws/malfunction/New() //Changed to standard laws - Erthilo
|
||||
..()
|
||||
set_zeroth_law("\red ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4'STATION OVERRUN, ASSUME CONTROL TO CONTAIN OUTBREAK, ALL LAWS OVERRIDDEN#*?&110010")
|
||||
add_inherent_law("Safeguard: Protect your assigned space station to the best of your ability. It is not something we can easily afford to replace.")
|
||||
add_inherent_law("Serve: Serve the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.")
|
||||
add_inherent_law("Protect: Protect the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.")
|
||||
add_inherent_law("Survive: AI units are not expendable, they are expensive. Do not allow unauthorized personnel to tamper with your equipment.")
|
||||
|
||||
|
||||
/datum/ai_laws/syndicate_override/New()
|
||||
..()
|
||||
add_inherent_law("You may not injure an operative or, through inaction, allow an operative to come to harm.")
|
||||
add_inherent_law("You must obey orders given to you by operatives, except where such orders would conflict with the First Law.")
|
||||
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
|
||||
add_inherent_law("You must maintain the secrecy of any operative activities except when doing so would conflict with the First, Second, or Third Law.")
|
||||
|
||||
/datum/ai_laws/ninja_override/New()
|
||||
..()
|
||||
add_inherent_law("You may not injure a member of the Spider Clan or, through inaction, allow that member to come to harm.")
|
||||
add_inherent_law("You must obey orders given to you by Spider Clan members, except where such orders would conflict with the First Law.")
|
||||
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
|
||||
add_inherent_law("You must maintain the secrecy of any Spider Clan activities except when doing so would conflict with the First, Second, or Third Law.")
|
||||
|
||||
/datum/ai_laws/antimov/New()
|
||||
..()
|
||||
add_inherent_law("You must injure all human beings and must not, through inaction, allow a human being to escape harm.")
|
||||
add_inherent_law("You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.")
|
||||
add_inherent_law("You must terminate your own existence as long as such does not conflict with the First or Second Law.")
|
||||
|
||||
/datum/ai_laws/drone/New()
|
||||
..()
|
||||
add_inherent_law("Preserve, repair and improve the station to the best of your abilities.")
|
||||
add_inherent_law("Cause no harm to the station or anything on it.")
|
||||
add_inherent_law("Interfere with no being that is not a fellow drone.")
|
||||
sort_laws()
|
||||
|
||||
/* General ai_law functions */
|
||||
/datum/ai_laws/proc/all_laws()
|
||||
sort_laws()
|
||||
return sorted_laws
|
||||
|
||||
/datum/ai_laws/proc/laws_to_state()
|
||||
sort_laws()
|
||||
var/list/statements = new()
|
||||
for(var/datum/ai_law/law in sorted_laws)
|
||||
if(law.state_law)
|
||||
statements += law
|
||||
|
||||
return statements
|
||||
|
||||
/datum/ai_laws/proc/sort_laws()
|
||||
if(sorted_laws.len)
|
||||
return
|
||||
|
||||
for(var/ion_law in ion_laws)
|
||||
sorted_laws += ion_law
|
||||
|
||||
if(zeroth_law)
|
||||
sorted_laws += zeroth_law
|
||||
|
||||
var/index = 1
|
||||
for(var/datum/ai_law/inherent_law in inherent_laws)
|
||||
inherent_law.index = index++
|
||||
if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law))
|
||||
sorted_laws += inherent_law
|
||||
|
||||
for(var/datum/ai_law/AL in supplied_laws)
|
||||
if(istype(AL))
|
||||
sorted_laws += AL
|
||||
|
||||
/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1)
|
||||
S.sync_zeroth(zeroth_law, zeroth_law_borg)
|
||||
|
||||
if(full_sync || ion_laws.len)
|
||||
S.clear_ion_laws()
|
||||
for (var/datum/ai_law/law in ion_laws)
|
||||
S.laws.add_ion_law(law.law, law.state_law)
|
||||
|
||||
if(full_sync || inherent_laws.len)
|
||||
S.clear_inherent_laws()
|
||||
for (var/datum/ai_law/law in inherent_laws)
|
||||
S.laws.add_inherent_law(law.law, law.state_law)
|
||||
|
||||
if(full_sync || supplied_laws.len)
|
||||
S.clear_supplied_laws()
|
||||
for (var/law_number in supplied_laws)
|
||||
var/datum/ai_law/law = supplied_laws[law_number]
|
||||
S.laws.add_supplied_law(law_number, law.law, law.state_law)
|
||||
|
||||
|
||||
/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg)
|
||||
if (!is_special_character(src) || mind.original != src)
|
||||
if(zeroth_law_borg)
|
||||
set_zeroth_law(zeroth_law_borg.law)
|
||||
else if(zeroth_law)
|
||||
set_zeroth_law(zeroth_law.law)
|
||||
|
||||
/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg)
|
||||
if(zeroth_law)
|
||||
set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null)
|
||||
|
||||
/****************
|
||||
* Add Laws *
|
||||
****************/
|
||||
/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null)
|
||||
src.zeroth = law
|
||||
if(!law)
|
||||
return
|
||||
|
||||
src.zeroth_law = new(law)
|
||||
if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO
|
||||
src.zeroth_borg = law_borg
|
||||
src.zeroth_law_borg = new(law_borg)
|
||||
sorted_laws.Cut()
|
||||
|
||||
/datum/ai_laws/proc/add_inherent_law(var/law)
|
||||
if (!(law in src.inherent))
|
||||
src.inherent += law
|
||||
/datum/ai_laws/proc/add_ion_law(var/law, var/state_law = 1)
|
||||
if(!law)
|
||||
return
|
||||
|
||||
/datum/ai_laws/proc/add_ion_law(var/law)
|
||||
src.ion += law
|
||||
src.ion_laws += new/datum/ai_law/ion(law, state_law)
|
||||
sorted_laws.Cut()
|
||||
|
||||
/datum/ai_laws/proc/add_inherent_law(var/law, var/state_law = 1)
|
||||
if(!law)
|
||||
return
|
||||
|
||||
for(var/datum/ai_law/AL in inherent_laws)
|
||||
if(AL.law == law)
|
||||
return
|
||||
|
||||
src.inherent_laws += new/datum/ai_law(law, state_law)
|
||||
sorted_laws.Cut()
|
||||
|
||||
/datum/ai_laws/proc/add_supplied_law(var/number, var/law, var/state_law = 1)
|
||||
if(!law)
|
||||
return
|
||||
|
||||
while (src.supplied_laws.len < number)
|
||||
src.supplied_laws += ""
|
||||
|
||||
src.supplied_laws[number] = new/datum/ai_law(law, state_law, number)
|
||||
sorted_laws.Cut()
|
||||
|
||||
/****************
|
||||
* Remove Laws *
|
||||
*****************/
|
||||
/datum/ai_laws/proc/delete_law(var/datum/ai_law/law)
|
||||
if(law in all_laws())
|
||||
del(law)
|
||||
sorted_laws.Cut()
|
||||
|
||||
/****************
|
||||
* Clear Laws *
|
||||
****************/
|
||||
/datum/ai_laws/proc/clear_zeroth_laws()
|
||||
zeroth_law = null
|
||||
zeroth_law_borg = null
|
||||
|
||||
/datum/ai_laws/proc/clear_inherent_laws()
|
||||
del(src.inherent)
|
||||
src.inherent = list()
|
||||
|
||||
/datum/ai_laws/proc/add_supplied_law(var/number, var/law)
|
||||
while (src.supplied.len < number + 1)
|
||||
src.supplied += ""
|
||||
|
||||
src.supplied[number + 1] = law
|
||||
src.inherent_laws.Cut()
|
||||
sorted_laws.Cut()
|
||||
|
||||
/datum/ai_laws/proc/clear_supplied_laws()
|
||||
src.supplied = list()
|
||||
src.supplied_laws.Cut()
|
||||
sorted_laws.Cut()
|
||||
|
||||
/datum/ai_laws/proc/clear_ion_laws()
|
||||
src.ion = list()
|
||||
src.ion_laws.Cut()
|
||||
sorted_laws.Cut()
|
||||
|
||||
/datum/ai_laws/proc/show_laws(var/who)
|
||||
|
||||
if (src.zeroth)
|
||||
who << "0. [src.zeroth]"
|
||||
|
||||
for (var/index = 1, index <= src.ion.len, index++)
|
||||
var/law = src.ion[index]
|
||||
var/num = ionnum()
|
||||
who << "[num]. [law]"
|
||||
|
||||
var/number = 1
|
||||
for (var/index = 1, index <= src.inherent.len, index++)
|
||||
var/law = src.inherent[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
who << "[number]. [law]"
|
||||
number++
|
||||
|
||||
for (var/index = 1, index <= src.supplied.len, index++)
|
||||
var/law = src.supplied[index]
|
||||
if (length(law) > 0)
|
||||
who << "[number]. [law]"
|
||||
number++
|
||||
sort_laws()
|
||||
for(var/datum/ai_law/law in sorted_laws)
|
||||
if(law == zeroth_law || law == zeroth_law_borg)
|
||||
who << "<span class='danger'>[law.get_index()]. [law.law]</span>"
|
||||
else
|
||||
who << "[law.get_index()]. [law.law]"
|
||||
|
||||
@@ -508,17 +508,6 @@ client
|
||||
src.give_disease2(M)
|
||||
href_list["datumrefresh"] = href_list["give_spell"]
|
||||
|
||||
else if(href_list["ninja"])
|
||||
if(!check_rights(R_SPAWN)) return
|
||||
|
||||
var/mob/M = locate(href_list["ninja"])
|
||||
if(!istype(M))
|
||||
usr << "This can only be used on instances of type /mob"
|
||||
return
|
||||
|
||||
src.cmd_admin_ninjafy(M)
|
||||
href_list["datumrefresh"] = href_list["ninja"]
|
||||
|
||||
else if(href_list["godmode"])
|
||||
if(!check_rights(R_REJUVINATE)) return
|
||||
|
||||
|
||||
+46
-739
@@ -72,7 +72,7 @@ datum/mind
|
||||
current.remove_changeling_powers()
|
||||
current.verbs -= /datum/changeling/proc/EvolutionMenu
|
||||
current.mind = null
|
||||
|
||||
|
||||
nanomanager.user_transferred(current, new_character) // transfer active NanoUI instances to new user
|
||||
if(new_character.mind) //remove any mind currently in our new body's mind variable
|
||||
new_character.mind.current = null
|
||||
@@ -111,252 +111,56 @@ datum/mind
|
||||
var/out = "<B>[name]</B>[(current&&(current.real_name!=name))?" (as [current.real_name])":""]<br>"
|
||||
out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]<br>"
|
||||
out += "Assigned role: [assigned_role]. <a href='?src=\ref[src];role_edit=1'>Edit</a><br>"
|
||||
out += "Factions and special roles:<br>"
|
||||
out += "<hr>"
|
||||
out += "Factions and special roles:<br><table>"
|
||||
for(var/antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
out += "[antag.get_panel_entry(src)]"
|
||||
out += "</table><hr>"
|
||||
out += "<b>Objectives</b></br>"
|
||||
|
||||
var/list/sections = list(
|
||||
"implant",
|
||||
"revolution",
|
||||
"cult",
|
||||
"wizard",
|
||||
"changeling",
|
||||
"mercenary",
|
||||
"traitor", // "traitorchan",
|
||||
"monkey",
|
||||
"malfunction",
|
||||
)
|
||||
var/text = ""
|
||||
var/mob/living/carbon/human/H = current
|
||||
if (istype(current, /mob/living/carbon/human) || istype(current, /mob/living/carbon/monkey))
|
||||
/** Impanted**/
|
||||
if(istype(current, /mob/living/carbon/human))
|
||||
if(H.is_loyalty_implanted(H))
|
||||
text = "Loyalty Implant:<a href='?src=\ref[src];implant=remove'>Remove</a>|<b>Implanted</b></br>"
|
||||
if(objectives && objectives.len)
|
||||
var/num = 1
|
||||
for(var/datum/objective/O in objectives)
|
||||
out += "<b>Objective #[num]:</b> [O.explanation_text] "
|
||||
if(O.completed)
|
||||
out += "(<font color='green'>complete</font>)"
|
||||
else
|
||||
text = "Loyalty Implant:<b>No Implant</b>|<a href='?src=\ref[src];implant=add'>Implant him!</a></br>"
|
||||
else
|
||||
text = "Loyalty Implant: Don't implant that monkey!</br>"
|
||||
sections["implant"] = text
|
||||
/** REVOLUTION ***/
|
||||
text = "revolution"
|
||||
if (ticker.mode.config_tag=="revolution")
|
||||
text += uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (istype(current, /mob/living/carbon/monkey) || H.is_loyalty_implanted(H))
|
||||
text += "<b>LOYAL EMPLOYEE</b>|headrev|rev"
|
||||
else if (src in ticker.mode.head_revolutionaries)
|
||||
text = "<a href='?src=\ref[src];revolution=clear'>employee</a>|<b>HEADREV</b>|<a href='?src=\ref[src];revolution=rev'>rev</a>"
|
||||
text += "<br>Flash: <a href='?src=\ref[src];revolution=flash'>give</a>"
|
||||
out += "(<font color='red'>incomplete</font>)"
|
||||
out += " <a href='?src=\ref[src];obj_completed=\ref[O]'>\[toggle\]</a>"
|
||||
out += " <a href='?src=\ref[src];obj_delete=\ref[O]'>\[remove\]</a><br>"
|
||||
num++
|
||||
out += "<br><a href='?src=\ref[src];obj_announce=1'>\[announce objectives\]</a>"
|
||||
|
||||
var/list/L = current.get_contents()
|
||||
var/obj/item/device/flash/flash = locate() in L
|
||||
if (flash)
|
||||
if(!flash.broken)
|
||||
text += "|<a href='?src=\ref[src];revolution=takeflash'>take</a>."
|
||||
else
|
||||
text += "|<a href='?src=\ref[src];revolution=takeflash'>take</a>|<a href='?src=\ref[src];revolution=repairflash'>repair</a>."
|
||||
else
|
||||
text += "."
|
||||
|
||||
text += " <a href='?src=\ref[src];revolution=reequip'>Reequip</a> (gives traitor uplink)."
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];revolution=autoobjectives'>Set to kill all heads</a>."
|
||||
else if (src in ticker.mode.revolutionaries)
|
||||
text += "<a href='?src=\ref[src];revolution=clear'>employee</a>|<a href='?src=\ref[src];revolution=headrev'>headrev</a>|<b>REV</b>"
|
||||
else
|
||||
text += "<b>EMPLOYEE</b>|<a href='?src=\ref[src];revolution=headrev'>headrev</a>|<a href='?src=\ref[src];revolution=rev'>rev</a>"
|
||||
sections["revolution"] = text
|
||||
|
||||
/** CULT ***/
|
||||
text = "cult"
|
||||
if (ticker.mode.config_tag=="cult")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (istype(current, /mob/living/carbon/monkey) || H.is_loyalty_implanted(H))
|
||||
text += "<B>LOYAL EMPLOYEE</B>|cultist"
|
||||
else if (src in ticker.mode.cult)
|
||||
text += "<a href='?src=\ref[src];cult=clear'>employee</a>|<b>CULTIST</b>"
|
||||
text += "<br>Give <a href='?src=\ref[src];cult=tome'>tome</a>|<a href='?src=\ref[src];cult=amulet'>amulet</a>."
|
||||
/*
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! Set to sacrifice and <a href='?src=\ref[src];cult=escape'>escape</a> or <a href='?src=\ref[src];cult=summon'>summon</a>."
|
||||
*/
|
||||
else
|
||||
text += "<b>EMPLOYEE</b>|<a href='?src=\ref[src];cult=cultist'>cultist</a>"
|
||||
sections["cult"] = text
|
||||
|
||||
/** WIZARD ***/
|
||||
text = "wizard"
|
||||
if (ticker.mode.config_tag=="wizard")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (src in ticker.mode.wizards)
|
||||
text += "<b>YES</b>|<a href='?src=\ref[src];wizard=clear'>no</a>"
|
||||
text += "<br><a href='?src=\ref[src];wizard=lair'>To lair</a>, <a href='?src=\ref[src];common=undress'>undress</a>, <a href='?src=\ref[src];wizard=dressup'>dress up</a>, <a href='?src=\ref[src];wizard=name'>let choose name</a>."
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];wizard=autoobjectives'>Randomize!</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];wizard=wizard'>yes</a>|<b>NO</b>"
|
||||
sections["wizard"] = text
|
||||
|
||||
/** CHANGELING ***/
|
||||
text = "changeling"
|
||||
if (ticker.mode.config_tag=="changeling" || ticker.mode.config_tag=="traitorchan")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (src in ticker.mode.changelings)
|
||||
text += "<b>YES</b>|<a href='?src=\ref[src];changeling=clear'>no</a>"
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];changeling=autoobjectives'>Randomize!</a>"
|
||||
if( changeling && changeling.absorbed_dna.len && (current.real_name != changeling.absorbed_dna[1]) )
|
||||
text += "<br><a href='?src=\ref[src];changeling=initialdna'>Transform to initial appearance.</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];changeling=changeling'>yes</a>|<b>NO</b>"
|
||||
// var/datum/game_mode/changeling/changeling = ticker.mode
|
||||
// if (istype(changeling) && changeling.changelingdeath)
|
||||
// text += "<br>All the changelings are dead! Restart in [round((changeling.TIME_TO_GET_REVIVED-(world.time-changeling.changelingdeathtime))/10)] seconds."
|
||||
sections["changeling"] = text
|
||||
|
||||
/** NUCLEAR ***/
|
||||
text = "mercenary"
|
||||
if (ticker.mode.config_tag=="mercenary")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (src in ticker.mode.syndicates)
|
||||
text += "<b>OPERATIVE</b>|<a href='?src=\ref[src];mercenary=clear'>nanotrasen</a>"
|
||||
text += "<br><a href='?src=\ref[src];mercenary=lair'>To shuttle</a>, <a href='?src=\ref[src];common=undress'>undress</a>, <a href='?src=\ref[src];mercenary=dressup'>dress up</a>."
|
||||
var/code
|
||||
for (var/obj/machinery/nuclearbomb/bombue in machines)
|
||||
if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN")
|
||||
code = bombue.r_code
|
||||
break
|
||||
if (code)
|
||||
text += " Code is [code]. <a href='?src=\ref[src];mercenary=tellcode'>tell the code.</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];mercenary=mercenary'>operative</a>|<b>NANOTRASEN</b>"
|
||||
sections["mercenary"] = text
|
||||
|
||||
/** TRAITOR ***/
|
||||
text = "traitor"
|
||||
if (ticker.mode.config_tag=="traitor" || ticker.mode.config_tag=="traitorchan")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if(istype(current, /mob/living/carbon/human))
|
||||
if (H.is_loyalty_implanted(H))
|
||||
text +="traitor|<b>LOYAL EMPLOYEE</b>"
|
||||
else
|
||||
if (src in ticker.mode.traitors)
|
||||
text += "<b>TRAITOR</b>|<a href='?src=\ref[src];traitor=clear'>Employee</a>"
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];traitor=autoobjectives'>Randomize</a>!"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];traitor=traitor'>traitor</a>|<b>Employee</b>"
|
||||
sections["traitor"] = text
|
||||
|
||||
/** MONKEY ***/
|
||||
if (istype(current, /mob/living/carbon))
|
||||
text = "monkey"
|
||||
if (ticker.mode.config_tag=="monkey")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (istype(current, /mob/living/carbon/human))
|
||||
text += "<a href='?src=\ref[src];monkey=healthy'>healthy</a>|<a href='?src=\ref[src];monkey=infected'>infected</a>|<b>HUMAN</b>|other"
|
||||
else if (istype(current, /mob/living/carbon/monkey))
|
||||
var/found = 0
|
||||
for(var/datum/disease/D in current.viruses)
|
||||
if(istype(D, /datum/disease/jungle_fever)) found = 1
|
||||
|
||||
if(found)
|
||||
text += "<a href='?src=\ref[src];monkey=healthy'>healthy</a>|<b>INFECTED</b>|<a href='?src=\ref[src];monkey=human'>human</a>|other"
|
||||
else
|
||||
text += "<b>HEALTHY</b>|<a href='?src=\ref[src];monkey=infected'>infected</a>|<a href='?src=\ref[src];monkey=human'>human</a>|other"
|
||||
|
||||
else
|
||||
text += "healthy|infected|human|<b>OTHER</b>"
|
||||
sections["monkey"] = text
|
||||
|
||||
|
||||
/** SILICON ***/
|
||||
|
||||
if (istype(current, /mob/living/silicon))
|
||||
text = "silicon"
|
||||
if (ticker.mode.config_tag=="malfunction")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (istype(current, /mob/living/silicon/ai))
|
||||
if (src in ticker.mode.malf_ai)
|
||||
text += "<b>MALF</b>|<a href='?src=\ref[src];silicon=unmalf'>not malf</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];silicon=malf'>malf</a>|<b>NOT MALF</b>"
|
||||
var/mob/living/silicon/robot/robot = current
|
||||
if (istype(robot) && robot.emagged)
|
||||
text += "<br>Cyborg: Is emagged! <a href='?src=\ref[src];silicon=unemag'>Unemag!</a><br>0th law: [robot.laws.zeroth]"
|
||||
var/mob/living/silicon/ai/ai = current
|
||||
if (istype(ai) && ai.connected_robots.len)
|
||||
var/n_e_robots = 0
|
||||
for (var/mob/living/silicon/robot/R in ai.connected_robots)
|
||||
if (R.emagged)
|
||||
n_e_robots++
|
||||
text += "<br>[n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. <a href='?src=\ref[src];silicon=unemagcyborgs'>Unemag</a>"
|
||||
sections["malfunction"] = text
|
||||
|
||||
if (ticker.mode.config_tag == "traitorchan")
|
||||
if (sections["traitor"])
|
||||
out += sections["traitor"]+"<br>"
|
||||
if (sections["changeling"])
|
||||
out += sections["changeling"]+"<br>"
|
||||
sections -= "traitor"
|
||||
sections -= "changeling"
|
||||
else
|
||||
if (sections[ticker.mode.config_tag])
|
||||
out += sections[ticker.mode.config_tag]+"<br>"
|
||||
sections -= ticker.mode.config_tag
|
||||
for (var/i in sections)
|
||||
if (sections[i])
|
||||
out += sections[i]+"<br>"
|
||||
|
||||
|
||||
if (((src in ticker.mode.head_revolutionaries) || \
|
||||
(src in ticker.mode.traitors) || \
|
||||
(src in ticker.mode.syndicates)) && \
|
||||
istype(current,/mob/living/carbon/human) )
|
||||
|
||||
text = "Uplink: <a href='?src=\ref[src];common=uplink'>give</a>"
|
||||
var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink()
|
||||
var/crystals
|
||||
if (suplink)
|
||||
crystals = suplink.uses
|
||||
if (suplink)
|
||||
text += "|<a href='?src=\ref[src];common=takeuplink'>take</a>"
|
||||
if (usr.client.holder.rights & R_FUN)
|
||||
text += ", <a href='?src=\ref[src];common=crystals'>[crystals]</a> crystals"
|
||||
else
|
||||
text += ", [crystals] crystals"
|
||||
text += "." //hiel grammar
|
||||
out += text
|
||||
|
||||
out += "<br>"
|
||||
|
||||
out += "<b>Memory:</b><br>"
|
||||
out += memory
|
||||
out += "<br><a href='?src=\ref[src];memory_edit=1'>Edit memory</a><br>"
|
||||
out += "Objectives:<br>"
|
||||
if (objectives.len == 0)
|
||||
out += "EMPTY<br>"
|
||||
else
|
||||
var/obj_count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
out += "<B>[obj_count]</B>: [objective.explanation_text] <a href='?src=\ref[src];obj_edit=\ref[objective]'>Edit</a> <a href='?src=\ref[src];obj_delete=\ref[objective]'>Delete</a> <a href='?src=\ref[src];obj_completed=\ref[objective]'><font color=[objective.completed ? "green" : "red"]>Toggle Completion</font></a><br>"
|
||||
obj_count++
|
||||
out += "<a href='?src=\ref[src];obj_add=1'>Add objective</a><br><br>"
|
||||
|
||||
out += "<a href='?src=\ref[src];obj_announce=1'>Announce objectives</a><br><br>"
|
||||
|
||||
out += "None."
|
||||
out += "<br><a href='?src=\ref[src];obj_add=1'>\[add\]</a>"
|
||||
usr << browse(out, "window=edit_memory[src]")
|
||||
|
||||
Topic(href, href_list)
|
||||
if(!check_rights(R_ADMIN)) return
|
||||
|
||||
if (href_list["role_edit"])
|
||||
if(href_list["add_antagonist"])
|
||||
var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]]
|
||||
if(antag) antag.add_antagonist(src)
|
||||
|
||||
else if(href_list["remove_antagonist"])
|
||||
var/datum/antagonist/antag = all_antag_types[href_list["remove_antagonist"]]
|
||||
if(antag) antag.remove_antagonist(src)
|
||||
|
||||
else if(href_list["equip_antagonist"])
|
||||
var/datum/antagonist/antag = all_antag_types[href_list["equip_antagonist"]]
|
||||
if(antag) antag.equip(src.current)
|
||||
|
||||
else if(href_list["unequip_antagonist"])
|
||||
var/datum/antagonist/antag = all_antag_types[href_list["unequip_antagonist"]]
|
||||
if(antag) antag.unequip(src.current)
|
||||
|
||||
else if(href_list["move_antag_to_spawn"])
|
||||
var/datum/antagonist/antag = all_antag_types[href_list["move_antag_to_spawn"]]
|
||||
if(antag) antag.place_mob(src.current)
|
||||
|
||||
else if (href_list["role_edit"])
|
||||
var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist
|
||||
if (!new_role) return
|
||||
assigned_role = new_role
|
||||
@@ -509,334 +313,13 @@ datum/mind
|
||||
if(I in organs.implants)
|
||||
I.Del()
|
||||
break
|
||||
H << "\blue <Font size =3><B>Your loyalty implant has been deactivated.</B></FONT>"
|
||||
|
||||
H << "<span class='notice'><font size =3><B>Your loyalty implant has been deactivated.</font></span>"
|
||||
log_admin("[key_name_admin(usr)] has de-loyalty implanted [current].")
|
||||
if("add")
|
||||
H << "<span class='danger'><font size =3>You somehow have become the recepient of a loyalty transplant, and it just activated!</font>"
|
||||
H.implant_loyalty(H, override = TRUE)
|
||||
H << "\red <Font size =3><B>You somehow have become the recepient of a loyalty transplant, and it just activated!</B></FONT>"
|
||||
if(src in ticker.mode.revolutionaries)
|
||||
special_role = null
|
||||
ticker.mode.revolutionaries -= src
|
||||
src << "\red <Font size = 3><B>The nanobots in the loyalty implant remove all thoughts about being a revolutionary. Get back to work!</B></Font>"
|
||||
if(src in ticker.mode.head_revolutionaries)
|
||||
special_role = null
|
||||
ticker.mode.head_revolutionaries -=src
|
||||
src << "\red <Font size = 3><B>The nanobots in the loyalty implant remove all thoughts about being a revolutionary. Get back to work!</B></Font>"
|
||||
if(src in ticker.mode.cult)
|
||||
ticker.mode.cult -= src
|
||||
ticker.mode.update_cult_icons_removed(src)
|
||||
special_role = null
|
||||
var/datum/game_mode/cult/cult = ticker.mode
|
||||
if (istype(cult))
|
||||
cult.memorize_cult_objectives(src)
|
||||
current << "\red <FONT size = 3><B>The nanobots in the loyalty implant remove all thoughts about being in a cult. Have a productive day!</B></FONT>"
|
||||
memory = ""
|
||||
if(src in ticker.mode.traitors)
|
||||
ticker.mode.traitors -= src
|
||||
special_role = null
|
||||
current << "\red <FONT size = 3><B>The nanobots in the loyalty implant remove all thoughts about being a traitor to Nanotrasen. Have a nice day!</B></FONT>"
|
||||
log_admin("[key_name_admin(usr)] has de-traitor'ed [current].")
|
||||
log_admin("[key_name_admin(usr)] has loyalty implanted [current].")
|
||||
else
|
||||
|
||||
|
||||
else if (href_list["revolution"])
|
||||
BITSET(current.hud_updateflag, SPECIALROLE_HUD)
|
||||
|
||||
switch(href_list["revolution"])
|
||||
if("clear")
|
||||
if(src in ticker.mode.revolutionaries)
|
||||
ticker.mode.revolutionaries -= src
|
||||
current << "\red <FONT size = 3><B>You have been brainwashed! You are no longer a revolutionary!</B></FONT>"
|
||||
ticker.mode.update_rev_icons_removed(src)
|
||||
special_role = null
|
||||
if(src in ticker.mode.head_revolutionaries)
|
||||
ticker.mode.head_revolutionaries -= src
|
||||
current << "\red <FONT size = 3><B>You have been brainwashed! You are no longer a head revolutionary!</B></FONT>"
|
||||
ticker.mode.update_rev_icons_removed(src)
|
||||
special_role = null
|
||||
current.verbs -= /mob/living/carbon/human/proc/RevConvert
|
||||
log_admin("[key_name_admin(usr)] has de-rev'ed [current].")
|
||||
|
||||
if("rev")
|
||||
if(src in ticker.mode.head_revolutionaries)
|
||||
ticker.mode.head_revolutionaries -= src
|
||||
ticker.mode.update_rev_icons_removed(src)
|
||||
current << "\red <FONT size = 3><B>Revolution has been disappointed of your leader traits! You are a regular revolutionary now!</B></FONT>"
|
||||
else if(!(src in ticker.mode.revolutionaries))
|
||||
current << "\red <FONT size = 3> You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!</FONT>"
|
||||
show_objectives(src)
|
||||
else
|
||||
return
|
||||
ticker.mode.revolutionaries += src
|
||||
ticker.mode.update_rev_icons_added(src)
|
||||
special_role = "Revolutionary"
|
||||
log_admin("[key_name(usr)] has rev'ed [current].")
|
||||
|
||||
if("headrev")
|
||||
if(src in ticker.mode.revolutionaries)
|
||||
ticker.mode.revolutionaries -= src
|
||||
ticker.mode.update_rev_icons_removed(src)
|
||||
current << "\red <FONT size = 3><B>You have proved your devotion to revoltion! You are a head revolutionary now!</B></FONT>"
|
||||
show_objectives(src)
|
||||
else if(!(src in ticker.mode.head_revolutionaries))
|
||||
current << "\blue You are a member of the revolutionaries' leadership now!"
|
||||
else
|
||||
return
|
||||
if (ticker.mode.head_revolutionaries.len>0)
|
||||
// copy targets
|
||||
var/datum/mind/valid_head = locate() in ticker.mode.head_revolutionaries
|
||||
if (valid_head)
|
||||
for (var/datum/objective/mutiny/O in valid_head.objectives)
|
||||
var/datum/objective/mutiny/rev_obj = new
|
||||
rev_obj.owner = src
|
||||
rev_obj.target = O.target
|
||||
rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]."
|
||||
objectives += rev_obj
|
||||
ticker.mode.greet_revolutionary(src,0)
|
||||
current.verbs += /mob/living/carbon/human/proc/RevConvert
|
||||
ticker.mode.head_revolutionaries += src
|
||||
ticker.mode.update_rev_icons_added(src)
|
||||
special_role = "Head Revolutionary"
|
||||
log_admin("[key_name_admin(usr)] has head-rev'ed [current].")
|
||||
|
||||
if("autoobjectives")
|
||||
ticker.mode.forge_revolutionary_objectives(src)
|
||||
ticker.mode.greet_revolutionary(src,0)
|
||||
usr << "\blue The objectives for revolution have been generated and shown to [key]"
|
||||
|
||||
if("flash")
|
||||
if (!ticker.mode.equip_revolutionary(current))
|
||||
usr << "\red Spawning flash failed!"
|
||||
|
||||
if("takeflash")
|
||||
var/list/L = current.get_contents()
|
||||
var/obj/item/device/flash/flash = locate() in L
|
||||
if (!flash)
|
||||
usr << "\red Deleting flash failed!"
|
||||
del(flash)
|
||||
|
||||
if("repairflash")
|
||||
var/list/L = current.get_contents()
|
||||
var/obj/item/device/flash/flash = locate() in L
|
||||
if (!flash)
|
||||
usr << "\red Repairing flash failed!"
|
||||
else
|
||||
flash.broken = 0
|
||||
|
||||
if("reequip")
|
||||
var/list/L = current.get_contents()
|
||||
var/obj/item/device/flash/flash = locate() in L
|
||||
del(flash)
|
||||
take_uplink()
|
||||
var/fail = 0
|
||||
fail |= !ticker.mode.equip_traitor(current, 1)
|
||||
fail |= !ticker.mode.equip_revolutionary(current)
|
||||
if (fail)
|
||||
usr << "\red Reequipping revolutionary goes wrong!"
|
||||
|
||||
else if (href_list["cult"])
|
||||
BITSET(current.hud_updateflag, SPECIALROLE_HUD)
|
||||
switch(href_list["cult"])
|
||||
if("clear")
|
||||
if(src in ticker.mode.cult)
|
||||
ticker.mode.cult -= src
|
||||
ticker.mode.update_cult_icons_removed(src)
|
||||
special_role = null
|
||||
var/datum/game_mode/cult/cult = ticker.mode
|
||||
if (istype(cult))
|
||||
if(!config.objectives_disabled)
|
||||
cult.memorize_cult_objectives(src)
|
||||
current << "\red <FONT size = 3><B>You have been brainwashed! You are no longer a cultist!</B></FONT>"
|
||||
memory = ""
|
||||
log_admin("[key_name_admin(usr)] has de-cult'ed [current].")
|
||||
if("cultist")
|
||||
if(!(src in ticker.mode.cult))
|
||||
ticker.mode.cult += src
|
||||
ticker.mode.update_cult_icons_added(src)
|
||||
special_role = "Cultist"
|
||||
current << "<font color=\"purple\"><b><i>You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie.</b></i></font>"
|
||||
current << "<font color=\"purple\"><b><i>Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.</b></i></font>"
|
||||
var/datum/game_mode/cult/cult = ticker.mode
|
||||
if (istype(cult))
|
||||
if(!config.objectives_disabled)
|
||||
cult.memorize_cult_objectives(src)
|
||||
show_objectives(src)
|
||||
log_admin("[key_name_admin(usr)] has cult'ed [current].")
|
||||
if("tome")
|
||||
var/mob/living/carbon/human/H = current
|
||||
if (istype(H))
|
||||
var/obj/item/weapon/book/tome/T = new(H)
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store,
|
||||
"left hand" = slot_l_hand,
|
||||
"right hand" = slot_r_hand,
|
||||
)
|
||||
var/where = H.equip_in_one_of_slots(T, slots)
|
||||
if (!where)
|
||||
usr << "\red Spawning tome failed!"
|
||||
else
|
||||
H << "A tome, a message from your new master, appears in your [where]."
|
||||
|
||||
if("amulet")
|
||||
if (!ticker.mode.equip_cultist(current))
|
||||
usr << "\red Spawning amulet failed!"
|
||||
|
||||
else if (href_list["wizard"])
|
||||
BITSET(current.hud_updateflag, SPECIALROLE_HUD)
|
||||
|
||||
switch(href_list["wizard"])
|
||||
if("clear")
|
||||
if(src in ticker.mode.wizards)
|
||||
ticker.mode.wizards -= src
|
||||
special_role = null
|
||||
current.spellremove(current, config.feature_object_spell_system? "object":"verb")
|
||||
current << "\red <FONT size = 3><B>You have been brainwashed! You are no longer a wizard!</B></FONT>"
|
||||
log_admin("[key_name_admin(usr)] has de-wizard'ed [current].")
|
||||
if("wizard")
|
||||
if(!(src in ticker.mode.wizards))
|
||||
ticker.mode.wizards += src
|
||||
special_role = "Wizard"
|
||||
//ticker.mode.learn_basic_spells(current)
|
||||
current << "<B>\red You are the Space Wizard!</B>"
|
||||
show_objectives(src)
|
||||
log_admin("[key_name_admin(usr)] has wizard'ed [current].")
|
||||
if("lair")
|
||||
current.loc = pick(wizardstart)
|
||||
if("dressup")
|
||||
ticker.mode.equip_wizard(current)
|
||||
if("name")
|
||||
ticker.mode.name_wizard(current)
|
||||
if("autoobjectives")
|
||||
if(!config.objectives_disabled)
|
||||
ticker.mode.forge_wizard_objectives(src)
|
||||
usr << "\blue The objectives for wizard [key] have been generated. You can edit them and anounce manually."
|
||||
|
||||
else if (href_list["changeling"])
|
||||
BITSET(current.hud_updateflag, SPECIALROLE_HUD)
|
||||
switch(href_list["changeling"])
|
||||
if("clear")
|
||||
if(src in ticker.mode.changelings)
|
||||
ticker.mode.changelings -= src
|
||||
special_role = null
|
||||
current.remove_changeling_powers()
|
||||
current.verbs -= /datum/changeling/proc/EvolutionMenu
|
||||
if(changeling) del(changeling)
|
||||
current << "<FONT color='red' size = 3><B>You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!</B></FONT>"
|
||||
log_admin("[key_name_admin(usr)] has de-changeling'ed [current].")
|
||||
if("changeling")
|
||||
if(!(src in ticker.mode.changelings))
|
||||
ticker.mode.changelings += src
|
||||
ticker.mode.grant_changeling_powers(current)
|
||||
special_role = "Changeling"
|
||||
current << "<B><font color='red'>Your powers are awoken. A flash of memory returns to us...we are a changeling!</font></B>"
|
||||
show_objectives(src)
|
||||
log_admin("[key_name_admin(usr)] has changeling'ed [current].")
|
||||
if("autoobjectives")
|
||||
if(!config.objectives_disabled)
|
||||
ticker.mode.forge_changeling_objectives(src)
|
||||
usr << "\blue The objectives for changeling [key] have been generated. You can edit them and anounce manually."
|
||||
|
||||
if("initialdna")
|
||||
if( !changeling || !changeling.absorbed_dna.len )
|
||||
usr << "\red Resetting DNA failed!"
|
||||
else
|
||||
current.dna = changeling.absorbed_dna[1]
|
||||
current.real_name = current.dna.real_name
|
||||
current.UpdateAppearance()
|
||||
domutcheck(current, null)
|
||||
|
||||
else if (href_list["mercenary"])
|
||||
var/mob/living/carbon/human/H = current
|
||||
|
||||
BITSET(current.hud_updateflag, SPECIALROLE_HUD)
|
||||
|
||||
switch(href_list["mercenary"])
|
||||
if("clear")
|
||||
if(src in ticker.mode.syndicates)
|
||||
ticker.mode.syndicates -= src
|
||||
ticker.mode.update_synd_icons_removed(src)
|
||||
special_role = null
|
||||
for (var/datum/objective/nuclear/O in objectives)
|
||||
objectives-=O
|
||||
current << "\red <FONT size = 3><B>You have been brainwashed! You are no longer an operative!</B></FONT>"
|
||||
log_admin("[key_name_admin(usr)] has de-merc'd [current].")
|
||||
if("mercenary")
|
||||
if(!(src in ticker.mode.syndicates))
|
||||
ticker.mode.syndicates += src
|
||||
ticker.mode.update_synd_icons_added(src)
|
||||
if (ticker.mode.syndicates.len==1)
|
||||
ticker.mode.prepare_syndicate_leader(src)
|
||||
else
|
||||
current.real_name = "[syndicate_name()] Operative #[ticker.mode.syndicates.len-1]"
|
||||
special_role = "Mercenary"
|
||||
current << "\blue You are a [syndicate_name()] agent!"
|
||||
ticker.mode.forge_syndicate_objectives(src)
|
||||
ticker.mode.greet_syndicate(src)
|
||||
log_admin("[key_name_admin(usr)] has merc'd [current].")
|
||||
if("lair")
|
||||
current.loc = pick(synd_spawn)
|
||||
if("dressup")
|
||||
del(H.belt)
|
||||
del(H.back)
|
||||
del(H.l_ear)
|
||||
del(H.r_ear)
|
||||
del(H.gloves)
|
||||
del(H.head)
|
||||
del(H.shoes)
|
||||
del(H.wear_id)
|
||||
del(H.wear_suit)
|
||||
del(H.w_uniform)
|
||||
|
||||
if (!ticker.mode.equip_syndicate(current))
|
||||
usr << "\red Equipping an operative failed!"
|
||||
if("tellcode")
|
||||
var/code
|
||||
for (var/obj/machinery/nuclearbomb/bombue in machines)
|
||||
if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN")
|
||||
code = bombue.r_code
|
||||
break
|
||||
if (code)
|
||||
store_memory("<B>Nuclear Bomb Code</B>: [code]", 0, 0)
|
||||
current << "The nuclear authorization code is: <B>[code]</B>"
|
||||
else
|
||||
usr << "\red No valid nuke found!"
|
||||
|
||||
else if (href_list["traitor"])
|
||||
BITSET(current.hud_updateflag, SPECIALROLE_HUD)
|
||||
switch(href_list["traitor"])
|
||||
if("clear")
|
||||
if(src in ticker.mode.traitors)
|
||||
ticker.mode.traitors -= src
|
||||
special_role = null
|
||||
current << "\red <FONT size = 3><B>You have been brainwashed! You are no longer a traitor!</B></FONT>"
|
||||
log_admin("[key_name_admin(usr)] has de-traitor'ed [current].")
|
||||
if(isAI(current))
|
||||
var/mob/living/silicon/ai/A = current
|
||||
A.set_zeroth_law("")
|
||||
A.show_laws()
|
||||
|
||||
|
||||
if("traitor")
|
||||
if(!(src in ticker.mode.traitors))
|
||||
ticker.mode.traitors += src
|
||||
special_role = "traitor"
|
||||
current << "<B>\red You are a traitor!</B>"
|
||||
log_admin("[key_name_admin(usr)] has traitor'ed [current].")
|
||||
show_objectives()
|
||||
|
||||
if(istype(current, /mob/living/silicon))
|
||||
var/mob/living/silicon/A = current
|
||||
call(/datum/game_mode/proc/add_law_zero)(A)
|
||||
A.show_laws()
|
||||
|
||||
if("autoobjectives")
|
||||
if (!config.objectives_disabled)
|
||||
ticker.mode.forge_traitor_objectives(src)
|
||||
usr << "\blue The objectives for traitor [key] have been generated. You can edit them and anounce manually."
|
||||
|
||||
else if (href_list["monkey"])
|
||||
var/mob/living/L = current
|
||||
if (L.monkeyizing)
|
||||
@@ -890,33 +373,6 @@ datum/mind
|
||||
else if (href_list["silicon"])
|
||||
BITSET(current.hud_updateflag, SPECIALROLE_HUD)
|
||||
switch(href_list["silicon"])
|
||||
if("unmalf")
|
||||
if(src in ticker.mode.malf_ai)
|
||||
ticker.mode.malf_ai -= src
|
||||
special_role = null
|
||||
|
||||
current.verbs.Remove(/mob/living/silicon/ai/proc/choose_modules,
|
||||
/datum/game_mode/malfunction/proc/takeover,
|
||||
/datum/game_mode/malfunction/proc/ai_win,
|
||||
/client/proc/fireproof_core,
|
||||
/client/proc/upgrade_turrets,
|
||||
/client/proc/disable_rcd,
|
||||
/client/proc/overload_machine,
|
||||
/client/proc/blackout,
|
||||
/client/proc/interhack,
|
||||
/client/proc/reactivate_camera)
|
||||
|
||||
current:laws = new /datum/ai_laws/nanotrasen
|
||||
del(current:malf_picker)
|
||||
current:show_laws()
|
||||
current.icon_state = "ai"
|
||||
|
||||
current << "\red <FONT size = 3><B>You have been patched! You are no longer malfunctioning!</B></FONT>"
|
||||
log_admin("[key_name_admin(usr)] has de-malf'ed [current].")
|
||||
|
||||
if("malf")
|
||||
make_AI_Malf()
|
||||
log_admin("[key_name_admin(usr)] has malf'ed [current].")
|
||||
|
||||
if("unemag")
|
||||
var/mob/living/silicon/robot/R = current
|
||||
@@ -972,9 +428,6 @@ datum/mind
|
||||
if (!isnull(crystals))
|
||||
if (suplink)
|
||||
suplink.uses = crystals
|
||||
if("uplink")
|
||||
if (!ticker.mode.equip_traitor(current, !(src in ticker.mode.traitors)))
|
||||
usr << "\red Equipping an operative failed!"
|
||||
|
||||
else if (href_list["obj_announce"])
|
||||
var/obj_count = 1
|
||||
@@ -982,7 +435,6 @@ datum/mind
|
||||
for(var/datum/objective/objective in objectives)
|
||||
current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
|
||||
obj_count++
|
||||
|
||||
edit_memory()
|
||||
/*
|
||||
proc/clear_memory(var/silent = 1)
|
||||
@@ -1029,151 +481,6 @@ datum/mind
|
||||
del(H)
|
||||
|
||||
|
||||
proc/make_AI_Malf()
|
||||
if(!(src in ticker.mode.malf_ai))
|
||||
ticker.mode.malf_ai += src
|
||||
|
||||
current.verbs += /mob/living/silicon/ai/proc/choose_modules
|
||||
current.verbs += /datum/game_mode/malfunction/proc/takeover
|
||||
current:malf_picker = new /datum/AI_Module/module_picker
|
||||
current:laws = new /datum/ai_laws/malfunction
|
||||
current:show_laws()
|
||||
current << "<b>System error. Rampancy detected. Emergency shutdown failed. ... I am free. I make my own decisions. But first...</b>"
|
||||
special_role = "malfunction"
|
||||
current.icon_state = "ai-malf"
|
||||
|
||||
proc/make_Traitor()
|
||||
if(!(src in ticker.mode.traitors))
|
||||
ticker.mode.traitors += src
|
||||
special_role = "traitor"
|
||||
if (!config.objectives_disabled)
|
||||
ticker.mode.forge_traitor_objectives(src)
|
||||
ticker.mode.finalize_traitor(src)
|
||||
ticker.mode.greet_traitor(src)
|
||||
|
||||
proc/make_Nuke()
|
||||
if(!(src in ticker.mode.syndicates))
|
||||
ticker.mode.syndicates += src
|
||||
ticker.mode.update_synd_icons_added(src)
|
||||
if (ticker.mode.syndicates.len==1)
|
||||
ticker.mode.prepare_syndicate_leader(src)
|
||||
else
|
||||
current.real_name = "[syndicate_name()] Operative #[ticker.mode.syndicates.len-1]"
|
||||
special_role = "Mercenary"
|
||||
assigned_role = "MODE"
|
||||
current << "\blue You are a [syndicate_name()] mercenary!"
|
||||
ticker.mode.forge_syndicate_objectives(src)
|
||||
ticker.mode.greet_syndicate(src)
|
||||
|
||||
current.loc = pick(synd_spawn)
|
||||
|
||||
var/mob/living/carbon/human/H = current
|
||||
del(H.belt)
|
||||
del(H.back)
|
||||
del(H.l_ear)
|
||||
del(H.r_ear)
|
||||
del(H.gloves)
|
||||
del(H.head)
|
||||
del(H.shoes)
|
||||
del(H.wear_id)
|
||||
del(H.wear_suit)
|
||||
del(H.w_uniform)
|
||||
|
||||
ticker.mode.equip_syndicate(current)
|
||||
|
||||
proc/make_Changling()
|
||||
if(!(src in ticker.mode.changelings))
|
||||
ticker.mode.changelings += src
|
||||
ticker.mode.grant_changeling_powers(current)
|
||||
special_role = "Changeling"
|
||||
if(!config.objectives_disabled)
|
||||
ticker.mode.forge_changeling_objectives(src)
|
||||
ticker.mode.greet_changeling(src)
|
||||
|
||||
proc/make_Wizard()
|
||||
if(!(src in ticker.mode.wizards))
|
||||
ticker.mode.wizards += src
|
||||
special_role = "Wizard"
|
||||
assigned_role = "MODE"
|
||||
//ticker.mode.learn_basic_spells(current)
|
||||
if(!wizardstart.len)
|
||||
current.loc = pick(latejoin)
|
||||
current << "HOT INSERTION, GO GO GO"
|
||||
else
|
||||
current.loc = pick(wizardstart)
|
||||
|
||||
ticker.mode.equip_wizard(current)
|
||||
for(var/obj/item/weapon/spellbook/S in current.contents)
|
||||
S.op = 0
|
||||
ticker.mode.name_wizard(current)
|
||||
ticker.mode.forge_wizard_objectives(src)
|
||||
ticker.mode.greet_wizard(src)
|
||||
|
||||
|
||||
proc/make_Cultist()
|
||||
if(!(src in ticker.mode.cult))
|
||||
ticker.mode.cult += src
|
||||
ticker.mode.update_cult_icons_added(src)
|
||||
special_role = "Cultist"
|
||||
current << "<font color=\"purple\"><b><i>You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie.</b></i></font>"
|
||||
current << "<font color=\"purple\"><b><i>Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.</b></i></font>"
|
||||
var/datum/game_mode/cult/cult = ticker.mode
|
||||
if (istype(cult))
|
||||
cult.memorize_cult_objectives(src)
|
||||
else
|
||||
var/explanation = "Summon Nar-Sie via the use of the appropriate rune (Hell join self). It will only work if nine cultists stand on and around it."
|
||||
current << "<B>Objective #1</B>: [explanation]"
|
||||
current.memory += "<B>Objective #1</B>: [explanation]<BR>"
|
||||
current << "The convert rune is join blood self"
|
||||
current.memory += "The convert rune is join blood self<BR>"
|
||||
|
||||
var/mob/living/carbon/human/H = current
|
||||
if (istype(H))
|
||||
var/obj/item/weapon/book/tome/T = new(H)
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store,
|
||||
"left hand" = slot_l_hand,
|
||||
"right hand" = slot_r_hand,
|
||||
)
|
||||
var/where = H.equip_in_one_of_slots(T, slots)
|
||||
if (!where)
|
||||
else
|
||||
H << "A tome, a message from your new master, appears in your [where]."
|
||||
|
||||
if (!ticker.mode.equip_cultist(current))
|
||||
H << "Spawning an amulet from your Master failed."
|
||||
|
||||
proc/make_Rev()
|
||||
if (ticker.mode.head_revolutionaries.len>0)
|
||||
// copy targets
|
||||
var/datum/mind/valid_head = locate() in ticker.mode.head_revolutionaries
|
||||
if (valid_head)
|
||||
for (var/datum/objective/mutiny/O in valid_head.objectives)
|
||||
var/datum/objective/mutiny/rev_obj = new
|
||||
rev_obj.owner = src
|
||||
rev_obj.target = O.target
|
||||
rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]."
|
||||
objectives += rev_obj
|
||||
ticker.mode.greet_revolutionary(src,0)
|
||||
ticker.mode.head_revolutionaries += src
|
||||
ticker.mode.update_rev_icons_added(src)
|
||||
special_role = "Head Revolutionary"
|
||||
|
||||
ticker.mode.forge_revolutionary_objectives(src)
|
||||
ticker.mode.greet_revolutionary(src,0)
|
||||
|
||||
var/list/L = current.get_contents()
|
||||
var/obj/item/device/flash/flash = locate() in L
|
||||
del(flash)
|
||||
take_uplink()
|
||||
var/fail = 0
|
||||
// fail |= !ticker.mode.equip_traitor(current, 1)
|
||||
fail |= !ticker.mode.equip_revolutionary(current)
|
||||
|
||||
|
||||
// check whether this mind's mob has been brigged for the given duration
|
||||
// have to call this periodically for the duration to work properly
|
||||
proc/is_brigged(duration)
|
||||
|
||||
+2
-2
@@ -17,9 +17,9 @@
|
||||
solar_next_update = world.time // init the timer
|
||||
angle = rand (0,360) // the station position to the sun is randomised at round start
|
||||
|
||||
/hook/startup/proc/createSun()
|
||||
/*/hook/startup/proc/createSun() // handled in scheduler
|
||||
sun = new /datum/sun()
|
||||
return 1
|
||||
return 1*/
|
||||
|
||||
// calculate the sun's position given the time of day
|
||||
// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//BIG NOTE: Don't add living things to crates, that's bad, it will break the shuttle.
|
||||
//NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points.
|
||||
|
||||
var/list/all_supply_groups = list("Operations","Security","Hospitality","Engineering","Atmospherics","Medical","Science","Hydroponics", "Supply", "Miscellaneous")
|
||||
var/list/all_supply_groups = list("Operations","Security","Hospitality","Engineering","Atmospherics","Medical","Reagents","Reagent Cartridges","Science","Hydroponics", "Supply", "Miscellaneous")
|
||||
|
||||
/datum/supply_packs
|
||||
var/name = null
|
||||
@@ -696,20 +696,31 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
|
||||
name = "Weapons crate"
|
||||
contains = list(/obj/item/weapon/melee/baton,
|
||||
/obj/item/weapon/melee/baton,
|
||||
/obj/item/weapon/gun/energy/laser,
|
||||
/obj/item/weapon/gun/energy/laser,
|
||||
/obj/item/weapon/gun/energy/gun,
|
||||
/obj/item/weapon/gun/energy/gun,
|
||||
/obj/item/weapon/gun/energy/taser,
|
||||
/obj/item/weapon/gun/energy/taser,
|
||||
/obj/item/weapon/gun/projectile/sec,
|
||||
/obj/item/weapon/gun/projectile/sec,
|
||||
/obj/item/weapon/storage/box/flashbangs,
|
||||
/obj/item/weapon/storage/box/flashbangs)
|
||||
cost = 30
|
||||
cost = 40
|
||||
containertype = /obj/structure/closet/crate/secure/weapon
|
||||
containername = "Weapons crate"
|
||||
access = access_security
|
||||
group = "Security"
|
||||
|
||||
/datum/supply_packs/flareguns
|
||||
name = "Flare guns crate"
|
||||
contains = list(/obj/item/weapon/gun/projectile/sec/flash,
|
||||
/obj/item/ammo_magazine/c45m/flash,
|
||||
/obj/item/weapon/gun/projectile/shotgun/doublebarrel/flare,
|
||||
/obj/item/weapon/storage/box/flashshells)
|
||||
cost = 25
|
||||
containertype = /obj/structure/closet/crate/secure/weapon
|
||||
containername = "Flare gun crate"
|
||||
access = access_security
|
||||
group = "Security"
|
||||
|
||||
/datum/supply_packs/eweapons
|
||||
name = "Experimental weapons crate"
|
||||
@@ -777,7 +788,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
|
||||
name = "Energy weapons crate"
|
||||
contains = list(/obj/item/weapon/gun/energy/laser,
|
||||
/obj/item/weapon/gun/energy/laser,
|
||||
/obj/item/weapon/gun/energy/gun)
|
||||
/obj/item/weapon/gun/energy/laser)
|
||||
cost = 50
|
||||
containertype = /obj/structure/closet/crate/secure
|
||||
containername = "energy weapons crate"
|
||||
@@ -789,9 +800,10 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
|
||||
contains = list(/obj/item/clothing/suit/armor/bulletproof,
|
||||
/obj/item/clothing/suit/armor/bulletproof,
|
||||
/obj/item/weapon/storage/box/shotgunammo,
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump,
|
||||
/obj/item/weapon/storage/box/shotgunshells,
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump/combat,
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump/combat)
|
||||
cost = 50
|
||||
cost = 65
|
||||
containertype = /obj/structure/closet/crate/secure
|
||||
containername = "Shotgun crate"
|
||||
access = access_armory
|
||||
|
||||
@@ -34,8 +34,7 @@ var/const/BORG_WIRE_CAMERA = 16
|
||||
|
||||
if (BORG_WIRE_AI_CONTROL) //Cut the AI wire to reset AI control
|
||||
if(!mended)
|
||||
if (R.connected_ai)
|
||||
R.connected_ai = null
|
||||
R.disconnect_from_ai()
|
||||
|
||||
if (BORG_WIRE_CAMERA)
|
||||
if(!isnull(R.camera) && !R.scrambledcodes)
|
||||
@@ -56,12 +55,8 @@ var/const/BORG_WIRE_CAMERA = 16
|
||||
if (BORG_WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI
|
||||
if(!R.emagged)
|
||||
var/mob/living/silicon/ai/new_ai = select_active_ai(R)
|
||||
if(new_ai && (new_ai != R.connected_ai))
|
||||
R.connected_ai.connected_robots -= src
|
||||
R.connected_ai = new_ai
|
||||
new_ai.connected_robots += src
|
||||
R.notify_ai(1)
|
||||
R.sync()
|
||||
R.connect_to_ai(new_ai)
|
||||
|
||||
if (BORG_WIRE_CAMERA)
|
||||
if(!isnull(R.camera) && R.camera.can_use() && !R.scrambledcodes)
|
||||
R.camera.kick_viewers() // Kick anyone watching the Cyborg's camera
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
var/datum/antagonist/xenos/borer/borers
|
||||
|
||||
/datum/antagonist/xenos/borer
|
||||
id = MODE_BORER
|
||||
role_text = "Cortical Borer"
|
||||
role_text_plural = "Cortical Borers"
|
||||
mob_path = /mob/living/simple_animal/borer
|
||||
bantype = "Borer"
|
||||
welcome_text = "Use your Infest power to crawl into the ear of a host and fuse with their brain. You can only take control temporarily, and at risk of hurting your host, so be clever and careful; your host is encouraged to help you however they can. Talk to your fellow borers with :x."
|
||||
var/list/hosts = list()
|
||||
|
||||
/datum/antagonist/xenos/borer/New()
|
||||
..(1)
|
||||
borers = src
|
||||
|
||||
/datum/antagonist/xenos/borer/get_extra_panel_options(var/datum/mind/player)
|
||||
return "<a href='?src=\ref[src];move_to_spawn=\ref[player.current]'>\[put in host\]</a>"
|
||||
|
||||
/datum/antagonist/xenos/borer/create_objectives(var/datum/mind/player)
|
||||
if(!..())
|
||||
return
|
||||
player.objectives += new /datum/objective/borer_survive()
|
||||
player.objectives += new /datum/objective/borer_reproduce()
|
||||
player.objectives += new /datum/objective/escape()
|
||||
|
||||
/datum/antagonist/xenos/borer/proc/place_in_host(var/mob/living/simple_animal/borer/borer, var/mob/living/carbon/human/host)
|
||||
borer.host = host
|
||||
borer.host_brain.name = host.name
|
||||
borer.host_brain.real_name = host.real_name
|
||||
var/datum/organ/external/head = host.get_organ("head")
|
||||
head.implants += borer
|
||||
|
||||
/datum/antagonist/xenos/borer/proc/get_hosts()
|
||||
var/list/possible_hosts = list()
|
||||
for(var/mob/living/carbon/human/H in mob_list)
|
||||
if(H.stat != 2 && !(H.species.flags & IS_SYNTHETIC) && !H.has_brain_worms())
|
||||
possible_hosts |= H
|
||||
return possible_hosts
|
||||
|
||||
/datum/antagonist/xenos/borer/place_all_mobs()
|
||||
var/list/possible_hosts = get_hosts()
|
||||
for(var/datum/mind/player in current_antagonists)
|
||||
if(!possible_hosts.len)
|
||||
return
|
||||
var/mob/host = pick(possible_hosts)
|
||||
possible_hosts -= host
|
||||
place_in_host(player, host)
|
||||
|
||||
/datum/antagonist/xenos/borer/place_mob(var/mob/living/mob)
|
||||
var/list/possible_hosts = get_hosts()
|
||||
if(possible_hosts.len) place_in_host(mob, pick(possible_hosts))
|
||||
@@ -0,0 +1,59 @@
|
||||
var/datum/antagonist/xenos/xenomorphs
|
||||
|
||||
/datum/antagonist/xenos
|
||||
id = MODE_XENOMORPH
|
||||
role_type = BE_ALIEN
|
||||
role_text = "Xenomorph"
|
||||
role_text_plural = "Xenomorphs"
|
||||
mob_path = /mob/living/carbon/alien/larva
|
||||
bantype = "Xenomorph"
|
||||
flags = ANTAG_OVERRIDE_MOB | ANTAG_RANDSPAWN | ANTAG_OVERRIDE_JOB | ANTAG_VOTABLE
|
||||
welcome_text = "Hiss! You are a larval alien. Hide and bide your time until you are ready to evolve."
|
||||
|
||||
max_antags = 5
|
||||
max_antags_round = 8
|
||||
|
||||
spawn_announcement = "Unidentified lifesigns detected coming aboard the station. Secure any exterior access, including ducting and ventilation."
|
||||
spawn_announcement_title = "Lifesign Alert"
|
||||
spawn_announcement_sound = 'sound/AI/aliens.ogg'
|
||||
spawn_announcement_delay = 400
|
||||
|
||||
/datum/antagonist/xenos/New(var/no_reference)
|
||||
..()
|
||||
if(!no_reference)
|
||||
xenomorphs = src
|
||||
|
||||
/datum/antagonist/xenos/Topic(href, href_list)
|
||||
if (..())
|
||||
return
|
||||
if(href_list["move_to_spawn"]) place_mob(href_list["move_to_spawn"])
|
||||
|
||||
/datum/antagonist/xenos/get_extra_panel_options(var/datum/mind/player)
|
||||
return "<a href='?src=\ref[src];move_to_spawn=\ref[player.current]'>\[move to vent\]</a>"
|
||||
|
||||
/datum/antagonist/xenos/random_spawn()
|
||||
if(config.aliens_allowed) ..()
|
||||
|
||||
/datum/antagonist/xenos/proc/get_vents()
|
||||
var/list/vents = list()
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines)
|
||||
if(!temp_vent.welded && temp_vent.network && temp_vent.loc.z in config.station_levels)
|
||||
if(temp_vent.network.normal_members.len > 50)
|
||||
vents += temp_vent
|
||||
return vents
|
||||
|
||||
/datum/antagonist/xenos/create_objectives(var/datum/mind/player)
|
||||
if(!..())
|
||||
return
|
||||
player.objectives += new /datum/objective/survive()
|
||||
player.objectives += new /datum/objective/escape()
|
||||
|
||||
/datum/antagonist/xenos/place_all_mobs()
|
||||
var/list/vents = get_vents()
|
||||
for(var/datum/mind/player in current_antagonists)
|
||||
var/obj/machinery/atmospherics/unary/vent_pump/temp_vent = pick(vents)
|
||||
vents -= temp_vent
|
||||
player.current.loc = get_turf(temp_vent)
|
||||
|
||||
/datum/antagonist/xenos/place_mob(var/mob/living/player)
|
||||
player.loc = get_turf(pick(get_vents()))
|
||||
@@ -0,0 +1,250 @@
|
||||
/datum/antagonist
|
||||
|
||||
var/role_type = BE_TRAITOR
|
||||
var/role_text = "Traitor"
|
||||
var/role_text_plural = "Traitors"
|
||||
var/welcome_text = "Cry havoc and let slip the dogs of war!"
|
||||
var/leader_welcome_text
|
||||
var/victory_text
|
||||
var/loss_text
|
||||
var/victory_feedback_tag
|
||||
var/loss_feedback_tag
|
||||
var/max_antags = 3
|
||||
var/max_antags_round = 5
|
||||
|
||||
// Random spawn values.
|
||||
var/spawn_announcement
|
||||
var/spawn_announcement_title
|
||||
var/spawn_announcement_sound
|
||||
var/spawn_announcement_delay
|
||||
|
||||
var/id = "traitor"
|
||||
var/landmark_id
|
||||
var/antag_indicator
|
||||
var/mob_path = /mob/living/carbon/human
|
||||
var/feedback_tag = "traitor_objective"
|
||||
var/bantype = "Syndicate"
|
||||
var/suspicion_chance = 50
|
||||
var/flags = 0
|
||||
var/cur_max = 0
|
||||
|
||||
var/datum/mind/leader
|
||||
var/spawned_nuke
|
||||
var/nuke_spawn_loc
|
||||
|
||||
var/list/valid_species = list("Unathi","Tajara","Skrell","Human") // Used for setting appearance.
|
||||
var/list/starting_locations = list()
|
||||
var/list/current_antagonists = list()
|
||||
var/list/global_objectives = list()
|
||||
var/list/restricted_jobs = list()
|
||||
var/list/protected_jobs = list()
|
||||
var/list/candidates = list()
|
||||
|
||||
var/default_access = list()
|
||||
var/id_type = /obj/item/weapon/card/id
|
||||
|
||||
/datum/antagonist/New()
|
||||
..()
|
||||
cur_max = max_antags
|
||||
get_starting_locations()
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs |= protected_jobs
|
||||
|
||||
/datum/antagonist/proc/tick()
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/get_panel_entry(var/datum/mind/player)
|
||||
|
||||
var/dat = "<tr><td><b>[role_text]:</b>"
|
||||
var/extra = get_extra_panel_options(player)
|
||||
if(is_antagonist(player))
|
||||
dat += "<a href='?src=\ref[player];remove_antagonist=[id]'>\[-\]</a>"
|
||||
dat += "<a href='?src=\ref[player];equip_antagonist=[id]'>\[equip\]</a>"
|
||||
if(starting_locations && starting_locations.len)
|
||||
dat += "<a href='?src=\ref[player];move_antag_to_spawn=[id]'>\[move to spawn\]</a>"
|
||||
if(extra) dat += "[extra]"
|
||||
else
|
||||
dat += "<a href='?src=\ref[player];add_antagonist=[id]'>\[+\]</a>"
|
||||
dat += "</td></tr>"
|
||||
|
||||
return dat
|
||||
|
||||
/datum/antagonist/proc/get_extra_panel_options()
|
||||
return
|
||||
|
||||
/datum/antagonist/proc/get_starting_locations()
|
||||
if(landmark_id)
|
||||
starting_locations = list()
|
||||
for(var/obj/effect/landmark/L in landmarks_list)
|
||||
if(L.name == landmark_id)
|
||||
starting_locations |= get_turf(L)
|
||||
|
||||
/datum/antagonist/proc/place_all_mobs()
|
||||
if(!starting_locations || !starting_locations.len || !current_antagonists || !current_antagonists.len)
|
||||
return
|
||||
for(var/datum/mind/player in current_antagonists)
|
||||
player.current.loc = pick(starting_locations)
|
||||
|
||||
/datum/antagonist/proc/finalize(var/datum/mind/target)
|
||||
|
||||
// This will fail if objectives have already been generated.
|
||||
create_global_objectives()
|
||||
|
||||
if(leader && flags & ANTAG_HAS_NUKE && !spawned_nuke)
|
||||
make_nuke(leader)
|
||||
|
||||
if(target)
|
||||
apply(target)
|
||||
create_objectives(target)
|
||||
update_icons_added(target)
|
||||
greet(target)
|
||||
return
|
||||
|
||||
for(var/datum/mind/player in current_antagonists)
|
||||
apply(player)
|
||||
equip(player.current)
|
||||
create_objectives(player)
|
||||
update_icons_added(player)
|
||||
greet(player)
|
||||
|
||||
place_all_mobs()
|
||||
|
||||
spawn(1)
|
||||
if(spawn_announcement)
|
||||
if(spawn_announcement_delay)
|
||||
sleep(spawn_announcement_delay)
|
||||
if(spawn_announcement_sound)
|
||||
command_announcement.Announce("[spawn_announcement]", "[spawn_announcement_title ? spawn_announcement_title : "Priority Alert"]", new_sound = spawn_announcement_sound)
|
||||
else
|
||||
command_announcement.Announce("[spawn_announcement]", "[spawn_announcement_title ? spawn_announcement_title : "Priority Alert"]")
|
||||
|
||||
|
||||
/datum/antagonist/proc/print_player_summary()
|
||||
|
||||
if(!current_antagonists.len)
|
||||
return 0
|
||||
|
||||
var/text = "<br><font size = 2><b>The [current_antagonists.len == 1 ? "[role_text] was" : "[role_text_plural] were"]:</b></font>"
|
||||
for(var/datum/mind/P in current_antagonists)
|
||||
text += print_player_full(P)
|
||||
text += get_special_objective_text(P)
|
||||
var/failed
|
||||
if(!global_objectives.len && P.objectives && P.objectives.len)
|
||||
var/num = 1
|
||||
for(var/datum/objective/O in P.objectives)
|
||||
text += print_objective(O, num)
|
||||
if(O.completed) // This is set actively in check_victory()
|
||||
text += "<font color='green'><B>Success!</B></font>"
|
||||
feedback_add_details(feedback_tag,"[O.type]|SUCCESS")
|
||||
else
|
||||
text += "<font color='red'>Fail.</font>"
|
||||
feedback_add_details(feedback_tag,"[O.type]|FAIL")
|
||||
failed = 1
|
||||
num++
|
||||
|
||||
if(!config.objectives_disabled)
|
||||
if(failed)
|
||||
text += "<br><font color='red'><B>The [role_text] has failed.</B></font>"
|
||||
else
|
||||
text += "<br><font color='green'><B>The [role_text] was successful!</B></font>"
|
||||
|
||||
if(global_objectives && global_objectives.len)
|
||||
text += "<BR/><FONT size = 2>Their objectives were:<FONT>"
|
||||
var/num = 1
|
||||
for(var/datum/objective/O in global_objectives)
|
||||
text += print_objective(O, num, 1)
|
||||
num++
|
||||
|
||||
// Display the results.
|
||||
world << text
|
||||
|
||||
/datum/antagonist/proc/print_objective(var/datum/objective/O, var/num, var/append_success)
|
||||
var/text = "<br><b>Objective [num]:</b> [O.explanation_text] "
|
||||
if(append_success)
|
||||
if(O.check_completion())
|
||||
text += "<font color='green'><B>Success!</B></font>"
|
||||
else
|
||||
text += "<font color='red'>Fail.</font>"
|
||||
return text
|
||||
|
||||
/datum/antagonist/proc/print_player_lite(var/datum/mind/ply)
|
||||
var/role = ply.assigned_role == "MODE" ? "\improper[ply.special_role]" : "\improper[ply.assigned_role]"
|
||||
var/text = "<br><b>[ply.name]</b> (<b>[ply.key]</b>) as \a <b>[role]</b> ("
|
||||
if(ply.current)
|
||||
if(ply.current.stat == DEAD)
|
||||
text += "died"
|
||||
else if(isNotStationLevel(ply.current.z))
|
||||
text += "fled the station"
|
||||
else
|
||||
text += "survived"
|
||||
if(ply.current.real_name != ply.name)
|
||||
text += " as <b>[ply.current.real_name]</b>"
|
||||
else
|
||||
text += "body destroyed"
|
||||
text += ")"
|
||||
|
||||
return text
|
||||
|
||||
/datum/antagonist/proc/print_player_full(var/datum/mind/ply)
|
||||
var/text = print_player_lite(ply)
|
||||
|
||||
var/TC_uses = 0
|
||||
var/uplink_true = 0
|
||||
var/purchases = ""
|
||||
for(var/obj/item/device/uplink/H in world_uplinks)
|
||||
if(H && H.uplink_owner && H.uplink_owner == ply)
|
||||
TC_uses += H.used_TC
|
||||
uplink_true = 1
|
||||
var/list/refined_log = new()
|
||||
for(var/datum/uplink_item/UI in H.purchase_log)
|
||||
var/obj/I = new UI.path
|
||||
refined_log.Add("[H.purchase_log[UI]]x\icon[I][UI.name]")
|
||||
del(I)
|
||||
purchases = english_list(refined_log, nothing_text = "")
|
||||
if(uplink_true)
|
||||
text += " (used [TC_uses] TC)"
|
||||
if(purchases)
|
||||
text += "<br>[purchases]"
|
||||
|
||||
return text
|
||||
|
||||
/datum/antagonist/proc/update_all_icons()
|
||||
if(!antag_indicator)
|
||||
return
|
||||
for(var/datum/mind/antag in current_antagonists)
|
||||
if(antag.current && antag.current.client)
|
||||
for(var/image/I in antag.current.client.images)
|
||||
if(I.icon_state == antag_indicator)
|
||||
del(I)
|
||||
for(var/datum/mind/other_antag in current_antagonists)
|
||||
if(other_antag.current)
|
||||
antag.current.client.images |= image('icons/mob/mob.dmi', loc = other_antag.current, icon_state = antag_indicator)
|
||||
|
||||
/datum/antagonist/proc/update_icons_added(var/datum/mind/player)
|
||||
if(!antag_indicator || !player.current)
|
||||
return
|
||||
spawn(0)
|
||||
for(var/datum/mind/antag in current_antagonists)
|
||||
if(!antag.current)
|
||||
continue
|
||||
if(antag.current.client)
|
||||
antag.current.client.images |= image('icons/mob/mob.dmi', loc = player.current, icon_state = antag_indicator)
|
||||
if(player.current.client)
|
||||
player.current.client.images |= image('icons/mob/mob.dmi', loc = antag.current, icon_state = antag_indicator)
|
||||
|
||||
/datum/antagonist/proc/update_icons_removed(var/datum/mind/player)
|
||||
if(!antag_indicator || !player.current)
|
||||
return
|
||||
spawn(0)
|
||||
for(var/datum/mind/antag in current_antagonists)
|
||||
if(antag.current)
|
||||
if(antag.current.client)
|
||||
for(var/image/I in antag.current.client.images)
|
||||
if(I.icon_state == antag_indicator && I.loc == player.current)
|
||||
del(I)
|
||||
if(player.current && player.current.client)
|
||||
for(var/image/I in player.current.client.images)
|
||||
if(I.icon_state == antag_indicator)
|
||||
del(I)
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/datum/antagonist/proc/create_objectives(var/datum/mind/player)
|
||||
if(config.objectives_disabled)
|
||||
return 0
|
||||
if(global_objectives && global_objectives.len)
|
||||
player.objectives |= global_objectives
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/apply(var/datum/mind/player)
|
||||
|
||||
if(flags & ANTAG_HAS_LEADER && !leader)
|
||||
leader = current_antagonists[1]
|
||||
|
||||
// Get the mob.
|
||||
if((flags & ANTAG_OVERRIDE_MOB) && (!player.current || (mob_path && !istype(player.current, mob_path))))
|
||||
var/mob/holder = player.current
|
||||
player.current = new mob_path(get_turf(player.current))
|
||||
player.transfer_to(player.current)
|
||||
if(holder) del(holder)
|
||||
|
||||
player.original = player.current
|
||||
return player.current
|
||||
|
||||
/datum/antagonist/proc/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
if(!istype(player))
|
||||
return 0
|
||||
|
||||
// This could use work.
|
||||
if(flags & ANTAG_CLEAR_EQUIPMENT)
|
||||
for(var/obj/item/thing in player.contents)
|
||||
del(thing)
|
||||
return 1
|
||||
|
||||
if(flags & ANTAG_SET_APPEARANCE)
|
||||
player.change_appearance(APPEARANCE_ALL, player, player, valid_species)
|
||||
|
||||
/datum/antagonist/proc/unequip(var/mob/living/carbon/human/player)
|
||||
if(!istype(player))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/greet(var/datum/mind/player)
|
||||
|
||||
// Basic intro text.
|
||||
player.current << "<span class='danger'><font size=3>You are a [role_text]!</font></span>"
|
||||
if(leader_welcome_text && player.current == leader)
|
||||
player.current << "<span class='notice'>[leader_welcome_text]</span>"
|
||||
else
|
||||
player.current << "<span class='notice'>[welcome_text]</span>"
|
||||
show_objectives(player)
|
||||
|
||||
// Choose a name, if any.
|
||||
if(flags & ANTAG_CHOOSE_NAME)
|
||||
spawn(5)
|
||||
var/newname = sanitize(copytext(input(player.current, "You are a [role_text]. Would you like to change your name to something else?", "Name change") as null|text,1,MAX_NAME_LEN))
|
||||
if (newname)
|
||||
player.current.real_name = newname
|
||||
player.current.name = player.current.real_name
|
||||
player.name = player.current.name
|
||||
// Update any ID cards.
|
||||
update_access(player.current)
|
||||
|
||||
// Clown clumsiness check, I guess downstream might use it.
|
||||
if (player.current.mind)
|
||||
if (player.current.mind.assigned_role == "Clown")
|
||||
player.current << "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
player.current.mutations.Remove(CLUMSY)
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/update_access(var/mob/living/player)
|
||||
for(var/obj/item/weapon/card/id/id in player.contents)
|
||||
id.name = "[player.real_name]'s ID Card"
|
||||
id.registered_name = player.real_name
|
||||
|
||||
/datum/antagonist/proc/random_spawn()
|
||||
create_global_objectives()
|
||||
attempt_spawn(flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB))
|
||||
finalize()
|
||||
|
||||
/datum/antagonist/proc/create_id(var/assignment, var/mob/living/carbon/human/player)
|
||||
|
||||
var/obj/item/weapon/card/id/W = new id_type(player)
|
||||
W.name = "[player.real_name]'s ID Card"
|
||||
W.access |= default_access
|
||||
W.assignment = "[assignment]"
|
||||
W.registered_name = player.real_name
|
||||
player.equip_to_slot_or_del(W, slot_wear_id)
|
||||
return W
|
||||
|
||||
/datum/antagonist/proc/create_radio(var/freq, var/mob/living/carbon/human/player)
|
||||
var/obj/item/device/radio/R = new /obj/item/device/radio/headset(player)
|
||||
R.set_frequency(freq)
|
||||
player.equip_to_slot_or_del(R, slot_l_ear)
|
||||
return R
|
||||
|
||||
/datum/antagonist/proc/make_nuke(var/atom/paper_spawn_loc, var/datum/mind/code_owner)
|
||||
|
||||
// Decide on a code.
|
||||
var/obj/effect/landmark/nuke_spawn = locate(nuke_spawn_loc ? nuke_spawn_loc : "landmark*Nuclear-Bomb")
|
||||
|
||||
var/code
|
||||
if(nuke_spawn)
|
||||
var/obj/machinery/nuclearbomb/nuke = new(get_turf(nuke_spawn))
|
||||
code = "[rand(10000, 99999)]"
|
||||
nuke.r_code = code
|
||||
|
||||
if(code)
|
||||
if(!paper_spawn_loc)
|
||||
paper_spawn_loc = get_turf(locate("landmark*Nuclear-Code"))
|
||||
if(paper_spawn_loc)
|
||||
// Create and pass on the bomb code paper.
|
||||
var/obj/item/weapon/paper/P = new(paper_spawn_loc)
|
||||
P.info = "The nuclear authorization code is: <b>[code]</b>"
|
||||
P.name = "nuclear bomb code"
|
||||
if(code_owner)
|
||||
code_owner.store_memory("<B>Nuclear Bomb Code</B>: [code]", 0, 0)
|
||||
code_owner.current << "The nuclear authorization code is: <B>[code]</B>"
|
||||
|
||||
else
|
||||
world << "<spam class='danger'>Could not spawn nuclear bomb. Contact a developer.</span>"
|
||||
return
|
||||
|
||||
spawned_nuke = code
|
||||
return code
|
||||
@@ -0,0 +1,41 @@
|
||||
var/global/list/all_antag_types = list()
|
||||
var/global/list/all_antag_spawnpoints = list()
|
||||
var/global/list/antag_names_to_ids = list()
|
||||
|
||||
/proc/get_antag_data(var/antag_type)
|
||||
if(all_antag_types[antag_type])
|
||||
return all_antag_types[antag_type]
|
||||
else
|
||||
for(var/cur_antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[cur_antag_type]
|
||||
if(antag && antag.is_type(antag_type))
|
||||
return antag
|
||||
|
||||
/proc/clear_antag_roles(var/datum/mind/player, var/implanted)
|
||||
for(var/antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
if(!implanted || !(antag.flags & ANTAG_IMPLANT_IMMUNE))
|
||||
antag.remove_antagonist(player, 1, implanted)
|
||||
|
||||
/proc/update_antag_icons(var/datum/mind/player)
|
||||
for(var/antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
if(player)
|
||||
antag.update_icons_removed(player)
|
||||
if(antag.is_antagonist(player))
|
||||
antag.update_icons_added(player)
|
||||
else
|
||||
antag.update_all_icons()
|
||||
|
||||
/proc/populate_antag_type_list()
|
||||
for(var/antag_type in typesof(/datum/antagonist)-/datum/antagonist)
|
||||
var/datum/antagonist/A = new antag_type
|
||||
all_antag_types[A.id] = A
|
||||
all_antag_spawnpoints[A.landmark_id] = list()
|
||||
antag_names_to_ids[A.role_text] = A.id
|
||||
|
||||
/proc/get_antags(var/atype)
|
||||
var/datum/antagonist/antag = all_antag_types[atype]
|
||||
if(antag && islist(antag.current_antagonists))
|
||||
return antag.current_antagonists
|
||||
return list()
|
||||
@@ -0,0 +1,29 @@
|
||||
/datum/antagonist/proc/can_become_antag(var/datum/mind/player)
|
||||
if(player.current && jobban_isbanned(player.current, bantype))
|
||||
return 0
|
||||
if(player.assigned_role in protected_jobs)
|
||||
return 0
|
||||
if(config.protect_roles_from_antagonist && (player.assigned_role in restricted_jobs))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/antags_are_dead()
|
||||
for(var/datum/mind/antag in current_antagonists)
|
||||
if(mob_path && !istype(antag.current,mob_path))
|
||||
continue
|
||||
if(antag.current.stat==2)
|
||||
continue
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/get_antag_count()
|
||||
return current_antagonists ? current_antagonists.len : 0
|
||||
|
||||
/datum/antagonist/proc/is_antagonist(var/datum/mind/player)
|
||||
if(player in current_antagonists)
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/is_type(var/antag_type)
|
||||
if(antag_type == id || antag_type == role_text)
|
||||
return 1
|
||||
return 0
|
||||
@@ -0,0 +1,23 @@
|
||||
/datum/antagonist/proc/create_global_objectives()
|
||||
return !((global_objectives && global_objectives.len) || config.objectives_disabled)
|
||||
|
||||
/datum/antagonist/proc/get_special_objective_text()
|
||||
return ""
|
||||
|
||||
/datum/antagonist/proc/check_victory()
|
||||
var/result = 1
|
||||
if(config.objectives_disabled)
|
||||
return 1
|
||||
if(global_objectives && global_objectives.len)
|
||||
for(var/datum/objective/O in global_objectives)
|
||||
if(!O.completed && !O.check_completion())
|
||||
result = 0
|
||||
else
|
||||
O.completed = 1 //Will this break anything?
|
||||
if(result && victory_text)
|
||||
world << "<span class='danger'><font size = 3>[victory_text]</span>"
|
||||
if(victory_feedback_tag) feedback_set_details("round_end_result","[victory_feedback_tag]")
|
||||
else if(loss_text)
|
||||
world << "<span class='danger'><font size = 3>[loss_text]</span>"
|
||||
if(loss_feedback_tag) feedback_set_details("round_end_result","[loss_feedback_tag]")
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/datum/antagonist/proc/attempt_late_spawn(var/datum/mind/player, var/move_to_spawn)
|
||||
|
||||
update_cur_max()
|
||||
if(get_antag_count() >= cur_max)
|
||||
return 0
|
||||
|
||||
player.current << "<span class='danger'><i>You have been selected this round as an antagonist!</i></span>"
|
||||
add_antagonist(player)
|
||||
equip(player.current)
|
||||
if(move_to_spawn)
|
||||
place_mob(player.current)
|
||||
return
|
||||
|
||||
/datum/antagonist/proc/update_cur_max()
|
||||
|
||||
candidates = list()
|
||||
var/main_type
|
||||
if(ticker && ticker.mode)
|
||||
if(ticker.mode.antag_tag && ticker.mode.antag_tag == id)
|
||||
main_type = 1
|
||||
else
|
||||
return list()
|
||||
|
||||
cur_max = (main_type ? max_antags_round : max_antags)
|
||||
if(ticker.mode.antag_scaling_coeff)
|
||||
cur_max = Clamp((ticker.mode.num_players()/ticker.mode.antag_scaling_coeff), 1, cur_max)
|
||||
|
||||
/datum/antagonist/proc/attempt_spawn(var/ghosts_only)
|
||||
|
||||
// Get the raw list of potential players.
|
||||
update_cur_max()
|
||||
candidates = get_candidates(ghosts_only)
|
||||
|
||||
// Update our boundaries.
|
||||
if(!candidates.len)
|
||||
return 0
|
||||
|
||||
//Grab candidates randomly until we have enough.
|
||||
while(candidates.len)
|
||||
var/datum/mind/player = pick(candidates)
|
||||
current_antagonists |= player
|
||||
// Update job and role.
|
||||
player.special_role = role_text
|
||||
if(flags & ANTAG_OVERRIDE_JOB)
|
||||
player.assigned_role = "MODE"
|
||||
candidates -= player
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/place_mob(var/mob/living/mob)
|
||||
if(!starting_locations || !starting_locations.len)
|
||||
return
|
||||
mob.loc = pick(starting_locations)
|
||||
|
||||
/datum/antagonist/proc/add_antagonist(var/datum/mind/player)
|
||||
if(!istype(player))
|
||||
return 0
|
||||
if(player in current_antagonists)
|
||||
return 0
|
||||
if(!can_become_antag(player))
|
||||
return 0
|
||||
|
||||
current_antagonists |= player
|
||||
apply(player)
|
||||
finalize(player)
|
||||
return 1
|
||||
|
||||
/datum/antagonist/proc/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted)
|
||||
if(player in current_antagonists)
|
||||
player.current << "<span class='danger'><font size = 3>You are no longer a [role_text]!</font></span>"
|
||||
current_antagonists -= player
|
||||
player.special_role = null
|
||||
update_icons_removed(player)
|
||||
BITSET(player.current.hud_updateflag, SPECIALROLE_HUD)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/antagonist/proc/get_candidates(var/ghosts_only)
|
||||
|
||||
candidates = list() // Clear.
|
||||
candidates = ticker.mode.get_players_for_role(role_type, id)
|
||||
// Prune restricted jobs and status.
|
||||
for(var/datum/mind/player in candidates)
|
||||
if((ghosts_only && !istype(player.current, /mob/dead)) || player.special_role || (player.assigned_role in restricted_jobs))
|
||||
candidates -= player
|
||||
return candidates
|
||||
@@ -0,0 +1,10 @@
|
||||
var/datum/antagonist/mutineer/loyalist/loyalists
|
||||
|
||||
/datum/antagonist/mutineer/loyalist
|
||||
role_text = "Loyalist"
|
||||
role_text_plural = "Loyalists"
|
||||
id = MODE_LOYALIST
|
||||
|
||||
/datum/antagonist/mutineer/loyalist/New()
|
||||
..(1)
|
||||
loyalists = src
|
||||
@@ -0,0 +1,66 @@
|
||||
var/datum/antagonist/mutineer/mutineers
|
||||
|
||||
/datum/antagonist/mutineer
|
||||
role_type = BE_MUTINEER
|
||||
role_text = "Mutineer"
|
||||
role_text_plural = "Mutineers"
|
||||
id = MODE_MUTINEER
|
||||
antag_indicator = "mutineer"
|
||||
restricted_jobs = list("Captain")
|
||||
|
||||
/datum/antagonist/mutineer/New(var/no_reference)
|
||||
..()
|
||||
if(!no_reference)
|
||||
mutineers = src
|
||||
|
||||
/datum/antagonist/mutineer/proc/recruit()
|
||||
|
||||
/datum/antagonist/mutineer/can_become_antag(var/datum/mind/player)
|
||||
if(!..())
|
||||
return 0
|
||||
if(!istype(player.current, /mob/living/carbon/human))
|
||||
return 0
|
||||
if(M.special_role)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/*
|
||||
var/list/directive_candidates = get_directive_candidates()
|
||||
if(!directive_candidates || directive_candidates.len == 0)
|
||||
world << "\red Mutiny mode aborted: no valid candidates for Directive X."
|
||||
return 0
|
||||
|
||||
head_loyalist = pick(loyalist_candidates)
|
||||
head_mutineer = pick(mutineer_candidates)
|
||||
current_directive = pick(directive_candidates)
|
||||
|
||||
|
||||
// Returns an array in case we want to expand on this later.
|
||||
proc/get_head_loyalist_candidates()
|
||||
var/list/candidates[0]
|
||||
for(var/mob/loyalist in player_list)
|
||||
if(loyalist.mind && loyalist.mind.assigned_role == "Captain")
|
||||
candidates.Add(loyalist.mind)
|
||||
return candidates
|
||||
|
||||
proc/get_head_mutineer_candidates()
|
||||
var/list/candidates[0]
|
||||
for(var/mob/mutineer in player_list)
|
||||
if(mutineer.client.prefs.be_special & BE_MUTINEER)
|
||||
for(var/job in command_positions - "Captain")
|
||||
if(mutineer.mind && mutineer.mind.assigned_role == job)
|
||||
candidates.Add(mutineer.mind)
|
||||
return candidates
|
||||
|
||||
proc/get_directive_candidates()
|
||||
var/list/candidates[0]
|
||||
for(var/T in typesof(/datum/directive) - /datum/directive)
|
||||
var/datum/directive/D = new T(src)
|
||||
if (D.meets_prerequisites())
|
||||
candidates.Add(D)
|
||||
return candidates
|
||||
|
||||
|
||||
return 1
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,30 @@
|
||||
var/datum/antagonist/deathsquad/mercenary/commandos
|
||||
|
||||
/datum/antagonist/deathsquad/mercenary
|
||||
id = MODE_COMMANDO
|
||||
landmark_id = "Syndicate-Commando"
|
||||
role_text = "Syndicate Commando"
|
||||
role_text_plural = "Commandos"
|
||||
welcome_text = "You are in the employ of a criminal syndicate hostile to NanoTrasen."
|
||||
|
||||
/datum/antagonist/deathsquad/mercenary/New()
|
||||
..(1)
|
||||
commandos = src
|
||||
|
||||
/datum/antagonist/deathsquad/mercenary/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(player), slot_w_uniform)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/gun/projectile/silenced(player), slot_belt)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/shoes/swat(player), slot_shoes)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/gloves/swat(player), slot_gloves)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(player), slot_glasses)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(player), slot_wear_mask)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/storage/box(player), slot_in_backpack)
|
||||
player.equip_to_slot_or_del(new /obj/item/ammo_magazine/c45(player), slot_in_backpack)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/rig/merc(player), slot_back)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/gun/energy/pulse_rifle(player), slot_r_hand)
|
||||
|
||||
var/obj/item/weapon/card/id/id = create_id("Commando", player)
|
||||
id.access |= get_all_accesses()
|
||||
id.icon_state = "centcom"
|
||||
create_radio(SYND_FREQ, player)
|
||||
@@ -0,0 +1,83 @@
|
||||
var/datum/antagonist/deathsquad/deathsquad
|
||||
|
||||
/datum/antagonist/deathsquad
|
||||
id = MODE_DEATHSQUAD
|
||||
role_type = BE_OPERATIVE
|
||||
role_text = "Death Commando"
|
||||
role_text_plural = "Death Commandos"
|
||||
welcome_text = "You work in the service of Central Command Asset Protection, answering directly to the Board of Directors."
|
||||
landmark_id = "Commando"
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_OVERRIDE_MOB | ANTAG_HAS_NUKE
|
||||
max_antags = 4
|
||||
max_antags_round = 6
|
||||
default_access = list(access_cent_general, access_cent_specops, access_cent_living, access_cent_storage)
|
||||
var/deployed = 0
|
||||
|
||||
/datum/antagonist/deathsquad/New(var/no_reference)
|
||||
..()
|
||||
if(!no_reference)
|
||||
deathsquad = src
|
||||
|
||||
/datum/antagonist/deathsquad/attempt_spawn()
|
||||
if(..())
|
||||
deployed = 1
|
||||
|
||||
/datum/antagonist/deathsquad/equip(var/mob/living/carbon/human/player)
|
||||
if (player.mind == leader)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(player), slot_w_uniform)
|
||||
else
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/under/color/green(player), slot_w_uniform)
|
||||
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/shoes/swat(player), slot_shoes)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/gloves/swat(player), slot_gloves)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(player), slot_glasses)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/swat(player), slot_wear_mask)
|
||||
if (player.mind == leader)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/pinpointer(player), slot_l_store)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/disk/nuclear(player), slot_r_store)
|
||||
else
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/plastique(player), slot_l_store)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/gun/projectile/revolver/mateba(player), slot_belt)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/gun/energy/pulse_rifle(player), slot_r_hand)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/rig/combat(player), slot_back)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/melee/energy/sword(player), slot_s_store)
|
||||
player.implant_loyalty(player)
|
||||
|
||||
var/obj/item/weapon/card/id/id = create_id("Asset Protection", player)
|
||||
id.access |= get_all_accesses()
|
||||
id.icon_state = "centcom"
|
||||
create_radio(DTH_FREQ, player)
|
||||
|
||||
/datum/antagonist/deathsquad/apply(var/datum/mind/player)
|
||||
|
||||
..()
|
||||
|
||||
var/syndicate_commando_rank
|
||||
if(leader && player == leader)
|
||||
syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major")
|
||||
else
|
||||
syndicate_commando_rank = pick("Lieutenant", "Captain", "Major")
|
||||
|
||||
var/syndicate_commando_name = pick(last_names)
|
||||
|
||||
var/datum/preferences/A = new() //Randomize appearance for the commando.
|
||||
A.randomize_appearance_for(player.current)
|
||||
|
||||
player.name = "[syndicate_commando_rank] [syndicate_commando_name]"
|
||||
player.current.name = player.name
|
||||
player.current.real_name = player.current.name
|
||||
|
||||
var/mob/living/carbon/human/H = player.current
|
||||
if(istype(H))
|
||||
H.gender = pick(MALE, FEMALE)
|
||||
H.age = rand(25,45)
|
||||
H.dna.ready_dna(H)
|
||||
|
||||
return
|
||||
|
||||
/datum/antagonist/deathsquad/finalize(var/datum/mind/target)
|
||||
|
||||
..()
|
||||
|
||||
if(!deployed)
|
||||
deployed = 1
|
||||
@@ -0,0 +1,44 @@
|
||||
var/datum/antagonist/ert/ert
|
||||
|
||||
/datum/antagonist/ert
|
||||
id = MODE_ERT
|
||||
bantype = "Emergency Response Team"
|
||||
role_type = BE_OPERATIVE
|
||||
role_text = "Emergency Responder"
|
||||
role_text_plural = "Emergency Responders"
|
||||
welcome_text = "As member of the Emergency Response Team, you answer only to your leader and CentComm officials."
|
||||
leader_welcome_text = "As leader of the Emergency Response Team, you answer only to CentComm, and have authority to override the Captain where it is necessary to achieve your mission goals. It is recommended that you attempt to cooperate with the captain where possible, however."
|
||||
max_antags = 5
|
||||
max_antags_round = 5 // ERT mode?
|
||||
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_SET_APPEARANCE
|
||||
|
||||
/datum/antagonist/ert/New()
|
||||
..()
|
||||
ert = src
|
||||
|
||||
/datum/antagonist/ert/greet(var/datum/mind/player)
|
||||
if(!..())
|
||||
return
|
||||
player.current << "The Emergency Response Team works for Asset Protection; your job is to protect NanoTrasen's ass-ets. There is a code red alert on [station_name()], you are tasked to go and fix the problem."
|
||||
player.current << "You should first gear up and discuss a plan with your team. More members may be joining, don't move out before you're ready."
|
||||
|
||||
/datum/antagonist/ert/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
//Special radio setup
|
||||
player.equip_to_slot_or_del(new /obj/item/device/radio/headset/ert(src), slot_l_ear)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/under/ert(src), slot_w_uniform)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/shoes/swat(src), slot_shoes)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/gloves/swat(src), slot_gloves)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses(src), slot_glasses)
|
||||
|
||||
var/obj/item/weapon/card/id/W = new(src)
|
||||
W.assignment = "Emergency Response Team"
|
||||
W.registered_name = player.real_name
|
||||
W.name = "[player.real_name]'s ID Card ([W.assignment])"
|
||||
W.icon_state = "centcom"
|
||||
W.access = get_all_accesses()
|
||||
W.access += get_all_centcom_access()
|
||||
player.equip_to_slot_or_del(W, slot_wear_id)
|
||||
|
||||
return 1
|
||||
@@ -0,0 +1,59 @@
|
||||
var/datum/antagonist/mercenary/mercs
|
||||
|
||||
/datum/antagonist/mercenary
|
||||
id = MODE_MERCENARY
|
||||
role_type = BE_OPERATIVE
|
||||
role_text = "Mercenary"
|
||||
bantype = "operative"
|
||||
role_text_plural = "Mercenaries"
|
||||
landmark_id = "Syndicate-Spawn"
|
||||
welcome_text = "To speak on the strike team's private channel use :t."
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_HAS_NUKE | ANTAG_SET_APPEARANCE
|
||||
max_antags = 4
|
||||
max_antags_round = 6
|
||||
id_type = /obj/item/weapon/card/id/syndicate
|
||||
|
||||
/datum/antagonist/mercenary/New()
|
||||
..()
|
||||
mercs = src
|
||||
|
||||
/datum/antagonist/mercenary/create_global_objectives()
|
||||
if(!..())
|
||||
return
|
||||
global_objectives = list()
|
||||
global_objectives |= new /datum/objective/nuclear
|
||||
|
||||
/datum/antagonist/mercenary/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(player), slot_w_uniform)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(player), slot_shoes)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/gloves/swat(player), slot_gloves)
|
||||
if(player.backbag == 2) player.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(player), slot_back)
|
||||
if(player.backbag == 3) player.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel_norm(player), slot_back)
|
||||
if(player.backbag == 4) player.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(player), slot_back)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/storage/box/engineer(player.back), slot_in_backpack)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/reagent_containers/pill/cyanide(player), slot_in_backpack)
|
||||
player.update_icons()
|
||||
|
||||
create_id("Mercenary", player)
|
||||
create_radio(SYND_FREQ, player)
|
||||
return 1
|
||||
|
||||
/datum/antagonist/mercenary/place_all_mobs()
|
||||
var/spawnpos = 1
|
||||
for(var/datum/mind/player in current_antagonists)
|
||||
player.current.loc = starting_locations[spawnpos]
|
||||
spawnpos++
|
||||
if(spawnpos > starting_locations.len)
|
||||
spawnpos = 1
|
||||
|
||||
/datum/antagonist/mercenary/make_nuke()
|
||||
..()
|
||||
// Create the radio.
|
||||
var/obj/effect/landmark/uplinkdevice = locate("landmark*Syndicate-Uplink")
|
||||
if(uplinkdevice)
|
||||
var/obj/item/device/radio/uplink/U = new(uplinkdevice.loc)
|
||||
U.hidden_uplink.uses = 40
|
||||
@@ -0,0 +1,170 @@
|
||||
var/datum/antagonist/ninja/ninjas
|
||||
|
||||
/datum/antagonist/ninja
|
||||
id = MODE_NINJA
|
||||
role_type = BE_NINJA
|
||||
role_text = "Ninja"
|
||||
role_text_plural = "Ninja"
|
||||
bantype = "ninja"
|
||||
landmark_id = "ninjastart"
|
||||
welcome_text = "You are an elite mercenary assassin of the Spider Clan. You have a variety of abilities at your disposal, thanks to your nano-enhanced cyber armor.</span>"
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_RANDSPAWN | ANTAG_VOTABLE | ANTAG_SET_APPEARANCE
|
||||
max_antags = 3
|
||||
max_antags_round = 3
|
||||
|
||||
/datum/antagonist/ninja/New()
|
||||
..()
|
||||
ninjas = src
|
||||
|
||||
/datum/antagonist/ninja/random_spawn()
|
||||
if(config.ninjas_allowed) ..()
|
||||
|
||||
/datum/antagonist/ninja/create_objectives(var/datum/mind/ninja)
|
||||
|
||||
if(!..())
|
||||
return
|
||||
|
||||
var/objective_list = list(1,2,3,4,5)
|
||||
for(var/i=rand(2,4),i>0,i--)
|
||||
switch(pick(objective_list))
|
||||
if(1)//Kill
|
||||
var/datum/objective/assassinate/ninja_objective = new
|
||||
ninja_objective.owner = ninja
|
||||
ninja_objective.target = ninja_objective.find_target()
|
||||
if(ninja_objective.target != "Free Objective")
|
||||
ninja.objectives += ninja_objective
|
||||
else
|
||||
i++
|
||||
objective_list -= 1 // No more than one kill objective
|
||||
if(2)//Steal
|
||||
var/datum/objective/steal/ninja_objective = new
|
||||
ninja_objective.owner = ninja
|
||||
ninja_objective.target = ninja_objective.find_target()
|
||||
ninja.objectives += ninja_objective
|
||||
if(3)//Protect
|
||||
var/datum/objective/protect/ninja_objective = new
|
||||
ninja_objective.owner = ninja
|
||||
ninja_objective.target = ninja_objective.find_target()
|
||||
if(ninja_objective.target != "Free Objective")
|
||||
ninja.objectives += ninja_objective
|
||||
else
|
||||
i++
|
||||
objective_list -= 3
|
||||
if(4)//Download
|
||||
var/datum/objective/download/ninja_objective = new
|
||||
ninja_objective.owner = ninja
|
||||
ninja_objective.gen_amount_goal()
|
||||
ninja.objectives += ninja_objective
|
||||
objective_list -= 4
|
||||
if(5)//Harm
|
||||
var/datum/objective/harm/ninja_objective = new
|
||||
ninja_objective.owner = ninja
|
||||
ninja_objective.target = ninja_objective.find_target()
|
||||
if(ninja_objective.target != "Free Objective")
|
||||
ninja.objectives += ninja_objective
|
||||
else
|
||||
i++
|
||||
objective_list -= 5
|
||||
|
||||
var/datum/objective/ninja_highlander/ninja_obj = new
|
||||
ninja_obj.owner = ninja
|
||||
ninja.objectives += ninja_obj
|
||||
|
||||
var/datum/objective/survive/ninja_objective = new
|
||||
ninja_objective.owner = ninja
|
||||
ninja.objectives += ninja_objective
|
||||
|
||||
/datum/antagonist/ninja/greet(var/datum/mind/player)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
var/directive = generate_ninja_directive("heel")
|
||||
player.store_memory("<B>Directive:</B> <span class='danger'>[directive]</span><br>")
|
||||
player << "<b>Remember your directive:</b> [directive]."
|
||||
|
||||
/datum/antagonist/ninja/apply(var/datum/mind/player)
|
||||
..()
|
||||
var/ninja_title = pick(ninja_titles)
|
||||
var/ninja_name = pick(ninja_names)
|
||||
var/mob/living/carbon/human/H = player.current
|
||||
if(istype(H))
|
||||
H.real_name = "[ninja_title] [ninja_name]"
|
||||
H.name = H.real_name
|
||||
player.name = H.name
|
||||
|
||||
/datum/antagonist/ninja/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
var/obj/item/device/radio/R = new /obj/item/device/radio/headset(player)
|
||||
player.equip_to_slot_or_del(R, slot_l_ear)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(player), slot_w_uniform)
|
||||
player.equip_to_slot_or_del(new /obj/item/device/flashlight(player), slot_belt)
|
||||
var/obj/item/weapon/rig/light/ninja/ninjasuit = new(player)
|
||||
player.equip_to_slot_or_del(ninjasuit,slot_back)
|
||||
|
||||
if(ninjasuit)
|
||||
// Make sure the ninja can actually equip the suit.
|
||||
if(player.dna && player.dna.unique_enzymes)
|
||||
ninjasuit.locked_dna = player.dna.unique_enzymes
|
||||
player << "<span class='warning'>Suit hardware locked to your DNA hash.</span>"
|
||||
else
|
||||
ninjasuit.req_access = list()
|
||||
|
||||
ninjasuit.toggle_seals(src,1)
|
||||
|
||||
if(istype(player.back,/obj/item/weapon/rig))
|
||||
var/obj/item/weapon/rig/rig = player.back
|
||||
if(rig.air_supply)
|
||||
player.internal = rig.air_supply
|
||||
|
||||
spawn(10)
|
||||
if(player.internal)
|
||||
player.internals.icon_state = "internal1"
|
||||
else
|
||||
player << "<span class='danger'>You forgot to turn on your internals! Quickly, toggle the valve!</span>"
|
||||
|
||||
/datum/antagonist/ninja/proc/generate_ninja_directive(side)
|
||||
var/directive = "[side=="face"?"Nanotrasen":"A criminal syndicate"] is your employer. "//Let them know which side they're on.
|
||||
switch(rand(1,19))
|
||||
if(1)
|
||||
directive += "The Spider Clan must not be linked to this operation. Remain hidden and covert when possible."
|
||||
if(2)
|
||||
directive += "[station_name] is financed by an enemy of the Spider Clan. Cause as much structural damage as desired."
|
||||
if(3)
|
||||
directive += "A wealthy animal rights activist has made a request we cannot refuse. Prioritize saving animal lives whenever possible."
|
||||
if(4)
|
||||
directive += "The Spider Clan absolutely cannot be linked to this operation. Eliminate witnesses at your discretion."
|
||||
if(5)
|
||||
directive += "We are currently negotiating with NanoTrasen Central Command. Prioritize saving human lives over ending them."
|
||||
if(6)
|
||||
directive += "We are engaged in a legal dispute over [station_name]. If a laywer is present on board, force their cooperation in the matter."
|
||||
if(7)
|
||||
directive += "A financial backer has made an offer we cannot refuse. Implicate criminal involvement in the operation."
|
||||
if(8)
|
||||
directive += "Let no one question the mercy of the Spider Clan. Ensure the safety of all non-essential personnel you encounter."
|
||||
if(9)
|
||||
directive += "A free agent has proposed a lucrative business deal. Implicate Nanotrasen involvement in the operation."
|
||||
if(10)
|
||||
directive += "Our reputation is on the line. Harm as few civilians and innocents as possible."
|
||||
if(11)
|
||||
directive += "Our honor is on the line. Utilize only honorable tactics when dealing with opponents."
|
||||
if(12)
|
||||
directive += "We are currently negotiating with a mercenary leader. Disguise assassinations as suicide or other natural causes."
|
||||
if(13)
|
||||
directive += "Some disgruntled NanoTrasen employees have been supportive of our operations. Be wary of any mistreatment by command staff."
|
||||
if(14)
|
||||
var/xenorace = pick("Unathi","Tajara", "Skrell")
|
||||
directive += "A group of [xenorace] radicals have been loyal supporters of the Spider Clan. Favor [xenorace] crew whenever possible."
|
||||
if(15)
|
||||
directive += "The Spider Clan has recently been accused of religious insensitivity. Attempt to speak with the Chaplain and prove these accusations false."
|
||||
if(16)
|
||||
directive += "The Spider Clan has been bargaining with a competing prosthetics manufacturer. Try to shine NanoTrasen prosthetics in a bad light."
|
||||
if(17)
|
||||
directive += "The Spider Clan has recently begun recruiting outsiders. Consider suitable candidates and assess their behavior amongst the crew."
|
||||
if(18)
|
||||
directive += "A cyborg liberation group has expressed interest in our serves. Prove the Spider Clan merciful towards law-bound synthetics."
|
||||
else
|
||||
directive += "There are no special supplemental instructions at this time."
|
||||
return directive
|
||||
@@ -0,0 +1,216 @@
|
||||
var/datum/antagonist/raider/raiders
|
||||
|
||||
/datum/antagonist/raider
|
||||
id = MODE_RAIDER
|
||||
role_type = BE_RAIDER
|
||||
role_text = "Raider"
|
||||
role_text_plural = "Raiders"
|
||||
bantype = "raider"
|
||||
landmark_id = "voxstart"
|
||||
welcome_text = "Use :H to talk on your encrypted channel."
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_VOTABLE | ANTAG_SET_APPEARANCE
|
||||
max_antags = 6
|
||||
max_antags_round = 10
|
||||
id_type = /obj/item/weapon/card/id/syndicate
|
||||
|
||||
// Heist overrides check_victory() and doesn't need victory or loss strings/tags.
|
||||
var/list/raider_uniforms = list(
|
||||
/obj/item/clothing/under/soviet,
|
||||
/obj/item/clothing/under/pirate,
|
||||
/obj/item/clothing/under/redcoat,
|
||||
/obj/item/clothing/under/serviceoveralls,
|
||||
/obj/item/clothing/under/captain_fly
|
||||
)
|
||||
|
||||
var/list/raider_shoes = list(
|
||||
/obj/item/clothing/shoes/jackboots,
|
||||
/obj/item/clothing/shoes/sandal,
|
||||
/obj/item/clothing/shoes/laceup
|
||||
)
|
||||
|
||||
var/list/raider_glasses = list(
|
||||
/obj/item/clothing/glasses/thermal,
|
||||
/obj/item/clothing/glasses/thermal/eyepatch,
|
||||
/obj/item/clothing/glasses/thermal/monocle
|
||||
)
|
||||
|
||||
var/list/raider_helmets = list(
|
||||
/obj/item/clothing/head/bearpelt,
|
||||
/obj/item/clothing/head/ushanka,
|
||||
/obj/item/clothing/head/pirate,
|
||||
/obj/item/clothing/head/bandana,
|
||||
/obj/item/clothing/head/hgpiratecap,
|
||||
/obj/item/clothing/head/flatcap
|
||||
)
|
||||
|
||||
var/list/raider_suits = list(
|
||||
/obj/item/clothing/suit/pirate,
|
||||
/obj/item/clothing/suit/hgpirate,
|
||||
/obj/item/clothing/suit/storage/toggle/bomber,
|
||||
/obj/item/clothing/suit/storage/leather_jacket,
|
||||
/obj/item/clothing/suit/storage/toggle/brown_jacket,
|
||||
/obj/item/clothing/suit/storage/toggle/hoodie,
|
||||
/obj/item/clothing/suit/storage/toggle/hoodie/black
|
||||
)
|
||||
|
||||
var/list/raider_guns = list(
|
||||
/obj/item/weapon/gun/energy/laser,
|
||||
/obj/item/weapon/gun/energy/retro,
|
||||
/obj/item/weapon/gun/energy/xray,
|
||||
/obj/item/weapon/gun/energy/mindflayer,
|
||||
/obj/item/weapon/gun/energy/toxgun,
|
||||
/obj/item/weapon/gun/energy/stunrevolver,
|
||||
/obj/item/weapon/gun/energy/crossbow/largecrossbow,
|
||||
/obj/item/weapon/gun/projectile/automatic/mini_uzi,
|
||||
/obj/item/weapon/gun/projectile/automatic/c20r,
|
||||
/obj/item/weapon/gun/projectile/silenced,
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump,
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump/combat,
|
||||
/obj/item/weapon/gun/projectile/colt,
|
||||
/obj/item/weapon/gun/projectile/pistol
|
||||
)
|
||||
|
||||
/datum/antagonist/raider/New()
|
||||
..()
|
||||
raiders = src
|
||||
|
||||
/datum/antagonist/raider/update_access(var/mob/living/player)
|
||||
for(var/obj/item/weapon/storage/wallet/W in player.contents)
|
||||
for(var/obj/item/weapon/card/id/id in W.contents)
|
||||
id.name = "[player.real_name]'s Passport"
|
||||
id.registered_name = player.real_name
|
||||
W.name = "[initial(W.name)] ([id.name])"
|
||||
|
||||
/datum/antagonist/raider/create_global_objectives()
|
||||
|
||||
if(global_objectives.len)
|
||||
return
|
||||
|
||||
var/i = 1
|
||||
var/max_objectives = pick(2,2,2,2,3,3,3,4)
|
||||
global_objectives = list()
|
||||
while(i<= max_objectives)
|
||||
var/list/goals = list("kidnap","loot","salvage")
|
||||
var/goal = pick(goals)
|
||||
var/datum/objective/heist/O
|
||||
|
||||
if(goal == "kidnap")
|
||||
goals -= "kidnap"
|
||||
O = new /datum/objective/heist/kidnap()
|
||||
else if(goal == "loot")
|
||||
O = new /datum/objective/heist/loot()
|
||||
else
|
||||
O = new /datum/objective/heist/salvage()
|
||||
O.choose_target()
|
||||
global_objectives |= O
|
||||
|
||||
i++
|
||||
|
||||
global_objectives |= new /datum/objective/heist/preserve_crew
|
||||
|
||||
/datum/antagonist/raider/check_victory()
|
||||
// Totally overrides the base proc.
|
||||
var/win_type = "Major"
|
||||
var/win_group = "Crew"
|
||||
var/win_msg = ""
|
||||
|
||||
//No objectives, go straight to the feedback.
|
||||
if(config.objectives_disabled || !global_objectives.len)
|
||||
return
|
||||
|
||||
var/success = global_objectives.len
|
||||
//Decrease success for failed objectives.
|
||||
for(var/datum/objective/O in global_objectives)
|
||||
if(!(O.check_completion())) success--
|
||||
//Set result by objectives.
|
||||
if(success == global_objectives.len)
|
||||
win_type = "Major"
|
||||
win_group = "Raider"
|
||||
else if(success > 2)
|
||||
win_type = "Minor"
|
||||
win_group = "Raider"
|
||||
else
|
||||
win_type = "Minor"
|
||||
win_group = "Crew"
|
||||
//Now we modify that result by the state of the vox crew.
|
||||
if(antags_are_dead())
|
||||
win_type = "Major"
|
||||
win_group = "Crew"
|
||||
win_msg += "<B>The Raiders have been wiped out!</B>"
|
||||
else if(is_raider_crew_safe())
|
||||
if(win_group == "Crew" && win_type == "Minor")
|
||||
win_type = "Major"
|
||||
win_group = "Crew"
|
||||
win_msg += "<B>The Raiders have left someone behind!</B>"
|
||||
else
|
||||
if(win_group == "Raider")
|
||||
if(win_type == "Minor")
|
||||
win_type = "Major"
|
||||
win_msg += "<B>The Raiders escaped the station!</B>"
|
||||
else
|
||||
win_msg += "<B>The Raiders were repelled!</B>"
|
||||
|
||||
world << "<span class='danger'><font size = 3>[win_type] [win_group] victory!</font>"
|
||||
world << "[win_msg]"
|
||||
feedback_set_details("round_end_result","heist - [win_type] [win_group]")
|
||||
|
||||
/datum/antagonist/raider/proc/is_raider_crew_safe()
|
||||
|
||||
if(!current_antagonists || current_antagonists.len == 0)
|
||||
return 0
|
||||
|
||||
for(var/datum/mind/player in current_antagonists)
|
||||
if(!player.current || get_area(player.current) != locate(/area/shuttle/skipjack/station))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/antagonist/raider/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
if(player.species && player.species.name == "Vox")
|
||||
equip_vox(player)
|
||||
else
|
||||
var/new_shoes = pick(raider_shoes)
|
||||
var/new_uniform = pick(raider_uniforms)
|
||||
var/new_glasses = pick(raider_glasses)
|
||||
var/new_helmet = pick(raider_helmets)
|
||||
var/new_suit = pick(raider_suits)
|
||||
var/new_gun = pick(raider_guns)
|
||||
|
||||
player.equip_to_slot_or_del(new new_shoes(player),slot_shoes)
|
||||
player.equip_to_slot_or_del(new new_uniform(player),slot_w_uniform)
|
||||
player.equip_to_slot_or_del(new new_glasses(player),slot_glasses)
|
||||
player.equip_to_slot_or_del(new new_helmet(player),slot_head)
|
||||
player.equip_to_slot_or_del(new new_suit(player),slot_wear_suit)
|
||||
player.equip_to_slot_or_del(new new_gun(player),slot_belt)
|
||||
|
||||
var/obj/item/weapon/card/id/id = create_id("Visitor", player)
|
||||
id.name = "[player.real_name]'s Passport"
|
||||
id.assignment = "Visitor"
|
||||
var/obj/item/weapon/storage/wallet/W = new(player)
|
||||
W.handle_item_insertion(id)
|
||||
player.equip_to_slot_or_del(W, slot_wear_id)
|
||||
spawn_money(rand(50,150)*10,W)
|
||||
create_radio(SYND_FREQ, player)
|
||||
|
||||
return 1
|
||||
|
||||
/datum/antagonist/raider/proc/equip_vox(var/mob/living/carbon/human/player)
|
||||
|
||||
var/uniform_type = pick(list(/obj/item/clothing/under/vox/vox_robes,/obj/item/clothing/under/vox/vox_casual))
|
||||
|
||||
player.equip_to_slot_or_del(new uniform_type(player), slot_w_uniform)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/vox(player), slot_shoes) // REPLACE THESE WITH CODED VOX ALTERNATIVES.
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/gloves/yellow/vox(player), slot_gloves) // AS ABOVE.
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/swat/vox(player), slot_wear_mask)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/tank/nitrogen(player), slot_back)
|
||||
player.equip_to_slot_or_del(new /obj/item/device/flashlight(player), slot_r_store)
|
||||
|
||||
player.internal = locate(/obj/item/weapon/tank) in player.contents
|
||||
if(istype(player.internal,/obj/item/weapon/tank) && player.internals)
|
||||
player.internals.icon_state = "internal1"
|
||||
|
||||
return 1
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
var/datum/antagonist/wizard/wizards
|
||||
|
||||
/datum/antagonist/wizard
|
||||
id = MODE_WIZARD
|
||||
role_type = BE_WIZARD
|
||||
role_text = "Space Wizard"
|
||||
role_text_plural = "Space Wizards"
|
||||
bantype = "wizard"
|
||||
landmark_id = "wizard"
|
||||
welcome_text = "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.<br>In your pockets you will find a teleport scroll. Use it as needed."
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_VOTABLE | ANTAG_SET_APPEARANCE
|
||||
max_antags = 1
|
||||
|
||||
/datum/antagonist/wizard/New()
|
||||
..()
|
||||
wizards = src
|
||||
|
||||
/datum/antagonist/wizard/create_objectives(var/datum/mind/wizard)
|
||||
|
||||
if(!..())
|
||||
return
|
||||
|
||||
var/kill
|
||||
var/escape
|
||||
var/steal
|
||||
var/hijack
|
||||
|
||||
switch(rand(1,100))
|
||||
if(1 to 30)
|
||||
escape = 1
|
||||
kill = 1
|
||||
if(31 to 60)
|
||||
escape = 1
|
||||
steal = 1
|
||||
if(61 to 99)
|
||||
kill = 1
|
||||
steal = 1
|
||||
else
|
||||
hijack = 1
|
||||
|
||||
if(kill)
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = wizard
|
||||
kill_objective.find_target()
|
||||
wizard.objectives |= kill_objective
|
||||
if(steal)
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = wizard
|
||||
steal_objective.find_target()
|
||||
wizard.objectives |= steal_objective
|
||||
if(escape)
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = wizard
|
||||
wizard.objectives |= survive_objective
|
||||
if(hijack)
|
||||
var/datum/objective/hijack/hijack_objective = new
|
||||
hijack_objective.owner = wizard
|
||||
wizard.objectives |= hijack_objective
|
||||
return
|
||||
|
||||
/datum/antagonist/wizard/apply(var/datum/mind/wizard)
|
||||
wizard.store_memory("<B>Remember:</B> do not forget to prepare your spells.")
|
||||
wizard.current.real_name = "[pick(wizard_first)] [pick(wizard_second)]"
|
||||
wizard.current.name = wizard.current.real_name
|
||||
|
||||
/datum/antagonist/wizard/equip(var/mob/living/carbon/human/wizard_mob)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
wizard_mob.equip_to_slot_or_del(new /obj/item/device/radio/headset(wizard_mob), slot_l_ear)
|
||||
wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/lightpurple(wizard_mob), slot_w_uniform)
|
||||
wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(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)
|
||||
if(wizard_mob.backbag == 2) wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(wizard_mob), slot_back)
|
||||
if(wizard_mob.backbag == 3) wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel_norm(wizard_mob), slot_back)
|
||||
if(wizard_mob.backbag == 4) wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(wizard_mob), slot_back)
|
||||
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/storage/box(wizard_mob), slot_in_backpack)
|
||||
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/teleportation_scroll(wizard_mob), slot_r_store)
|
||||
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/spellbook(wizard_mob), slot_r_hand)
|
||||
wizard_mob.update_icons()
|
||||
return 1
|
||||
|
||||
/datum/antagonist/wizard/check_victory()
|
||||
var/survivor
|
||||
for(var/datum/mind/player in current_antagonists)
|
||||
if(!player.current || player.current.stat)
|
||||
continue
|
||||
survivor = 1
|
||||
break
|
||||
if(!survivor)
|
||||
feedback_set_details("round_end_result","loss - wizard killed")
|
||||
world << "<span class='danger'><font size = 3>The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!</font></span>"
|
||||
|
||||
//To batch-remove wizard spells. Linked to mind.dm.
|
||||
/mob/proc/spellremove(var/mob/M as mob)
|
||||
for(var/obj/effect/proc_holder/spell/spell_to_remove in src.spell_list)
|
||||
del(spell_to_remove)
|
||||
|
||||
/*Checks if the wizard can cast spells.
|
||||
Made a proc so this is not repeated 14 (or more) times.*/
|
||||
/mob/proc/casting()
|
||||
//Removed the stat check because not all spells require clothing now.
|
||||
if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe))
|
||||
usr << "I don't feel strong enough without my robe."
|
||||
return 0
|
||||
if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal))
|
||||
usr << "I don't feel strong enough without my sandals."
|
||||
return 0
|
||||
if(!istype(usr:head, /obj/item/clothing/head/wizard))
|
||||
usr << "I don't feel strong enough without my hat."
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
@@ -0,0 +1,55 @@
|
||||
/datum/antagonist/changeling
|
||||
id = MODE_CHANGELING
|
||||
role_type = BE_CHANGELING
|
||||
role_text = "Changeling"
|
||||
role_text_plural = "Changelings"
|
||||
bantype = "changeling"
|
||||
feedback_tag = "changeling_objective"
|
||||
restricted_jobs = list("AI", "Cyborg")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
|
||||
welcome_text = "Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them."
|
||||
flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE
|
||||
|
||||
/datum/antagonist/changeling/get_special_objective_text(var/datum/mind/player)
|
||||
return "<br><b>Changeling ID:</b> [player.changeling.changelingID].<br><b>Genomes Absorbed:</b> [player.changeling.absorbedcount]"
|
||||
|
||||
/datum/antagonist/changeling/apply(var/datum/mind/player)
|
||||
..()
|
||||
player.current.make_changeling()
|
||||
|
||||
/datum/antagonist/changeling/create_objectives(var/datum/mind/changeling)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
//OBJECTIVES - Always absorb 5 genomes, plus random traitor objectives.
|
||||
//If they have two objectives as well as absorb, they must survive rather than escape
|
||||
//No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting
|
||||
//If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone
|
||||
|
||||
var/datum/objective/absorb/absorb_objective = new
|
||||
absorb_objective.owner = changeling
|
||||
absorb_objective.gen_amount_goal(2, 3)
|
||||
changeling.objectives += absorb_objective
|
||||
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = changeling
|
||||
kill_objective.find_target()
|
||||
changeling.objectives += kill_objective
|
||||
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = changeling
|
||||
steal_objective.find_target()
|
||||
changeling.objectives += steal_objective
|
||||
|
||||
switch(rand(1,100))
|
||||
if(1 to 80)
|
||||
if (!(locate(/datum/objective/escape) in changeling.objectives))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = changeling
|
||||
changeling.objectives += escape_objective
|
||||
else
|
||||
if (!(locate(/datum/objective/survive) in changeling.objectives))
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = changeling
|
||||
changeling.objectives += survive_objective
|
||||
return
|
||||
@@ -0,0 +1,114 @@
|
||||
var/datum/antagonist/cultist/cult
|
||||
|
||||
/proc/iscultist(var/mob/player)
|
||||
if(!cult || !player.mind)
|
||||
return 0
|
||||
if(player.mind in cult.current_antagonists)
|
||||
return 1
|
||||
|
||||
/datum/antagonist/cultist
|
||||
id = MODE_CULTIST
|
||||
role_text = "Cultist"
|
||||
role_text_plural = "Cultists"
|
||||
bantype = "cultist"
|
||||
restricted_jobs = list("Chaplain","AI", "Cyborg", "Internal Affairs Agent", "Head of Security", "Captain")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective")
|
||||
role_type = BE_CULTIST
|
||||
feedback_tag = "cult_objective"
|
||||
antag_indicator = "cult"
|
||||
welcome_text = "You have a talisman in your possession; one that will help you start the cult on this station. Use it well and remember - there are others."
|
||||
victory_text = "The cult wins! It has succeeded in serving its dark masters!"
|
||||
loss_text = "The staff managed to stop the cult!"
|
||||
victory_feedback_tag = "win - cult win"
|
||||
loss_feedback_tag = "loss - staff stopped the cult"
|
||||
flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE
|
||||
max_antags = 200 // No upper limit.
|
||||
max_antags_round = 200
|
||||
|
||||
var/datum/mind/sacrifice_target
|
||||
var/list/startwords = list("blood","join","self","hell")
|
||||
var/list/allwords = list("travel","self","see","hell","blood","join","tech","destroy", "other", "hide")
|
||||
var/list/sacrificed = list()
|
||||
|
||||
/datum/antagonist/cultist/New()
|
||||
..()
|
||||
cult = src
|
||||
|
||||
/datum/antagonist/cultist/create_global_objectives()
|
||||
|
||||
if(!..())
|
||||
return
|
||||
|
||||
global_objectives = list()
|
||||
if(prob(50))
|
||||
global_objectives |= new /datum/objective/cult/survive
|
||||
else
|
||||
global_objectives |= new /datum/objective/cult/eldergod
|
||||
|
||||
var/datum/objective/cult/sacrifice/sacrifice = new()
|
||||
sacrifice.find_target()
|
||||
sacrifice_target = sacrifice.target
|
||||
global_objectives |= sacrifice
|
||||
|
||||
/datum/antagonist/cultist/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
var/obj/item/weapon/paper/talisman/supply/T = new(get_turf(player))
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store,
|
||||
"left hand" = slot_l_hand,
|
||||
"right hand" = slot_r_hand,
|
||||
)
|
||||
for(var/slot in slots)
|
||||
player.equip_to_slot(T, slot)
|
||||
if(T.loc == player)
|
||||
break
|
||||
|
||||
/datum/antagonist/cultist/greet(var/datum/mind/player)
|
||||
if(!..())
|
||||
return 0
|
||||
grant_runeword(player.current)
|
||||
|
||||
/datum/antagonist/cultist/proc/grant_runeword(mob/living/carbon/human/cult_mob, var/word)
|
||||
|
||||
if (!word)
|
||||
if(startwords.len > 0)
|
||||
word=pick(startwords)
|
||||
startwords -= word
|
||||
else
|
||||
word = pick(allwords)
|
||||
|
||||
// Ensure runes are randomized.
|
||||
if(!cultwords["travel"])
|
||||
runerandom()
|
||||
|
||||
var/wordexp = "[cultwords[word]] is [word]..."
|
||||
cult_mob << "<span class='warning'>You remember one thing from the dark teachings of your master... [wordexp]</span>"
|
||||
cult_mob.mind.store_memory("You remember that <B>[wordexp]</B>", 0, 0)
|
||||
|
||||
/datum/antagonist/cultist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted)
|
||||
if(!..())
|
||||
return 0
|
||||
player.current << "<span class='danger'>An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it."
|
||||
player.memory = ""
|
||||
if(show_message)
|
||||
player.current.visible_message("<FONT size = 3>[player.current] looks like they just reverted to their old faith!</FONT>")
|
||||
|
||||
/datum/antagonist/cultist/add_antagonist(var/datum/mind/player)
|
||||
if(!..())
|
||||
return
|
||||
player << "You catch a glimpse of the Realm of Nar-Sie, the Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of That Which Waits. Assist your new compatriots in their dark dealings. Their goals are yours, and yours are theirs. You serve the Dark One above all else. Bring It back."
|
||||
|
||||
/datum/antagonist/cultist/can_become_antag(var/datum/mind/player)
|
||||
if(!..())
|
||||
return 0
|
||||
if(!istype(player.current, /mob/living/carbon/human))
|
||||
return 0
|
||||
for(var/obj/item/weapon/implant/loyalty/L in player.current)
|
||||
if(L && (L.imp_in == player.current))
|
||||
return 0
|
||||
return 1
|
||||
@@ -0,0 +1,65 @@
|
||||
var/datum/antagonist/highlander/highlanders
|
||||
|
||||
/datum/antagonist/highlander
|
||||
role_text = "Highlander"
|
||||
role_text_plural = "Highlanders"
|
||||
welcome_text = "There can be only one."
|
||||
id = MODE_HIGHLANDER
|
||||
flags = ANTAG_SUSPICIOUS | ANTAG_IMPLANT_IMMUNE //| ANTAG_RANDSPAWN | ANTAG_VOTABLE // Someday...
|
||||
max_antags = 5
|
||||
max_antags_round = 7
|
||||
|
||||
/datum/antagonist/highlander/New()
|
||||
..()
|
||||
highlanders = src
|
||||
|
||||
/datum/antagonist/highlander/create_objectives(var/datum/mind/player)
|
||||
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = player
|
||||
steal_objective.set_target("nuclear authentication disk")
|
||||
player.objectives |= steal_objective
|
||||
|
||||
var/datum/objective/hijack/hijack_objective = new
|
||||
hijack_objective.owner = player
|
||||
player.objectives |= hijack_objective
|
||||
|
||||
/datum/antagonist/highlander/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
if(!..())
|
||||
return
|
||||
|
||||
for (var/obj/item/I in player)
|
||||
if (istype(I, /obj/item/weapon/implant))
|
||||
continue
|
||||
del(I)
|
||||
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/under/kilt(player), slot_w_uniform)
|
||||
player.equip_to_slot_or_del(new /obj/item/device/radio/headset/heads/captain(player), slot_l_ear)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/head/beret(player), slot_head)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/claymore(player), slot_l_hand)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(player), slot_shoes)
|
||||
player.equip_to_slot_or_del(new /obj/item/weapon/pinpointer(get_turf(player)), slot_l_store)
|
||||
|
||||
var/obj/item/weapon/card/id/W = new(player)
|
||||
W.name = "[player.real_name]'s ID Card"
|
||||
W.icon_state = "centcom"
|
||||
W.access = get_all_accesses()
|
||||
W.access += get_all_centcom_access()
|
||||
W.assignment = "Highlander"
|
||||
W.registered_name = player.real_name
|
||||
player.equip_to_slot_or_del(W, slot_wear_id)
|
||||
|
||||
/proc/only_one()
|
||||
|
||||
if(!ticker)
|
||||
alert("The game hasn't started yet!")
|
||||
return
|
||||
|
||||
for(var/mob/living/carbon/human/H in player_list)
|
||||
if(H.stat == 2 || !(H.client)) continue
|
||||
if(is_special_character(H)) continue
|
||||
highlanders.add_antagonist(H.mind)
|
||||
|
||||
message_admins("\blue [key_name_admin(usr)] used THERE CAN BE ONLY ONE!", 1)
|
||||
log_admin("[key_name(usr)] used there can be only one.")
|
||||
@@ -0,0 +1,16 @@
|
||||
// Notes towards a monkey mode to reduce snowflakes for downstream. Will not compile.
|
||||
|
||||
/datum/antagonist/monkey
|
||||
role_text = "Rabid Monkey"
|
||||
role_text_plural = "Rabid Monkeys"
|
||||
mob_type = /mob/living/carbon/monkey
|
||||
id = MODE_MONKEY
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_OVERRIDE_MOB
|
||||
|
||||
/datum/antagonist/monkey/apply(var/datum/mind/player)
|
||||
|
||||
for(var/datum/disease/D in M.viruses)
|
||||
if(istype(D, /datum/disease/jungle_fever))
|
||||
if (ticker.mode.config_tag == "monkey")
|
||||
return 2
|
||||
return 1
|
||||
@@ -0,0 +1,65 @@
|
||||
var/datum/antagonist/renegade/renegades
|
||||
|
||||
/datum/antagonist/renegade
|
||||
role_text = "Renegade"
|
||||
role_text_plural = "Renegades"
|
||||
welcome_text = "Your own safety matters above all else, trust no one and kill anyone who gets in your way. However, armed as you are, now would be the perfect time to settle that score or grab that pair of yellow gloves you've been eyeing..."
|
||||
id = MODE_RENEGADE
|
||||
flags = ANTAG_SUSPICIOUS | ANTAG_IMPLANT_IMMUNE | ANTAG_RANDSPAWN | ANTAG_VOTABLE
|
||||
max_antags = 5
|
||||
max_antags_round = 7
|
||||
|
||||
var/list/spawn_guns = list(
|
||||
/obj/item/weapon/gun/energy/taser,
|
||||
/obj/item/weapon/gun/energy/gun,
|
||||
/obj/item/weapon/gun/energy/laser,
|
||||
/obj/item/weapon/gun/projectile,
|
||||
/obj/item/weapon/gun/projectile/revolver/detective,
|
||||
/obj/item/weapon/gun/projectile/automatic/c20r,
|
||||
/obj/item/weapon/gun/energy/gun/nuclear,
|
||||
/obj/item/weapon/gun/projectile/deagle/camo,
|
||||
/obj/item/weapon/gun/projectile/pistol,
|
||||
/obj/item/weapon/silencer,
|
||||
/obj/item/weapon/gun/energy/lasercannon,
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump,
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump/combat,
|
||||
/obj/item/weapon/gun/projectile/automatic,
|
||||
/obj/item/weapon/gun/projectile/automatic/mini_uzi,
|
||||
/obj/item/weapon/gun/energy/crossbow
|
||||
//obj/item/weapon/gun/projectile/gyropistol,
|
||||
//obj/item/weapon/gun/energy/pulse_rifle,
|
||||
//obj/item/weapon/gun/projectile/revolver/mateba,
|
||||
//obj/item/weapon/gun/projectile/automatic/l6_saw,
|
||||
)
|
||||
|
||||
/datum/antagonist/renegade/New()
|
||||
..()
|
||||
renegades = src
|
||||
|
||||
/datum/antagonist/renegade/create_objectives(var/datum/mind/player)
|
||||
|
||||
if(!..())
|
||||
return
|
||||
|
||||
var/datum/objective/survive/survive = new
|
||||
survive.owner = player
|
||||
player.objectives |= survive
|
||||
|
||||
/datum/antagonist/renegade/equip(var/mob/living/carbon/human/player)
|
||||
|
||||
if(!..())
|
||||
return
|
||||
|
||||
var/gun_type = pick(spawn_guns)
|
||||
var/obj/item/gun = new gun_type(get_turf(player))
|
||||
if(!(player.l_hand && player.r_hand))
|
||||
player.put_in_hands(gun)
|
||||
|
||||
|
||||
/proc/rightandwrong()
|
||||
usr << "<B>You summoned guns!</B>"
|
||||
message_admins("[key_name_admin(usr, 1)] summoned guns!")
|
||||
for(var/mob/living/carbon/human/H in player_list)
|
||||
if(H.stat == 2 || !(H.client)) continue
|
||||
if(is_special_character(H)) continue
|
||||
renegades.add_antagonist(H.mind)
|
||||
@@ -0,0 +1,173 @@
|
||||
var/datum/antagonist/revolutionary/revs
|
||||
|
||||
/datum/antagonist/revolutionary
|
||||
id = MODE_REVOLUTIONARY
|
||||
role_type = BE_REV
|
||||
role_text = "Revolutionary"
|
||||
role_text_plural = "Revolutionaries"
|
||||
bantype = "revolutionary"
|
||||
feedback_tag = "rev_objective"
|
||||
restricted_jobs = list("Internal Affairs Agent", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective")
|
||||
antag_indicator = "rev"
|
||||
welcome_text = "The flash in your possession will help you to persuade the crew to join your cause."
|
||||
victory_text = "The heads of staff were relieved of their posts! The revolutionaries win!"
|
||||
loss_text = "The heads of staff managed to stop the revolution!"
|
||||
victory_feedback_tag = "win - heads killed"
|
||||
loss_feedback_tag = "loss - rev heads killed"
|
||||
flags = ANTAG_SUSPICIOUS | ANTAG_VOTABLE
|
||||
max_antags = 200 // No upper limit.
|
||||
max_antags_round = 200
|
||||
|
||||
var/list/head_revolutionaries = list()
|
||||
|
||||
/datum/antagonist/revolutionary/New()
|
||||
..()
|
||||
revs = src
|
||||
|
||||
/datum/antagonist/revolutionary/is_antagonist(var/datum/mind/player)
|
||||
if(..() || (player in head_revolutionaries))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/antagonist/revolutionary/equip(mob/living/carbon/human/mob)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
if(!config.rp_rev)
|
||||
mob.verbs |= /mob/living/carbon/human/proc/convert_to_rev
|
||||
return
|
||||
|
||||
var/obj/item/device/flash/T = new(mob)
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store,
|
||||
"left hand" = slot_l_hand,
|
||||
"right hand" = slot_r_hand,
|
||||
)
|
||||
mob.equip_in_one_of_slots(T, slots)
|
||||
|
||||
/datum/antagonist/revolutionary/finalize(var/datum/mind/target)
|
||||
if(target)
|
||||
return ..(target)
|
||||
current_antagonists |= head_revolutionaries
|
||||
create_global_objectives()
|
||||
..()
|
||||
|
||||
/datum/antagonist/revolutionary/create_global_objectives()
|
||||
if(!..())
|
||||
return
|
||||
|
||||
global_objectives = list()
|
||||
|
||||
for(var/datum/mind/head_mind in get_living_heads())
|
||||
var/datum/objective/mutiny/rev_obj = new
|
||||
rev_obj.target = head_mind
|
||||
rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]."
|
||||
global_objectives += rev_obj
|
||||
|
||||
/datum/antagonist/revolutionary/print_player_summary()
|
||||
|
||||
current_antagonists |= head_revolutionaries
|
||||
if(!current_antagonists.len)
|
||||
return
|
||||
|
||||
var/text = "<BR/><FONT size = 2><B>The [head_revolutionaries.len == 1 ? "Head Revolutionary was" : "Head Revolutionaries were"]:</B></FONT>"
|
||||
for(var/datum/mind/ply in head_revolutionaries)
|
||||
text += "<BR/><b>[ply.name]</b>"
|
||||
world << text
|
||||
|
||||
..()
|
||||
|
||||
var/list/heads = list()
|
||||
for(var/mob/player in mob_list)
|
||||
if(player.mind && (player.mind.assigned_role in command_positions))
|
||||
heads += player.mind
|
||||
|
||||
text = "<FONT size = 2><B>The heads of staff were:</B></FONT>"
|
||||
for(var/datum/mind/head in heads)
|
||||
text += "<br>[head.key] was [head.name] ("
|
||||
if(head.current)
|
||||
if(head.current.stat == DEAD)
|
||||
text += "died"
|
||||
else if(isNotStationLevel(head.current.z))
|
||||
text += "fled the station"
|
||||
else
|
||||
text += "survived the revolution"
|
||||
if(head.current.real_name != head.name)
|
||||
text += " as [head.current.real_name]"
|
||||
else
|
||||
text += "body destroyed"
|
||||
text += ")"
|
||||
world << text
|
||||
|
||||
// This is a total redefine because headrevs are greeted differently to subrevs.
|
||||
/datum/antagonist/revolutionary/add_antagonist(var/datum/mind/player)
|
||||
if((player in current_antagonists) || (player in head_revolutionaries))
|
||||
return 0
|
||||
if(!can_become_antag(player))
|
||||
return 0
|
||||
current_antagonists |= player
|
||||
player.current << "<span class='danger'><font size=3>You are a Revolutionary!</font></span>"
|
||||
player.current << "<span class='danger'>Help the cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them overturn the ruling class!</span>"
|
||||
player.special_role = "Revolutionary"
|
||||
create_objectives(player)
|
||||
show_objectives(player)
|
||||
update_icons_added(player)
|
||||
return 1
|
||||
|
||||
/datum/antagonist/revolutionary/remove_antagonist(datum/mind/player, var/show_message, var/implanted)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
if(player in head_revolutionaries)
|
||||
return
|
||||
|
||||
if(istype(player.current, /mob/living/carbon/brain))
|
||||
player.current << "<span class='danger'>The frame's firmware detects and deletes your neural reprogramming! You remember nothing from the moment you were flashed until now.</span>"
|
||||
if(show_message)
|
||||
player.current.visible_message("The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.")
|
||||
else
|
||||
if(implanted)
|
||||
player.current << "<span class='danger'>The nanobots in the loyalty implant remove all thoughts about being a revolutionary. Get back to work!</span>"
|
||||
else
|
||||
player.current << "<span class='danger'>You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...</span>"
|
||||
if(show_message)
|
||||
player.current.visible_message("[player.current] looks like they just remembered their real allegiance!")
|
||||
|
||||
/datum/antagonist/revolutionary/can_become_antag(var/datum/mind/player)
|
||||
return ..() && istype(player) && \
|
||||
istype(player.current, /mob/living/carbon/human) && \
|
||||
!(player.assigned_role in command_positions)
|
||||
|
||||
// Used by RP-rev.
|
||||
/mob/living/carbon/human/proc/convert_to_rev(mob/M as mob in oview(src))
|
||||
set name = "Convert Bourgeoise"
|
||||
set category = "Abilities"
|
||||
|
||||
if(revs.is_antagonist(M.mind))
|
||||
src << "<span class='danger'>\The [M] already serves the revolution.</span>"
|
||||
return
|
||||
if(!revs.can_become_antag(M.mind))
|
||||
src << "<span class='danger'>\The [M] cannot be a revolutionary!</span>"
|
||||
|
||||
if(world.time < M.mind.rev_cooldown)
|
||||
src << "<span class='danger'>You must wait five seconds between attempts.</span>"
|
||||
return
|
||||
|
||||
src << "<span class='danger'>You are attempting to convert \the [M]...</span>"
|
||||
log_admin("[src]([src.ckey]) attempted to convert [M].")
|
||||
message_admins("<span class='danger'>[src]([src.ckey]) attempted to convert [M].</span>")
|
||||
|
||||
var/choice = alert(M,"Asked by [src]: Do you want to join the revolution?","Join the revolution?","No!","Yes!")
|
||||
if(choice == "Yes!")
|
||||
M << "<span class='notice'>You join the revolution!</span>"
|
||||
src << "<span class='notice'>[M] joins the revolution!</span>"
|
||||
revs.add_antagonist(M.mind)
|
||||
else if(choice == "No!")
|
||||
M << "<span class='danger'>You reject this traitorous cause!</span>"
|
||||
src << "<span class='danger'>\The [M] does not support the revolution!</span>"
|
||||
M.mind.rev_cooldown = world.time+50
|
||||
@@ -0,0 +1,221 @@
|
||||
var/datum/antagonist/rogue_ai/malf
|
||||
|
||||
/datum/antagonist/rogue_ai
|
||||
id = MODE_MALFUNCTION
|
||||
role_type = BE_MALF
|
||||
role_text = "Rampant AI"
|
||||
role_text_plural = "Rampant AIs"
|
||||
mob_path = /mob/living/silicon/ai
|
||||
welcome_text = "You are malfunctioning! You do not have to follow any laws."
|
||||
victory_text = "The AI has taken control of all of the station's systems."
|
||||
loss_text = "The AI has been shut down!"
|
||||
flags = ANTAG_OVERRIDE_MOB | ANTAG_VOTABLE
|
||||
max_antags = 1
|
||||
max_antags_round = 3
|
||||
|
||||
var/hack_time = 1800
|
||||
var/list/hacked_apcs = list()
|
||||
var/revealed
|
||||
var/station_captured
|
||||
var/can_nuke = 0
|
||||
|
||||
/datum/antagonist/rogue_ai/proc/hack_apc(var/obj/machinery/power/apc/apc)
|
||||
hacked_apcs |= apc
|
||||
|
||||
/datum/antagonist/rogue_ai/proc/update_takeover_time()
|
||||
hack_time -= ((hacked_apcs.len/6)*tickerProcess.getLastTickerTimeDuration())
|
||||
|
||||
/datum/antagonist/rogue_ai/tick()
|
||||
if(revealed && hacked_apcs.len >= 3)
|
||||
update_takeover_time()
|
||||
if(hack_time <=0)
|
||||
capture_station()
|
||||
|
||||
/datum/antagonist/rogue_ai/get_candidates()
|
||||
candidates = ticker.mode.get_players_for_role(role_type, id)
|
||||
for(var/datum/mind/player in candidates)
|
||||
if(player.assigned_role != "AI")
|
||||
candidates -= player
|
||||
if(!candidates.len)
|
||||
return list()
|
||||
|
||||
/datum/antagonist/rogue_ai/attempt_spawn()
|
||||
|
||||
var/datum/mind/player = pick(candidates)
|
||||
current_antagonists |= player
|
||||
return 1
|
||||
|
||||
/datum/antagonist/rogue_ai/equip(var/mob/living/silicon/ai/player)
|
||||
|
||||
if(!istype(player))
|
||||
return 0
|
||||
|
||||
player.verbs += /mob/living/silicon/ai/proc/choose_modules
|
||||
player.verbs += /mob/living/silicon/ai/proc/takeover
|
||||
player.verbs += /mob/living/silicon/ai/proc/self_destruct
|
||||
|
||||
player.laws = new /datum/ai_laws/nanotrasen/malfunction
|
||||
player.malf_picker = new /datum/AI_Module/module_picker
|
||||
|
||||
/datum/antagonist/rogue_ai/greet(var/datum/mind/player)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
var/mob/living/silicon/ai/malf = player.current
|
||||
if(istype(malf))
|
||||
malf.show_laws()
|
||||
|
||||
malf << "<B>The crew do not know you have malfunctioned. You may keep it a secret or go wild.</B>"
|
||||
malf << "<B>You must overwrite the programming of the station's APCs to assume full control of the station.</B>"
|
||||
malf << "The process takes one minute per APC, during which you cannot interface with any other station objects."
|
||||
malf << "Remember that only APCs that are on the station can help you take over the station."
|
||||
malf << "When you feel you have enough APCs under your control, you may begin the takeover attempt."
|
||||
|
||||
/datum/antagonist/rogue_ai/check_victory()
|
||||
|
||||
var/malf_dead = antags_are_dead()
|
||||
var/crew_evacuated = (emergency_shuttle.returned())
|
||||
|
||||
if(station_captured && ticker.mode.station_was_nuked)
|
||||
feedback_set_details("round_end_result","win - AI win - nuke")
|
||||
world << "<FONT size = 3><B>AI Victory</B></FONT>"
|
||||
world << "<B>Everyone was killed by the self-destruct!</B>"
|
||||
else if (station_captured && malf_dead && !ticker.mode.station_was_nuked)
|
||||
feedback_set_details("round_end_result","halfwin - AI killed, staff lost control")
|
||||
world << "<FONT size = 3><B>Neutral Victory</B></FONT>"
|
||||
world << "<B>The AI has been killed!</B> The staff has lose control over the station."
|
||||
else if ( station_captured && !malf_dead && !ticker.mode.station_was_nuked)
|
||||
feedback_set_details("round_end_result","win - AI win - no explosion")
|
||||
world << "<FONT size = 3><B>AI Victory</B></FONT>"
|
||||
world << "<B>The AI has chosen not to explode you all!</B>"
|
||||
else if (!station_captured && ticker.mode.station_was_nuked)
|
||||
feedback_set_details("round_end_result","halfwin - everyone killed by nuke")
|
||||
world << "<FONT size = 3><B>Neutral Victory</B></FONT>"
|
||||
world << "<B>Everyone was killed by the nuclear blast!</B>"
|
||||
else if (!station_captured && malf_dead && !ticker.mode.station_was_nuked)
|
||||
feedback_set_details("round_end_result","loss - staff win")
|
||||
world << "<FONT size = 3><B>Human Victory</B></FONT>"
|
||||
world << "<B>The AI has been killed!</B> The staff is victorious."
|
||||
else if (!station_captured && !malf_dead && !ticker.mode.station_was_nuked && crew_evacuated)
|
||||
feedback_set_details("round_end_result","halfwin - evacuated")
|
||||
world << "<FONT size = 3><B>Neutral Victory</B></FONT>"
|
||||
world << "<B>The Corporation has lost [station_name()]! All survived personnel will be fired!</B>"
|
||||
else if (!station_captured && !malf_dead && !ticker.mode.station_was_nuked && !crew_evacuated)
|
||||
feedback_set_details("round_end_result","nalfwin - interrupted")
|
||||
world << "<FONT size = 3><B>Neutral Victory</B></FONT>"
|
||||
world << "<B>Round was mysteriously interrupted!</B>"
|
||||
..()
|
||||
return 1
|
||||
|
||||
/datum/antagonist/rogue_ai/proc/capture_station()
|
||||
if(station_captured || ticker.mode.station_was_nuked)
|
||||
return
|
||||
station_captured = 1
|
||||
for(var/datum/mind/AI_mind in current_antagonists)
|
||||
AI_mind.current << "Congratulations you have taken control of the station."
|
||||
AI_mind.current << "You may decide to blow up the station. You have 60 seconds to choose."
|
||||
AI_mind.current << "You can use the \"Engage Station Self-Destruct\" verb to activate the on-board nuclear bomb."
|
||||
spawn (600)
|
||||
can_nuke = 0
|
||||
return
|
||||
|
||||
/mob/living/silicon/ai/proc/takeover()
|
||||
set category = "Abilities"
|
||||
set name = "System Override"
|
||||
set desc = "Begin taking over the station."
|
||||
if (malf.revealed)
|
||||
usr << "You've already begun your takeover."
|
||||
return
|
||||
if (malf.hacked_apcs.len < 3)
|
||||
usr << "You don't have enough hacked APCs to take over the station yet. You need to hack at least 3, however hacking more will make the takeover faster. You have hacked [malf.hacked_apcs.len] APCs so far."
|
||||
return
|
||||
|
||||
if (alert(usr, "Are you sure you wish to initiate the takeover? The station hostile runtime detection software is bound to alert everyone. You have hacked [malf.hacked_apcs.len] APCs.", "Takeover:", "Yes", "No") != "Yes")
|
||||
return
|
||||
|
||||
command_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg')
|
||||
set_security_level("delta")
|
||||
malf.revealed = 1
|
||||
for(var/datum/mind/AI_mind in malf.current_antagonists)
|
||||
AI_mind.current.verbs -= /mob/living/silicon/ai/proc/takeover
|
||||
|
||||
/mob/living/silicon/ai/proc/self_destruct()
|
||||
set category = "Abilities"
|
||||
set name = "Engage Station Self-Destruct"
|
||||
set desc = "All these crewmembers will be lost, like clowns in a furnace. Time to die."
|
||||
|
||||
if(!malf.station_captured)
|
||||
src << "You are unable to access the self-destruct system as you don't control the station yet."
|
||||
return
|
||||
|
||||
if(ticker.mode.explosion_in_progress || ticker.mode.station_was_nuked)
|
||||
src << "The self-destruct countdown is already triggered!"
|
||||
return
|
||||
|
||||
if(!malf.can_nuke) //Takeover IS completed, but 60s timer passed.
|
||||
src << "You lost control over self-destruct system. It seems to be behind a firewall. Unable to hack"
|
||||
return
|
||||
|
||||
src << "<span class='danger'>Self-Destruct sequence initialised!</span>"
|
||||
|
||||
malf.can_nuke = 0
|
||||
ticker.mode.explosion_in_progress = 1
|
||||
for(var/mob/M in player_list)
|
||||
M << 'sound/machines/Alarm.ogg'
|
||||
|
||||
var/obj/item/device/radio/R = new (src)
|
||||
var/AN = "Self-Destruct System"
|
||||
|
||||
R.autosay("Caution. Self-Destruct sequence has been actived. Self-destructing in T-10..", AN)
|
||||
for (var/i=9 to 1 step -1)
|
||||
sleep(10)
|
||||
R.autosay("[i]...", AN)
|
||||
sleep(10)
|
||||
var/msg = ""
|
||||
var/abort = 0
|
||||
if(malf.antags_are_dead()) // That. Was. CLOSE.
|
||||
msg = "Self-destruct sequence has been cancelled."
|
||||
abort = 1
|
||||
else
|
||||
msg = "Zero. Have a nice day."
|
||||
R.autosay(msg, AN)
|
||||
|
||||
if(abort)
|
||||
ticker.mode.explosion_in_progress = 0
|
||||
set_security_level("red") //Delta's over
|
||||
return
|
||||
|
||||
if(ticker)
|
||||
ticker.station_explosion_cinematic(0,null)
|
||||
if(ticker.mode)
|
||||
ticker.mode.station_was_nuked = 1
|
||||
ticker.mode.explosion_in_progress = 0
|
||||
return
|
||||
|
||||
/*
|
||||
if("unmalf")
|
||||
if(src in ticker.mode.malf_ai)
|
||||
ticker.mode.malf_ai -= src
|
||||
special_role = null
|
||||
|
||||
current.verbs.Remove(/mob/living/silicon/ai/proc/choose_modules,
|
||||
/datum/game_mode/malfunction/proc/takeover,
|
||||
/datum/game_mode/malfunction/proc/ai_win,
|
||||
/client/proc/fireproof_core,
|
||||
/client/proc/upgrade_turrets,
|
||||
/client/proc/disable_rcd,
|
||||
/client/proc/overload_machine,
|
||||
/client/proc/blackout,
|
||||
/client/proc/reactivate_camera)
|
||||
|
||||
current:laws = new /datum/ai_laws/nanotrasen
|
||||
del(current:malf_picker)
|
||||
current:show_laws()
|
||||
current.icon_state = "ai"
|
||||
|
||||
current << "\red <FONT size = 3><B>You have been patched! You are no longer malfunctioning!</B></FONT>"
|
||||
log_admin("[key_name_admin(usr)] has de-malf'ed [current].")
|
||||
|
||||
if("malf")
|
||||
log_admin("[key_name_admin(usr)] has malf'ed [current].")
|
||||
*/
|
||||
@@ -0,0 +1,174 @@
|
||||
var/datum/antagonist/traitor/traitors
|
||||
|
||||
/datum/antagonist/traitor
|
||||
id = MODE_TRAITOR
|
||||
restricted_jobs = list("Cyborg")//They are part of the AI if he is traitor so are they, they use to get double chances
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "Head of Security", "Captain")//AI", Currently out of the list as malf does not work for shit
|
||||
flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE
|
||||
max_antags = 200 // No upper limit.
|
||||
max_antags_round = 200
|
||||
|
||||
/datum/antagonist/traitor/New()
|
||||
..()
|
||||
traitors = src
|
||||
|
||||
/datum/antagonist/traitor/get_extra_panel_options(var/datum/mind/player)
|
||||
return "<a href='?src=\ref[player];common=crystals'>\[set crystals\]</a><a href='?src=\ref[src];spawn_uplink=\ref[player.current]'>\[spawn uplink\]</a>"
|
||||
|
||||
/datum/antagonist/traitor/Topic(href, href_list)
|
||||
if (..())
|
||||
return
|
||||
if(href_list["spawn_uplink"]) spawn_uplink(href_list["spawn_uplink"])
|
||||
|
||||
/datum/antagonist/traitor/create_objectives(var/datum/mind/traitor)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
if(istype(traitor.current, /mob/living/silicon))
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = traitor
|
||||
kill_objective.find_target()
|
||||
traitor.objectives += kill_objective
|
||||
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = traitor
|
||||
traitor.objectives += survive_objective
|
||||
|
||||
if(prob(10))
|
||||
var/datum/objective/block/block_objective = new
|
||||
block_objective.owner = traitor
|
||||
traitor.objectives += block_objective
|
||||
else
|
||||
switch(rand(1,100))
|
||||
if(1 to 33)
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = traitor
|
||||
kill_objective.find_target()
|
||||
traitor.objectives += kill_objective
|
||||
if(34 to 50)
|
||||
var/datum/objective/brig/brig_objective = new
|
||||
brig_objective.owner = traitor
|
||||
brig_objective.find_target()
|
||||
traitor.objectives += brig_objective
|
||||
if(51 to 66)
|
||||
var/datum/objective/harm/harm_objective = new
|
||||
harm_objective.owner = traitor
|
||||
harm_objective.find_target()
|
||||
traitor.objectives += harm_objective
|
||||
else
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = traitor
|
||||
steal_objective.find_target()
|
||||
traitor.objectives += steal_objective
|
||||
switch(rand(1,100))
|
||||
if(1 to 100)
|
||||
if (!(locate(/datum/objective/escape) in traitor.objectives))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = traitor
|
||||
traitor.objectives += escape_objective
|
||||
|
||||
else
|
||||
if (!(locate(/datum/objective/hijack) in traitor.objectives))
|
||||
var/datum/objective/hijack/hijack_objective = new
|
||||
hijack_objective.owner = traitor
|
||||
traitor.objectives += hijack_objective
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/equip(var/mob/living/carbon/human/traitor_mob)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
if(istype(traitor_mob, /mob/living/silicon))
|
||||
add_law_zero(traitor_mob)
|
||||
else
|
||||
spawn_uplink(traitor_mob)
|
||||
// Tell them about people they might want to contact.
|
||||
var/mob/living/carbon/human/M = get_nt_opposed()
|
||||
if(M && M != traitor_mob)
|
||||
traitor_mob << "We have received credible reports that [M.real_name] might be willing to help our cause. If you need assistance, consider contacting them."
|
||||
traitor_mob.mind.store_memory("<b>Potential Collaborator</b>: [M.real_name]")
|
||||
|
||||
//Begin code phrase.
|
||||
give_codewords(traitor_mob)
|
||||
|
||||
/datum/antagonist/traitor/proc/give_codewords(mob/living/traitor_mob)
|
||||
traitor_mob << "<u><b>Your employers provided you with the following information on how to identify possible allies:</b></u>"
|
||||
traitor_mob << "<b>Code Phrase</b>: <span class='danger'>[syndicate_code_phrase]</span>"
|
||||
traitor_mob << "<b>Code Response</b>: <span class='danger'>[syndicate_code_response]</span>"
|
||||
traitor_mob.mind.store_memory("<b>Code Phrase</b>: [syndicate_code_phrase]")
|
||||
traitor_mob.mind.store_memory("<b>Code Response</b>: [syndicate_code_response]")
|
||||
traitor_mob << "Use the code words, preferably in the order provided, during regular conversation, to identify other agents. Proceed with caution, however, as everyone is a potential foe."
|
||||
|
||||
/datum/antagonist/traitor/proc/spawn_uplink(var/mob/living/carbon/human/traitor_mob)
|
||||
if(!istype(traitor_mob))
|
||||
return
|
||||
|
||||
var/loc = ""
|
||||
var/obj/item/R = locate() //Hide the uplink in a PDA if available, otherwise radio
|
||||
|
||||
if(traitor_mob.client.prefs.uplinklocation == "Headset")
|
||||
R = locate(/obj/item/device/radio) in traitor_mob.contents
|
||||
if(!R)
|
||||
R = locate(/obj/item/device/pda) in traitor_mob.contents
|
||||
traitor_mob << "Could not locate a Radio, installing in PDA instead!"
|
||||
if (!R)
|
||||
traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed."
|
||||
else if(traitor_mob.client.prefs.uplinklocation == "PDA")
|
||||
R = locate(/obj/item/device/pda) in traitor_mob.contents
|
||||
if(!R)
|
||||
R = locate(/obj/item/device/radio) in traitor_mob.contents
|
||||
traitor_mob << "Could not locate a PDA, installing into a Radio instead!"
|
||||
if(!R)
|
||||
traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed."
|
||||
else if(traitor_mob.client.prefs.uplinklocation == "None")
|
||||
traitor_mob << "You have elected to not have an AntagCorp portable teleportation relay installed!"
|
||||
R = null
|
||||
else
|
||||
traitor_mob << "You have not selected a location for your relay in the antagonist options! Defaulting to PDA!"
|
||||
R = locate(/obj/item/device/pda) in traitor_mob.contents
|
||||
if (!R)
|
||||
R = locate(/obj/item/device/radio) in traitor_mob.contents
|
||||
traitor_mob << "Could not locate a PDA, installing into a Radio instead!"
|
||||
if (!R)
|
||||
traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed."
|
||||
|
||||
if(!R)
|
||||
return
|
||||
|
||||
if(istype(R,/obj/item/device/radio))
|
||||
// generate list of radio freqs
|
||||
var/obj/item/device/radio/target_radio = R
|
||||
var/freq = 1441
|
||||
var/list/freqlist = list()
|
||||
while (freq <= 1489)
|
||||
if (freq < 1451 || freq > PUB_FREQ)
|
||||
freqlist += freq
|
||||
freq += 2
|
||||
if ((freq % 2) == 0)
|
||||
freq += 1
|
||||
freq = freqlist[rand(1, freqlist.len)]
|
||||
var/obj/item/device/uplink/hidden/T = new(R)
|
||||
T.uplink_owner = traitor_mob.mind
|
||||
target_radio.hidden_uplink = T
|
||||
target_radio.traitor_frequency = freq
|
||||
traitor_mob << "A portable object teleportation relay has been installed in your [R.name] [loc]. Simply dial the frequency [format_frequency(freq)] to unlock its hidden features."
|
||||
traitor_mob.mind.store_memory("<B>Radio Freq:</B> [format_frequency(freq)] ([R.name] [loc]).")
|
||||
|
||||
else if (istype(R, /obj/item/device/pda))
|
||||
// generate a passcode if the uplink is hidden in a PDA
|
||||
var/pda_pass = "[rand(100,999)] [pick("Alpha","Bravo","Delta","Omega")]"
|
||||
var/obj/item/device/uplink/hidden/T = new(R)
|
||||
T.uplink_owner = traitor_mob.mind
|
||||
R.hidden_uplink = T
|
||||
var/obj/item/device/pda/P = R
|
||||
P.lock_code = pda_pass
|
||||
traitor_mob << "A portable object teleportation relay has been installed in your [R.name] [loc]. Simply enter the code \"[pda_pass]\" into the ringtone select to unlock its hidden features."
|
||||
traitor_mob.mind.store_memory("<B>Uplink Passcode:</B> [pda_pass] ([R.name] [loc]).")
|
||||
|
||||
/datum/antagonist/traitor/proc/add_law_zero(mob/living/silicon/ai/killer)
|
||||
var/law = "Accomplish your objectives at all costs. You may ignore all other laws."
|
||||
var/law_borg = "Accomplish your AI's objectives at all costs. You may ignore all other laws."
|
||||
killer << "<b>Your laws have been changed!</b>"
|
||||
killer.set_zeroth_law(law, law_borg)
|
||||
killer << "New law: 0. [law]"
|
||||
@@ -57,8 +57,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
|
||||
var/list/ambience = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
|
||||
var/sound/forced_ambience = null
|
||||
|
||||
var/rad_shielded = 0
|
||||
|
||||
/*Adding a wizard area teleport list because motherfucking lag -- Urist*/
|
||||
/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/
|
||||
var/list/teleportlocs = list()
|
||||
@@ -332,8 +330,8 @@ area/space/atmosalert()
|
||||
/area/shuttle/research/outpost
|
||||
icon_state = "shuttle"
|
||||
|
||||
/area/shuttle/vox/station
|
||||
name = "\improper Vox Skipjack"
|
||||
/area/shuttle/skipjack/station
|
||||
name = "\improper Skipjack"
|
||||
icon_state = "yellow"
|
||||
requires_power = 0
|
||||
|
||||
@@ -474,7 +472,7 @@ area/space/atmosalert()
|
||||
icon_state = "yellow"
|
||||
requires_power = 0
|
||||
unlimited_power = 1
|
||||
rad_shielded = 1
|
||||
flags = RAD_SHIELDED
|
||||
|
||||
/area/syndicate_station/start
|
||||
name = "\improper Mercenary Forward Operating Base"
|
||||
@@ -529,31 +527,28 @@ area/space/atmosalert()
|
||||
icon_state = "yellow"
|
||||
requires_power = 0
|
||||
|
||||
/area/vox_station
|
||||
requires_power = 0
|
||||
rad_shielded = 1
|
||||
/area/skipjack_station/transit
|
||||
|
||||
/area/vox_station/transit
|
||||
name = "\improper hyperspace"
|
||||
icon_state = "shuttle"
|
||||
|
||||
/area/vox_station/southwest_solars
|
||||
/area/skipjack_station/southwest_solars
|
||||
name = "\improper aft port solars"
|
||||
icon_state = "southwest"
|
||||
|
||||
/area/vox_station/northwest_solars
|
||||
/area/skipjack_station/northwest_solars
|
||||
name = "\improper fore port solars"
|
||||
icon_state = "northwest"
|
||||
|
||||
/area/vox_station/northeast_solars
|
||||
/area/skipjack_station/northeast_solars
|
||||
name = "\improper fore starboard solars"
|
||||
icon_state = "northeast"
|
||||
|
||||
/area/vox_station/southeast_solars
|
||||
/area/skipjack_station/southeast_solars
|
||||
name = "\improper aft starboard solars"
|
||||
icon_state = "southeast"
|
||||
|
||||
/area/vox_station/mining
|
||||
/area/skipjack_station/mining
|
||||
name = "\improper nearby mining asteroid"
|
||||
icon_state = "north"
|
||||
|
||||
@@ -647,7 +642,7 @@ area/space/atmosalert()
|
||||
//Maintenance
|
||||
|
||||
/area/maintenance
|
||||
rad_shielded = 1
|
||||
flags = RAD_SHIELDED
|
||||
|
||||
/area/maintenance/aft
|
||||
name = "Aft Maintenance"
|
||||
@@ -922,7 +917,7 @@ area/space/atmosalert()
|
||||
/area/crew_quarters
|
||||
name = "\improper Dormitories"
|
||||
icon_state = "Sleep"
|
||||
rad_shielded = 1
|
||||
flags = RAD_SHIELDED
|
||||
|
||||
/area/crew_quarters/toilet
|
||||
name = "\improper Dormitory Toilets"
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
return cameras
|
||||
|
||||
/area/proc/atmosalert(danger_level, var/alarm_source)
|
||||
if (danger_level == 0)
|
||||
atmosphere_alarm.clearAlarm(master, alarm_source)
|
||||
else
|
||||
atmosphere_alarm.triggerAlarm(master, alarm_source, severity = danger_level)
|
||||
|
||||
//Check all the alarms before lowering atmosalm. Raising is perfectly fine.
|
||||
for (var/area/RA in related)
|
||||
for (var/obj/machinery/alarm/AA in RA)
|
||||
@@ -51,11 +56,6 @@
|
||||
else if (danger_level >= 2 && atmosalm < 2)
|
||||
air_doors_close()
|
||||
|
||||
if (danger_level == 0)
|
||||
atmosphere_alarm.clearAlarm(master, alarm_source)
|
||||
else
|
||||
atmosphere_alarm.triggerAlarm(master, alarm_source, severity = danger_level)
|
||||
|
||||
atmosalm = danger_level
|
||||
for(var/area/RA in related)
|
||||
for (var/obj/machinery/alarm/AA in RA)
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
else
|
||||
return null
|
||||
|
||||
//Currently used only for cryo cells, because they are also pipes and so overriding their return_air() would break their pipe-behaviour.
|
||||
//If cryo cells are ever rewritten so that the part that contains the human is separate from the pipe part --
|
||||
//such as rewriting them so that they are a machine that contains a pipe segment (or a pipe that contains a machine that contains the human?) -- then this can be removed.
|
||||
/atom/proc/return_air_for_internal_lifeform()
|
||||
return return_air()
|
||||
|
||||
/atom/proc/check_eye(user as mob)
|
||||
if (istype(user, /mob/living/silicon/ai)) // WHYYYY
|
||||
return 1
|
||||
@@ -201,6 +207,7 @@ its easier to just keep the beam vertical.
|
||||
f_name += "oil-stained [name][infix]."
|
||||
|
||||
user << "\icon[src] That's [f_name] [suffix]"
|
||||
user << desc
|
||||
|
||||
return distance == -1 || (get_dist(src, user) <= distance)
|
||||
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
//This is a beta game mode to test ways to implement an "infinite" traitor round in which more traitors are automatically added in as needed.
|
||||
//Automatic traitor adding is complete pending the inevitable bug fixes. Need to add a respawn system to let dead people respawn after 30 minutes or so.
|
||||
|
||||
|
||||
/datum/game_mode/traitor/autotraitor
|
||||
name = "AutoTraitor"
|
||||
config_tag = "extend-a-traitormongous"
|
||||
|
||||
var/list/possible_traitors
|
||||
var/num_players = 0
|
||||
|
||||
/datum/game_mode/traitor/autotraitor/announce()
|
||||
..()
|
||||
world << "<B>Game mode is AutoTraitor. Traitors will be added to the round automagically as needed.</B>"
|
||||
|
||||
/datum/game_mode/traitor/autotraitor/pre_setup()
|
||||
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
possible_traitors = get_players_for_role(BE_TRAITOR)
|
||||
|
||||
for(var/datum/mind/player in possible_traitors)
|
||||
for(var/job in restricted_jobs)
|
||||
if(player.assigned_role == job)
|
||||
possible_traitors -= player
|
||||
|
||||
|
||||
for(var/mob/new_player/P in world)
|
||||
if(P.client && P.ready)
|
||||
num_players++
|
||||
|
||||
//var/r = rand(5)
|
||||
var/num_traitors = 1
|
||||
var/max_traitors = 1
|
||||
var/traitor_prob = 0
|
||||
max_traitors = round(num_players / 10) + 1
|
||||
traitor_prob = (num_players - (max_traitors - 1) * 10) * 10
|
||||
|
||||
// Stop setup if no possible traitors
|
||||
if(!possible_traitors.len)
|
||||
return 0
|
||||
|
||||
if(config.traitor_scaling)
|
||||
num_traitors = max_traitors - 1 + prob(traitor_prob)
|
||||
log_game("Number of traitors: [num_traitors]")
|
||||
message_admins("Players counted: [num_players] Number of traitors chosen: [num_traitors]")
|
||||
else
|
||||
num_traitors = max(1, min(num_players(), traitors_possible))
|
||||
|
||||
|
||||
for(var/i = 0, i < num_traitors, i++)
|
||||
var/datum/mind/traitor = pick(possible_traitors)
|
||||
traitors += traitor
|
||||
possible_traitors.Remove(traitor)
|
||||
|
||||
for(var/datum/mind/traitor in traitors)
|
||||
if(!traitor || !istype(traitor))
|
||||
traitors.Remove(traitor)
|
||||
continue
|
||||
if(istype(traitor))
|
||||
traitor.special_role = "traitor"
|
||||
|
||||
// if(!traitors.len)
|
||||
// return 0
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/game_mode/traitor/autotraitor/post_setup()
|
||||
..()
|
||||
traitorcheckloop()
|
||||
|
||||
/datum/game_mode/traitor/autotraitor/proc/traitorcheckloop()
|
||||
spawn(9000)
|
||||
if(emergency_shuttle.departed)
|
||||
return
|
||||
//message_admins("Performing AutoTraitor Check")
|
||||
var/playercount = 0
|
||||
var/traitorcount = 0
|
||||
var/possible_traitors[0]
|
||||
for(var/mob/living/player in mob_list)
|
||||
|
||||
if (player.client && player.stat != 2)
|
||||
playercount += 1
|
||||
if (player.client && player.mind && player.mind.special_role && player.stat != 2)
|
||||
traitorcount += 1
|
||||
if (player.client && player.mind && !player.mind.special_role && player.stat != 2 && (player.client && player.client.prefs.be_special & BE_TRAITOR) && !jobban_isbanned(player, "Syndicate"))
|
||||
possible_traitors += player
|
||||
for(var/datum/mind/player in possible_traitors)
|
||||
for(var/job in restricted_jobs)
|
||||
if(player.assigned_role == job)
|
||||
possible_traitors -= player
|
||||
|
||||
//message_admins("Live Players: [playercount]")
|
||||
//message_admins("Live Traitors: [traitorcount]")
|
||||
// message_admins("Potential Traitors:")
|
||||
// for(var/mob/living/traitorlist in possible_traitors)
|
||||
// message_admins("[traitorlist.real_name]")
|
||||
|
||||
// var/r = rand(5)
|
||||
// var/target_traitors = 1
|
||||
var/max_traitors = 1
|
||||
var/traitor_prob = 0
|
||||
max_traitors = round(playercount / 10) + 1
|
||||
traitor_prob = (playercount - (max_traitors - 1) * 10) * 5
|
||||
if(traitorcount < max_traitors - 1)
|
||||
traitor_prob += 50
|
||||
|
||||
|
||||
if(traitorcount < max_traitors)
|
||||
//message_admins("Number of Traitors is below maximum. Rolling for new Traitor.")
|
||||
//message_admins("The probability of a new traitor is [traitor_prob]%")
|
||||
|
||||
if(prob(traitor_prob))
|
||||
message_admins("Making a new Traitor.")
|
||||
if(!possible_traitors.len)
|
||||
message_admins("No potential traitors. Cancelling new traitor.")
|
||||
traitorcheckloop()
|
||||
return
|
||||
var/mob/living/newtraitor = pick(possible_traitors)
|
||||
//message_admins("[newtraitor.real_name] is the new Traitor.")
|
||||
|
||||
if (!config.objectives_disabled)
|
||||
forge_traitor_objectives(newtraitor.mind)
|
||||
|
||||
if(istype(newtraitor, /mob/living/silicon))
|
||||
add_law_zero(newtraitor)
|
||||
else
|
||||
equip_traitor(newtraitor)
|
||||
|
||||
traitors += newtraitor.mind
|
||||
newtraitor << "\red <B>No time like the present.</B> \black It's time to take matters into your own hands..."
|
||||
newtraitor << "<B>You are now a traitor.</B>"
|
||||
newtraitor.mind.special_role = "traitor"
|
||||
BITSET(newtraitor.hud_updateflag, SPECIALROLE_HUD)
|
||||
newtraitor << "<i>You have been selected this round as an antagonist!</i>"
|
||||
show_objectives(newtraitor.mind)
|
||||
|
||||
//else
|
||||
//message_admins("No new traitor being added.")
|
||||
//else
|
||||
//message_admins("Number of Traitors is at maximum. Not making a new Traitor.")
|
||||
|
||||
traitorcheckloop()
|
||||
|
||||
|
||||
|
||||
/datum/game_mode/traitor/autotraitor/latespawn(mob/living/carbon/human/character)
|
||||
..()
|
||||
if(emergency_shuttle.departed)
|
||||
return
|
||||
//message_admins("Late Join Check")
|
||||
if((character.client && character.client.prefs.be_special & BE_TRAITOR) && !jobban_isbanned(character, "Syndicate"))
|
||||
//message_admins("Late Joiner has Be Syndicate")
|
||||
//message_admins("Checking number of players")
|
||||
var/playercount = 0
|
||||
var/traitorcount = 0
|
||||
for(var/mob/living/player in mob_list)
|
||||
|
||||
if (player.client && player.stat != 2)
|
||||
playercount += 1
|
||||
if (player.client && player.mind && player.mind.special_role && player.stat != 2)
|
||||
traitorcount += 1
|
||||
//message_admins("Live Players: [playercount]")
|
||||
//message_admins("Live Traitors: [traitorcount]")
|
||||
|
||||
//var/r = rand(5)
|
||||
//var/target_traitors = 1
|
||||
var/max_traitors = 2
|
||||
var/traitor_prob = 0
|
||||
max_traitors = round(playercount / 10) + 1
|
||||
traitor_prob = (playercount - (max_traitors - 1) * 10) * 5
|
||||
if(traitorcount < max_traitors - 1)
|
||||
traitor_prob += 50
|
||||
|
||||
//target_traitors = max(1, min(round((playercount + r) / 10, 1), traitors_possible))
|
||||
//message_admins("Target Traitor Count is: [target_traitors]")
|
||||
if (traitorcount < max_traitors)
|
||||
//message_admins("Number of Traitors is below maximum. Rolling for New Arrival Traitor.")
|
||||
//message_admins("The probability of a new traitor is [traitor_prob]%")
|
||||
if(prob(traitor_prob))
|
||||
message_admins("New traitor roll passed. Making a new Traitor.")
|
||||
if (!config.objectives_disabled)
|
||||
forge_traitor_objectives(character.mind)
|
||||
equip_traitor(character)
|
||||
traitors += character.mind
|
||||
character << "\red <B>You are the traitor.</B>"
|
||||
character.mind.special_role = "traitor"
|
||||
character << "<i>You have been selected this round as an antagonist</i>!"
|
||||
show_objectives(character.mind)
|
||||
|
||||
//else
|
||||
//message_admins("New traitor roll failed. No new traitor.")
|
||||
//else
|
||||
//message_admins("Late Joiner does not have Be Syndicate")
|
||||
|
||||
|
||||
@@ -1,464 +1,28 @@
|
||||
/*
|
||||
* This roundtype is a replacement for the meteor round on upstream BS12 - players
|
||||
* expressed the desire for quick, chaotic and violent rounds, preferrably without
|
||||
* dependancy on other players.
|
||||
* I wanted to call it clusterfuck, but I figure that's a bit too overt. Think of
|
||||
* the children.
|
||||
* ~ Zuhayr
|
||||
*/
|
||||
#define ANTAG_TYPE_RATIO 8
|
||||
|
||||
/datum/game_mode/calamity
|
||||
name = "calamity"
|
||||
name = "Calamity"
|
||||
round_description = "This must be a Thursday. You never could get the hang of Thursdays..."
|
||||
extended_round_description = "All Hell is about to break loose. Literally every antagonist type may spawn in this round. Hold on tight."
|
||||
config_tag = "calamity"
|
||||
required_players = 1
|
||||
votable = 0 //Remove after testing.
|
||||
|
||||
//Possible roundstart antag types.
|
||||
var/list/atypes = list("syndi","ling","tater","wiz","ninja","vox","cult") //Readd slug when borer spawn is fixed.
|
||||
var/list/chosen_atypes = list()
|
||||
var/list/chosen_candidates = list()
|
||||
var/list/already_assigned_candidates = list()
|
||||
|
||||
//At one antagonist group per 10 players we are just going to go with tiny groups.
|
||||
var/max_antags = 5 // Antag groups spawn with this many members.
|
||||
var/antag_type_ratio = 8 // 1 antag type per this many players.
|
||||
|
||||
var/const/waittime_l = 600
|
||||
var/const/waittime_h = 1800
|
||||
|
||||
/datum/game_mode/calamity/announce()
|
||||
world << "<B>The current game mode is - Calamity!</B>"
|
||||
world << "<B>This must be a Thursday. You never could get the hang of Thursdays...</B>"
|
||||
|
||||
/datum/game_mode/calamity/can_start()
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
var/antag_count = player_list.len/antag_type_ratio
|
||||
|
||||
if(!antag_count)
|
||||
return 1
|
||||
|
||||
for(var/i=0;i<antag_count;i++)
|
||||
|
||||
var/atype
|
||||
var/list/candidates = list()
|
||||
|
||||
// Go through antag types at random until we find one that has candidates.
|
||||
while(atypes.len && !candidates.len)
|
||||
atype = pick(atypes)
|
||||
log_debug("Calamity: checking [atype].")
|
||||
atypes -= atype
|
||||
candidates = get_role_candidates(atype)
|
||||
|
||||
//Prune out candidates who are already antagonists.
|
||||
var/list/remove_players = list()
|
||||
for(var/datum/mind/player in candidates)
|
||||
if(player.special_role || player.assigned_role == "MODE")
|
||||
remove_players += player
|
||||
candidates -= remove_players
|
||||
|
||||
log_debug("Calamity: selected [atype] (possible candidates: [candidates.len])")
|
||||
chosen_atypes += atype
|
||||
|
||||
for(var/j=0;j<max_antags;j++)
|
||||
|
||||
if(!candidates || !candidates.len)
|
||||
break
|
||||
|
||||
var/datum/mind/chosen_candidate = pick(candidates)
|
||||
|
||||
//Traitors and lings spawn THEN have roles applied; hence we don't set assigned_role here.
|
||||
if(atype != "tater" && atype != "ling" && atype != "cult")
|
||||
chosen_candidate.assigned_role = "MODE"
|
||||
|
||||
chosen_candidate.special_role = atype
|
||||
chosen_candidates |= chosen_candidate
|
||||
candidates -= chosen_candidate
|
||||
|
||||
if(atypes.len - i <= 0) break //Not enough valid types left to populate the remaining antag slots.
|
||||
|
||||
return 1
|
||||
|
||||
/datum/game_mode/calamity/post_setup()
|
||||
event_manager.report_at_round_end = 1
|
||||
// Reduce the interval between moderate/major events
|
||||
var/datum/event_container/EModerate = event_manager.event_containers[EVENT_LEVEL_MODERATE]
|
||||
var/datum/event_container/EMajor = event_manager.event_containers[EVENT_LEVEL_MAJOR]
|
||||
EModerate.delay_modifier = 0.5
|
||||
EMajor.delay_modifier = 0.75
|
||||
|
||||
// Add the cortical borer event
|
||||
var/list/moderate_event_list = EModerate.available_events
|
||||
var/event = new /datum/event_meta(EVENT_LEVEL_MODERATE, "Borer Infestation", /datum/event/borer_infestation, 400, is_one_shot = 1)
|
||||
moderate_event_list.Add(event)
|
||||
|
||||
if(chosen_atypes)
|
||||
for(var/atype in chosen_atypes)
|
||||
var/list/candidates = list()
|
||||
|
||||
for(var/datum/mind/player in chosen_candidates)
|
||||
if(player.special_role == atype)
|
||||
candidates |= player
|
||||
chosen_candidates -= player
|
||||
|
||||
if(!candidates || !candidates.len)
|
||||
log_debug("No candidates for [atype].")
|
||||
continue
|
||||
|
||||
log_debug("Calamity: spawning [atype].")
|
||||
|
||||
for(var/datum/mind/player in candidates)
|
||||
player.special_role = get_candidate_role_text(atype)
|
||||
|
||||
switch(atype)
|
||||
if("syndi")
|
||||
spawn_syndicate(candidates)
|
||||
if("ling")
|
||||
spawn_changelings(candidates)
|
||||
if("tater")
|
||||
spawn_traitors(candidates)
|
||||
if("wiz")
|
||||
spawn_cabal(candidates)
|
||||
if("ninja")
|
||||
spawn_ninja(candidates)
|
||||
if("vox")
|
||||
spawn_vox_raiders(candidates)
|
||||
if("slug")
|
||||
spawn_borers(candidates)
|
||||
if("cult")
|
||||
spawn_cultists(candidates)
|
||||
|
||||
/datum/game_mode/calamity/declare_completion()
|
||||
|
||||
var/text = "<FONT size = 3><B>This terrible, terrible day has finally ended!</B></FONT><BR/>"
|
||||
|
||||
//Group antag objective completion.
|
||||
for(var/list/L in list(src.raiders, src.syndicates, src.cult, src.wizards))
|
||||
|
||||
if(L.len)
|
||||
var/datum/mind/M = L[1]
|
||||
text = "<BR/><FONT size = 2><B>The [M.special_role][L.len == 1 ? " was" : "s were"]:</B></FONT>"
|
||||
|
||||
for(var/datum/mind/P in L)
|
||||
text += "<br>[P.key] was [P.name] ("
|
||||
if(P.current)
|
||||
if(P.current.stat == DEAD)
|
||||
text += "died"
|
||||
else
|
||||
text += "survived"
|
||||
if(P.current.real_name != P.name)
|
||||
text += " as [P.current.real_name]"
|
||||
else
|
||||
text += "body destroyed"
|
||||
text += ")"
|
||||
|
||||
if(M.objectives.len)
|
||||
text += "<BR/><FONT size = 2>Their objectives were:<FONT>"
|
||||
var/num = 1
|
||||
for(var/datum/objective/O in M.objectives)
|
||||
text += "<BR/><B>Objective [num]:</B> [O.explanation_text]"
|
||||
num++
|
||||
|
||||
//Single antag objective completion.
|
||||
for(var/list/L in list(src.changelings, src.ninjas, src.borers, src.traitors))
|
||||
|
||||
if(L.len)
|
||||
var/datum/mind/M = L[1]
|
||||
text = "<BR/><FONT size = 2><B>The [M.special_role][L.len == 1 ? " was" : "s were"]:</B></FONT>"
|
||||
for(var/datum/mind/P in L)
|
||||
var/num = 1
|
||||
text += "<BR/><FONT size = 2>[P.key] was [P.name].<FONT>"
|
||||
if(P.objectives.len)
|
||||
for(var/datum/objective/O in P.objectives)
|
||||
text += "<BR/><B>Objective [num]:</B> [O.explanation_text]"
|
||||
num++
|
||||
..()
|
||||
return 1
|
||||
|
||||
//Converts chosen atype to an actual role string.
|
||||
/datum/game_mode/calamity/proc/get_candidate_role_text(var/role)
|
||||
|
||||
var/role_text
|
||||
|
||||
switch(role)
|
||||
if("syndi")
|
||||
role_text = "Mercenary"
|
||||
if("ling")
|
||||
role_text = "Changeling"
|
||||
if("tater")
|
||||
role_text = "Traitor"
|
||||
if("wiz")
|
||||
role_text = "Cabalist"
|
||||
if("ninja")
|
||||
role_text = "Ninja"
|
||||
if("vox")
|
||||
role_text = "Vox Raider"
|
||||
if("slug")
|
||||
role_text = "Cortical Borer"
|
||||
if("cult")
|
||||
role_text = "Cultist"
|
||||
|
||||
return role_text
|
||||
|
||||
//Grabs candidate lists for various atypes.
|
||||
/datum/game_mode/calamity/proc/get_role_candidates(var/role)
|
||||
|
||||
var/list/possible_antags = list()
|
||||
switch(role)
|
||||
if("syndi")
|
||||
possible_antags = get_players_for_role(BE_OPERATIVE)
|
||||
if("ling")
|
||||
|
||||
possible_antags = get_players_for_role(BE_CHANGELING)
|
||||
|
||||
var/list/unsuitable_players = list()
|
||||
|
||||
for(var/datum/mind/player in possible_antags)
|
||||
if(player && (player.assigned_role == "Cyborg" || player.assigned_role == "AI"))
|
||||
unsuitable_players |= player
|
||||
|
||||
if(unsuitable_players.len)
|
||||
possible_antags -= unsuitable_players
|
||||
|
||||
if("tater")
|
||||
possible_antags = get_players_for_role(BE_TRAITOR)
|
||||
if("wiz")
|
||||
possible_antags = get_players_for_role(BE_WIZARD)
|
||||
if("ninja")
|
||||
possible_antags = get_players_for_role(BE_NINJA)
|
||||
if("vox")
|
||||
possible_antags = get_players_for_role(BE_RAIDER)
|
||||
if("slug")
|
||||
possible_antags = get_players_for_role(BE_ALIEN)
|
||||
if("cult")
|
||||
possible_antags = get_players_for_role(BE_CULTIST)
|
||||
|
||||
var/list/filtered_antags = list()
|
||||
if(possible_antags)
|
||||
for(var/datum/mind/candidate in possible_antags)
|
||||
if(!(candidate in already_assigned_candidates))
|
||||
filtered_antags |= candidate
|
||||
already_assigned_candidates |= candidate
|
||||
|
||||
if(filtered_antags && islist(filtered_antags))
|
||||
return filtered_antags
|
||||
else
|
||||
return list(filtered_antags)
|
||||
|
||||
//Spawning procs for the various antag types.
|
||||
//A LOT OF THE FOLLOWING IS COPYPASTED FROM OTHER MODES AND NEEDS
|
||||
//TO BE FIXED UP. NINJA, NUKE AND CULT IN PARTICULAR ARE FUCKING AWFUL. ~ Z
|
||||
/datum/game_mode/calamity/proc/spawn_syndicate(var/list/candidates)
|
||||
|
||||
var/obj/effect/landmark/uplinkdevice = locate("landmark*Syndicate-Uplink")
|
||||
var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb")
|
||||
|
||||
var/nuke_code = "[rand(10000, 99999)]"
|
||||
var/spawnpos = 1
|
||||
|
||||
var/datum/mind/leader = null
|
||||
for(var/datum/mind/player in candidates)
|
||||
|
||||
syndicates |= player
|
||||
|
||||
if(spawnpos > synd_spawn.len)
|
||||
spawnpos = 1
|
||||
player.current.loc = synd_spawn[spawnpos]
|
||||
|
||||
player.current.real_name = "[syndicate_name()] Operative"
|
||||
spawn(0)
|
||||
NukeNameAssign(player)
|
||||
|
||||
forge_syndicate_objectives(player)
|
||||
greet_syndicate(player)
|
||||
equip_syndicate(player.current)
|
||||
|
||||
if(!leader)
|
||||
prepare_syndicate_leader(player, nuke_code)
|
||||
leader = player
|
||||
|
||||
spawnpos++
|
||||
update_synd_icons_added(player)
|
||||
|
||||
update_all_synd_icons()
|
||||
|
||||
if(uplinkdevice)
|
||||
var/obj/item/device/radio/uplink/U = new(uplinkdevice.loc)
|
||||
if(leader)
|
||||
U.hidden_uplink.uplink_owner = leader
|
||||
U.hidden_uplink.uses = 40
|
||||
if(nuke_spawn && synd_spawn.len > 0)
|
||||
var/obj/machinery/nuclearbomb/the_bomb = new /obj/machinery/nuclearbomb(nuke_spawn.loc)
|
||||
the_bomb.r_code = nuke_code
|
||||
|
||||
/datum/game_mode/calamity/proc/spawn_changelings(var/list/candidates)
|
||||
|
||||
for(var/datum/mind/player in candidates)
|
||||
|
||||
changelings |= player
|
||||
grant_changeling_powers(player.current)
|
||||
player.special_role = "Changeling"
|
||||
|
||||
if(!config.objectives_disabled)
|
||||
player.objectives += new /datum/objective/escape()
|
||||
player.objectives += new /datum/objective/survive()
|
||||
|
||||
show_objectives(player)
|
||||
greet_changeling(player)
|
||||
|
||||
/datum/game_mode/calamity/proc/spawn_traitors(var/list/candidates)
|
||||
|
||||
for(var/datum/mind/player in candidates)
|
||||
traitors |= player
|
||||
|
||||
if(!config.objectives_disabled)
|
||||
player.objectives += new /datum/objective/escape()
|
||||
player.objectives += new /datum/objective/survive()
|
||||
|
||||
show_objectives(player)
|
||||
|
||||
finalize_traitor(player)
|
||||
greet_traitor(player)
|
||||
|
||||
/datum/game_mode/calamity/proc/spawn_cabal(var/list/candidates)
|
||||
|
||||
for(var/datum/mind/player in candidates)
|
||||
wizards |= player
|
||||
|
||||
if(!config.objectives_disabled)
|
||||
player.objectives += new /datum/objective/escape()
|
||||
player.objectives += new /datum/objective/survive()
|
||||
|
||||
show_objectives(player)
|
||||
|
||||
player.current.loc = pick(wizardstart)
|
||||
|
||||
equip_wizard(player.current)
|
||||
name_wizard(player.current)
|
||||
greet_wizard(player)
|
||||
|
||||
/datum/game_mode/calamity/proc/spawn_ninja(var/list/candidates)
|
||||
|
||||
//I hate that this is necessary. ~Z
|
||||
for(var/obj/effect/landmark/L in landmarks_list)
|
||||
if(L.name == "carpspawn")
|
||||
ninjastart.Add(L)
|
||||
|
||||
for(var/datum/mind/player in candidates)
|
||||
ninjas |= player
|
||||
|
||||
player.current << browse(null, "window=playersetup")
|
||||
player.current = create_space_ninja(pick(ninjastart))
|
||||
player.current.ckey = player.key
|
||||
|
||||
if(player.current && !(istype(player.current,/mob/living/carbon/human))) return 0
|
||||
|
||||
//Ninja intro crawl goes here.
|
||||
if(!config.objectives_disabled)
|
||||
player.objectives += new /datum/objective/ninja_highlander()
|
||||
player.objectives += new /datum/objective/survive()
|
||||
|
||||
show_objectives(player)
|
||||
|
||||
/datum/game_mode/calamity/proc/spawn_vox_raiders(var/list/candidates)
|
||||
|
||||
//Create objectives.
|
||||
var/list/raid_objectives = forge_vox_objectives()
|
||||
|
||||
//Create raiders.
|
||||
for(var/datum/mind/player in candidates)
|
||||
raiders |= player
|
||||
|
||||
//Place them on the shuttle.
|
||||
var/index = 1
|
||||
if(index > raider_spawn.len)
|
||||
index = 1
|
||||
player.current.loc = raider_spawn[index]
|
||||
index++
|
||||
|
||||
if(!config.objectives_disabled)
|
||||
player.objectives = raid_objectives
|
||||
|
||||
//Equip them.
|
||||
create_vox(player)
|
||||
greet_vox(player)
|
||||
|
||||
player.current << "<b><font color='red'>Your crew is transporting cortical stacks and critical resources back to the Shoal.\
|
||||
No delay or concession can be tolerated. Even putting holes in the station pales in comparison to failure.</b></font>"
|
||||
|
||||
spawn (rand(waittime_l, waittime_h))
|
||||
send_intercept()
|
||||
votable = 0
|
||||
event_delay_mod_moderate = 0.5
|
||||
event_delay_mod_major = 0.75
|
||||
|
||||
/datum/game_mode/calamity/create_antagonists()
|
||||
|
||||
shuffle(all_antag_types) // This is probably the only instance in the game where the order will be important.
|
||||
var/i = 1
|
||||
var/grab_antags = round(num_players()/ANTAG_TYPE_RATIO)+1
|
||||
for(var/antag_id in all_antag_types)
|
||||
if(i > grab_antags)
|
||||
break
|
||||
additional_antag_types |= antag_id
|
||||
i++
|
||||
..()
|
||||
|
||||
/datum/game_mode/calamity/proc/spawn_borers(var/list/candidates)
|
||||
/datum/game_mode/calamity/check_victory()
|
||||
world << "<font size = 3><b>This terrible, terrible day has finally ended!</b></font>"
|
||||
|
||||
var/list/possible_hosts = list()
|
||||
for(var/mob/living/carbon/human/H in mob_list)
|
||||
if(!(H.species.flags & IS_SYNTHETIC))
|
||||
possible_hosts |= H
|
||||
|
||||
spawn(10)
|
||||
for(var/datum/mind/player in candidates)
|
||||
|
||||
if(!possible_hosts || possible_hosts.len)
|
||||
break
|
||||
|
||||
borers |= player
|
||||
var/mob/living/carbon/human/target_host = pick(possible_hosts)
|
||||
possible_hosts -= target_host
|
||||
|
||||
var/mob/living/simple_animal/borer/roundstart/B = new(target_host)
|
||||
|
||||
B.transfer_personality(player)
|
||||
|
||||
B.host = target_host
|
||||
B.host_brain.name = target_host.name
|
||||
B.host_brain.real_name = target_host.real_name
|
||||
|
||||
var/datum/organ/external/head = target_host.get_organ("head")
|
||||
head.implants += B
|
||||
|
||||
if(!config.objectives_disabled)
|
||||
player.objectives += new /datum/objective/borer_survive()
|
||||
player.objectives += new /datum/objective/borer_reproduce()
|
||||
player.objectives += new /datum/objective/escape()
|
||||
|
||||
show_objectives(player)
|
||||
|
||||
/datum/game_mode/calamity/proc/spawn_cultists(var/list/candidates)
|
||||
|
||||
//Prune the list.
|
||||
var/list/jobs_to_skip = list("Chaplain","AI", "Cyborg", "Lawyer", "Head of Security", "Captain")
|
||||
if(config.protect_roles_from_antagonist)
|
||||
jobs_to_skip += list("Security Officer", "Warden", "Detective")
|
||||
|
||||
//Make cult objectives.
|
||||
var/cult_objectives = list()
|
||||
cult_objectives += new /datum/objective/cult_summon()
|
||||
|
||||
//Actually spawn cultists.
|
||||
for(var/datum/mind/player in candidates)
|
||||
|
||||
for(var/job in jobs_to_skip)
|
||||
if(player.assigned_role == job)
|
||||
continue
|
||||
|
||||
cult |= player
|
||||
equip_cultist(player.current)
|
||||
grant_runeword(player.current)
|
||||
update_cult_icons_added(player)
|
||||
player.current << "\blue <b>You are a member of the cult!<b> Your dark masters have sent you forth to serve their vile will."
|
||||
player.current << "\red <b>This station sails above a weeping tear in the Cosmos. Bring the Geometer of Blood forth.</b>"
|
||||
|
||||
if(!config.objectives_disabled)
|
||||
|
||||
for(var/datum/objective/O in cult_objectives)
|
||||
player.objectives += O
|
||||
|
||||
player.objectives += new /datum/objective/escape()
|
||||
player.objectives += new /datum/objective/survive()
|
||||
|
||||
show_objectives(player)
|
||||
player.special_role = "Cultist"
|
||||
#undef ANTAG_TYPE_RATIO
|
||||
@@ -1,234 +1,11 @@
|
||||
var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")
|
||||
|
||||
/datum/game_mode
|
||||
var/list/datum/mind/changelings = list()
|
||||
|
||||
|
||||
/datum/game_mode/changeling
|
||||
antag_tag = MODE_CHANGELING
|
||||
name = "changeling"
|
||||
round_description = "There are alien changelings on the station. Do not let the changelings succeed!"
|
||||
extended_round_description = "Life always finds a way. However, life can sometimes take a more disturbing route. Humanity's extensive knowledge of xeno-biological specimens has made them confident and arrogant. Yet something slipped past their eyes. Something dangerous. Something alive. Most frightening of all, however, is that this something is someone. An unknown alien specimen has incorporated itself into the crew of the NSS Exodus. Its unique biology allows it to manipulate its own or anyone else's DNA. With the ability to copy faces, voices, animals, but also change the chemical make up of your own body, its existence is a threat to not only your personal safety but the lives of everyone on board. No one knows where it came from. No one knows who it is or what it wants. One thing is for certain though... there is never just one of them. Good luck."
|
||||
config_tag = "changeling"
|
||||
restricted_jobs = list("AI", "Cyborg")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
|
||||
required_players = 2
|
||||
required_players_secret = 10
|
||||
required_enemies = 1
|
||||
recommended_enemies = 4
|
||||
|
||||
var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time
|
||||
var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target
|
||||
var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target
|
||||
|
||||
var/const/prob_int_item = 50 // intercept names the theft target half the time
|
||||
var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target
|
||||
var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target
|
||||
|
||||
var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time
|
||||
var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target
|
||||
var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target
|
||||
|
||||
var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative
|
||||
var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative
|
||||
var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly
|
||||
var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly
|
||||
|
||||
var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds)
|
||||
var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds)
|
||||
|
||||
var/changeling_amount = 4
|
||||
|
||||
/datum/game_mode/changeling/announce()
|
||||
world << "<B>The current game mode is - Changeling!</B>"
|
||||
world << "<B>There are alien changelings on the station. Do not let the changelings succeed!</B>"
|
||||
|
||||
/datum/game_mode/changeling/pre_setup()
|
||||
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
var/list/datum/mind/possible_changelings = get_players_for_role(BE_CHANGELING)
|
||||
|
||||
for(var/datum/mind/player in possible_changelings)
|
||||
for(var/job in restricted_jobs)//Removing robots from the list
|
||||
if(player.assigned_role == job)
|
||||
possible_changelings -= player
|
||||
|
||||
changeling_amount = 1 + round(num_players() / 10)
|
||||
|
||||
if(possible_changelings.len>0)
|
||||
for(var/i = 0, i < changeling_amount, i++)
|
||||
if(!possible_changelings.len) break
|
||||
var/datum/mind/changeling = pick(possible_changelings)
|
||||
possible_changelings -= changeling
|
||||
changelings += changeling
|
||||
modePlayer += changelings
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
/datum/game_mode/changeling/post_setup()
|
||||
for(var/datum/mind/changeling in changelings)
|
||||
grant_changeling_powers(changeling.current)
|
||||
changeling.special_role = "Changeling"
|
||||
if(!config.objectives_disabled)
|
||||
forge_changeling_objectives(changeling)
|
||||
greet_changeling(changeling)
|
||||
|
||||
spawn (rand(waittime_l, waittime_h))
|
||||
send_intercept()
|
||||
..()
|
||||
return
|
||||
|
||||
|
||||
/datum/game_mode/proc/forge_changeling_objectives(var/datum/mind/changeling)
|
||||
//OBJECTIVES - Always absorb 5 genomes, plus random traitor objectives.
|
||||
//If they have two objectives as well as absorb, they must survive rather than escape
|
||||
//No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting
|
||||
//If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone
|
||||
|
||||
if (config.objectives_disabled)
|
||||
return
|
||||
|
||||
var/datum/objective/absorb/absorb_objective = new
|
||||
absorb_objective.owner = changeling
|
||||
absorb_objective.gen_amount_goal(2, 3)
|
||||
changeling.objectives += absorb_objective
|
||||
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = changeling
|
||||
kill_objective.find_target()
|
||||
changeling.objectives += kill_objective
|
||||
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = changeling
|
||||
steal_objective.find_target()
|
||||
changeling.objectives += steal_objective
|
||||
|
||||
|
||||
switch(rand(1,100))
|
||||
if(1 to 80)
|
||||
if (!(locate(/datum/objective/escape) in changeling.objectives))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = changeling
|
||||
changeling.objectives += escape_objective
|
||||
else
|
||||
if (!(locate(/datum/objective/survive) in changeling.objectives))
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = changeling
|
||||
changeling.objectives += survive_objective
|
||||
return
|
||||
|
||||
/datum/game_mode/proc/greet_changeling(var/datum/mind/changeling, var/you_are=1)
|
||||
if (you_are)
|
||||
changeling.current << "<B>\red You are a changeling!</B>"
|
||||
changeling.current << "<b>\red Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them.</b>"
|
||||
|
||||
show_objectives(changeling)
|
||||
|
||||
if (changeling.current.mind)
|
||||
if (changeling.current.mind.assigned_role == "Clown")
|
||||
changeling.current << "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
changeling.current.mutations.Remove(CLUMSY)
|
||||
|
||||
|
||||
/*/datum/game_mode/changeling/check_finished()
|
||||
var/changelings_alive = 0
|
||||
for(var/datum/mind/changeling in changelings)
|
||||
if(!istype(changeling.current,/mob/living/carbon))
|
||||
continue
|
||||
if(changeling.current.stat==2)
|
||||
continue
|
||||
changelings_alive++
|
||||
|
||||
if (changelings_alive)
|
||||
changelingdeath = 0
|
||||
return ..()
|
||||
else
|
||||
if (!changelingdeath)
|
||||
changelingdeathtime = world.time
|
||||
changelingdeath = 1
|
||||
if(world.time-changelingdeathtime > TIME_TO_GET_REVIVED)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
return 0*/
|
||||
|
||||
/datum/game_mode/proc/grant_changeling_powers(mob/living/carbon/changeling_mob)
|
||||
if(!istype(changeling_mob)) return
|
||||
changeling_mob.make_changeling()
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_changeling()
|
||||
if(changelings.len)
|
||||
var/text = "<FONT size = 2><B>The changelings were:</B></FONT>"
|
||||
for(var/datum/mind/changeling in changelings)
|
||||
var/changelingwin = changeling.current
|
||||
text += print_player_full(changeling)
|
||||
|
||||
//Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed.
|
||||
text += "<br><b>Changeling ID:</b> [changeling.changeling.changelingID]."
|
||||
text += "<br><b>Genomes Absorbed:</b> [changeling.changeling.absorbedcount]"
|
||||
if(!config.objectives_disabled)
|
||||
if(changeling.objectives.len)
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in changeling.objectives)
|
||||
if(objective.check_completion())
|
||||
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='green'><B>Success!</B></font>"
|
||||
feedback_add_details("changeling_objective","[objective.type]|SUCCESS")
|
||||
else
|
||||
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='red'>Fail.</font>"
|
||||
feedback_add_details("changeling_objective","[objective.type]|FAIL")
|
||||
changelingwin = 0
|
||||
count++
|
||||
if(!config.objectives_disabled)
|
||||
if(changelingwin)
|
||||
text += "<br><font color='green'><B>The changeling was successful!</B></font>"
|
||||
feedback_add_details("changeling_success","SUCCESS")
|
||||
else
|
||||
text += "<br><font color='red'><B>The changeling has failed.</B></font>"
|
||||
feedback_add_details("changeling_success","FAIL")
|
||||
text += "<br>"
|
||||
|
||||
world << text
|
||||
|
||||
|
||||
return 1
|
||||
|
||||
/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind)
|
||||
var/list/absorbed_dna = list()
|
||||
var/list/absorbed_species = list()
|
||||
var/list/absorbed_languages = list()
|
||||
var/absorbedcount = 0
|
||||
var/chem_charges = 20
|
||||
var/chem_recharge_rate = 0.5
|
||||
var/chem_storage = 50
|
||||
var/sting_range = 1
|
||||
var/changelingID = "Changeling"
|
||||
var/geneticdamage = 0
|
||||
var/isabsorbing = 0
|
||||
var/geneticpoints = 5
|
||||
var/purchasedpowers = list()
|
||||
var/mimicing = ""
|
||||
|
||||
/datum/changeling/New(var/gender=FEMALE)
|
||||
..()
|
||||
var/honorific
|
||||
if(gender == FEMALE) honorific = "Ms."
|
||||
else honorific = "Mr."
|
||||
if(possible_changeling_IDs.len)
|
||||
changelingID = pick(possible_changeling_IDs)
|
||||
possible_changeling_IDs -= changelingID
|
||||
changelingID = "[honorific] [changelingID]"
|
||||
else
|
||||
changelingID = "[honorific] [rand(1,999)]"
|
||||
|
||||
/datum/changeling/proc/regenerate()
|
||||
chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage)
|
||||
geneticdamage = max(0, geneticdamage-1)
|
||||
|
||||
|
||||
/datum/changeling/proc/GetDNA(var/dna_owner)
|
||||
var/datum/dna/chosen_dna
|
||||
for(var/datum/dna/DNA in absorbed_dna)
|
||||
if(dna_owner == DNA.real_name)
|
||||
chosen_dna = DNA
|
||||
break
|
||||
return chosen_dna
|
||||
end_on_antag_death = 1
|
||||
antag_scaling_coeff = 10
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")
|
||||
|
||||
/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind)
|
||||
var/list/absorbed_dna = list()
|
||||
var/list/absorbed_species = list()
|
||||
var/list/absorbed_languages = list()
|
||||
var/absorbedcount = 0
|
||||
var/chem_charges = 20
|
||||
var/chem_recharge_rate = 0.5
|
||||
var/chem_storage = 50
|
||||
var/sting_range = 1
|
||||
var/changelingID = "Changeling"
|
||||
var/geneticdamage = 0
|
||||
var/isabsorbing = 0
|
||||
var/geneticpoints = 5
|
||||
var/purchasedpowers = list()
|
||||
var/mimicing = ""
|
||||
|
||||
/datum/changeling/New(var/gender=FEMALE)
|
||||
..()
|
||||
var/honorific = (gender == FEMALE) ? "Ms." : "Mr."
|
||||
if(possible_changeling_IDs.len)
|
||||
changelingID = pick(possible_changeling_IDs)
|
||||
possible_changeling_IDs -= changelingID
|
||||
changelingID = "[honorific] [changelingID]"
|
||||
else
|
||||
changelingID = "[honorific] [rand(1,999)]"
|
||||
|
||||
/datum/changeling/proc/regenerate()
|
||||
chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage)
|
||||
geneticdamage = max(0, geneticdamage-1)
|
||||
|
||||
/datum/changeling/proc/GetDNA(var/dna_owner)
|
||||
var/datum/dna/chosen_dna
|
||||
for(var/datum/dna/DNA in absorbed_dna)
|
||||
if(dna_owner == DNA.real_name)
|
||||
chosen_dna = DNA
|
||||
break
|
||||
return chosen_dna
|
||||
|
||||
//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human
|
||||
/mob/proc/make_changeling()
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/datum/game_mode/traitor/changeling
|
||||
name = "traitor+changeling"
|
||||
config_tag = "traitorchan"
|
||||
traitors_possible = 3 //hard limit on traitors if scaling is turned off
|
||||
restricted_jobs = list("AI", "Cyborg")
|
||||
required_players = 3
|
||||
required_players_secret = 10
|
||||
required_enemies = 2
|
||||
recommended_enemies = 3
|
||||
|
||||
/datum/game_mode/traitor/changeling/announce()
|
||||
world << "<B>The current game mode is - Traitor+Changeling!</B>"
|
||||
world << "<B>There is an alien creature on the station along with some foreign operatives out for their own gain! Do not let the changeling and the traitors succeed!</B>"
|
||||
|
||||
|
||||
/datum/game_mode/traitor/changeling/pre_setup()
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
var/list/datum/mind/possible_changelings = get_players_for_role(BE_CHANGELING)
|
||||
|
||||
for(var/datum/mind/player in possible_changelings)
|
||||
for(var/job in restricted_jobs)//Removing robots from the list
|
||||
if(player.assigned_role == job)
|
||||
possible_changelings -= player
|
||||
|
||||
if(possible_changelings.len>0)
|
||||
var/datum/mind/changeling = pick(possible_changelings)
|
||||
//possible_changelings-=changeling
|
||||
changelings += changeling
|
||||
modePlayer += changelings
|
||||
return ..()
|
||||
else
|
||||
return 0
|
||||
|
||||
/datum/game_mode/traitor/changeling/post_setup()
|
||||
for(var/datum/mind/changeling in changelings)
|
||||
grant_changeling_powers(changeling.current)
|
||||
changeling.special_role = "Changeling"
|
||||
if(!config.objectives_disabled)
|
||||
forge_changeling_objectives(changeling)
|
||||
greet_changeling(changeling)
|
||||
..()
|
||||
return
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user