Merge branch 'master' of https://github.com/ParadiseSS13/Paradise into BookClub

This commit is contained in:
Crazylemon64
2017-01-06 05:01:43 -08:00
158 changed files with 4150 additions and 1040 deletions
+2 -1
View File
@@ -41,7 +41,8 @@ var/global/nologevent = 0
body += "<body>Options panel for <b>[M]</b>"
if(M.client)
body += " played by <b>[M.client]</b> "
body += "\[<A href='?_src_=holder;editrights=rank;ckey=[M.ckey]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\]"
body += "\[<A href='?_src_=holder;editrights=rank;ckey=[M.ckey]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\] "
body += "\[<A href='?_src_=holder;getplaytimewindow=[M.UID()]'>" + M.client.get_exp_living() + "</a>\]"
if(istype(M, /mob/new_player))
body += " <B>Hasn't Entered Game</B> "
+1
View File
@@ -74,6 +74,7 @@ var/list/admin_verbs_admin = list(
/client/proc/debug_variables,
/client/proc/show_snpc_verbs,
/client/proc/reset_all_tcs, /*resets all telecomms scripts*/
/client/proc/cmd_admin_check_player_exp, /* shows players by playtime */
/client/proc/toggle_mentor_chat
)
var/list/admin_verbs_ban = list(
+9
View File
@@ -1996,6 +1996,15 @@
fax_panel(usr)
else if(href_list["getplaytimewindow"])
if(!check_rights(R_ADMIN))
return
var/mob/M = locateUID(href_list["getplaytimewindow"])
if(!M)
to_chat(usr, "ERROR: Mob not found.")
return
cmd_show_exp_panel(M.client)
else if(href_list["jumpto"])
if(!check_rights(R_ADMIN)) return
@@ -0,0 +1,278 @@
/obj/machinery/computer/mob_battle_terminal
name = "Nano-Mob Hunter GO! Battle Terminal"
desc = "Insert a mob card to partake in life-like Nano-Mob Battle Action!"
icon_state = "mob_battle_empty"
icon_screen = null
icon_keyboard = null
density = 0
anchored = 1
var/obj/item/weapon/nanomob_card/card
var/datum/mob_hunt/mob_info
var/obj/effect/nanomob/battle/avatar
var/ready = 0
var/team = "Grey"
var/avatar_x_offset = 4
var/avatar_y_offset = 0
/obj/machinery/computer/mob_battle_terminal/red
pixel_y = 24
dir = SOUTH
team = "Red"
avatar_y_offset = -1
/obj/machinery/computer/mob_battle_terminal/blue
pixel_y = -24
dir = NORTH
team = "Blue"
avatar_y_offset = 1
/obj/machinery/computer/mob_battle_terminal/red/initialize()
..()
check_connection()
/obj/machinery/computer/mob_battle_terminal/blue/initialize()
..()
check_connection()
/obj/machinery/computer/mob_battle_terminal/update_icon()
if(card)
icon_state = "mob_battle_loaded"
else
icon_state = "mob_battle_empty"
..()
/obj/machinery/computer/mob_battle_terminal/Destroy()
eject_card(1)
if(mob_hunt_server)
if(mob_hunt_server.battle_turn)
mob_hunt_server.battle_turn = null
if(mob_hunt_server.red_terminal == src)
mob_hunt_server.red_terminal = null
if(mob_hunt_server.blue_terminal == src)
mob_hunt_server.blue_terminal = null
if(avatar)
qdel(avatar)
return ..()
/obj/machinery/computer/mob_battle_terminal/attackby(obj/item/O, mob/user)
if(istype(O, /obj/item/weapon/nanomob_card))
insert_card(O, user)
/obj/machinery/computer/mob_battle_terminal/proc/insert_card(obj/item/weapon/nanomob_card/new_card, mob/user)
if(!new_card)
return
if(card)
to_chat(user, "<span class='warning'>The card slot is currently filled.</span>")
return
if(!new_card.mob_data)
to_chat(user, "<span class='danger'>This is a blank mob card.</span>")
return
if(new_card.mob_data && !new_card.mob_data.cur_health)
to_chat(user, "<span class='warning'>This mob is incapacitated! Heal it before attempting to use it in battle!</span>")
return
user.unEquip(new_card)
new_card.forceMove(src)
card = new_card
mob_info = card.mob_data
update_icon()
update_avatar()
updateUsrDialog()
/obj/machinery/computer/mob_battle_terminal/proc/eject_card(override = 0)
if(!override)
if(ready && mob_hunt_server.battle_turn != team)
audible_message("You can't recall on your rival's turn!", null, 2)
return
card.mob_data = mob_info
mob_info = null
card.forceMove(get_turf(src))
card = null
update_avatar()
update_icon()
updateUsrDialog()
/obj/machinery/computer/mob_battle_terminal/proc/update_avatar()
//if we don't have avatars yet, spawn them
if(!avatar)
avatar = new(locate((x + avatar_x_offset), (y + avatar_y_offset), z))
//update avatar info from card
if(mob_info)
avatar.mob_info = mob_info
else
avatar.mob_info = null
//tell the avatar to update themself with the new info
avatar.update_self()
/obj/machinery/computer/mob_battle_terminal/attack_hand(mob/user)
add_fingerprint(user)
interact(user)
/obj/machinery/computer/mob_battle_terminal/attack_ai(mob/user)
to_chat(user, "<span class='warning'>You cannot interface with this portion of the simulation.</span>")
return
/obj/machinery/computer/mob_battle_terminal/interact(mob/user)
check_connection()
var/dat = ""
dat += "<table border='1' style='width:75%'>"
dat += "<tr>"
dat += "<td>"
dat += "[team] PLAYER"
dat += "</td>"
dat += "</tr>"
dat += "<tr>"
dat += "<td>"
if(!card)
dat += "<h1>No Nano-Mob card loaded.</h1>"
dat += "</td>"
dat += "</tr>"
if(ready && mob_hunt_server.battle_turn) //offer the surrender option if they are in a battle (ready), but don't have a card loaded
dat += "<tr>"
dat += "<td><a href='?src=[UID()];surrender=1'>Surrender!</a></td>"
dat += "</tr>"
else
dat += "<table>"
dat += "<tr>"
dat += "<td>"
dat += "<h1>[mob_info.mob_name]</h1>"
dat += "</td>"
if(mob_info.nickname)
dat += "<td rowspan='2'>"
else
dat += "<td>"
var/img_src = "[mob_info.icon_state_normal].png"
if(mob_info.is_shiny)
dat += "[mob_info.icon_state_shiny].png"
dat += "<img src='[img_src]'>"
dat += "</td>"
dat += "</tr>"
if(mob_info.nickname)
dat += "<tr>"
dat += "<td>"
dat += "<h2>[mob_info.nickname]</h2>"
dat += "</td>"
dat += "</tr>"
dat += "</table>"
dat += "<hr>"
dat += "Health: [mob_info.cur_health] / [mob_info.max_health]<br>"
dat += "<table border='1'>"
dat += "<tr>"
if(mob_info.cur_health)
dat += "<td><a href='?src=[UID()];attack=1'>Attack!</a></td>"
else
dat += "<td>Incapacitated!</td>"
dat += "<td><a href='?src=[UID()];eject=1'>Recall!</a></td>"
dat += "</tr>"
dat += "</table>"
dat += "</td>"
dat += "</tr>"
if(!ready)
dat += "<tr>"
dat += "<td><a href='?src=[UID()];ready=1'>Battle!</a></td>"
dat += "</tr>"
if(ready && !mob_hunt_server.battle_turn)
dat += "<tr>"
dat += "<td><a href='?src=[UID()];ready=2'>Cancel Battle!</a></td>"
dat += "</tr>"
dat += "</table>"
var/datum/browser/popup = new(user, "mob_battle_terminal", "Nano-Mob Hunter GO! Battle Terminal", 575, 400)
popup.set_content(dat)
popup.open()
/obj/machinery/computer/mob_battle_terminal/Topic(href, href_list)
if(..())
return 1
if(href_list["attack"])
do_attack()
if(href_list["eject"])
eject_card()
if(href_list["surrender"])
surrender()
if(href_list["ready"])
var/option = text2num(href_list["ready"])
if(option == 1)
start_battle()
else if(option == 2)
ready = 0
audible_message("[team] Player cancels their battle challenge.", null, 5)
updateUsrDialog()
/obj/machinery/computer/mob_battle_terminal/proc/check_connection()
if(team == "Red")
if(mob_hunt_server && !mob_hunt_server.red_terminal)
mob_hunt_server.red_terminal = src
else if(team == "Blue")
if(mob_hunt_server && !mob_hunt_server.blue_terminal)
mob_hunt_server.blue_terminal = src
/obj/machinery/computer/mob_battle_terminal/proc/do_attack()
if(!ready) //no attacking if you arent ready to fight (duh)
return
if(!mob_hunt_server || team != mob_hunt_server.battle_turn) //don't attack unless it is actually our turn
return
else
var/message = "[mob_info.mob_name] attacks!"
if(mob_info.nickname)
message = "[mob_info.nickname] attacks!"
audible_message(message, null, 5)
mob_hunt_server.launch_attack(team, mob_info.get_raw_damage(), mob_info.get_attack_type())
/obj/machinery/computer/mob_battle_terminal/proc/start_battle()
if(ready) //don't do anything if we are still ready
return
if(!card) //don't do anything if there isn't a card inserted
return
ready = 1
audible_message("[team] Player is ready for battle! Waiting for rival...", null, 5)
mob_hunt_server.start_check()
/obj/machinery/computer/mob_battle_terminal/proc/receive_attack(raw_damage, datum/mob_type/attack_type)
var/message = mob_info.take_damage(raw_damage, attack_type)
avatar.audible_message(message, null, 5)
if(!mob_info.cur_health)
mob_hunt_server.end_battle(team)
eject_card(1) //force the card out, they were defeated
else
mob_hunt_server.end_turn()
/obj/machinery/computer/mob_battle_terminal/proc/surrender()
audible_message("[team] Player surrenders the battle!", null, 5)
mob_hunt_server.end_battle(team, 1)
//////////////////////////////
// Mob Healing Terminal //
// (Pokemon Center) //
//////////////////////////////
/obj/machinery/computer/mob_healer_terminal
name = "Nano-Mob Hunter GO! Restoration Terminal"
desc = "Swipe a mob card to instantly restore it to full health!"
icon_state = "mob_battle_loaded"
icon_screen = null
icon_keyboard = null
density = 0
anchored = 1
dir = EAST
/obj/machinery/computer/mob_healer_terminal/attackby(obj/item/O, mob/user)
if(istype(O, /obj/item/weapon/nanomob_card))
heal_card(O, user)
/obj/machinery/computer/mob_healer_terminal/proc/heal_card(obj/item/weapon/nanomob_card/patient, mob/user)
if(!patient)
return
if(!patient.mob_data)
to_chat(user, "<span class='danger'>This is a blank mob card.</span>")
return
if(patient.mob_data && patient.mob_data.cur_health == patient.mob_data.max_health)
to_chat(user, "<span class='warning'>This mob is already at maximum health!</span>")
return
patient.mob_data.cur_health = patient.mob_data.max_health
to_chat(user, "<span class='notify'>[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!</span>")
+162
View File
@@ -0,0 +1,162 @@
/obj/effect/nanomob
name = "Nano-Mob Avatar" //will be overridden by the mob datum name value when created
desc = "A wild Nano-Mob appeared! Hit it with your PDA with the game open to attempt to capture it!"
invisibility = 101
alpha = 128
anchored = 1 //just in case
density = 0
icon = 'icons/effects/mob_hunt.dmi'
var/state_name
var/datum/mob_hunt/mob_info = null
var/list/clients_encountered = list() //tracks who has already interacted with us, so they can't attempt a second capture
var/image/avatar
/obj/effect/nanomob/New(loc, datum/mob_hunt/new_info)
..()
if(!new_info)
qdel(src)
return
mob_info = new_info
update_self()
forceMove(mob_info.spawn_point)
if(!mob_info.is_trap)
addtimer(src, "despawn", mob_info.lifetime)
/obj/effect/nanomob/proc/update_self()
if(!mob_info)
return
name = mob_info.mob_name
desc = "A wild [name] (level [mob_info.level]) appeared! Hit it with your PDA with the game open to attempt to capture it!"
if(mob_info.is_shiny)
state_name = mob_info.icon_state_shiny
else
state_name = mob_info.icon_state_normal
avatar = image(icon, src, state_name)
avatar.override = 1
add_alt_appearance("nanomob_avatar", avatar)
/obj/effect/nanomob/attackby(obj/item/O, mob/user)
if(istype(O, /obj/item/device/pda))
var/obj/item/device/pda/P = O
attempt_capture(P, -20) //attempting a melee capture reduces the mob's effective run_chance by 20% to balance the risk of triggering a trap mob
return 1
/obj/effect/nanomob/hitby(obj/item/O)
if(istype(O, /obj/item/device/pda))
var/obj/item/device/pda/P = O
attempt_capture(P) //attempting a ranged capture does not affect the mob's effective run_chance but does prevent you from being shocked by a trap mob
return 1
/obj/effect/nanomob/proc/attempt_capture(obj/item/device/pda/P, catch_mod = 0) //negative catch_mods lower effective run chance,
if(!P || !P.current_app || !istype(P.current_app, /datum/data/pda/app/mob_hunter_game) || !P.cartridge)
return
var/datum/data/pda/app/mob_hunter_game/client = P.current_app
var/total_catch_mod = client.catch_mod + catch_mod //negative values decrease the chance of the mob running, positive values makes it more likely to flee
if(!client.connected) //must be connected to attempt captures
P.audible_message("[bicon(P)] No server connection. Capture aborted.", null, 4)
return
if(mob_info.is_trap) //traps work even if you ran into them before, which is why this is before the clients_encountered check
if(client.hacked) //hacked copies of the game (copies capable of setting traps) are protected from traps
return
if(iscarbon(P.loc))
var/mob/living/carbon/C = P.loc
//Strike them down with a lightning bolt to complete the illusion (copied from the surge reagent overdose, probably could make this a general-use proc in the future)
playsound(get_turf(C), 'sound/effects/eleczap.ogg', 75, 1)
var/icon/I=new('icons/obj/zap.dmi',"lightningend")
I.Turn(-135)
var/obj/effect/overlay/beam/B = new(get_turf(C))
B.pixel_x = rand(-20, 0)
B.pixel_y = rand(-20, 0)
B.icon = I
//then actually do the damage/stun
C.electrocute_act(20, P, 1) //same damage as a revenant's overload ability, except subject to gloves/species shock resistance (for human mobs at least)
return
if(client in clients_encountered) //we've already dealt with you, go away!
return
else //deal with the new hunter by either running away or getting caught
clients_encountered += client
var/message = "[bicon(P)] "
var/effective_run_chance = mob_info.run_chance + total_catch_mod
if((effective_run_chance > 0) && prob(effective_run_chance))
message += "Capture failed! [name] escaped [P.owner ? "from [P.owner]" : "from this hunter"]!"
conceal(client)
else
if(client.register_capture(mob_info, 1))
message += "Capture success! [P.owner ? P.owner : "This hunter"] captured [name]!"
conceal(client)
else
message += "Capture error! Try again."
clients_encountered -= client //if the capture registration failed somehow, let them have another chance with this mob
P.audible_message(message, null, 4)
/obj/effect/nanomob/proc/despawn()
if(mob_hunt_server)
if(mob_info.is_trap)
mob_hunt_server.trap_spawns -= src
else
mob_hunt_server.normal_spawns -= src
qdel(src)
/obj/effect/nanomob/proc/reveal()
if(!mob_hunt_server)
return
var/list/show_to = list()
for(var/A in mob_hunt_server.connected_clients)
if((A in clients_encountered) || !mob_hunt_server.connected_clients[A])
continue
show_to |= mob_hunt_server.connected_clients[A]
display_alt_appearance("nanomob_avatar", show_to)
/obj/effect/nanomob/proc/conceal(list/hide_from)
if(!mob_hunt_server)
return
var/list/hiding_from = list()
if(hide_from)
hiding_from = hide_from
else
for(var/A in mob_hunt_server.connected_clients)
if((A in clients_encountered) && mob_hunt_server.connected_clients[A])
hiding_from |= mob_hunt_server.connected_clients[A]
hide_alt_appearance("nanomob_avatar", hiding_from)
// BATTLE MOB AVATARS
/obj/effect/nanomob/battle
name = "Nano-Mob Battle Avatar"
desc = "A new challenger approaches!"
invisibility = 0
icon_state = "placeholder"
var/obj/machinery/computer/mob_battle_terminal/my_terminal
/obj/effect/nanomob/battle/New(loc, datum/mob_hunt/new_info)
if(new_info)
mob_info = new_info
update_self()
/obj/effect/nanomob/battle/update_self()
if(!mob_info)
name = "Nano-Mob Battle Avatar"
desc = "A new challenger approaches"
icon_state = "placeholder"
else
name = mob_info.mob_name
desc = "A tamed [name] (level [mob_info.level]) ready for battle!"
if(mob_info.is_shiny)
icon_state = mob_info.icon_state_shiny
else
icon_state = mob_info.icon_state_normal
/obj/effect/nanomob/battle/attempt_capture(obj/item/device/pda/P, catch_mod = 0)
//you can't capture battle avatars, since they belong to someone already
return
//battle avatars are always visible, so we can ignore reveal and conceal calls for them
/obj/effect/nanomob/battle/reveal()
return
/obj/effect/nanomob/battle/conceal()
return
+43
View File
@@ -0,0 +1,43 @@
//Standard Cards
/obj/item/weapon/nanomob_card
name = "Nano-Mob Hunter Trading Card"
desc = "A blank Nano-Mob Hunter Trading Card. Worthless!"
icon = 'icons/obj/card.dmi'
icon_state = "trade_card"
force = 0
throwforce = 1
w_class = 1
var/datum/mob_hunt/mob_data
/obj/item/weapon/nanomob_card/proc/update_info()
if(!mob_data)
return
if(mob_data.is_shiny)
name = "Holographic [mob_data.mob_name] Nano-Mob Hunter Card"
desc = "WOW! A holographic trading card containing a level [mob_data.level] [mob_data.mob_name]!"
icon_state = "trade_card_holo"
else
name = "[mob_data.mob_name] Nano-Mob Hunter Card"
desc = "A trading card containing a level [mob_data.level] [mob_data.mob_name]!"
//Booster Pack Cards (random mob data)
/obj/item/weapon/nanomob_card/booster
name = "Nano-Mob Hunter Booster Pack Card"
desc = "A random Nano-Mob Trading Card from a Booster Pack. Wonder what it is?"
/obj/item/weapon/nanomob_card/booster/New()
var/datum/mob_hunt/mob_info = pick(subtypesof(/datum/mob_hunt))
mob_data = new mob_info(0,null,1)
update_info()
//Booster Packs (Box of booster pack cards)
/obj/item/weapon/storage/box/nanomob_booster_pack
name = "Nano-Mob Hunter Trading Card Booster Pack"
desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!"
can_hold = list("/obj/item/weapon/nanomob_card")
/obj/item/weapon/storage/box/nanomob_booster_pack/New()
..()
for(var/i in 1 to 6)
new /obj/item/weapon/nanomob_card/booster(src)
+501
View File
@@ -0,0 +1,501 @@
#define TYPE_FIRE /datum/mob_type/fire
#define TYPE_WATER /datum/mob_type/water
#define TYPE_GRASS /datum/mob_type/grass
#define TYPE_ELECTRIC /datum/mob_type/electric
#define TYPE_GROUND /datum/mob_type/ground
#define TYPE_ROCK /datum/mob_type/rock
#define TYPE_BUG /datum/mob_type/bug
#define TYPE_POISON /datum/mob_type/poison
#define TYPE_NORMAL /datum/mob_type/normal
#define TYPE_FIGHTING /datum/mob_type/fighting
#define TYPE_PSYCHIC /datum/mob_type/psychic
#define TYPE_GHOST /datum/mob_type/ghost
#define TYPE_ICE /datum/mob_type/ice
#define TYPE_FLYING /datum/mob_type/flying
#define TYPE_BLUESPACE /datum/mob_type/bluespace
#define TYPE_DARK /datum/mob_type/dark
#define TYPE_STEEL /datum/mob_type/steel
/datum/mob_hunt
//GENERAL STATS AND VARIABLES
var/mob_name = "Generic Mob" //the mob's original name (its species/type/whatever)
var/nickname = "" //the mob's nickname (if given by the owner)
var/run_chance = 0 //percent chance the mob will escape capture attempts (higher is obviously more likely to get away)
//COMBAT STATS AND VARIABLES
var/level = 0 //actual level of this mob (don't set this, it gets overwritten in New())
var/min_level = 1 //minimum level of this mob (used for randomizing the actual level)
var/max_level = 1 //maximum level of this mob (used for randomizing the actual level)
var/exp = 0 //number of battles the mob has won towards the next level (resets to 0 on level-up)
var/exp_to_level = 3 //number of battles the mob must win to level up (in case we want to make some mobs harder or easier to level)
//the types of the mob will be used for battles to determine damage resistance or weakness (mob_type_datums.dm)
var/datum/mob_type/primary_type //Make sure you set this or the mob will be unable to deal damage and will take absurd damage in battles
var/datum/mob_type/secondary_type //Don't set if not a dual-type mob so the mob will only calculate damage based on primary type
var/base_attack = 5 //base damage dealt by the mob's attacks for battling (effectively damage dealt at level 0)
var/base_health = 5 //base health of the mob for battling (effectively max health at level 0)
var/attack_multiplier = 1 //how much additional damage per level the mob deals (level * attack_multiplier)
var/health_multiplier = 1 //how much additional health per level the mob gets (level * health_multiplier) for calculating max health
var/cur_health = 0
var/max_health = 0
//SPAWN PREFERENCES AND VARIABLES
//A note on mob spawn preferences: The mob types also have preferences, which are handled prior to per-mob preferences, so ultimately you use a combined set of preferences
var/list/area_blacklist = list() //list of areas this mob can NOT spawn in (such as the bridge)
var/list/turf_blacklist = list() //list of turfs this mob can NOT spawn on (such as wood floors)
var/list/area_whitelist = list() //list of areas this mob is more likely to spawn in (can be used to reinclude subtypes of blacklisted areas)
var/list/turf_whitelist = list() //list of turfs this mob is more likely to spawn on (can be used to reinclude subtypes of blacklisted turfs)
var/turf/spawn_point //gets set and sent to the game server to spawn its avatar (generated in select_spawn or assigned via set_trap)
var/lifetime = 6000 //number of deciseconds the mob will remain before despawning (REMEMBER: DECISECONDS! So 6000 is 600 seconds which is 10 minutes)
var/is_shiny = 0 //if this gets set at spawn (super rare), the mob is considered "shiny" and will use the shiny icon_state and holographic cards
//the icon file for mob_hunt stuff is 'icons/effects/mob_hunt.dmi' so reference that for the following vars
var/icon_state_normal = "placeholder" //the icon_state for this mob's normal version
var/icon_state_shiny = "placeholder" //the icon_state for this mob's rare shiney version
var/is_trap = 0 //if this gets set, the mob is a booby-trap and will electrocute any players that dare attempt to catch it
/datum/mob_hunt/New(set_trap = 0, turf/trap_turf = null, no_register = 0)
if(set_trap)
level = max_level
is_trap = 1
spawn_point = trap_turf
else
level = rand(min_level, max_level)
if(prob(1) && prob(1))
is_shiny = 1
max_health = base_health + (level * health_multiplier)
cur_health = max_health
if(primary_type)
primary_type = new primary_type()
if(secondary_type)
secondary_type = new secondary_type()
if(no_register) //for booster pack cards
return
if(mob_hunt_server)
if(set_trap)
if(mob_hunt_server.register_trap(src))
return
else if(select_spawn())
if(mob_hunt_server.register_spawn(src))
return
qdel(src) //if you reach this, the datum is just pure clutter, so delete it
/datum/mob_hunt/proc/select_spawn()
var/list/possible_areas = get_possible_areas()
if(!possible_areas.len)
log_admin("No possible areas to spawn [type] found. Possible code/mapping error?")
return 0
while(possible_areas.len)
//randomly select an area from our possible_areas list to try spawning in, then remove it from possible_areas so it won't get picked over and over forever.
var/area/spawn_area = locate(pickweight(possible_areas))
possible_areas -= spawn_area
if(!spawn_area)
break
//clear and generate a fresh list of turfs in the selected area, weighted based on white/black lists
var/list/possible_turfs = get_possible_turfs(spawn_area)
if(!possible_turfs.len) //If we don't have any possible turfs, this attempt was a failure. Try again.
continue
//if we got this far, we're spawning on this attempt, hooray!
spawn_point = pickweight(possible_turfs)
break
if(!spawn_point)
//if we get to this, we failed every attempt to find a suitable turf for EVERY area in our list of possible areas. DAMN.
log_admin("No acceptable turfs to spawn [type] on could be located. Possible code/mapping error, or someone replaced/destroyed all the acceptable turf types?")
return 0
return 1
/datum/mob_hunt/proc/get_possible_areas()
var/list/possible_areas = list()
//setup, sets all station areas (and subtypes) to weight 1
for(var/A in the_station_areas)
if(A == /area/holodeck) //don't allow holodeck areas as possible spawns since it will allow it to spawn in the holodeck rooms on z2 as well
continue
if(A in possible_areas)
continue
for(var/areapath in typesof(A))
possible_areas[areapath] = 1
//primary type preferences
if(primary_type)
for(var/A in primary_type.area_whitelist)
for(var/areapath in typesof(A))
possible_areas[areapath] += 4
for(var/A in primary_type.area_blacklist)
for(var/areapath in typesof(A))
possible_areas[areapath] -= 2
//secondary type preferences
if(secondary_type)
for(var/A in secondary_type.area_whitelist)
for(var/areapath in typesof(A))
possible_areas[areapath] += 4
for(var/A in secondary_type.area_blacklist)
for(var/areapath in typesof(A))
possible_areas[areapath] -= 2
//mob preferences
for(var/A in area_whitelist)
for(var/areapath in typesof(A))
possible_areas[areapath] += 4
for(var/A in area_blacklist)
for(var/areapath in typesof(A))
possible_areas[areapath] -= 2
//removes "bad areas" which shouldn't be on-station but are subtypes of station areas. probably should the unused ones and consider repathing the rest
var/list/bad_areas = list(subtypesof(/area/construction), /area/solar/derelict_starboard, /area/solar/derelict_aft, /area/solar/constructionsite)
for(var/A in bad_areas)
possible_areas -= A
//weight check, remove negative or zero weight areas from the list, then return the list.
for(var/areapath in possible_areas)
//remove any areas that shouldn't be on the station-level
if(possible_areas[areapath] < 1)
possible_areas -= areapath
continue
return possible_areas
/datum/mob_hunt/proc/get_possible_turfs(area/spawn_area)
if(!spawn_area)
return list()
var/list/possible_turfs = list()
//setup, sets all turfs in spawn_area to weight 1
for(var/turf/T in spawn_area)
if(!is_station_level(T.z)) //mobs will only consider station-level turfs for spawning. Largely here so we won't have to worry about mapping errors or mobs on the derelict solars
continue
possible_turfs[T] = 1
//primary type preferences
if(primary_type)
if(is_type_in_list(T, primary_type.turf_whitelist))
possible_turfs[T] += 4
if(is_type_in_list(T, primary_type.turf_blacklist))
possible_turfs[T] -= 2
//secondary type preferences
if(secondary_type)
if(is_type_in_list(T, secondary_type.turf_whitelist))
possible_turfs[T] += 4
if(is_type_in_list(T, secondary_type.turf_blacklist))
possible_turfs[T] -= 2
//mob preferences
if(is_type_in_list(T, turf_whitelist))
possible_turfs[T] += 4
if(is_type_in_list(T, turf_blacklist))
possible_turfs[T] -= 2
//weight check, remove negative or zero weight turfs from the list, then return the list
if(possible_turfs[T] < 1)
possible_turfs -= T
return possible_turfs
/datum/mob_hunt/proc/calc_def_multiplier(datum/mob_type/attack_type)
if(!primary_type)
return 99 //typeless mobs are weak to everything since they shouldn't exist
if(!attack_type) //typeless attacks will return a multiplier of 1 in case we want to use this for calculating unmodified damage for some reason (UI maybe?)
return 1
var/multiplier = 1
if(attack_type in primary_type.immunity)
return 0 //a single immunity negates all damage
else if(attack_type in primary_type.resistance)
multiplier *= 0.5
else if(attack_type in primary_type.weakness)
multiplier *= 2
else
multiplier *= 1
if(!secondary_type) //if we don't have a second type, we're done here
return multiplier
if(attack_type in secondary_type.immunity)
return 0 //a single immunity negates all damage
else if(attack_type in secondary_type.resistance)
multiplier *= 0.5
else if(attack_type in secondary_type.weakness)
multiplier *= 2
else
multiplier *= 1
return multiplier
/datum/mob_hunt/proc/take_damage(raw_damage, datum/mob_type/attack_type)
var/message = ""
var/multiplier = calc_def_multiplier(attack_type)
var/total_damage = raw_damage * multiplier
if(!cur_health) //it's already downed, quit hitting it
return null
if(!total_damage)
message += "The attack is completely ineffective! "
else
cur_health = max(cur_health - total_damage, 0)
switch(multiplier)
if(0)
message += "The attack is completely ineffective! "
if(0.25)
message += "It's barely effective... "
if(0.5)
message += "It's not very effective... "
if(2)
message += "It's super effective! "
if(4)
message += "It's ultra effective! "
if(99)
message += pick("REKT! ", "DUNKED! ", "DEFENSE BREAK! ", "WOMBO-COMBO'D!")
if(!cur_health)
message += "[nickname ? nickname : mob_name] is downed!"
return message
/datum/mob_hunt/proc/get_raw_damage()
return (level * attack_multiplier)
/datum/mob_hunt/proc/get_attack_type()
var/datum/mob_type/attack_type = primary_type
if(secondary_type && prob(40))
attack_type = secondary_type
return attack_type
/datum/mob_hunt/proc/gain_exp()
exp++
var/message = "[nickname ? nickname : mob_name] gained EXP! ([exp] / [exp_to_level] EXP)"
if(exp >= exp_to_level)
message = levelup()
return message
/datum/mob_hunt/proc/levelup()
var/message = ""
level++
exp = 0
if(level > max_level) //This is where we would trigger an evolution, when those are added (need to add evolved forms first)
level = max_level //for now though, we'll just cap them back at their max_level
message += "[nickname ? nickname : mob_name] can't get any stronger right now!"
else
max_health = base_health + (level * health_multiplier)
cur_health = max_health //full heal on level-up
message += "[nickname ? nickname : mob_name] has reached level [level]!"
return message
/datum/mob_hunt/proc/get_type1()
if(!primary_type)
return "Typeless"
else
return primary_type.name
/datum/mob_hunt/proc/get_type2()
if(!secondary_type)
return null
else
return secondary_type.name
/datum/mob_hunt/nemabug
mob_name = "Nemabug"
run_chance = 50
min_level = 1
max_level = 10
primary_type = TYPE_BUG
icon_state_normal = "nemabug"
icon_state_shiny = "nemabug_shiny"
lifetime = 6000
/datum/mob_hunt/stoutquill
mob_name = "Stoutquill"
run_chance = 50
min_level = 5
max_level = 15
primary_type = TYPE_ICE
icon_state_normal = "stoutquill"
icon_state_shiny = "stoutquill_shiny"
lifetime = 4500
/datum/mob_hunt/spectra
mob_name = "Spectra"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_POISON
icon_state_normal = "spectra"
icon_state_shiny = "spectra_shiny"
lifetime = 6000
/datum/mob_hunt/dunny
mob_name = "Dunny"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_FIRE
icon_state_normal = "dunny"
icon_state_shiny = "dunny_shiny"
lifetime = 6000
/datum/mob_hunt/buffsel
mob_name = "Buffsel"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_ROCK
icon_state_normal = "buffsel"
icon_state_shiny = "buffsel_shiny"
lifetime = 6000
/datum/mob_hunt/quarrel
mob_name = "Quarrel"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_NORMAL
icon_state_normal = "quarrel"
icon_state_shiny = "quarrel_shiny"
lifetime = 6000
/datum/mob_hunt/vulerrt
mob_name = "Vulerrt"
run_chance = 50
min_level = 5
max_level = 15
primary_type = TYPE_DARK
icon_state_normal = "vulerrt"
icon_state_shiny = "vulerrt_shiny"
turf_whitelist = list()
lifetime = 4500
/datum/mob_hunt/strudel
mob_name = "Strudel"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_ELECTRIC
icon_state_normal = "strudel"
icon_state_shiny = "strudel_shiny"
lifetime = 4500
/datum/mob_hunt/folstick
mob_name = "Folstick"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_WATER
icon_state_normal = "folstick"
icon_state_shiny = "folstick_shiny"
lifetime = 6000
/datum/mob_hunt/glimmerflare
mob_name = "Glimmerflare"
run_chance = 50
min_level = 5
max_level = 15
primary_type = TYPE_PSYCHIC
icon_state_normal = "glimmerflare"
icon_state_shiny = "glimmerflare_shiny"
lifetime = 4500
/datum/mob_hunt/leecoon
mob_name = "Leecoon"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_GRASS
icon_state_normal = "leecoon"
icon_state_shiny = "leecoon_shiny"
lifetime = 6000
/datum/mob_hunt/halk
mob_name = "Halk"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_FLYING
icon_state_normal = "halk"
icon_state_shiny = "halk_shiny"
lifetime = 6000
/datum/mob_hunt/gooby
mob_name = "Gooby"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_ELECTRIC
secondary_type = TYPE_BUG
icon_state_normal = "gooby"
icon_state_shiny = "gooby_shiny"
lifetime = 3000
/datum/mob_hunt/pandoom
mob_name = "Pandoom"
run_chance = 50
min_level = 5
max_level = 15
primary_type = TYPE_GHOST
icon_state_normal = "pandoom"
icon_state_shiny = "pandoom_shiny"
lifetime = 4500
/datum/mob_hunt/relish
mob_name = "Relish"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_FIRE
secondary_type = TYPE_GROUND
icon_state_normal = "relish"
icon_state_shiny = "relish_shiny"
lifetime = 3000
/datum/mob_hunt/xofine
mob_name = "Xofine"
run_chance = 50
min_level = 5
max_level = 10
primary_type = TYPE_FIRE
secondary_type = TYPE_NORMAL
icon_state_normal = "xofine"
icon_state_shiny = "xofine_shiny"
lifetime = 3000
/datum/mob_hunt/gitten
mob_name = "Gitten"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_WATER
secondary_type = TYPE_POISON
icon_state_normal = "gitten"
icon_state_shiny = "gitten_shiny"
lifetime = 3000
/datum/mob_hunt/nai
mob_name = "Nai"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_GRASS
secondary_type = TYPE_NORMAL
icon_state_normal = "nai"
icon_state_shiny = "nai_shiny"
lifetime = 3000
/datum/mob_hunt/pyroghast
mob_name = "Pyroghast"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_FIRE
secondary_type = TYPE_GHOST
icon_state_normal = "pyroghast"
icon_state_shiny = "pyroghast"
lifetime = 4500
/datum/mob_hunt/starslam
mob_name = "Starslam"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_FIGHTING
secondary_type = TYPE_ICE
icon_state_normal = "starslam"
icon_state_shiny = "starslam_shiny"
lifetime = 2500
/datum/mob_hunt/pheron
mob_name = "Pheron"
run_chance = 85
min_level = 10
max_level = 20
primary_type = TYPE_BLUESPACE
icon_state_normal = "pheron"
icon_state_shiny = "pheron_shiny"
area_blacklist = list()
turf_blacklist = list()
area_whitelist = list()
turf_whitelist = list()
lifetime = 2000
@@ -0,0 +1,256 @@
/datum/mob_type
var/name = "Typeless"
var/list/weakness = list()
var/list/resistance = list()
var/list/immunity = list()
//Type-based spawn preferences (to eliminate copy-pasting the same area and turf lists on each mob type)
var/list/area_blacklist = list() //areas to be avoided
var/list/area_whitelist = list() //areas to be more preferred
var/list/turf_blacklist = list() //turf types to be avoided
var/list/turf_whitelist = list() //turf types to be more preferred
//Type defines, to avoid spelling mistakes
/datum/mob_type/fire
name = "Fire"
weakness = list(TYPE_WATER,
TYPE_ROCK,
TYPE_GROUND)
resistance = list(TYPE_BUG,
TYPE_FIRE,
TYPE_GRASS,
TYPE_ICE,
TYPE_STEEL)
area_blacklist = list(/area/crew_quarters/toilet,
/area/crew_quarters/sleep_male/toilet_male,
/area/crew_quarters/sleep_female/toilet_female,
/area/crew_quarters/locker/locker_toilet,
/area/toxins/server_coldroom)
area_whitelist = list(/area/maintenance/turbine,
/area/maintenance/incinerator,
/area/crew_quarters/kitchen)
turf_blacklist = list(/turf/simulated/floor/beach/water)
/datum/mob_type/water
name = "Water"
weakness = list(TYPE_ELECTRIC,
TYPE_GRASS)
resistance = list(TYPE_FIRE,
TYPE_ICE,
TYPE_STEEL,
TYPE_WATER)
area_blacklist = list(/area/maintenance/turbine,
/area/maintenance/incinerator,
/area/crew_quarters/kitchen)
area_whitelist = list(/area/crew_quarters/toilet,
/area/crew_quarters/sleep_male/toilet_male,
/area/crew_quarters/sleep_female/toilet_female,
/area/crew_quarters/locker/locker_toilet)
turf_whitelist = list(/turf/simulated/floor/beach/water)
/datum/mob_type/grass
name = "Grass"
weakness = list(TYPE_FIRE,
TYPE_BUG,
TYPE_POISON,
TYPE_ICE,
TYPE_FLYING)
resistance = list(TYPE_WATER,
TYPE_GRASS,
TYPE_ELECTRIC,
TYPE_GROUND)
area_blacklist = list(/area/toxins)
area_whitelist = list(/area/hydroponics,
/area/hallway/secondary/construction)
turf_whitelist = list(/turf/simulated/floor/grass)
/datum/mob_type/electric
name = "Electric"
weakness = list(TYPE_GROUND)
resistance = list(TYPE_ELECTRIC,
TYPE_FLYING,
TYPE_STEEL)
area_whitelist = list(/area/engine,
/area/toxins/server,
/area/maintenance,
/area/turret_protected/ai,
/area/turret_protected/ai_upload,
/area/turret_protected/aisat_interior,
/area/aisat,
/area/assembly)
turf_whitelist = list(/turf/simulated/floor/bluegrid)
/datum/mob_type/ground
name = "Ground"
weakness = list(TYPE_WATER,
TYPE_GRASS,
TYPE_ICE)
resistance = list(TYPE_ROCK,
TYPE_POISON)
immunity = list(TYPE_ELECTRIC)
/datum/mob_type/rock
name = "Rock"
weakness = list(TYPE_WATER,
TYPE_GRASS,
TYPE_GROUND,
TYPE_FIGHTING,
TYPE_STEEL)
resistance = list(TYPE_FIRE,
TYPE_FLYING,
TYPE_POISON,
TYPE_NORMAL)
area_whitelist = list(/area/quartermaster,
/area/maintenance/disposal)
turf_whitelist = list(/turf/simulated/wall,
/turf/simulated/floor/mineral)
/datum/mob_type/bug
name = "Bug"
weakness = list(TYPE_FIRE,
TYPE_ROCK,
TYPE_FLYING)
resistance = list(TYPE_GRASS,
TYPE_GROUND,
TYPE_FIGHTING)
area_blacklist = list(/area/toxins)
area_whitelist = list(/area/hydroponics,
/area/hallway/secondary/construction)
turf_whitelist = list(/turf/simulated/floor/grass)
/datum/mob_type/poison
name = "Poison"
weakness = list(TYPE_GROUND,
TYPE_PSYCHIC)
resistance = list(TYPE_GRASS,
TYPE_BUG,
TYPE_POISON,
TYPE_FIGHTING)
area_blacklist = list(/area/medical,
/area/security/medbay,
/area/janitor)
area_whitelist = list(/area/medical/virology,
/area/toxins,
/area/medical/research,
/area/medical/research_shuttle_dock,
/area/crew_quarters/hor,
/area/maintenance/asmaint2)
/datum/mob_type/normal
name = "Normal"
weakness = list(TYPE_FIGHTING)
immunity = list(TYPE_GHOST)
/datum/mob_type/fighting
name = "Fighting"
weakness = list(TYPE_PSYCHIC,
TYPE_FLYING)
resistance = list(TYPE_ROCK,
TYPE_BUG,
TYPE_DARK)
area_blacklist = list(/area/medical)
area_whitelist = list(/area/crew_quarters/bar,
/area/crew_quarters/fitness,
/area/security)
turf_whitelist = list(/turf/simulated/floor/wood)
/datum/mob_type/psychic
name = "Psychic"
weakness = list(TYPE_BUG,
TYPE_GHOST,
TYPE_DARK)
resistance = list(TYPE_FIGHTING,
TYPE_PSYCHIC)
area_blacklist = list(/area/toxins,
/area/medical/research,
/area/medical/research_shuttle_dock,
/area/crew_quarters/hor,
/area/maintenance/asmaint2,
/area/teleporter,
/area/gateway)
area_whitelist = list(/area/library,
/area/chapel,
/area/medical/psych)
/datum/mob_type/ghost
name = "Ghost"
weakness = list(TYPE_GHOST,
TYPE_DARK)
resistance = list(TYPE_BUG,
TYPE_POISON)
immunity = list(TYPE_NORMAL,
TYPE_FIGHTING)
area_whitelist = list(/area/medical/morgue,
/area/chapel,
/area/medical/genetics_cloning)
/datum/mob_type/ice
name = "Ice"
weakness = list(TYPE_FIRE,
TYPE_ROCK,
TYPE_FIGHTING,
TYPE_STEEL)
resistance = list(TYPE_ICE)
area_blacklist = list(/area/maintenance/turbine,
/area/maintenance/incinerator,
/area/crew_quarters/kitchen)
area_whitelist = list(/area/toxins/server_coldroom)
/datum/mob_type/flying
name = "Flying"
weakness = list(TYPE_ELECTRIC,
TYPE_ROCK,
TYPE_ICE)
resistance = list(TYPE_GRASS,
TYPE_BUG,
TYPE_FIGHTING)
immunity = list(TYPE_GROUND)
area_blacklist = list(/area/maintenance)
area_whitelist = list(/area/hallway,
/area/escapepodbay,
/area/engine/mechanic_workshop,
/area/security/podbay)
/datum/mob_type/bluespace
name = "Bluespace"
weakness = list(TYPE_ICE,
TYPE_BLUESPACE)
resistance = list(TYPE_FIRE,
TYPE_WATER,
TYPE_GRASS,
TYPE_ELECTRIC)
/datum/mob_type/dark
name = "Dark"
weakness = list(TYPE_BUG,
TYPE_FIGHTING)
resistance = list(TYPE_GHOST,
TYPE_DARK)
immunity = list(TYPE_PSYCHIC)
area_blacklist = list(/area/solar,
/area/maintenance/auxsolarport,
/area/maintenance/starboardsolar,
/area/maintenance/portsolar,
/area/maintenance/auxsolarstarboard,
/area/clownoffice)
area_whitelist = list(/area/maintenance,
/area/assembly/assembly_line,
/area/mimeoffice)
turf_blacklist = list(/turf/simulated/floor/light)
/datum/mob_type/steel
name = "Steel"
weakness = list(TYPE_FIRE,
TYPE_GROUND,
TYPE_FIGHTING)
resistance = list(TYPE_GRASS,
TYPE_ROCK,
TYPE_BUG,
TYPE_NORMAL,
TYPE_PSYCHIC,
TYPE_ICE,
TYPE_FLYING,
TYPE_BLUESPACE,
TYPE_STEEL)
immunity = list(TYPE_POISON)
+6
View File
@@ -190,6 +190,12 @@ var/global/datum/prizes/global_prizes = new
typepath = /obj/item/weapon/spellbook/oneuse/fake_gib
cost = 100
/datum/prize_item/nanomob_booster
name = "Nano-Mob Hunter Trading Card Booster Pack"
desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!"
typepath = /obj/item/weapon/storage/box/nanomob_booster_pack
cost = 100
/datum/prize_item/capgun
name = "Capgun Revolver"
desc = "Do you feel lucky... punk?"
@@ -24,7 +24,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/artillerycontrol/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/artillerycontrol/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/time_to_wait = round(reload_cooldown - ((world.time / 10) - last_fire), 1)
+24 -16
View File
@@ -9,14 +9,14 @@ var/obj/machinery/gateway/centerstation/the_gateway = null
unacidable = 1
var/active = 0
/obj/machinery/gateway/initialize()
..()
update_icon()
update_density_from_dir()
/obj/machinery/gateway/New()
if(!the_gateway)
the_gateway = src
spawn(25)
update_icon()
if(dir == 2)
density = 0
/obj/machinery/gateway/proc/update_density_from_dir()
if(dir == 2)
density = 0
/obj/machinery/gateway/update_icon()
@@ -40,13 +40,18 @@ var/obj/machinery/gateway/centerstation/the_gateway = null
var/obj/machinery/gateway/centeraway/awaygate = null
/obj/machinery/gateway/centerstation/New()
..()
if(!the_gateway)
the_gateway = src
spawn(25)
update_icon()
wait = world.time + config.gateway_delay //+ thirty minutes default
awaygate = locate(/obj/machinery/gateway/centeraway) in world
/obj/machinery/gateway/centerstation/initialize()
..()
update_icon()
wait = world.time + config.gateway_delay //+ thirty minutes default
awaygate = locate(/obj/machinery/gateway/centeraway) in world
/obj/machinery/gateway/centerstation/update_density_from_dir()
return
/obj/machinery/gateway/centerstation/Destroy()
if(the_gateway == src)
@@ -61,7 +66,7 @@ var/obj/machinery/gateway/centerstation/the_gateway = null
obj/machinery/gateway/centerstation/process()
/obj/machinery/gateway/centerstation/process()
if(stat & (NOPOWER))
if(active) toggleoff()
return
@@ -165,12 +170,15 @@ obj/machinery/gateway/centerstation/process()
var/obj/machinery/gateway/centeraway/stationgate = null
/obj/machinery/gateway/centeraway/New()
spawn(25)
update_icon()
stationgate = locate(/obj/machinery/gateway/centerstation) in world
/obj/machinery/gateway/centeraway/initialize()
..()
update_icon()
stationgate = locate(/obj/machinery/gateway/centerstation) in world
/obj/machinery/gateway/centeraway/update_density_from_dir()
return
/obj/machinery/gateway/centeraway/update_icon()
if(active)
icon_state = "oncenter"
+20 -1
View File
@@ -261,6 +261,8 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
send_asset_list(client, uncommon)
send_asset_list(client, common)
//Pill sprites for UIs
/datum/asset/chem_master
var/assets = list()
var/verify = FALSE
@@ -274,4 +276,21 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
register_asset(asset_name, assets[asset_name])
/datum/asset/chem_master/send(client)
send_asset_list(client,assets,verify)
send_asset_list(client, assets, verify)
//Mob Hunt sprites for UIs
/datum/asset/mob_hunt
var/assets = list()
var/verify = FALSE
/datum/asset/mob_hunt/register()
for(var/state in icon_states('icons/effects/mob_hunt.dmi'))
if(state == "Placeholder")
continue
assets["[state].png"] = icon('icons/effects/mob_hunt.dmi', state)
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
/datum/asset/mob_hunt/send(client)
send_asset_list(client, assets, verify)
+146 -24
View File
@@ -111,49 +111,49 @@
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Barber",5)
DB_job_unlock("Barber",5)
return
if("2")
if(karma <5)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Brig Physician",5)
DB_job_unlock("Brig Physician",5)
return
if("3")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Nanotrasen Representative",30)
DB_job_unlock("Nanotrasen Representative",30)
return
if("5")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Blueshield",30)
DB_job_unlock("Blueshield",30)
return
if("6")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Mechanic",30)
DB_job_unlock("Mechanic",30)
return
if("7")
if(karma <45)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Magistrate",45)
DB_job_unlock("Magistrate",45)
return
if("9")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Security Pod Pilot",30)
DB_job_unlock("Security Pod Pilot",30)
return
if(href_list["KarmaBuy2"])
var/karma=verify_karma()
@@ -163,55 +163,55 @@
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Machine",15)
DB_species_unlock("Machine",15)
return
if("2")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Kidan",30)
DB_species_unlock("Kidan",30)
return
if("3")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Grey",30)
DB_species_unlock("Grey",30)
return
if("4")
if(karma <45)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Vox",45)
DB_species_unlock("Vox",45)
return
if("5")
if(karma <45)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Slime People",45)
DB_species_unlock("Slime People",45)
return
if("6")
if(karma <100)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Plasmaman",100)
DB_species_unlock("Plasmaman",100)
return
if("7")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Drask",30)
DB_species_unlock("Drask",30)
return
if(href_list["KarmaRefund"])
var/type = href_list["KarmaRefundType"]
var/job = href_list["KarmaRefund"]
var/cost = href_list["KarmaRefundCost"]
src.karmarefund(type,job,cost)
karmarefund(type,job,cost)
return
switch(href_list["_src_"])
@@ -273,6 +273,7 @@
//CONNECT//
///////////
/client/New(TopicData)
var/tdata = TopicData //save this for later use
chatOutput = new /datum/chatOutput(src) // Right off the bat.
TopicData = null //Prevent calls to client.Topic from connect
@@ -290,8 +291,8 @@
// Change the way they should download resources.
if(config.resource_urls)
src.preload_rsc = pick(config.resource_urls)
else src.preload_rsc = 1 // If config.resource_urls is not set, preload like normal.
preload_rsc = pick(config.resource_urls)
else preload_rsc = 1 // If config.resource_urls is not set, preload like normal.
to_chat(src, "\red If the title screen is black, resources are still downloading. Please be patient until the title screen appears.")
@@ -340,7 +341,7 @@
winset(src, null, "command=\".configure graphics-hwmode off\"")
winset(src, null, "command=\".configure graphics-hwmode on\"")
log_client_to_db()
log_client_to_db(tdata)
if(ckey in clientmessages)
for(var/message in clientmessages[ckey])
@@ -399,7 +400,7 @@
donator_level = text2num(query_donor_select.item[2])
break
/client/proc/log_client_to_db()
/client/proc/log_client_to_db(connectiontopic)
if(IsGuestKey(key))
return
@@ -430,6 +431,14 @@
if(ckey != query_cid.item[1])
related_accounts_cid.Add("[query_cid.item[1]]")
var/admin_rank = "Player"
if(holder)
admin_rank = holder.rank
// Admins don't get slammed by this, I guess
else
if(check_randomizer(connectiontopic))
return
//Log all the alts
if(related_accounts_cid.len)
log_access("Alts: [key_name(src)]:[jointext(related_accounts_cid, " - ")]")
@@ -446,10 +455,6 @@
if(!isnum(sql_id))
return
var/admin_rank = "Player"
if(src.holder)
admin_rank = src.holder.rank
var/sql_ip = sanitizeSQL(address)
var/sql_computerid = sanitizeSQL(computer_id)
var/sql_admin_rank = sanitizeSQL(admin_rank)
@@ -469,11 +474,128 @@
var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[ckey]','[sql_ip]','[sql_computerid]');")
query_accesslog.Execute()
#undef TOPIC_SPAM_DELAY
#undef UPLOAD_LIMIT
#undef MIN_CLIENT_VERSION
// Returns true if a randomizer is being used
/client/proc/check_randomizer(topic)
. = FALSE
if(connection != "seeker") //Invalid connection type.
return null
topic = params2list(topic)
if(!config.check_randomizer)
return
// Stash o' ckeys
var/static/cidcheck = list()
var/static/tokens = list()
// Ckeys that failed the test, stored to send acceptance messages only for atoners
var/static/cidcheck_failedckeys = list()
var/static/cidcheck_spoofckeys = list()
var/oldcid = cidcheck[ckey]
if(!oldcid)
var/DBQuery/query_cidcheck = dbcon.NewQuery("SELECT computerid FROM [format_table_name("player")] WHERE ckey = '[ckey]'")
query_cidcheck.Execute()
var/lastcid = computer_id
if(query_cidcheck.NextRow())
lastcid = query_cidcheck.item[1]
if(computer_id != lastcid)
// Their current CID does not match what the DB says - OFF WITH THEIR HEAD
cidcheck[ckey] = computer_id
// Disable the reconnect button to force a CID change
winset(src, "reconnectbutton", "is-disable=true")
tokens[ckey] = cid_check_reconnect()
sleep(10) // Since browse is non-instant, and kinda async
to_chat(src, "<pre class=\"system system\">you're a huge nerd. wakka wakka doodle doop nobody's ever gonna see this, the chat system shouldn't be online by this point</pre>")
del(src)
return TRUE
else
if (!topic || !topic["token"] || !tokens[ckey] || topic["token"] != tokens[ckey])
if (!cidcheck_spoofckeys[ckey])
message_admins("<span class='adminnotice'>[key_name(src)] appears to have attempted to spoof a cid randomizer check.</span>")
cidcheck_spoofckeys[ckey] = TRUE
cidcheck[ckey] = computer_id
tokens[ckey] = cid_check_reconnect()
sleep(10) //browse is queued, we don't want them to disconnect before getting the browse() command.
del(src)
return TRUE
// We DO have their cached CID handy - compare it, now
if(oldcid != computer_id)
// Change detected, they are randomizing
cidcheck -= ckey // To allow them to try again after removing CID randomization
to_chat(src, "<span class='userdanger'>Connection Error:</span>")
to_chat(src, "<span class='danger'>Invalid ComputerID(spoofed). Please remove the ComputerID spoofer from your BYOND installation and try again.</span>")
if(!cidcheck_failedckeys[ckey])
message_admins("<span class='adminnotice'>[key_name(src)] has been detected as using a CID randomizer. Connection rejected.</span>")
send2irc(config.cidrandomizer_irc, "[key_name(src)] has been detected as using a CID randomizer. Connection rejected.")
cidcheck_failedckeys[ckey] = TRUE
note_randomizer_user()
log_access("Failed Login: [key] [computer_id] [address] - CID randomizer confirmed (oldcid: [oldcid])")
del(src)
return TRUE
else
// don't shoot, I'm innocent
if(cidcheck_failedckeys[ckey])
// Atonement
message_admins("<span class='adminnotice'>[key_name_admin(src)] has been allowed to connect after showing they removed their cid randomizer</span>")
send2irc(config.cidrandomizer_irc, "[key_name(src)] has been allowed to connect after showing they removed their cid randomizer.")
cidcheck_failedckeys -= ckey
if (cidcheck_spoofckeys[ckey])
message_admins("<span class='adminnotice'>[key_name_admin(src)] has been allowed to connect after appearing to have attempted to spoof a cid randomizer check because it <i>appears</i> they aren't spoofing one this time</span>")
cidcheck_spoofckeys -= ckey
cidcheck -= ckey
/client/proc/note_randomizer_user()
var/const/adminckey = "CID-Error"
// Check for notes in the last day - only 1 note per 24 hours
var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT id from [format_table_name("notes")] WHERE ckey = '[ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW()")
if(!query_get_notes.Execute())
var/err = query_get_notes.ErrorMsg()
log_game("SQL ERROR obtaining id from notes table. Error : \[[err]\]\n")
return
if(query_get_notes.NextRow())
return
// Only add a note if their most recent note isn't from the randomizer blocker, either
query_get_notes = dbcon.NewQuery("SELECT adminckey FROM [format_table_name("notes")] WHERE ckey = '[ckey]' ORDER BY timestamp DESC LIMIT 1")
if(!query_get_notes.Execute())
var/err = query_get_notes.ErrorMsg()
log_game("SQL ERROR obtaining adminckey from notes table. Error : \[[err]\]\n")
return
if(query_get_notes.NextRow())
if(query_get_notes.item[1] == adminckey)
return
add_note(ckey, "Detected as using a cid randomizer.", null, adminckey, logged = 0)
/client/proc/cid_check_reconnect()
var/token = md5("[rand(0,9999)][world.time][rand(0,9999)][ckey][rand(0,9999)][address][rand(0,9999)][computer_id][rand(0,9999)]")
. = token
log_access("Failed Login: [key] [computer_id] [address] - CID randomizer check")
var/url = winget(src, null, "url")
//special javascript to make them reconnect under a new window.
src << browse("<a id='link' href='byond://[url]?token=[token]'>\
byond://[url]?token=[token]\
</a>\
<script type='text/javascript'>\
document.getElementById(\"link\").click();\
window.location=\"byond://winset?command=.quit\"\
</script>",
"border=0;titlebar=0;size=1x1")
to_chat(src, "<a href='byond://[url]?token=[token]'>You will be automatically taken to the game, if not, click here to be taken manually</a>. Except you can't, since the chat window doesn't exist yet.")
//checks if a client is afk
//3000 frames = 5 minutes
/client/proc/is_afk(duration=3000)
@@ -74,3 +74,8 @@
/datum/gear/skullbandana
display_name = "bandana, skull"
path = /obj/item/clothing/mask/bandana/skull
/datum/gear/mob_hunt_game
display_name = "Nano-Mob Hunter GO! Cartridge"
path = /obj/item/weapon/cartridge/mob_hunt_game
cost = 2
@@ -82,6 +82,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
//game-preferences
var/lastchangelog = "" //Saved changlog filesize to detect if there was a change
var/exp
var/ooccolor = "#b82e00"
var/be_special = list() //Special role selection
var/UI_style = "Midnight"
@@ -593,6 +594,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
if(jobban_isbanned(user, rank))
HTML += "<del>[rank]</del></td><td><b> \[BANNED]</b></td></tr>"
continue
var/available_in_playtime = job.available_in_playtime(user.client)
if(available_in_playtime)
HTML += "<del>[rank]</del></td><td> \[ " + get_exp_format(available_in_playtime) + " as " + job.get_exp_req_type() + "</td></tr>"
continue
if(!job.player_old_enough(user.client))
var/available_in_days = job.available_in_days(user.client)
HTML += "<del>[rank]</del></td><td> \[IN [(available_in_days)] DAYS]</td></tr>"
@@ -13,7 +13,8 @@
volume,
nanoui_fancy,
show_ghostitem_attack,
lastchangelog
lastchangelog,
exp
FROM [format_table_name("player")]
WHERE ckey='[C.ckey]'"}
)
@@ -40,6 +41,7 @@
nanoui_fancy = text2num(query.item[11])
show_ghostitem_attack = text2num(query.item[12])
lastchangelog = query.item[13]
exp = query.item[14]
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
@@ -54,6 +56,7 @@
nanoui_fancy = sanitize_integer(nanoui_fancy, 0, 1, initial(nanoui_fancy))
show_ghostitem_attack = sanitize_integer(show_ghostitem_attack, 0, 1, initial(show_ghostitem_attack))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
exp = sanitize_text(exp, initial(exp))
return 1
/datum/preferences/proc/save_preferences(client/C)
+1 -1
View File
@@ -154,7 +154,7 @@
var/invis_override = 0
var/emagged = 0
var/color_view = null//overrides client.color while worn
var/list/color_view = null//overrides client.color while worn
var/prescription = 0
var/prescription_upgradable = 0
strip_delay = 20 // but seperated to allow items to protect but not impair vision, like space helmets
+15 -6
View File
@@ -243,26 +243,35 @@
return 1
/obj/item/clothing/glasses/sunglasses/noir/proc/toggle_noir()
var/list/difference = difflist(usr.client.color, color_view)
if(!noir_mode)
if(color_view && usr.client && !usr.client.color)
if(color_view && usr.client && (!usr.client.color || difference))
animate(usr.client, color = color_view, time = 10)
noir_mode = 1
else
if(usr.client && usr.client.color)
animate(usr.client, color = null, time = 10)
if(usr.client && usr.client.color && !difference)
animate(usr.client, color = initial(usr.client.color), time = 10)
noir_mode = 0
/obj/item/clothing/glasses/sunglasses/noir/equipped(mob/user, slot)
var/list/difference = difflist(user.client.color, color_view)
if(slot == slot_glasses)
if(noir_mode)
if(color_view && user.client && !user.client.color)
if(color_view && user.client && (!user.client.color || difference.len))
animate(user.client, color = color_view, time = 10)
else
if(user.client && user.client.color && !difference.len)
animate(user.client, color = initial(user.client.color), time = 10)
..(user, slot)
/obj/item/clothing/glasses/sunglasses/noir/dropped(mob/living/carbon/human/user)
var/list/difference = difflist(user.client.color, color_view)
if(istype(user) && user.glasses == src)
if(user.client && user.client.color)
animate(user.client, color = null, time = 10)
if(user.client && user.client.color && !difference.len)
animate(user.client, color = initial(user.client.color), time = 10)
..(user)
/obj/item/clothing/glasses/sunglasses/yeah
+1 -1
View File
@@ -514,7 +514,7 @@
ui.open()
ui.set_auto_update(1)
/obj/item/weapon/rig/ui_data(mob/user, datum/topic_state/state = inventory_state)
/obj/item/weapon/rig/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state)
var/data[0]
data["primarysystem"] = null
+5
View File
@@ -313,6 +313,11 @@
icon_state = "eyepro"
item_state = "eyepro"
/obj/item/clothing/glasses/hud/security/sunglasses/fluff/voxxyhud //LP Spartan: Kaskreyarawkta
name = "VoxxyHUD"
desc = "A worn down visor from a vox raider's gear, crudely ripped from its helmet and linked into the security systems of the station. The word 'Kask' is scratched into the side."
icon_state = "hud-spartan"
//////////// Hats ////////////
/obj/item/clothing/head/fluff/heather_winceworth // Regens: Heather Winceworth
name= "Heather's rose"
+1 -1
View File
@@ -83,7 +83,7 @@
ui = new(user, src, ui_key, "accounts_terminal.tmpl", src.name, 400, 640)
ui.open()
/obj/machinery/computer/account_database/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/account_database/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["src"] = UID()
@@ -322,7 +322,7 @@
ui = new(user, src, ui_key, "smartfridge.tmpl", name, 400, 500)
ui.open()
/obj/machinery/smartfridge/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/smartfridge/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["contents"] = null
+9 -3
View File
@@ -634,14 +634,20 @@
chems[rid] = gene_chem.Copy()
continue
// Normally a length 2 list - but sometimes, it's length 1 - poisonberries, etc.
// This means the reagent does not scale with potency.
// Index 1 is the base value, index 2 is the potency per u of reagent
var/list/rgnt_list = chems[rid]
rgnt_list.len = max(gene_chem.len, rgnt_list.len)
for(var/i=1;i<=gene_chem.len;i++)
if(isnull(gene_chem[i])) gene_chem[i] = 0
if(chems[rid][i])
chems[rid][i] = max(1,round((gene_chem[i] + chems[rid][i])/2))
if(rgnt_list[i])
rgnt_list[i] = max(1,round((gene_chem[i] + rgnt_list[i])/2))
else
chems[rid][i] = gene_chem[i]
rgnt_list[i] = gene_chem[i]
var/list/new_gasses = gene.values["[TRAIT_EXUDE_GASSES]"]
if(islist(new_gasses))
+2 -2
View File
@@ -166,7 +166,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/botany/extractor/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/botany/extractor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/list/geneMasks[0]
@@ -318,7 +318,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/botany/editor/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/botany/editor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["activity"] = active
+14 -1
View File
@@ -428,6 +428,19 @@
flags = RESTRICTED | HIVEMIND
follow = 1
/datum/language/terrorspider
name = "Spider Hivemind"
desc = "Terror spiders have a limited ability to commune over a psychic hivemind, similar to xenomorphs."
speech_verb = "chitters"
ask_verb = "chitters"
exclaim_verb = "chitters"
colour = "terrorspider"
key = "ts"
flags = RESTRICTED | HIVEMIND
follow = 1
/datum/language/ling
name = "Changeling"
desc = "Although they are normally wary and suspicious of each other, changelings can commune over a distance."
@@ -559,7 +572,7 @@
flags = RESTRICTED | HIVEMIND
drone_only = 1
follow = 1
/datum/language/drone
name = "Drone"
desc = "An encrypted stream of data converted to speech patterns."
+1 -2
View File
@@ -454,8 +454,7 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
totalMembers |= pipeline.other_atmosmch
for(var/obj/machinery/atmospherics/A in totalMembers)
if(!A.pipe_image)
A.pipe_image = image(A, A.loc, layer = 20, dir = A.dir) //the 20 puts it above Byond's darkness (not its opacity view)
A.pipe_image.plane = HUD_PLANE
A.update_pipe_image()
pipes_shown += A.pipe_image
client.images += A.pipe_image
@@ -47,10 +47,12 @@
to_chat(src, "Alert cancelled. Power has been restored without our assistance.")
aiRestorePowerRoutine = 0
clear_fullscreen("blind")
update_sight()
else if(aiRestorePowerRoutine == 3)
to_chat(src, "Alert cancelled. Power has been restored.")
aiRestorePowerRoutine = 0
clear_fullscreen("blind")
update_sight()
else
@@ -114,8 +114,6 @@
C.toff = 1
..()
/mob/living/silicon/pai/Login()
..()
// this function shows the information about being silenced as a pAI in the Status panel
/mob/living/silicon/pai/proc/show_silenced()
@@ -41,7 +41,12 @@ var/global/list/default_pai_software = list()
if(ui_key != "main")
var/datum/pai_software/S = software[ui_key]
if(S && !S.toggle)
S.on_ui_interact(src, ui, force_open)
ui = nanomanager.try_update_ui(user, src, S.id, ui, force_open)
if(!ui)
ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state)
ui.open()
if(S.autoupdate)
ui.set_auto_update(1)
else
if(ui)
ui.set_status(STATUS_CLOSE, 0)
@@ -53,9 +58,15 @@ var/global/list/default_pai_software = list()
ui.open()
ui.set_auto_update(1)
/mob/living/silicon/pai/ui_data(mob/user, datum/topic_state/state = self_state)
/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = self_state)
var/data[0]
if(ui_key != "main")
var/datum/pai_software/S = software[ui_key]
if(S && !S.toggle)
return S.on_ui_data(user, state)
log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src)
return
// Software we have bought
var/bought_software[0]
// Software we have not bought
File diff suppressed because it is too large Load Diff
@@ -285,6 +285,7 @@
if(player.mob && player.mob.mind)
player.mob.mind.transfer_to(src)
player.mob.mind.assigned_role = "Drone"
lawupdate = 0
to_chat(src, "<b>Systems rebooted</b>. Loading base pattern maintenance protocol... <b>loaded</b>.")
@@ -0,0 +1,11 @@
#define SPINNING_WEB 1
#define LAYING_EGGS 2
#define MOVING_TO_TARGET 3
#define SPINNING_COCOON 4
#define TS_TIER_1 1
#define TS_TIER_2 2
#define TS_TIER_3 3
#define TS_TIER_4 4
#define TS_TIER_5 5
@@ -0,0 +1,127 @@
/datum/action/innate/terrorspider/web
name = "Web"
icon_icon = 'icons/effects/effects.dmi'
button_icon_state = "stickyweb1"
/datum/action/innate/terrorspider/web/Activate()
var/mob/living/simple_animal/hostile/poison/terror_spider/user = owner
user.Web()
/datum/action/innate/terrorspider/wrap
name = "Wrap"
icon_icon = 'icons/effects/effects.dmi'
button_icon_state = "cocoon_large1"
/datum/action/innate/terrorspider/wrap/Activate()
var/mob/living/simple_animal/hostile/poison/terror_spider/user = owner
user.FindWrapTarget()
user.DoWrap()
// ---------- WEB
/mob/living/simple_animal/hostile/poison/terror_spider/proc/Web()
visible_message("<span class='notice'>[src] begins to secrete a sticky substance.</span>")
if(do_after(src, 40, target = loc))
var/obj/effect/spider/terrorweb/T = locate() in get_turf(src)
if(T)
to_chat(src, "<span class='danger'>There is already a web here.</span>")
else
new /obj/effect/spider/terrorweb(loc)
/obj/effect/spider/terrorweb
name = "terror web"
desc = "it's stringy and sticky"
icon = 'icons/effects/effects.dmi'
anchored = 1 // prevents people dragging it
density = 0 // prevents it blocking all movement
health = 20 // two welders, or one laser shot (15 for the normal spider webs)
icon_state = "stickyweb1"
/obj/effect/spider/terrorweb/New()
..()
if(prob(50))
icon_state = "stickyweb2"
/obj/effect/spider/terrorweb/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /mob/living/simple_animal/hostile/poison/terror_spider))
return 1
if(isliving(mover))
if(prob(80))
to_chat(mover, "<span class='danger'>You get stuck in [src] for a moment.</span>")
var/mob/living/M = mover
M.Stun(4) // 8 seconds.
M.Weaken(4) // 8 seconds.
return 1
else
return 0
if(istype(mover, /obj/item/projectile))
return prob(20)
return ..()
// ---------- WRAP
/mob/living/simple_animal/hostile/poison/terror_spider/proc/FindWrapTarget()
if(!cocoon_target)
var/list/choices = list()
for(var/mob/living/L in oview(1,src))
if(Adjacent(L))
if(L.stat == DEAD)
choices += L
for(var/obj/O in oview(1,src))
if(Adjacent(O) && !O.anchored)
if(!istype(O, /obj/effect/spider/terrorweb) && !istype(O, /obj/effect/spider/cocoon))
choices += O
if(choices.len)
cocoon_target = input(src,"What do you wish to cocoon?") in null|choices
else
to_chat(src, "<span class='danger'>There is nothing nearby you can wrap.</span>")
/mob/living/simple_animal/hostile/poison/terror_spider/proc/DoWrap()
if(cocoon_target && busy != SPINNING_COCOON)
busy = SPINNING_COCOON
visible_message("<span class='notice'>[src] begins to secrete a sticky substance around [cocoon_target].</span>")
stop_automated_movement = 1
walk(src,0)
if(do_after(src, 40, target = cocoon_target.loc))
if(busy == SPINNING_COCOON)
if(cocoon_target && isturf(cocoon_target.loc) && get_dist(src,cocoon_target) <= 1)
var/obj/effect/spider/cocoon/C = new(cocoon_target.loc)
var/large_cocoon = 0
C.pixel_x = cocoon_target.pixel_x
C.pixel_y = cocoon_target.pixel_y
for(var/obj/O in C.loc)
if(istype(O, /obj/item))
O.loc = C
else if(istype(O, /obj/machinery) || istype(O, /obj/structure))
O.loc = C
large_cocoon = 1
for(var/mob/living/L in C.loc)
if(istype(L, /mob/living/simple_animal/hostile/poison/terror_spider))
continue
if(L.stat != DEAD)
continue
if(iscarbon(L))
regen_points += regen_points_per_kill
fed++
large_cocoon = 1
last_cocoon_object = 0
L.loc = C
C.pixel_x = L.pixel_x
C.pixel_y = L.pixel_y
visible_message("<span class='danger'>[src] sticks a proboscis into [L] and sucks a viscous substance out.</span>")
break
if(large_cocoon)
C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3")
cocoon_target = null
busy = 0
stop_automated_movement = 0
@@ -0,0 +1,38 @@
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: T2 BLACK TERROR ------------------------------
// --------------------------------------------------------------------------------
// -------------: ROLE: assassin, poisoner, DoT expert
// -------------: AI: attacks to inject its venom, then retreats. Will inject its enemies multiple times then hang back to ensure they die.
// -------------: SPECIAL: venom that does more damage the more of it is in you
// -------------: TO FIGHT IT: if bitten once, retreat, get charcoal/etc treatment, and come back with a gun.
// -------------: SPRITES FROM: FoS, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=386
/mob/living/simple_animal/hostile/poison/terror_spider/black
name = "Black Terror spider"
desc = "An ominous-looking spider, black as the darkest night, and with merciless eyes and a blood-red hourglass pattern on its back."
spider_role_summary = "Hit-and-run attacker with extremely venomous bite."
icon_state = "terror_widow"
icon_living = "terror_widow"
icon_dead = "terror_widow_dead"
maxHealth = 120 // same health as hunter spider, aka, pretty weak.. but its bite will kill you!
health = 120
melee_damage_lower = 5
melee_damage_upper = 10
move_to_delay = 5
stat_attack = 1 // ensures they will target people in crit, too!
spider_tier = TS_TIER_2
/mob/living/simple_animal/hostile/poison/terror_spider/black/spider_specialattack(mob/living/carbon/human/L, poisonable)
if(!poisonable)
return ..()
if(L.reagents.has_reagent("terror_black_toxin", 50))
return ..()
var/inject_target = pick("chest", "head")
if(L.stunned || L.can_inject(null, 0, inject_target, 0))
L.reagents.add_reagent("terror_black_toxin", 15) // inject our special poison
visible_message("<span class='danger'>[src] buries its long fangs deep into the [inject_target] of [target]!</span>")
else
visible_message("<span class='danger'>[src] bites [target], but cannot inject venom into their [inject_target]!</span>")
L.attack_animal(src)
@@ -0,0 +1,33 @@
// Terror Spider, Black, Deadly Venom
/datum/reagent/terror_black_toxin
name = "Black Widow venom"
id = "terror_black_toxin"
description = "An incredibly toxic venom injected by the Black Widow spider."
color = "#CF3600"
metabolization_rate = 0.1
/datum/reagent/terror_black_toxin/on_mob_life(mob/living/M)
if(volume < 15)
// bitten once, die slowly. Easy to survive a single bite - just go to medbay.
// total damage: 2/tick, human health 150 until crit, = 75 ticks, = 150 seconds = 2.5 minutes to get to medbay.
M.adjustToxLoss(2) // same damage/tick as tabun cycle 0 to 60
else if(volume < 30)
// bitten twice, die more quickly, muscle cramps make movement difficult. Call medics immediately.
// total damage: 4, human health 150 until crit, = 37.5 ticks, = 75s = 1m15s until death
M.adjustToxLoss(4)
M.Confused(3)
M.EyeBlurry(3)
else if(volume < 45)
// bitten thrice, die very quickly, severe muscle cramps make movement very difficult. Even calling for help probably won't save you.
// total damage: 8, human health 150 until crit, = 18.75 ticks, = 37s until death
M.adjustToxLoss(8) // a bit worse than coiine
M.Confused(6)
M.EyeBlurry(6)
else
// bitten 4 or more times, whole body goes into shock/death
// total damage: 12, human health 150 until crit, = 12.5 ticks, = 25s until death
M.adjustToxLoss(12)
M.EyeBlurry(6)
M.Paralyse(5)
..()
@@ -0,0 +1,43 @@
/mob/living/simple_animal/hostile/poison/terror_spider/Topic(href, href_list)
if(href_list["activate"])
var/mob/dead/observer/ghost = usr
if(istype(ghost))
humanize_spider(ghost)
/mob/living/simple_animal/hostile/poison/terror_spider/attack_ghost(mob/user)
humanize_spider(user)
/mob/living/simple_animal/hostile/poison/terror_spider/proc/humanize_spider(mob/user)
if(key)//Someone is in it
return
var/error_on_humanize = ""
var/humanize_prompt = "Take direct control of [src]?"
humanize_prompt += "Role: [spider_role_summary]"
if(user.ckey in ts_ckey_blacklist)
error_on_humanize = "You are not able to control any terror spider this round."
else if(!ai_playercontrol_allowingeneral)
error_on_humanize = "Terror spiders cannot currently be player-controlled."
else if(spider_awaymission)
error_on_humanize = "Terror spiders that are part of an away mission cannot be controlled by ghosts."
else if(!ai_playercontrol_allowtype)
error_on_humanize = "This specific type of terror spider is not player-controllable."
else if(stat == DEAD)
error_on_humanize = "Dead spiders are not player-controllable."
if(jobban_isbanned(user, "Syndicate") || jobban_isbanned(user, "alien"))
to_chat(user,"You are jobbanned from role of syndicate and/or alien lifeform.")
return
if(error_on_humanize == "")
var/spider_ask = alert(humanize_prompt, "Join as Terror Spider?", "Yes", "No")
if(spider_ask == "No" || !src || qdeleted(src))
return
else
to_chat(user, "Cannot inhabit spider: [error_on_humanize]")
return
if(key)
to_chat(user, "<span class='notice'>Someone else already took this spider.</span>")
return
key = user.key
for(var/mob/dead/observer/G in player_list)
G.show_message("<i>A ghost has taken control of <b>[src]</b>. ([ghost_follow_link(src, ghost=G)]).</i>")
@@ -0,0 +1,40 @@
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: T1 GRAY TERROR -------------------------------
// --------------------------------------------------------------------------------
// -------------: ROLE: fast, weak terror spider
// -------------: SPECIAL: silences targets
// -------------: TO FIGHT IT: shoot it!
// -------------: SPRITES FROM: FoS, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=386
/mob/living/simple_animal/hostile/poison/terror_spider/gray
name = "Gray Terror spider"
desc = "An ominous-looking gray spider, it seems jittery."
spider_role_summary = "Fast-moving but weak spider."
icon_state = "terror_gray"
icon_living = "terror_gray"
icon_dead = "terror_gray_dead"
maxHealth = 120 // same health as hunter spider, aka, pretty weak.. but silences its targets
health = 120
melee_damage_lower = 10
melee_damage_upper = 20
ventcrawler = 1
move_to_delay = 5 // normal speed
stat_attack = 1 // ensures they will target people in crit, too!
environment_smash = 1
/mob/living/simple_animal/hostile/poison/terror_spider/gray/spider_specialattack(mob/living/carbon/human/L, poisonable)
if(!poisonable)
..()
return
if(L.silent >= 10)
L.attack_animal(src)
else
var/inject_target = pick("chest","head")
if(L.stunned || L.can_inject(null,0,inject_target,0))
L.Silence(20) // instead of having a venom that only lasts seconds, we just add the silence directly.
visible_message("<span class='danger'>[src] buries grey fangs deep into the [inject_target] of [target]!</span>")
else
visible_message("<span class='danger'>[src] bites [target], but cannot inject venom into their [inject_target]!</span>")
L.attack_animal(src)
@@ -0,0 +1,35 @@
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: T3 PRINCE OF TERROR --------------------------
// --------------------------------------------------------------------------------
// -------------: ROLE: boss
// -------------: AI: no special ai
// -------------: SPECIAL: massive health
// -------------: TO FIGHT IT: a squad of at least 4 people with laser rifles.
// -------------: SPRITES FROM: Travelling Merchant, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=2766
/mob/living/simple_animal/hostile/poison/terror_spider/prince
name = "Prince of Terror spider"
desc = "An enormous, terrifying spider. It looks like it is judging everything it sees. Its hide seems armored, and it bears the scars of many battles."
spider_role_summary = "Boss-level terror spider. Lightning bruiser. Capable of taking on a squad by itself."
icon_state = "terror_allblack"
icon_living = "terror_allblack"
icon_dead = "terror_allblack_dead"
maxHealth = 400 // 20 laser shots.
health = 400
melee_damage_lower = 15
melee_damage_upper = 25
move_to_delay = 5
ventcrawler = 1
loot = list(/obj/item/clothing/accessory/medal)
spider_tier = TS_TIER_3
spider_opens_doors = 2
/mob/living/simple_animal/hostile/poison/terror_spider/prince/spider_specialattack(mob/living/carbon/human/L)
if(prob(15))
visible_message("<span class='danger'>[src] rams into [L], knocking them to the floor!</span>")
L.Weaken(5)
L.Stun(5)
else
..()
@@ -0,0 +1,60 @@
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: T1 RED TERROR --------------------------------
// --------------------------------------------------------------------------------
// -------------: ROLE: generic attack spider
// -------------: AI: uses very powerful fangs to wreck people in melee
// -------------: SPECIAL: the more you hurt it, the harder it bites you
// -------------: TO FIGHT IT: shoot it from range. Kite it.
// -------------: SPRITES FROM: FoS, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=386
/mob/living/simple_animal/hostile/poison/terror_spider/red
name = "Red Terror spider"
desc = "An ominous-looking red spider, it has eight beady red eyes, and nasty, big, pointy fangs! It looks like it has a vicious streak a mile wide."
spider_role_summary = "High health, high damage, very slow, melee juggernaut"
icon_state = "terror_red"
icon_living = "terror_red"
icon_dead = "terror_red_dead"
maxHealth = 200
health = 200
melee_damage_lower = 15
melee_damage_upper = 20
move_to_delay = 20
spider_opens_doors = 2
var/enrage = 0
var/melee_damage_lower_rage0 = 15
var/melee_damage_upper_rage0 = 20
var/melee_damage_lower_rage1 = 15
var/melee_damage_upper_rage1 = 35
var/melee_damage_lower_rage2 = 35
var/melee_damage_upper_rage2 = 40
/mob/living/simple_animal/hostile/poison/terror_spider/red/AttackingTarget()
if(enrage == 0)
if(health < maxHealth)
enrage = 1
visible_message("<span class='danger'>[src] growls, flexing its fangs!</span>")
melee_damage_lower = melee_damage_lower_rage1
melee_damage_upper = melee_damage_upper_rage1
else if(enrage == 1)
if(health == maxHealth)
enrage = 0
visible_message("<span class='notice'>[src] retracts its fangs a little.</span>")
melee_damage_lower = melee_damage_lower_rage0
melee_damage_upper = melee_damage_upper_rage0
else if(health < (maxHealth/2))
enrage = 2
visible_message("<span class='danger'>[src] growls, spreading its fangs wide!</span>")
melee_damage_lower = melee_damage_lower_rage2
melee_damage_upper = melee_damage_upper_rage2
else if(enrage == 2)
if(health > (maxHealth/2))
enrage = 1
visible_message("<span class='notice'>[src] retracts its fangs a little.</span>")
melee_damage_lower = melee_damage_lower_rage0
melee_damage_upper = melee_damage_upper_rage0
..()
@@ -0,0 +1,329 @@
var/global/list/ts_ckey_blacklist = list()
var/global/ts_count_dead = 0
var/global/ts_count_alive_awaymission = 0
var/global/ts_count_alive_station = 0
var/global/ts_death_last = 0
var/global/ts_death_window = 9000 // 15 minutes
var/global/list/ts_spiderlist = list()
// --------------------------------------------------------------------------------
// --------------------- TERROR SPIDERS: DEFAULTS ---------------------------------
// --------------------------------------------------------------------------------
// Because: http://tvtropes.org/pmwiki/pmwiki.php/Main/SpidersAreScary
/mob/living/simple_animal/hostile/poison/terror_spider
// Name / Description
name = "terror spider"
desc = "The generic parent of all other terror spider types. If you see this in-game, it is a bug."
// Icons
icon = 'icons/mob/terrorspider.dmi'
icon_state = "terror_red"
icon_living = "terror_red"
icon_dead = "terror_red_dead"
// Health
maxHealth = 120
health = 120
// Melee attacks
melee_damage_lower = 15
melee_damage_upper = 20
attacktext = "bites"
attack_sound = 'sound/weapons/bite.ogg'
poison_type = "" // we do not use that silly system.
// Movement
move_to_delay = 6
turns_per_move = 5
pass_flags = PASSTABLE
// Ventcrawling
ventcrawler = 1 // allows player ventcrawling
// Speech
speak_chance = 0 // quiet but deadly
speak_emote = list("hisses")
emote_hear = list("hisses")
// Languages are handled in terror_spider/New()
// Interaction keywords
response_help = "pets"
response_disarm = "gently pushes aside"
// regeneration settings - overridable by child classes
var/regen_points = 0 // number of regen points they have by default
var/regen_points_max = 100 // max number of points they can accumulate
var/regen_points_per_tick = 1 // gain one regen point per tick
var/regen_points_per_kill = 90 // gain extra regen points if you kill something
var/regen_points_per_hp = 3 // every X regen points = 1 health point you can regen
// desired: 20hp/minute unmolested, 40hp/min on food boost, assuming one tick every 2 seconds
// 90/kill means bonus 30hp/kill regenerated over the next 1-2 minutes
// Vision
idle_vision_range = 10
aggro_vision_range = 10
see_in_dark = 10
nightvision = 1
vision_type = new /datum/vision_override/nightvision/thermals/ling_augmented_eyesight
see_invisible = 5
// player control by ghosts
var/ai_playercontrol_allowingeneral = 1 // if 0, no spiders are player controllable. Default set in code, can be changed by queens.
var/ai_playercontrol_allowtype = 1 // if 0, this specific class of spider is not player-controllable. Default set in code for each class, cannot be changed.
var/spider_opens_doors = 1 // all spiders can open firedoors (they have no security). 1 = can open depowered doors. 2 = can open powered doors
faction = list("terrorspiders")
var/spider_awaymission = 0 // if 1, limits certain behavior in away missions
var/spider_role_summary = "UNDEFINED"
var/spider_placed = 0
// AI variables designed for use in procs
var/atom/cocoon_target // for queen and nurse
var/obj/machinery/atmospherics/unary/vent_pump/entry_vent // nearby vent they are going to try to get to, and enter
var/obj/machinery/atmospherics/unary/vent_pump/exit_vent // remote vent they intend to come out of
var/obj/machinery/atmospherics/unary/vent_pump/nest_vent // home vent, usually used by queens
var/fed = 0
var/travelling_in_vent = 0
var/busy = 0 // leave this alone!
var/spider_tier = TS_TIER_1 // 1 for red,gray,green. 2 for purple,black,white, 3 for prince, mother. 4 for queen, 5 for empress.
var/hasdied = 0
var/attackstep = 0
var/attackcycles = 0
var/mylocation = null
var/chasecycles = 0
var/last_cocoon_object = 0 // leave this, changed by procs.
var/killcount = 0
// Breathing, Pressure & Fire
// - No breathing / cannot be suffocated (spiders can hold their breath, look it up)
// - No pressure damage either - they have effectively exoskeletons
// - HOWEVER they can be burned to death!
// - Normal SPACE spiders should probably be immune to SPACE too, but meh, we try to leave the base spiders alone.
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 1500
heat_damage_per_tick = 5 //amount of damage applied if animal's body temperature is higher than maxbodytemp
// DEBUG OPTIONS & COMMANDS
var/spider_debug = 0
var/datum/action/innate/terrorspider/web/web_action
var/datum/action/innate/terrorspider/wrap/wrap_action
// --------------------------------------------------------------------------------
// --------------------- TERROR SPIDERS: SHARED ATTACK CODE -----------------------
// --------------------------------------------------------------------------------
/mob/living/simple_animal/hostile/poison/terror_spider/AttackingTarget()
if(istype(target, /mob/living/simple_animal/hostile/poison/terror_spider))
var/mob/living/simple_animal/hostile/poison/terror_spider/T = target
if(T.spider_tier > spider_tier)
visible_message("<span class='notice'>[src] bows in respect for the terrifying presence of [target].</span>")
else if(T.spider_tier == spider_tier)
visible_message("<span class='notice'>[src] nuzzles [target].</span>")
else if(T.spider_tier < spider_tier && spider_tier >= 4)
visible_message("<span class='notice'>[src] gives [target] a stern look.</span>")
else
visible_message("<span class='notice'>[src] harmlessly nuzzles [target].</span>")
T.CheckFaction()
CheckFaction()
else if(istype(target, /obj/effect/spider/cocoon))
to_chat(src, "Destroying our own cocoons would not help us.")
else if(istype(target, /obj/machinery/door/firedoor))
var/obj/machinery/door/firedoor/F = target
if(F.density)
if(F.blocked)
to_chat(src, "The fire door is welded shut.")
else
visible_message("<span class='danger'>\The [src] pries open the firedoor!</span>")
F.open()
else
to_chat(src, "Closing fire doors does not help.")
else if(istype(target, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/A = target
if(A.density)
try_open_airlock(A)
else if(isliving(target))
var/mob/living/G = target
if(issilicon(G))
G.attack_animal(src)
return
else if(G.reagents && (iscarbon(G)))
var/can_poison = 1
if(ishuman(G))
var/mob/living/carbon/human/H = G
if(!(H.species.reagent_tag & PROCESS_ORG) || (H.species.flags & NO_POISON))
can_poison = 0
spider_specialattack(G,can_poison)
else
G.attack_animal(src)
else
target.attack_animal(src)
/mob/living/simple_animal/hostile/poison/terror_spider/proc/spider_specialattack(mob/living/carbon/human/L, var/poisonable)
L.attack_animal(src)
// --------------------------------------------------------------------------------
// --------------------- TERROR SPIDERS: PROC OVERRIDES ---------------------------
// --------------------------------------------------------------------------------
/mob/living/simple_animal/hostile/poison/terror_spider/examine(mob/user)
..()
var/list/msgs = list()
if(stat == DEAD)
msgs += "<span class='notice'>It appears to be dead.</span>\n"
else
if(key)
msgs += "<span class='warning'>Its eyes regard you with a curious intelligence.</span>"
if(health > (maxHealth*0.95))
msgs += "<span class='notice'>It is in excellent health.</span>"
else if(health > (maxHealth*0.75))
msgs += "<span class='notice'>It has a few injuries.</span>"
else if(health > (maxHealth*0.55))
msgs += "<span class='warning'>It has many injuries.</span>"
else if(health > (maxHealth*0.25))
msgs += "<span class='warning'>It is barely clinging on to life!</span>"
else if(health < maxHealth && regen_points > regen_points_per_kill)
msgs += "<span class='notice'>It appears to be regenerating quickly.</span>"
if(killcount >= 1)
msgs += "<span class='warning'>It has blood dribbling from its mouth.</span>"
to_chat(usr,msgs.Join("<BR>"))
/mob/living/simple_animal/hostile/poison/terror_spider/New()
..()
ts_spiderlist += src
add_language("Spider Hivemind")
add_language("Galactic Common")
default_language = all_languages["Spider Hivemind"]
web_action = new()
web_action.Grant(src)
wrap_action = new()
wrap_action.Grant(src)
name += " ([rand(1, 1000)])"
msg_terrorspiders("[src] has grown in [get_area(src)].")
if(is_away_level(z))
spider_awaymission = 1
ts_count_alive_awaymission++
else
ts_count_alive_station++
// after 30 seconds, assuming nobody took control of it yet, offer it to ghosts.
addtimer(src, "CheckFaction", 150)
addtimer(src, "announcetoghosts", 300)
/mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts()
if(spider_awaymission)
return
if(stat == DEAD)
return
if(ckey)
var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state)
notify_ghosts("[src] has appeared in [get_area(src)]. (already player-controlled)", source = src, alert_overlay = alert_overlay)
else if(ai_playercontrol_allowingeneral && ai_playercontrol_allowtype)
var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state)
notify_ghosts("[src] has appeared in [get_area(src)].", enter_link = "<a href=?src=[UID()];activate=1>(Click to control)</a>", source = src, alert_overlay = alert_overlay, attack_not_jump = 1)
/mob/living/simple_animal/hostile/poison/terror_spider/Destroy()
ts_spiderlist -= src
handle_dying()
return ..()
/mob/living/simple_animal/hostile/poison/terror_spider/Life()
..()
if(stat == DEAD)
if(prob(2))
// 2% chance every cycle to decompose
visible_message("<span class='notice'>\The dead body of the [src] decomposes!</span>")
gib()
else
if(regen_points < regen_points_max)
regen_points += regen_points_per_tick
if((bruteloss > 0) || (fireloss > 0))
if(regen_points > regen_points_per_hp)
if(bruteloss > 0)
adjustBruteLoss(-1)
regen_points -= regen_points_per_hp
else if(fireloss > 0)
adjustFireLoss(-1)
regen_points -= regen_points_per_hp
if(prob(5))
CheckFaction()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/handle_dying()
if(!hasdied)
hasdied = 1
ts_count_dead++
ts_death_last = world.time
if(spider_awaymission)
ts_count_alive_awaymission--
else
ts_count_alive_station--
/mob/living/simple_animal/hostile/poison/terror_spider/death(gibbed)
if(!gibbed)
msg_terrorspiders("[src] has died in [get_area(src)].")
handle_dying()
..()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/spider_special_action()
return
/mob/living/simple_animal/hostile/poison/terror_spider/Bump(atom/A)
if(istype(A, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/L = A
if(L.density)
try_open_airlock(L)
if(istype(A, /obj/machinery/door/firedoor))
var/obj/machinery/door/firedoor/F = A
if(F.density && !F.blocked)
F.open()
..()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/msg_terrorspiders(msgtext)
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist)
if(T.stat != DEAD)
to_chat(T, "<span class='terrorspider'>TerrorSense: [msgtext]</span>")
/mob/living/simple_animal/hostile/poison/terror_spider/proc/CheckFaction()
if(faction.len != 1 || (!("terrorspiders" in faction)) || master_commander != null)
visible_message("<span class='danger'>[src] writhes in pain!</span>")
log_runtime(EXCEPTION("Terror spider created with incorrect faction list at: [atom_loc_line(src)]"))
death()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/try_open_airlock(obj/machinery/door/airlock/D)
if(D.operating)
return
if(!D.density)
to_chat(src, "<span class='warning'>Closing doors does not help us.</span>")
else if(D.welded)
to_chat(src, "<span class='warning'>The door is welded shut.</span>")
else if(D.locked)
to_chat(src, "<span class='warning'>The door is bolted shut.</span>")
else if(D.allowed(src))
D.open(1)
else if(D.arePowerSystemsOn() && (spider_opens_doors != 2))
to_chat(src, "<span class='warning'>The door's motors resist your efforts to force it.</span>")
else if(!spider_opens_doors)
to_chat(src, "<span class='warning'>Your type of spider is not strong enough to force open doors.</span>")
else
visible_message("<span class='danger'>[src] pries open the door!</span>")
playsound(src.loc, "sparks", 100, 1)
D.open(1)
+3 -1
View File
@@ -244,8 +244,10 @@
if(!job.is_position_available()) return 0
if(jobban_isbanned(src,rank)) return 0
if(!is_job_whitelisted(src, rank)) return 0
if(!job.player_old_enough(src.client)) return 0
if(!job.player_old_enough(client)) return 0
if(job.admin_only && !(check_rights(R_EVENT, 0))) return 0
if(job.available_in_playtime(client))
return 0
if(config.assistantlimit)
if(job.title == "Civilian")
+1 -1
View File
@@ -62,7 +62,7 @@
ui.open()
ui.set_auto_update(1)
/datum/nano_module/alarm_monitor/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/categories[0]
+1 -1
View File
@@ -39,7 +39,7 @@
ui.set_auto_update(1)
ui_ref = ui
/datum/nano_module/atmos_control/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/nano_module/atmos_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["alarms"] = air_alarm_repository.air_alarm_data(monitored_alarms)
return data
+1 -1
View File
@@ -31,7 +31,7 @@
// should make the UI auto-update; doesn't seem to?
ui.set_auto_update(1)
/datum/nano_module/crew_monitor/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/nano_module/crew_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/turf/T = get_turf(nano_host())
@@ -217,7 +217,7 @@
ui.open()
ui.set_auto_update(1)
/datum/nano_module/appearance_changer/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/nano_module/appearance_changer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
generate_data(check_whitelist, whitelist, blacklist)
var/data[0]
+1 -1
View File
@@ -154,7 +154,7 @@
ui.open()
ui.set_auto_update(1)
/datum/nano_module/law_manager/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/nano_module/law_manager/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
owner.lawsync()
+1 -1
View File
@@ -18,7 +18,7 @@
ui.open()
ui.set_auto_update(1)
/datum/nano_module/power_monitor/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/nano_module/power_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["powermonitor"] = powermonitor
+2 -1
View File
@@ -44,11 +44,12 @@
* The ui_data proc is used to get data for the interface
*
* @param user /mob The mob who is viewing this ui
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
* @param state /datum/topic_state Current topic state of the UI
*
* @return list()
*/
/datum/proc/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/proc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
return list()
// Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob
+1 -1
View File
@@ -23,7 +23,7 @@
ui = get_open_ui(user, src_object, ui_key)
if(!isnull(ui))
var/data = src_object.ui_data(user, ui.state) // Get data from src_object.
var/data = src_object.ui_data(user, ui_key, ui.state) // Get data from src_object.
if(!force_open)
ui.push_data(data)
else
+1 -1
View File
@@ -415,7 +415,7 @@ nanoui is used to open and update nano browser uis
return
if(!initial_data)
set_initial_data(src_object.ui_data(user, state)) // Get the UI data.
set_initial_data(src_object.ui_data(user, ui_key, state)) // Get the UI data.
user << browse(get_html(), "window=[window_id];[window_size][window_options]")
winset(user, "mapwindow.map", "focus=true") // return keyboard focus to map
+11 -9
View File
@@ -62,7 +62,7 @@ var/list/alldepartments = list()
ui = new(user, src, ui_key, "faxmachine.tmpl", "Fax Machine UI", 540, 450)
ui.open()
/obj/machinery/photocopier/faxmachine/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/photocopier/faxmachine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
if(scan)
@@ -109,16 +109,18 @@ var/list/alldepartments = list()
if(href_list["paper"])
if(copyitem)
copyitem.loc = usr.loc
usr.put_in_hands(copyitem)
to_chat(usr, "<span class='notice'>You take \the [copyitem] out of \the [src].</span>")
copyitem.forceMove(get_turf(src))
if(ishuman(usr))
if(!usr.get_active_hand())
usr.put_in_hands(copyitem)
to_chat(usr, "<span class='notice'>You eject \the [copyitem] from \the [src].</span>")
copyitem = null
else
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/weapon/paper) || istype(I, /obj/item/weapon/photo) || istype(I, /obj/item/weapon/paper_bundle))
usr.drop_item()
copyitem = I
I.loc = src
I.forceMove(src)
to_chat(usr, "<span class='notice'>You insert \the [I] into \the [src].</span>")
flick(insert_anim, src)
@@ -163,24 +165,24 @@ var/list/alldepartments = list()
/obj/machinery/photocopier/faxmachine/proc/scan(var/obj/item/weapon/card/id/card = null)
if(scan) // Card is in machine
if(ishuman(usr))
scan.loc = usr.loc
scan.forceMove(get_turf(usr))
if(!usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
else
scan.loc = src.loc
scan.forceMove(get_turf(src))
scan = null
else
if(!card)
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/weapon/card/id))
usr.drop_item()
I.loc = src
I.forceMove(src)
scan = I
else
if(istype(card))
usr.drop_item()
card.loc = src
card.forceMove(src)
scan = card
nanomanager.update_uis(src)
+11 -9
View File
@@ -79,8 +79,10 @@
updateUsrDialog()
else if(href_list["remove"])
if(copyitem)
copyitem.loc = usr.loc
usr.put_in_hands(copyitem)
copyitem.forceMove(get_turf(src))
if(ishuman(usr))
if(!usr.get_active_hand())
usr.put_in_hands(copyitem)
to_chat(usr, "<span class='notice'>You take \the [copyitem] out of \the [src].</span>")
copyitem = null
updateUsrDialog()
@@ -125,7 +127,7 @@
if(!copyitem)
user.drop_item()
copyitem = O
O.loc = src
O.forceMove(src)
to_chat(user, "<span class='notice'>You insert \the [O] into \the [src].</span>")
flick(insert_anim, src)
updateUsrDialog()
@@ -150,10 +152,10 @@
if(ismob(G.affecting) && G.affecting != ass)
var/mob/GM = G.affecting
visible_message("<span class='warning'>[usr] drags [GM.name] onto the photocopier!</span>")
GM.loc = get_turf(src)
GM.forceMove(get_turf(src))
ass = GM
if(copyitem)
copyitem.loc = src.loc
copyitem.forceMove(get_turf(src))
copyitem = null
updateUsrDialog()
return
@@ -290,10 +292,10 @@
W = copy(W)
else if(istype(W, /obj/item/weapon/photo))
W = photocopy(W)
W.loc = p
W.forceMove(p)
p.amount++
p.amount--
p.loc = src.loc
p.forceMove(get_turf(src))
p.update_icon()
p.icon_state = "paper_words"
p.name = bundle.name
@@ -313,10 +315,10 @@
if(target.anchored) return
if(!ishuman(user)) return
visible_message("<span class='warning'>[usr] drags [target.name] onto the photocopier!</span>")
target.loc = get_turf(src)
target.forceMove(get_turf(src))
ass = target
if(copyitem)
copyitem.loc = src.loc
copyitem.forceMove(get_turf(src))
visible_message("<span class='notice'>[copyitem] is shoved out of the way by [ass]!</span>")
copyitem = null
updateUsrDialog()
+13 -1
View File
@@ -129,7 +129,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
// auto update every Master Controller tick
ui.set_auto_update(auto_update)
/obj/item/device/pda/ui_data(mob/user, datum/topic_state/state = inventory_state)
/obj/item/device/pda/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state)
var/data[0]
data["owner"] = owner // Who is your daddy...
@@ -427,6 +427,9 @@ var/global/list/obj/item/device/pda/PDAs = list()
user.drop_item()
C.forceMove(src)
to_chat(user, "<span class='notice'>You slide \the [C] into \the [src].</span>")
else if(istype(C, /obj/item/weapon/nanomob_card))
if(cartridge && istype(cartridge, /obj/item/weapon/cartridge/mob_hunt_game))
cartridge.attackby(C, user, params)
/obj/item/device/pda/attack(mob/living/C as mob, mob/living/user as mob)
if(istype(C, /mob/living/carbon) && scanmode)
@@ -498,3 +501,12 @@ var/global/list/obj/item/device/pda/PDAs = list()
else
close(usr)
return 0
/obj/item/device/pda/process()
if(current_app)
current_app.program_process()
/obj/item/device/pda/hit_check(speed)
if(current_app)
current_app.program_hit_check()
..()
+13 -1
View File
@@ -1,6 +1,6 @@
// Base class for anything that can show up on home screen
/datum/data/pda
var/icon = "tasks"
var/icon = "tasks" //options comes from http://fontawesome.io/icons/
var/notify_icon = "exclamation-circle"
var/notify_silent = 0
var/hidden = 0 // program not displayed in main menu
@@ -12,6 +12,16 @@
return ..()
/datum/data/pda/proc/start()
return
/datum/data/pda/proc/stop()
return
/datum/data/pda/proc/program_process()
return
/datum/data/pda/proc/program_hit_check()
return
/datum/data/pda/proc/notify(message, blink = 1)
if(message)
@@ -56,6 +66,8 @@
title = name
/datum/data/pda/app/start()
if(pda.current_app)
pda.current_app.stop()
pda.current_app = src
return 1
+41 -24
View File
@@ -22,7 +22,7 @@
if(radio)
radio.initialize()
..()
/obj/item/weapon/cartridge/Destroy()
if(radio)
qdel(radio)
@@ -53,8 +53,7 @@
/obj/item/weapon/cartridge/atmos
name = "BreatheDeep Cartridge"
icon_state = "cart-a"
programs = list(
new/datum/data/pda/utility/scanmode/gas)
programs = list(new/datum/data/pda/utility/scanmode/gas)
/obj/item/weapon/cartridge/medical
name = "Med-U Cartridge"
@@ -66,8 +65,7 @@
/obj/item/weapon/cartridge/chemistry
name = "ChemWhiz Cartridge"
icon_state = "cart-chem"
programs = list(
new/datum/data/pda/utility/scanmode/reagent)
programs = list(new/datum/data/pda/utility/scanmode/reagent)
/obj/item/weapon/cartridge/security
name = "R.O.B.U.S.T. Cartridge"
@@ -94,30 +92,25 @@
name = "CustodiPRO Cartridge"
desc = "The ultimate in clean-room design."
icon_state = "cart-j"
programs = list(
new/datum/data/pda/app/janitor)
programs = list(new/datum/data/pda/app/janitor)
/obj/item/weapon/cartridge/lawyer
name = "P.R.O.V.E. Cartridge"
icon_state = "cart-s"
programs = list(
new/datum/data/pda/app/crew_records/security)
programs = list(new/datum/data/pda/app/crew_records/security)
/obj/item/weapon/cartridge/clown
name = "Honkworks 5.0"
icon_state = "cart-clown"
charges = 5
programs = list(
new/datum/data/pda/utility/honk)
messenger_plugins = list(
new/datum/data/pda/messenger_plugin/virus/clown)
programs = list(new/datum/data/pda/utility/honk)
messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/clown)
/obj/item/weapon/cartridge/mime
name = "Gestur-O 1000"
icon_state = "cart-mi"
charges = 5
messenger_plugins = list(
new/datum/data/pda/messenger_plugin/virus/mime)
messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/mime)
/*
/obj/item/weapon/cartridge/botanist
@@ -129,8 +122,7 @@
/obj/item/weapon/cartridge/signal
name = "generic signaler cartridge"
desc = "A data cartridge with an integrated radio signaler module."
programs = list(
new/datum/data/pda/app/signaller)
programs = list(new/datum/data/pda/app/signaller)
/obj/item/weapon/cartridge/signal/initialize()
radio = new /obj/item/radio/integrated/signal(src)
@@ -162,8 +154,7 @@
/obj/item/weapon/cartridge/head
name = "Easy-Record DELUXE"
icon_state = "cart-h"
programs = list(
new/datum/data/pda/app/status_display)
programs = list(new/datum/data/pda/app/status_display)
/obj/item/weapon/cartridge/hop
name = "HumanResources9001"
@@ -303,12 +294,38 @@
icon_state = "cart"
var/initial_remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing.
charges = 4
programs = list(
new/datum/data/pda/utility/toggle_door)
messenger_plugins = list(
new/datum/data/pda/messenger_plugin/virus/detonate)
programs = list(new/datum/data/pda/utility/toggle_door)
messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/detonate)
/obj/item/weapon/cartridge/syndicate/New()
var/datum/data/pda/utility/toggle_door/D = programs[1]
if(istype(D))
D.remote_door_id = initial_remote_door_id
D.remote_door_id = initial_remote_door_id
/obj/item/weapon/cartridge/mob_hunt_game
name = "Nano-Mob Hunter GO! Cartridge"
desc = "The hit new PDA game that lets you track down and capture your favorite Nano-Mobs living in your world!"
icon_state = "cart-eye"
programs = list(new/datum/data/pda/app/mob_hunter_game)
var/emagged = 0
/obj/item/weapon/cartridge/mob_hunt_game/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/weapon/nanomob_card))
var/obj/item/weapon/nanomob_card/card = O
var/datum/data/pda/app/mob_hunter_game/my_game = programs[1]
if(my_game.register_capture(card.mob_data))
to_chat(user, "<span class='notice'>Transfer successful!</span>")
qdel(card)
else
to_chat(user, "<span class='warning'>Transfer failed. Could not read mob data from card.</span>")
else
..()
/obj/item/weapon/cartridge/mob_hunt_game/emag_act(mob/user)
if(!emagged)
emagged = 1
var/datum/data/pda/app/mob_hunter_game/my_game = programs[1]
my_game.hacked = 1
to_chat(user, "<span class='warning'>TR4P_M45T3R.mod successfully initialized. ToS violated. User Agreement nullified. Gotta pwn them all.</span>")
to_chat(user, "<span class='warning'>You can now create trapped versions of any mob in your collection that will damage hunters who attempt to capture it.</span>")
description_antag = "This copy of Nano-Mob Hunter GO! has been hacked to allow the creation of trap mobs which will cause any PDA that attempts to capture it to shock anyone holding it. Hacked copies of the game will not trigger the trap."
+182
View File
@@ -0,0 +1,182 @@
/*
This stuff isn't included with in core_apps.dm because it is so distinct and would end up making the file huge (more than it already is).
I put it in it's own file in order to help it stay organized and easier to locate the procs and such.
For reference, the other Nano-mob Hunter GO files are in /code/game/modules/arcade/mob_hunt
*/
/datum/data/pda/app/mob_hunter_game
name = "Nano-Mob Hunter GO"
icon = "gamepad"
template = "pda_mob_hunt"
category = "Games"
var/list/my_collection = list()
var/current_index = 0
var/connected = 0
var/hacked = 0 //if set, this cartridge is able to spawn trap mobs from its collection (set via emag_act on the cartridge)
var/catch_mod = 0 //used to adjust the likelihood of a mob running from this client, a negative value means it is less likely to run (support for temporary bonuses)
var/wild_captures = 0 //used to track the total number of mobs captured from the wild (does not count card mobs) by this client
var/scan_range = 3 //maximum distance (in tiles) from which the client can reveal nearby mobs
/datum/data/pda/app/mob_hunter_game/start()
..()
processing_objects.Add(pda)
/datum/data/pda/app/mob_hunter_game/stop()
..()
disconnect("Program Terminated")
processing_objects.Remove(pda)
/datum/data/pda/app/mob_hunter_game/proc/scan_nearby()
if(!mob_hunt_server || !connected)
return
for(var/turf/T in range(scan_range, get_turf(pda)))
for(var/obj/effect/nanomob/N in T.contents)
if(src in N.clients_encountered)
//hide the mob
N.conceal()
else
//reveal the mob
N.reveal()
/datum/data/pda/app/mob_hunter_game/proc/reconnect()
if(!mob_hunt_server || !mob_hunt_server.server_status || connected)
//show a message about the server being unavailable (because it doesn't exist / didn't get set to the global var / is offline)
return 0
mob_hunt_server.connected_clients += src
connected = 1
if(pda)
pda.audible_message("[bicon(pda)] Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!", null, 2)
return 1
/datum/data/pda/app/mob_hunter_game/proc/get_player()
if(!pda)
return null
if(ishuman(pda.loc))
var/mob/living/carbon/human/H = pda.loc
return H
return null
/datum/data/pda/app/mob_hunter_game/proc/disconnect(reason = null)
if(!mob_hunt_server || !connected)
return
mob_hunt_server.connected_clients -= src
for(var/obj/effect/nanomob/N in (mob_hunt_server.normal_spawns + mob_hunt_server.trap_spawns))
N.conceal(list(get_player()))
connected = 0
//show a disconnect message if we were disconnected involuntarily (reason argument provided)
if(pda && reason)
pda.audible_message("[bicon(pda)] Disconnected from server. Reason: [reason].", null, 2)
/datum/data/pda/app/mob_hunter_game/program_process()
if(!mob_hunt_server || !connected)
return
scan_nearby()
/datum/data/pda/app/mob_hunter_game/program_hit_check()
if(!pda)
return
for(var/obj/effect/nanomob/hit_mob in get_turf(pda))
hit_mob.hitby(pda)
/datum/data/pda/app/mob_hunter_game/proc/register_capture(datum/mob_hunt/captured, wild = 0)
if(!captured)
return 0
my_collection.Add(captured)
if(wild)
wild_captures++
return 1
/datum/data/pda/app/mob_hunter_game/update_ui(mob/user, list/data)
if(!mob_hunt_server || !(src in mob_hunt_server.connected_clients))
data["connected"] = 0
else
data["connected"] = 1
data["wild_captures"] = wild_captures
data["no_collection"] = 0
if(!my_collection.len)
data["no_collection"] = 1
return
var/datum/mob_hunt/mob_info
if(!current_index)
current_index = 1
if(current_index > my_collection.len)
current_index = 1
if(current_index < 1)
current_index = my_collection.len
mob_info = my_collection[current_index]
var/list/entry = list(
"nickname" = mob_info.nickname,
"real_name" = mob_info.mob_name,
"level" = mob_info.level,
"type1" = mob_info.get_type1(),
"type2" = mob_info.get_type2(),
"sprite" = "[mob_info.icon_state_normal].png",
"is_hacked" = hacked
)
if(mob_info.is_shiny)
entry["sprite"] = "[mob_info.icon_state_shiny].png"
data["entry"] = entry
/datum/data/pda/app/mob_hunter_game/proc/assign_nickname()
if(!my_collection.len)
return
var/datum/mob_hunt/mob_info = my_collection[current_index]
var/old_name = mob_info.mob_name
if(mob_info.nickname)
old_name = mob_info.nickname
mob_info.nickname = input("Give a nickname to [old_name]?", "Nickname", old_name)
/datum/data/pda/app/mob_hunter_game/proc/release()
if(!my_collection.len)
return
if(alert("Are you sure you want to release this mob back into the wild?", "Confirm Release", "Yes", "No") == "Yes")
remove_mob()
/datum/data/pda/app/mob_hunter_game/proc/print_card()
if(!pda || !my_collection.len)
return
var/obj/item/weapon/nanomob_card/card = new/obj/item/weapon/nanomob_card(null)
var/datum/mob_hunt/mob_info = my_collection[current_index]
card.mob_data = mob_info
card.update_info()
card.forceMove(get_turf(pda))
remove_mob()
/datum/data/pda/app/mob_hunter_game/proc/remove_mob()
if(!my_collection.len)
return
my_collection.Remove(my_collection[current_index])
if(current_index > my_collection.len)
current_index = my_collection.len
/datum/data/pda/app/mob_hunter_game/proc/set_trap()
if(!my_collection.len || !pda || !hacked)
return
var/datum/mob_hunt/bait = my_collection[current_index]
bait = bait.type
new bait(1, get_turf(pda))
/datum/data/pda/app/mob_hunter_game/Topic(href, list/href_list)
switch(href_list["choice"])
if("Rename")
assign_nickname()
if("Release")
release()
if("Next")
current_index++
if(current_index > my_collection.len)
current_index = 1
if("Prev")
current_index--
if(current_index < 1)
current_index = my_collection.len
if("Reconnect")
reconnect()
if("Disconnect")
disconnect()
if("Transfer")
print_card()
if("Set_Trap")
set_trap()
+1 -1
View File
@@ -787,7 +787,7 @@
// Auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/power/apc/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["locked"] = is_locked(user)
data["isOperating"] = operating
+1 -1
View File
@@ -327,7 +327,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/power/port_gen/pacman/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/power/port_gen/pacman/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["active"] = active
@@ -95,10 +95,10 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
if(usr.stat || !usr.canmove || usr.restrained())
return
if(src.anchored)
if(anchored)
to_chat(usr, "It is fastened to the floor!")
return 0
src.dir = turn(src.dir, 270)
dir = turn(dir, 270)
return 1
/obj/structure/particle_accelerator/AltClick(mob/user)
@@ -116,29 +116,29 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
if(usr.stat || !usr.canmove || usr.restrained())
return
if(src.anchored)
if(anchored)
to_chat(usr, "It is fastened to the floor!")
return 0
src.dir = turn(src.dir, 90)
dir = turn(dir, 90)
return 1
/obj/structure/particle_accelerator/examine(mob/user)
switch(src.construction_state)
switch(construction_state)
if(0)
src.desc = text("A [name], looks like it's not attached to the flooring")
desc = text("A [name], looks like it's not attached to the flooring")
if(1)
src.desc = text("A [name], it is missing some cables")
desc = text("A [name], it is missing some cables")
if(2)
src.desc = text("A [name], the panel is open")
desc = text("A [name], the panel is open")
if(3)
src.desc = text("The [name] is assembled")
desc = text("The [name] is assembled")
if(powered)
src.desc = src.desc_holder
desc = desc_holder
..(user)
/obj/structure/particle_accelerator/attackby(obj/item/W, mob/user, params)
if(istool(W))
if(src.process_tool_hit(W,user))
if(process_tool_hit(W,user))
return
..()
return
@@ -208,7 +208,7 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
/obj/structure/particle_accelerator/proc/connect_master(var/obj/O)
if(O && istype(O,/obj/machinery/particle_accelerator/control_box))
if(O.dir == src.dir)
if(O.dir == dir)
master = O
return 1
return 0
@@ -219,51 +219,51 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
return 0
if(!ismob(user) || !isobj(O))
return 0
var/temp_state = src.construction_state
var/temp_state = construction_state
switch(src.construction_state)//TODO:Might be more interesting to have it need several parts rather than a single list of steps
switch(construction_state)//TODO:Might be more interesting to have it need several parts rather than a single list of steps
if(0)
if(iswrench(O) && !isinspace())
playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
src.anchored = 1
user.visible_message("[user.name] secures the [src.name] to the floor.", \
playsound(loc, 'sound/items/Ratchet.ogg', 75, 1)
anchored = 1
user.visible_message("[user.name] secures the [name] to the floor.", \
"You secure the external bolts.")
temp_state++
if(1)
if(iswrench(O))
playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
src.anchored = 0
user.visible_message("[user.name] detaches the [src.name] from the floor.", \
playsound(loc, 'sound/items/Ratchet.ogg', 75, 1)
anchored = 0
user.visible_message("[user.name] detaches the [name] from the floor.", \
"You remove the external bolts.")
temp_state--
else if(iscoil(O))
var/obj/item/stack/cable_coil/C = O
if(C.use(1))
user.visible_message("[user.name] adds wires to the [src.name].", \
user.visible_message("[user.name] adds wires to the [name].", \
"You add some wires.")
temp_state++
else
to_chat(user, "<span class='warning'>You need one length of cable to wire the [src.name]!</span>")
to_chat(user, "<span class='warning'>You need one length of cable to wire the [name]!</span>")
return
if(2)
if(iswirecutter(O))//TODO:Shock user if its on?
user.visible_message("[user.name] removes some wires from the [src.name].", \
user.visible_message("[user.name] removes some wires from the [name].", \
"You remove some wires.")
temp_state--
else if(isscrewdriver(O))
user.visible_message("[user.name] closes the [src.name]'s access panel.", \
user.visible_message("[user.name] closes the [name]'s access panel.", \
"You close the access panel.")
temp_state++
if(3)
if(isscrewdriver(O))
user.visible_message("[user.name] opens the [src.name]'s access panel.", \
user.visible_message("[user.name] opens the [name]'s access panel.", \
"You open the access panel.")
temp_state--
if(temp_state == src.construction_state)//Nothing changed
if(temp_state == construction_state)//Nothing changed
return 0
else
src.construction_state = temp_state
if(src.construction_state < 3)//Was taken apart, update state
construction_state = temp_state
if(construction_state < 3)//Was taken apart, update state
update_state()
update_icon()
return 1
@@ -296,10 +296,10 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
if(usr.stat || !usr.canmove || usr.restrained())
return
if(src.anchored)
if(anchored)
to_chat(usr, "It is fastened to the floor!")
return 0
src.dir = turn(src.dir, 270)
dir = turn(dir, 270)
return 1
/obj/machinery/particle_accelerator/verb/rotateccw()
@@ -309,10 +309,10 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
if(usr.stat || !usr.canmove || usr.restrained())
return
if(src.anchored)
if(anchored)
to_chat(usr, "It is fastened to the floor!")
return 0
src.dir = turn(src.dir, 90)
dir = turn(dir, 90)
return 1
/obj/machinery/particle_accelerator/update_icon()
@@ -320,7 +320,7 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
/obj/machinery/particle_accelerator/attackby(obj/item/W, mob/user, params)
if(istool(W))
if(src.process_tool_hit(W,user))
if(process_tool_hit(W,user))
return
..()
return
@@ -357,51 +357,53 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
return 0
if(!ismob(user) || !isobj(O))
return 0
var/temp_state = src.construction_state
switch(src.construction_state)//TODO:Might be more interesting to have it need several parts rather than a single list of steps
var/temp_state = construction_state
switch(construction_state)//TODO:Might be more interesting to have it need several parts rather than a single list of steps
if(0)
if(iswrench(O))
playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
src.anchored = 1
user.visible_message("[user.name] secures the [src.name] to the floor.", \
playsound(loc, 'sound/items/Ratchet.ogg', 75, 1)
anchored = 1
user.visible_message("[user.name] secures the [name] to the floor.", \
"You secure the external bolts.")
temp_state++
power_change()
if(1)
if(iswrench(O))
playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
src.anchored = 0
user.visible_message("[user.name] detaches the [src.name] from the floor.", \
playsound(loc, 'sound/items/Ratchet.ogg', 75, 1)
anchored = 0
user.visible_message("[user.name] detaches the [name] from the floor.", \
"You remove the external bolts.")
temp_state--
power_change()
else if(iscoil(O))
if(O:use(1))
user.visible_message("[user.name] adds wires to the [src.name].", \
user.visible_message("[user.name] adds wires to the [name].", \
"You add some wires.")
temp_state++
if(2)
if(iswirecutter(O))//TODO:Shock user if its on?
user.visible_message("[user.name] removes some wires from the [src.name].", \
user.visible_message("[user.name] removes some wires from the [name].", \
"You remove some wires.")
temp_state--
else if(isscrewdriver(O))
user.visible_message("[user.name] closes the [src.name]'s access panel.", \
user.visible_message("[user.name] closes the [name]'s access panel.", \
"You close the access panel.")
temp_state++
if(3)
if(isscrewdriver(O))
user.visible_message("[user.name] opens the [src.name]'s access panel.", \
user.visible_message("[user.name] opens the [name]'s access panel.", \
"You open the access panel.")
temp_state--
active = 0
if(temp_state == src.construction_state)//Nothing changed
if(temp_state == construction_state)//Nothing changed
return 0
else
if(src.construction_state < 3)//Was taken apart, update state
if(construction_state < 3)//Was taken apart, update state
update_state()
if(use_power)
use_power = 0
src.construction_state = temp_state
if(src.construction_state >= 3)
construction_state = temp_state
if(construction_state >= 3)
use_power = 1
update_icon()
return 1
@@ -24,6 +24,7 @@
/obj/machinery/particle_accelerator/control_box/New()
wires = new(src)
connected_parts = list()
update_icon()
..()
/obj/machinery/particle_accelerator/control_box/Destroy()
@@ -34,7 +35,7 @@
return ..()
/obj/machinery/particle_accelerator/control_box/attack_ghost(user as mob)
return src.attack_hand(user)
return attack_hand(user)
/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user as mob)
if(construction_state >= 3)
@@ -62,9 +63,12 @@
/obj/machinery/particle_accelerator/control_box/update_icon()
if(active)
icon_state = "[reference]p1"
icon_state = "[reference]p[strength]"
else
if(use_power)
if(stat & NOPOWER)
icon_state = "[reference]w"
return
else if(use_power)
if(assembled)
icon_state = "[reference]p"
else
@@ -89,16 +93,16 @@
to_chat(usr, "<span class='error'>ERROR: Request timed out. Check wire contacts.</span>")
return
if( href_list["close"] )
if(href_list["close"])
usr << browse(null, "window=pacontrol")
usr.unset_machine()
return
if(href_list["togglep"])
if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE))
src.toggle_power()
toggle_power()
else if(href_list["scan"])
src.part_scan()
part_scan()
else if(href_list["strengthup"])
if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE))
@@ -108,8 +112,8 @@
if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE))
remove_strength()
src.updateDialog()
src.update_icon()
updateDialog()
update_icon()
return
@@ -152,32 +156,39 @@
use_power = 0
else if(!stat && construction_state <= 3)
use_power = 1
update_icon()
if((stat & NOPOWER) || (!stat && construction_state <= 3)) //Only update the part icons if something's changed (i.e. any of the above condition sets are met).
for(var/obj/structure/particle_accelerator/part in connected_parts)
part.strength = null
part.powered = 0
part.update_icon()
return
/obj/machinery/particle_accelerator/control_box/process()
if(src.active)
if(active)
//a part is missing!
if( length(connected_parts) < 6 )
if(length(connected_parts) < 6)
investigate_log("lost a connected part; It <font color='red'>powered down</font>.","singulo")
src.toggle_power()
toggle_power()
return
//emit some particles
for(var/obj/structure/particle_accelerator/particle_emitter/PE in connected_parts)
if(PE)
PE.emit_particle(src.strength)
PE.emit_particle(strength)
return
/obj/machinery/particle_accelerator/control_box/proc/part_scan()
for(var/obj/structure/particle_accelerator/fuel_chamber/F in orange(1,src))
src.dir = F.dir
dir = F.dir
connected_parts = list()
var/tally = 0
var/ldir = turn(dir,-90)
var/rdir = turn(dir,90)
var/odir = turn(dir,180)
var/turf/T = src.loc
var/turf/T = loc
T = get_step(T,rdir)
if(check_part(T,/obj/structure/particle_accelerator/fuel_chamber))
tally++
@@ -213,26 +224,26 @@
if(istype(PA, type))
if(PA.connect_master(src))
if(PA.report_ready(src))
src.connected_parts.Add(PA)
connected_parts.Add(PA)
return 1
return 0
/obj/machinery/particle_accelerator/control_box/proc/toggle_power()
src.active = !src.active
active = !active
investigate_log("turned [active?"<font color='red'>ON</font>":"<font color='green'>OFF</font>"] by [usr ? usr.key : "outside forces"]","singulo")
if(active)
msg_admin_attack("PA Control Computer turned ON by [key_name(usr, usr.client)](<A HREF='?_src_=holder;adminmoreinfo=\ref[usr]'>?</A>) in ([x],[y],[z] - <A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)",0,1)
log_game("PA Control Computer turned ON by [usr.ckey]([usr]) in ([x],[y],[z])")
use_log += text("\[[time_stamp()]\] <font color='red'>[usr.name] ([usr.ckey]) has turned on the PA Control Computer.</font>")
if(src.active)
src.use_power = 2
if(active)
use_power = 2
for(var/obj/structure/particle_accelerator/part in connected_parts)
part.strength = src.strength
part.strength = strength
part.powered = 1
part.update_icon()
else
src.use_power = 1
use_power = 1
for(var/obj/structure/particle_accelerator/part in connected_parts)
part.strength = null
part.powered = 0
@@ -262,13 +273,13 @@
else
dat += "Off <BR>"
dat += "<A href='?src=[UID()];togglep=1'>Toggle Power</A><BR><BR>"
dat += "Particle Strength: [src.strength] "
dat += "Particle Strength: [strength] "
dat += "<A href='?src=[UID()];strengthdown=1'>--</A>|<A href='?src=[UID()];strengthup=1'>++</A><BR><BR>"
//user << browse(dat, "window=pacontrol;size=420x500")
//onclose(user, "pacontrol")
var/datum/browser/popup = new(user, "pacontrol", name, 420, 500)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
return
+1 -1
View File
@@ -375,7 +375,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/power/smes/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/power/smes/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["nameTag"] = name_tag
+1 -1
View File
@@ -395,7 +395,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/power/solar_control/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/power/solar_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["generated"] = round(lastgen)
@@ -265,7 +265,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/power/supermatter_shard/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/power/supermatter_shard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["integrity_percentage"] = round(get_integrity())
@@ -121,7 +121,7 @@
// open the new ui window
ui.open()
/obj/machinery/chem_dispenser/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/chem_dispenser/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["amount"] = amount
@@ -136,7 +136,7 @@
ui = new(user, src, ui_key, "chem_heater.tmpl", "ChemHeater", 350, 270)
ui.open()
/obj/machinery/chem_heater/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/chem_heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["targetTemp"] = desired_temp
@@ -328,7 +328,7 @@
ui = new(user, src, ui_key, "chem_master.tmpl", name, 575, 400)
ui.open()
/obj/machinery/chem_master/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/chem_master/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["condi"] = condi
+1 -1
View File
@@ -247,7 +247,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/disposal/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/disposal/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/pressure = Clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100)
+1 -1
View File
@@ -652,7 +652,7 @@ won't update every console in existence) but it's more of a hassle to do. Also,
ui = new(user, src, ui_key, "r_n_d.tmpl", src.name, 800, 550)
ui.open()
/obj/machinery/computer/rdconsole/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/rdconsole/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
files.RefreshResearch()
@@ -126,7 +126,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/radiocarbon_spectrometer/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/radiocarbon_spectrometer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["scanned_item"] = (scanned_item ? scanned_item.name : "")
data["scanned_item_desc"] = (scanned_item ? (scanned_item.desc ? scanned_item.desc : "No information on record.") : "")
@@ -75,7 +75,7 @@
ui = new(user, src, ui_key, "keycard_auth.tmpl", "Keycard Authentication Device UI", 540, 320)
ui.open()
/obj/machinery/keycard_auth/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/keycard_auth/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["screen"] = screen
data["event"] = event
+7 -4
View File
@@ -8,10 +8,13 @@
/atom/movable/lighting_overlay/onShuttleMove()
return 0
/obj/onShuttleMove()
if(invisibility >= 101)
return 0
. = ..()
/obj/effect/landmark/shuttle_import/onShuttleMove()
// Used for marking where to preview/load shuttles
return 0
/obj/docking_port/onShuttleMove()
// Stationary ports shouldn't move, mobile ones move themselves
return 0
/obj/machinery/door/onShuttleMove()
. = ..()
+1 -2
View File
@@ -59,13 +59,12 @@
ui_interact(user)
/obj/machinery/shuttle_manipulator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// update the ui if it exists, returns null if no ui is passed/found
ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "shuttle_manipulator.tmpl", "Shuttle Manipulator", 660, 700)
ui = new(user, src, ui_key, "shuttle_manipulator.tmpl", "Shuttle Manipulator", 660, 700, null, null, admin_state)
// when the ui is first opened this is the data it will use
// open the new ui window
ui.open()
+2 -2
View File
@@ -430,7 +430,7 @@
ui = new(user, src, ui_key, "order_console.tmpl", name, ORDER_SCREEN_WIDTH, ORDER_SCREEN_HEIGHT)
ui.open()
/obj/machinery/computer/ordercomp/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/ordercomp/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["last_viewed_group"] = last_viewed_group
@@ -579,7 +579,7 @@
ui = new(user, src, ui_key, "supply_console.tmpl", name, SUPPLY_SCREEN_WIDTH, SUPPLY_SCREEN_HEIGHT)
ui.open()
/obj/machinery/computer/supplycomp/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/supplycomp/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["last_viewed_group"] = last_viewed_group
+4 -3
View File
@@ -30,7 +30,7 @@
var/turf/T
var/obj/item/pod_parts/pod_frame/linked
var/obj/item/pod_parts/pod_frame/pointer
var/connectedparts = list()
var/list/connectedparts = list()
neededparts -= src
//log_admin("Starting with [src]")
linked = src
@@ -44,7 +44,8 @@
connectedparts += pointer
linked = pointer
pointer = null
//log_admin("Parts left: [neededparts.len]") //len not working
if(connectedparts.len < 4)
return 0
for(var/i = 1; i <=4; i++)
var/obj/item/pod_parts/pod_frame/F = connectedparts[i]
if(F.type in neededparts) //if one of the items can be founded in neededparts
@@ -122,4 +123,4 @@
name = "civilian pod armor"
icon = 'icons/goonstation/pods/pod_parts.dmi'
icon_state = "pod_armor_civ"
desc = "Spacepod armor. This is the civilian version. It looks rather flimsy."
desc = "Spacepod armor. This is the civilian version. It looks rather flimsy."
+6
View File
@@ -57,6 +57,12 @@
typepath = /obj/item/weapon/storage/box/dice
cost = 200
/datum/storeitem/nanomob_booster
name = "Nano-Mob Hunter Trading Card Booster Pack"
desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!"
typepath = /obj/item/weapon/storage/box/nanomob_booster_pack
cost = 250
/datum/storeitem/crayons
name = "Crayons"
desc = "Let security know how they're doing by scrawling lovenotes all over their hallways."