mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-18 10:33:30 +01:00
Merge branch 'master' of https://github.com/ParadiseSS13/Paradise into ann2
# Conflicts: # code/_globalvars/lists/objects.dm
This commit is contained in:
@@ -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>")
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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?"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -128,6 +128,7 @@ var/global/dmm_suite/preloader/_preloader = new
|
||||
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], min(ycrd, world.maxy))
|
||||
|
||||
var/maxx = xcrdStart
|
||||
log_debug("[xcrdStart]")
|
||||
if(measureOnly)
|
||||
for(var/line in gridLines)
|
||||
maxx = max(maxx, xcrdStart + length(line) / key_len - 1)
|
||||
@@ -154,7 +155,6 @@ var/global/dmm_suite/preloader/_preloader = new
|
||||
maxx = max(maxx, xcrd)
|
||||
++xcrd
|
||||
--ycrd
|
||||
|
||||
bounds[MAP_MAXX] = max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx)
|
||||
|
||||
CHECK_TICK
|
||||
@@ -166,7 +166,7 @@ var/global/dmm_suite/preloader/_preloader = new
|
||||
log_debug("Loaded map in [stop_watch(watch)]s.")
|
||||
qdel(LM)
|
||||
if(bounds[MAP_MINX] == 1.#INF) // Shouldn't need to check every item
|
||||
log_runtime(EXCEPTION("Bad Map bounds."), src, list(
|
||||
log_runtime(EXCEPTION("Bad Map bounds in [fname]"), src, list(
|
||||
"Min x: [bounds[MAP_MINX]]",
|
||||
"Min y: [bounds[MAP_MINY]]",
|
||||
"Min z: [bounds[MAP_MINZ]]",
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -148,13 +148,15 @@
|
||||
flags = GLASSESCOVERSEYES
|
||||
slot_flags = SLOT_EYES
|
||||
materials = list(MAT_GLASS = 250)
|
||||
var/emagged = 0
|
||||
var/vision_flags = 0
|
||||
var/darkness_view = 0//Base human is 2
|
||||
var/invis_override = 0
|
||||
var/darkness_view = 0 //Base human is 2
|
||||
var/invis_view = SEE_INVISIBLE_LIVING
|
||||
var/invisa_view = 0
|
||||
var/color_view = null//overrides client.color while worn
|
||||
var/invis_override = 0
|
||||
|
||||
var/emagged = 0
|
||||
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
|
||||
put_on_delay = 25
|
||||
burn_state = FIRE_PROOF
|
||||
@@ -277,8 +279,8 @@ BLIND // can't see anything
|
||||
var/blockTracking // Do we block AI tracking?
|
||||
var/HUDType = null
|
||||
var/darkness_view = 0
|
||||
var/helmet_goggles_invis_view = 0
|
||||
var/vision_flags = 0
|
||||
var/see_darkness = 1
|
||||
var/can_toggle = null
|
||||
|
||||
//Mask
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
/obj/item/clothing/glasses
|
||||
name = "glasses"
|
||||
icon = 'icons/obj/clothing/glasses.dmi'
|
||||
//w_class = 2
|
||||
//flags = GLASSESCOVERSEYES
|
||||
//slot_flags = SLOT_EYES
|
||||
//var/vision_flags = 0
|
||||
//var/darkness_view = 0//Base human is 2
|
||||
var/prescription = 0
|
||||
var/prescription_upgradable = 0
|
||||
var/see_darkness = 1
|
||||
|
||||
/obj/item/clothing/glasses/New()
|
||||
. = ..()
|
||||
if(prescription_upgradable && prescription)
|
||||
@@ -49,8 +37,8 @@
|
||||
item_state = "glasses"
|
||||
origin_tech = "magnets=2;engineering=2"
|
||||
vision_flags = SEE_TURFS
|
||||
invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
prescription_upgradable = 1
|
||||
see_darkness = 0 //don't render darkness while wearing mesons
|
||||
species_fit = list("Vox")
|
||||
sprite_sheets = list(
|
||||
"Vox" = 'icons/mob/species/vox/eyes.dmi',
|
||||
@@ -112,7 +100,7 @@
|
||||
icon_state = "nvpurple"
|
||||
item_state = "glasses"
|
||||
darkness_view = 8
|
||||
see_darkness = 0
|
||||
invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
|
||||
/obj/item/clothing/glasses/janitor
|
||||
name = "Janitorial Goggles"
|
||||
@@ -131,7 +119,7 @@
|
||||
item_state = "glasses"
|
||||
origin_tech = "magnets=2"
|
||||
darkness_view = 8
|
||||
see_darkness = 0
|
||||
invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
species_fit = list("Vox")
|
||||
sprite_sheets = list(
|
||||
"Vox" = 'icons/mob/species/vox/eyes.dmi',
|
||||
@@ -255,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
|
||||
@@ -400,7 +397,6 @@
|
||||
item_state = "glasses"
|
||||
origin_tech = "magnets=3"
|
||||
vision_flags = SEE_MOBS
|
||||
invisa_view = 2
|
||||
flash_protect = -1
|
||||
species_fit = list("Vox")
|
||||
sprite_sheets = list(
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
icon_state = "healthhudnight"
|
||||
item_state = "glasses"
|
||||
darkness_view = 8
|
||||
see_darkness = 0
|
||||
invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
prescription_upgradable = 0
|
||||
|
||||
/obj/item/clothing/glasses/hud/diagnostic
|
||||
@@ -66,7 +66,7 @@
|
||||
icon_state = "diagnostichudnight"
|
||||
item_state = "glasses"
|
||||
darkness_view = 8
|
||||
see_darkness = 0
|
||||
invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
prescription_upgradable = 0
|
||||
|
||||
/obj/item/clothing/glasses/hud/security
|
||||
@@ -95,14 +95,13 @@
|
||||
icon_state = "jensenshades"
|
||||
item_state = "jensenshades"
|
||||
vision_flags = SEE_MOBS
|
||||
invisa_view = 2
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/night
|
||||
name = "\improper Night Vision Security HUD"
|
||||
desc = "An advanced heads-up display which provides id data and vision in complete darkness."
|
||||
icon_state = "securityhudnight"
|
||||
darkness_view = 8
|
||||
see_darkness = 0
|
||||
invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
prescription_upgradable = 0
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/sunglasses
|
||||
@@ -133,5 +132,5 @@
|
||||
icon_state = "hydroponichudnight"
|
||||
item_state = "glasses"
|
||||
darkness_view = 8
|
||||
see_darkness = 0
|
||||
invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
prescription_upgradable = 0
|
||||
@@ -63,7 +63,7 @@
|
||||
name = "night-vision helmet"
|
||||
desc = "A helmet with a built-in pair of night vision goggles."
|
||||
icon_state = "helmetNVG"
|
||||
see_darkness = 0
|
||||
helmet_goggles_invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
|
||||
/obj/item/clothing/head/helmet/alt
|
||||
name = "bulletproof helmet"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
|
||||
unacidable = 1
|
||||
vision_flags = SEE_MOBS
|
||||
see_darkness = 0
|
||||
helmet_goggles_invis_view = SEE_INVISIBLE_MINIMUM //don't render darkness while wearing these
|
||||
HUDType = MEDHUD
|
||||
strip_delay = 130
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
if(!computer.cardslot)
|
||||
computer.Crash(MISSING_PERIPHERAL)
|
||||
return
|
||||
usr.set_machine(src)
|
||||
scan = computer.cardslot.reader
|
||||
if(!interactable())
|
||||
return
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
if(!computer.cardslot)
|
||||
computer.Crash(MISSING_PERIPHERAL)
|
||||
return
|
||||
usr.set_machine(src)
|
||||
scan = computer.cardslot.reader
|
||||
|
||||
if(computer.cardslot.dualslot)
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
var/alarms = list("Fire"=list(), "Atmosphere"=list(), "Power"=list())
|
||||
|
||||
interact(mob/user)
|
||||
usr.set_machine(src)
|
||||
if(!interactable())
|
||||
return
|
||||
var/dat = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n"
|
||||
|
||||
@@ -16,6 +16,8 @@ var/global/datum/controller/process/garbage_collector/garbageCollector
|
||||
// Whether a datum was hard-deleted by the GC; 0 if not, 1 if it was queued, -1 if directly deleted
|
||||
/datum/var/hard_deleted = 0
|
||||
|
||||
|
||||
|
||||
/datum/controller/process/garbage_collector
|
||||
var/list/queue = new
|
||||
var/del_everything = 0
|
||||
@@ -25,6 +27,11 @@ var/global/datum/controller/process/garbage_collector/garbageCollector
|
||||
var/hard_dels = 0
|
||||
var/soft_dels = 0
|
||||
|
||||
// all types that did not respect qdel(A, force=TRUE) and returned one
|
||||
// of the immortality qdel hints
|
||||
var/list/noforcerespect = list()
|
||||
|
||||
|
||||
/datum/controller/process/garbage_collector/proc/addTrash(var/datum/D)
|
||||
if(!istype(D) || del_everything)
|
||||
del(D)
|
||||
@@ -86,7 +93,7 @@ var/global/datum/controller/process/garbage_collector/garbageCollector
|
||||
dels_count++
|
||||
|
||||
// Effectively replaces del for any datum-based type
|
||||
/proc/qdel(var/datum/D)
|
||||
/proc/qdel(datum/D, force=FALSE)
|
||||
if(isnull(D))
|
||||
return
|
||||
|
||||
@@ -105,7 +112,7 @@ var/global/datum/controller/process/garbage_collector/garbageCollector
|
||||
var/hint
|
||||
D.gcDestroyed = world.time
|
||||
try
|
||||
hint = D.Destroy()
|
||||
hint = D.Destroy(force)
|
||||
catch(var/exception/e)
|
||||
if(istype(e))
|
||||
log_runtime(e, D, "Caught by qdel() destroying [D.type]")
|
||||
@@ -124,10 +131,19 @@ var/global/datum/controller/process/garbage_collector/garbageCollector
|
||||
switch(hint)
|
||||
if(QDEL_HINT_QUEUE) //qdel should queue the object for deletion
|
||||
garbageCollector.addTrash(D)
|
||||
if(QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destroy.
|
||||
return
|
||||
if(QDEL_HINT_IWILLGC) //functionally the same as the above. qdel should assume the object will gc on its own, and not check it.
|
||||
return
|
||||
if (QDEL_HINT_LETMELIVE, QDEL_HINT_IWILLGC) //qdel should let the object live after calling destory.
|
||||
if(!force)
|
||||
return
|
||||
// Returning LETMELIVE after being told to force destroy
|
||||
// indicates the objects Destroy() does not respect force
|
||||
if(!("[D.type]" in garbageCollector.noforcerespect))
|
||||
garbageCollector.noforcerespect += "[D.type]"
|
||||
gcwarning("WARNING: [D.type] has been force deleted, but is \
|
||||
returning an immortal QDEL_HINT, indicating it does \
|
||||
not respect the force flag for qdel(). It has been \
|
||||
placed in the queue, further instances of this type \
|
||||
will also be queued.")
|
||||
garbageCollector.addTrash(D)
|
||||
if(QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
|
||||
D.hard_deleted = -1 // -1 means "this hard del skipped the queue", used for profiling
|
||||
del(D)
|
||||
@@ -157,7 +173,7 @@ var/global/datum/controller/process/garbage_collector/garbageCollector
|
||||
* Like Del(), but for qdel.
|
||||
* Called BEFORE qdel moves shit.
|
||||
*/
|
||||
/datum/proc/Destroy()
|
||||
/datum/proc/Destroy(force=FALSE)
|
||||
tag = null
|
||||
return QDEL_HINT_QUEUE // Garbage Collect everything.
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -125,6 +125,11 @@
|
||||
update_hair() //rebuild hair
|
||||
update_fhair()
|
||||
update_head_accessory()
|
||||
// Bandanas and paper hats go on the head but are not head clothing
|
||||
if(istype(I,/obj/item/clothing/head))
|
||||
var/obj/item/clothing/head/hat = I
|
||||
if(hat.vision_flags || hat.darkness_view || hat.helmet_goggles_invis_view)
|
||||
update_sight()
|
||||
head_update(I)
|
||||
update_inv_head()
|
||||
else if(I == r_ear)
|
||||
@@ -275,6 +280,11 @@
|
||||
update_hair(redraw_mob) //rebuild hair
|
||||
update_fhair(redraw_mob)
|
||||
update_head_accessory(redraw_mob)
|
||||
// paper + bandanas
|
||||
if(istype(W, /obj/item/clothing/head))
|
||||
var/obj/item/clothing/head/hat = W
|
||||
if(hat.vision_flags || hat.darkness_view || hat.helmet_goggles_invis_view)
|
||||
update_sight()
|
||||
head_update(W)
|
||||
update_inv_head(redraw_mob)
|
||||
if(slot_shoes)
|
||||
|
||||
@@ -630,10 +630,10 @@ It'll return null if the organ doesn't correspond, so include null checks when u
|
||||
if(hat.darkness_view) // Pick the lowest of the two darkness_views between the glasses and helmet.
|
||||
lesser_darkview_bonus = min(hat.darkness_view,lesser_darkview_bonus)
|
||||
|
||||
if(!hat.see_darkness)
|
||||
H.see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
if(hat.helmet_goggles_invis_view)
|
||||
H.see_invisible = min(hat.helmet_goggles_invis_view, H.see_invisible)
|
||||
|
||||
if(istype(H.back, /obj/item/weapon/rig)) ///ahhhg so snowflakey
|
||||
if(istype(H.back, /obj/item/weapon/rig)) ///aghhh so snowflakey
|
||||
var/obj/item/weapon/rig/rig = H.back
|
||||
if(rig.visor)
|
||||
if(!rig.helmet || (H.head && rig.helmet == H.head))
|
||||
@@ -644,8 +644,7 @@ It'll return null if the organ doesn't correspond, so include null checks when u
|
||||
if(G.vision_flags) // MESONS
|
||||
H.sight |= G.vision_flags
|
||||
|
||||
if(!G.see_darkness)
|
||||
H.see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
H.see_invisible = min(G.invis_view, H.see_invisible)
|
||||
|
||||
if(lesser_darkview_bonus != INFINITY)
|
||||
H.see_in_dark = max(lesser_darkview_bonus, H.see_in_dark)
|
||||
@@ -660,6 +659,7 @@ It'll return null if the organ doesn't correspond, so include null checks when u
|
||||
if(XRAY in H.mutations)
|
||||
H.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
|
||||
H.see_in_dark = max(H.see_in_dark, 8)
|
||||
H.see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
|
||||
if(H.see_override) //Override all
|
||||
H.see_invisible = H.see_override
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
/mob/living/simple_animal/hostile/winter/snowman
|
||||
name = "snowman"
|
||||
desc = "A very angry snowman. Doesn't look like it wants to play around..."
|
||||
maxHealth = 75 //slightly beefier to account for it's need to get in your face
|
||||
health = 75
|
||||
icon_state = "snowman"
|
||||
icon_living = "snowman"
|
||||
icon_dead = "snowman-dead"
|
||||
@@ -34,7 +36,7 @@
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/winter/snowman/death(gibbed)
|
||||
if(prob(50)) //50% chance to drop weapon on death, if it has one to drop
|
||||
if(prob(50) && !ranged) //50% chance to drop candy cane sword on death, if it has one to drop
|
||||
loot = list(/obj/item/weapon/melee/candy_sword)
|
||||
if(prob(20)) //chance to become a stationary snowman structure instead of a corpse
|
||||
loot.Add(/obj/structure/snowman)
|
||||
@@ -45,6 +47,8 @@
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/winter/snowman/ranged
|
||||
maxHealth = 50
|
||||
health = 50
|
||||
ranged = 1
|
||||
retreat_distance = 5
|
||||
minimum_distance = 5
|
||||
@@ -57,7 +61,8 @@
|
||||
icon_living = "reindeer"
|
||||
icon_dead = "reindeer-dead"
|
||||
butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat = 3)
|
||||
|
||||
maxHealth = 80
|
||||
health = 80
|
||||
melee_damage_lower = 5
|
||||
melee_damage_upper = 10
|
||||
gold_core_spawnable = CHEM_MOB_SPAWN_HOSTILE
|
||||
@@ -83,17 +88,21 @@
|
||||
qdel(src) //hide the body
|
||||
|
||||
/mob/living/simple_animal/hostile/winter/santa/stage_1 //stage 1: slow melee
|
||||
maxHealth = 150
|
||||
health = 150
|
||||
desc = "GET THE FAT MAN!"
|
||||
next_stage = /mob/living/simple_animal/hostile/winter/santa/stage_2
|
||||
death_message = "<span class='danger'>HO HO HO! YOU THOUGHT IT WOULD BE THIS EASY?!?</span>"
|
||||
speed = 2
|
||||
melee_damage_lower = 5
|
||||
melee_damage_upper = 15
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 20
|
||||
|
||||
/mob/living/simple_animal/hostile/winter/santa/stage_2 //stage 2: slow ranged
|
||||
desc = "GET THE FAT MAN AGAIN!"
|
||||
next_stage = /mob/living/simple_animal/hostile/winter/santa/stage_3
|
||||
death_message = "<span class='danger'>YOU'VE BEEN VERY NAUGHTY! PREPARE TO DIE!</span>"
|
||||
maxHealth = 200 //DID YOU REALLY BELIEVE IT WOULD BE THIS EASY!??!!
|
||||
health = 200
|
||||
ranged = 1
|
||||
projectiletype = /obj/item/projectile/ornament
|
||||
retreat_distance = 5
|
||||
@@ -103,6 +112,8 @@
|
||||
desc = "WHY WON'T HE DIE ALREADY!?"
|
||||
next_stage = /mob/living/simple_animal/hostile/winter/santa/stage_4
|
||||
death_message = "<span class='danger'>FACE MY FINAL FORM AND KNOW DESPAIR!</span>"
|
||||
maxHealth = 250
|
||||
health = 250
|
||||
ranged = 1
|
||||
rapid = 1
|
||||
speed = 0 //he's lost some weight from the fighting
|
||||
@@ -113,13 +124,13 @@
|
||||
/mob/living/simple_animal/hostile/winter/santa/stage_4 //stage 4: fast spinebreaker
|
||||
name = "Final Form Santa"
|
||||
desc = "WHAT THE HELL IS HE!?! WHY WON'T HE STAY DEAD!?!"
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
maxHealth = 300 //YOU FACE JARAX- I MEAN SANTA!
|
||||
health = 300
|
||||
speed = 0 //he's lost some weight from the fighting
|
||||
|
||||
environment_smash = 2 //naughty walls must be punished too
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 25 //that's gonna leave a mark, for sure
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 30 //that's gonna leave a mark, for sure
|
||||
|
||||
/mob/living/simple_animal/hostile/winter/santa/stage_4/death(gibbed)
|
||||
to_chat(world, "<span class='notice'><hr></span>")
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -207,7 +207,7 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s
|
||||
..()
|
||||
|
||||
|
||||
/obj/item/device/camera/proc/get_icon(list/turfs, turf/center,mob/user)
|
||||
/obj/item/device/camera/proc/get_icon(list/turfs, turf/center, mob/user)
|
||||
|
||||
//Bigger icon base to capture those icons that were shifted to the next tile
|
||||
//i.e. pretty much all wall-mounted machinery
|
||||
@@ -218,11 +218,11 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s
|
||||
|
||||
var/atoms[] = list()
|
||||
for(var/turf/the_turf in turfs)
|
||||
// Add outselves to the list of stuff to draw
|
||||
// Add ourselves to the list of stuff to draw
|
||||
atoms.Add(the_turf);
|
||||
// As well as anything that isn't invisible.
|
||||
for(var/atom/A in the_turf)
|
||||
if(A.invisibility )
|
||||
if(A.invisibility)
|
||||
if(see_ghosts && istype(A,/mob/dead/observer))
|
||||
var/mob/dead/observer/O = A
|
||||
if(O.following)
|
||||
@@ -234,7 +234,16 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s
|
||||
else//its not a ghost
|
||||
continue
|
||||
else//not invisable, not a spookyghost add it.
|
||||
atoms.Add(A)
|
||||
var/disguised = null
|
||||
if(user.viewing_alternate_appearances && user.viewing_alternate_appearances.len && ishuman(A) && A.alternate_appearances && A.alternate_appearances.len) //This whole thing and the stuff below just checks if the atom is a Solid Snake cosplayer.
|
||||
for(var/datum/alternate_appearance/alt_appearance in user.viewing_alternate_appearances)
|
||||
if(alt_appearance.owner == A) //If it turns out they are, don't blow their cover. That'd be rude.
|
||||
atoms.Add(image(alt_appearance.img, A.loc, layer = 4, dir = A.dir)) //Render their disguise.
|
||||
atoms.Remove(A) //Don't blow their cover.
|
||||
disguised = 1
|
||||
continue
|
||||
if(!disguised) //If they aren't, treat them normally.
|
||||
atoms.Add(A)
|
||||
|
||||
|
||||
// Sort the atoms into their layers
|
||||
@@ -320,14 +329,10 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s
|
||||
on = 1
|
||||
|
||||
/obj/item/device/camera/proc/can_capture_turf(turf/T, mob/user)
|
||||
var/mob/dummy = new(T) //Go go visibility check dummy
|
||||
var/viewer = user
|
||||
if(user.client) //To make shooting through security cameras possible
|
||||
viewer = user.client.eye
|
||||
var/can_see = (dummy in viewers(world.view, viewer)) != null
|
||||
|
||||
dummy.loc = null
|
||||
dummy = null //Alas, nameless creature //garbage collect it instead
|
||||
var/can_see = (T in view(viewer)) //No x-ray vision cameras.
|
||||
return can_see
|
||||
|
||||
/obj/item/device/camera/proc/captureimage(atom/target, mob/user, flag)
|
||||
@@ -350,7 +355,7 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s
|
||||
printpicture(user, P)
|
||||
|
||||
/obj/item/device/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag)
|
||||
var/icon/photoimage = get_icon(turfs, target,user)
|
||||
var/icon/photoimage = get_icon(turfs, target, user)
|
||||
|
||||
var/icon/small_img = icon(photoimage)
|
||||
var/icon/tiny_img = icon(photoimage)
|
||||
@@ -532,13 +537,13 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s
|
||||
if(get_dist(src, M) <= canhear_range)
|
||||
talk_into(M, msg)
|
||||
for(var/obj/machinery/computer/security/telescreen/T in machines)
|
||||
if(T.current == camera)
|
||||
if(T.watchers[M] == camera)
|
||||
T.audible_message("<span class='game radio'><span class='name'>(Newscaster) [M]</span> says, '[msg]'", hearing_distance = 2)
|
||||
|
||||
/obj/item/device/videocam/hear_message(mob/M as mob, msg)
|
||||
if(camera && on)
|
||||
for(var/obj/machinery/computer/security/telescreen/T in machines)
|
||||
if(T.current == camera)
|
||||
if(T.watchers[M] == camera)
|
||||
T.audible_message("<span class='game radio'><span class='name'>(Newscaster) [M]</span> [msg]", hearing_distance = 2)
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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."
|
||||
@@ -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()
|
||||
@@ -896,7 +896,7 @@
|
||||
return 1
|
||||
|
||||
/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob)
|
||||
if(isobserver(user) && check_rights(R_ADMIN, 0, user))
|
||||
if(user.can_admin_interact())
|
||||
return 1
|
||||
if(isAI(user) || isrobot(user))
|
||||
return 1
|
||||
@@ -904,7 +904,7 @@
|
||||
return !locked
|
||||
|
||||
/obj/machinery/power/apc/proc/is_locked(mob/user as mob)
|
||||
if(isobserver(user) && check_rights(R_ADMIN, 0, user))
|
||||
if(user.can_admin_interact())
|
||||
return 0
|
||||
if(isAI(user) || isrobot(user))
|
||||
return 0
|
||||
|
||||
@@ -352,7 +352,7 @@ obj/item/projectile/kinetic/New()
|
||||
name = "snowball"
|
||||
icon_state = "snowball"
|
||||
hitsound = 'sound/items/dodgeball.ogg'
|
||||
damage = 3
|
||||
damage = 4
|
||||
damage_type = BURN
|
||||
|
||||
/obj/item/projectile/snowball/on_hit(atom/target) //chilling
|
||||
@@ -366,7 +366,7 @@ obj/item/projectile/kinetic/New()
|
||||
name = "ornament"
|
||||
icon_state = "ornament-1"
|
||||
hitsound = 'sound/effects/Glasshit.ogg'
|
||||
damage = 5
|
||||
damage = 7
|
||||
damage_type = BRUTE
|
||||
|
||||
/obj/item/projectile/ornament/New()
|
||||
|
||||
@@ -91,6 +91,9 @@
|
||||
var/datum/announcement/priority/emergency_shuttle_called = new(0, new_sound = sound('sound/AI/shuttlecalled.ogg'))
|
||||
var/datum/announcement/priority/emergency_shuttle_recalled = new(0, new_sound = sound('sound/AI/shuttlerecalled.ogg'))
|
||||
|
||||
var/canRecall = TRUE //no bad condom, do not recall the crew transfer shuttle!
|
||||
|
||||
|
||||
/obj/docking_port/mobile/emergency/register()
|
||||
if(!..())
|
||||
return 0 //shuttle master not initialized
|
||||
@@ -98,6 +101,16 @@
|
||||
shuttle_master.emergency = src
|
||||
return 1
|
||||
|
||||
/obj/docking_port/mobile/emergency/Destroy(force)
|
||||
if(force)
|
||||
// This'll make the shuttle subsystem use the backup shuttle.
|
||||
if(shuttle_master.emergency == src)
|
||||
// If we're the selected emergency shuttle
|
||||
shuttle_master.emergencyDeregister()
|
||||
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/docking_port/mobile/emergency/timeLeft(divisor)
|
||||
if(divisor <= 0)
|
||||
divisor = 10
|
||||
@@ -138,6 +151,9 @@
|
||||
emergency_shuttle_called.Announce("The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][shuttle_master.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]")
|
||||
|
||||
/obj/docking_port/mobile/emergency/cancel(area/signalOrigin)
|
||||
if(!canRecall)
|
||||
return
|
||||
|
||||
if(mode != SHUTTLE_CALL)
|
||||
return
|
||||
|
||||
@@ -286,3 +302,19 @@
|
||||
var/list/turfs = get_area_turfs(target_area)
|
||||
var/turf/T = pick(turfs)
|
||||
src.loc = T
|
||||
|
||||
/obj/docking_port/mobile/emergency/backup
|
||||
name = "backup shuttle"
|
||||
id = "backup"
|
||||
dwidth = 2
|
||||
width = 8
|
||||
height = 8
|
||||
dir = 4
|
||||
|
||||
roundstart_move = "backup_away"
|
||||
|
||||
/obj/docking_port/mobile/emergency/backup/register()
|
||||
var/current_emergency = shuttle_master.emergency
|
||||
..()
|
||||
shuttle_master.emergency = current_emergency
|
||||
shuttle_master.backup_shuttle = src
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Shuttle on-movement //
|
||||
/atom/movable/proc/onShuttleMove(turf/T1, rotation)
|
||||
if(rotation)
|
||||
shuttleRotate(rotation)
|
||||
forceMove(T1)
|
||||
return 1
|
||||
|
||||
/atom/movable/lighting_overlay/onShuttleMove()
|
||||
return 0
|
||||
|
||||
/obj/onShuttleMove()
|
||||
if(invisibility >= 101)
|
||||
return 0
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/door/onShuttleMove()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
addtimer(src, "close", 0, TRUE, 0, 1)
|
||||
// Close any nearby airlocks as well
|
||||
for(var/obj/machinery/door/D in orange(1, src))
|
||||
addtimer(D, "close", 0, TRUE, 0, 1)
|
||||
|
||||
/obj/machinery/door/airlock/onShuttleMove()
|
||||
. = ..()
|
||||
if(id_tag == "s_docking_airlock")
|
||||
addtimer(src, "lock", 0, TRUE)
|
||||
|
||||
/mob/onShuttleMove()
|
||||
if(!move_on_shuttle)
|
||||
return 0
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(client)
|
||||
if(buckled)
|
||||
shake_camera(src, 2, 1) // turn it down a bit come on
|
||||
else
|
||||
shake_camera(src, 7, 1)
|
||||
|
||||
/mob/living/carbon/onShuttleMove()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!buckled)
|
||||
Weaken(3)
|
||||
|
||||
// After docking //
|
||||
/atom/proc/postDock(obj/docking_port/S1)
|
||||
if(smooth)
|
||||
smooth_icon(src)
|
||||
|
||||
/obj/machinery/door/airlock/postDock(obj/docking_port/stationary/S1)
|
||||
. = ..()
|
||||
if(!S1.lock_shuttle_doors && id_tag == "s_docking_airlock")
|
||||
addtimer(src, "unlock", 0, TRUE)
|
||||
|
||||
// Shuttle Rotation //
|
||||
/atom/proc/shuttleRotate(rotation)
|
||||
//rotate our direction
|
||||
dir = angle2dir(rotation+dir2angle(dir))
|
||||
|
||||
//rotate the pixel offsets too.
|
||||
if(pixel_x || pixel_y)
|
||||
if(rotation < 0)
|
||||
rotation += 360
|
||||
for(var/turntimes=rotation/90;turntimes>0;turntimes--)
|
||||
var/oldPX = pixel_x
|
||||
var/oldPY = pixel_y
|
||||
pixel_x = oldPY
|
||||
pixel_y = (oldPX*(-1))
|
||||
+151
-136
@@ -18,9 +18,18 @@
|
||||
var/dwidth = 0 //position relative to covered area, perpendicular to dir
|
||||
var/dheight = 0 //position relative to covered area, parallel to dir
|
||||
|
||||
// A timid shuttle will not register itself with the shuttle subsystem
|
||||
// All shuttle templates are timid
|
||||
var/timid = FALSE
|
||||
|
||||
//these objects are indestructable
|
||||
/obj/docking_port/Destroy()
|
||||
return QDEL_HINT_LETMELIVE
|
||||
/obj/docking_port/Destroy(force)
|
||||
if(force)
|
||||
..()
|
||||
. = QDEL_HINT_HARDDEL_NOW
|
||||
else
|
||||
|
||||
return QDEL_HINT_LETMELIVE
|
||||
|
||||
/obj/docking_port/singularity_pull()
|
||||
return
|
||||
@@ -212,6 +221,14 @@
|
||||
highlight("#0f0")
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/docking_port/mobile/initialize()
|
||||
if(!timid)
|
||||
register()
|
||||
..()
|
||||
|
||||
/obj/docking_port/mobile/register()
|
||||
if(!shuttle_master)
|
||||
throw EXCEPTION("docking port [src] could not initialize.")
|
||||
@@ -226,6 +243,13 @@
|
||||
|
||||
return 1
|
||||
|
||||
/obj/docking_port/mobile/Destroy(force)
|
||||
if(force)
|
||||
shuttle_master.mobile -= src
|
||||
areaInstance = null
|
||||
destination = null
|
||||
previous = null
|
||||
return ..()
|
||||
|
||||
//this is a hook for custom behaviour. Maybe at some point we could add checks to see if engines are intact
|
||||
/obj/docking_port/mobile/proc/canMove()
|
||||
@@ -234,29 +258,50 @@
|
||||
//this is to check if this shuttle can physically dock at dock S
|
||||
/obj/docking_port/mobile/proc/canDock(obj/docking_port/stationary/S)
|
||||
if(!istype(S))
|
||||
return 1
|
||||
return SHUTTLE_NOT_A_DOCKING_PORT
|
||||
if(istype(S, /obj/docking_port/stationary/transit))
|
||||
return 0
|
||||
return SHUTTLE_CAN_DOCK
|
||||
//check dock is big enough to contain us
|
||||
if(dwidth > S.dwidth)
|
||||
return 2
|
||||
return SHUTTLE_DWIDTH_TOO_LARGE
|
||||
if(width-dwidth > S.width-S.dwidth)
|
||||
return 3
|
||||
return SHUTTLE_WIDTH_TOO_LARGE
|
||||
if(dheight > S.dheight)
|
||||
return 4
|
||||
return SHUTTLE_DHEIGHT_TOO_LARGE
|
||||
if(height-dheight > S.height-S.dheight)
|
||||
return 5
|
||||
return SHUTTLE_HEIGHT_TOO_LARGE
|
||||
//check the dock isn't occupied
|
||||
if(S.get_docked())
|
||||
return 6
|
||||
return 0 //0 means we can dock
|
||||
var/currently_docked = S.get_docked()
|
||||
if(currently_docked)
|
||||
// by someone other than us
|
||||
if(currently_docked != src)
|
||||
return SHUTTLE_SOMEONE_ELSE_DOCKED
|
||||
else
|
||||
// This isn't an error, per se, but we can't let the shuttle code
|
||||
// attempt to move us where we currently are, it will get weird.
|
||||
return SHUTTLE_ALREADY_DOCKED
|
||||
return SHUTTLE_CAN_DOCK
|
||||
|
||||
/obj/docking_port/mobile/proc/check_dock(obj/docking_port/stationary/S)
|
||||
var/status = canDock(S)
|
||||
if(status == SHUTTLE_CAN_DOCK)
|
||||
return TRUE
|
||||
else if(status == SHUTTLE_ALREADY_DOCKED)
|
||||
// We're already docked there, don't need to do anything.
|
||||
// Triggering shuttle movement code in place is weird
|
||||
return FALSE
|
||||
else
|
||||
var/msg = "check_dock(): shuttle [src] cannot dock at [S], error: [status]"
|
||||
message_admins(msg)
|
||||
throw EXCEPTION(msg)
|
||||
return FALSE
|
||||
|
||||
|
||||
//call the shuttle to destination S
|
||||
/obj/docking_port/mobile/proc/request(obj/docking_port/stationary/S)
|
||||
if(canDock(S))
|
||||
. = 1
|
||||
throw EXCEPTION("request(): shuttle cannot dock")
|
||||
return 1 //we can't dock at S
|
||||
|
||||
if(!check_dock(S))
|
||||
return
|
||||
|
||||
switch(mode)
|
||||
if(SHUTTLE_CALL)
|
||||
@@ -301,36 +346,48 @@
|
||||
else
|
||||
WARNING("shuttle \"[id]\" could not enter transit space. S0=[S0 ? S0.id : "null"] S1=[S1 ? S1.id : "null"]")
|
||||
|
||||
//default shuttleRotate
|
||||
/atom/proc/shuttleRotate(rotation)
|
||||
//rotate our direction
|
||||
dir = angle2dir(rotation+dir2angle(dir))
|
||||
|
||||
//rotate the pixel offsets too.
|
||||
if(pixel_x || pixel_y)
|
||||
if(rotation < 0)
|
||||
rotation += 360
|
||||
for(var/turntimes=rotation/90;turntimes>0;turntimes--)
|
||||
var/oldPX = pixel_x
|
||||
var/oldPY = pixel_y
|
||||
pixel_x = oldPY
|
||||
pixel_y = (oldPX*(-1))
|
||||
|
||||
/atom/proc/postDock()
|
||||
if(smooth)
|
||||
smooth_icon(src)
|
||||
/obj/docking_port/mobile/proc/jumpToNullSpace()
|
||||
// Destroys the docking port and the shuttle contents.
|
||||
// Not in a fancy way, it just ceases.
|
||||
var/obj/docking_port/stationary/S0 = get_docked()
|
||||
var/turf_type = /turf/space
|
||||
var/area_type = /area/space
|
||||
if(S0)
|
||||
if(S0.turf_type)
|
||||
turf_type = S0.turf_type
|
||||
if(S0.area_type)
|
||||
area_type = S0.area_type
|
||||
|
||||
var/list/L0 = return_ordered_turfs(x, y, z, dir, areaInstance)
|
||||
|
||||
//remove area surrounding docking port
|
||||
if(areaInstance.contents.len)
|
||||
var/area/A0 = locate("[area_type]")
|
||||
if(!A0)
|
||||
A0 = new area_type(null)
|
||||
for(var/turf/T0 in L0)
|
||||
A0.contents += T0
|
||||
|
||||
for(var/i in L0)
|
||||
var/turf/T0 = i
|
||||
if(!T0)
|
||||
continue
|
||||
T0.empty(turf_type)
|
||||
|
||||
qdel(src, force=TRUE)
|
||||
|
||||
//this is the main proc. It instantly moves our mobile port to stationary port S1
|
||||
//it handles all the generic behaviour, such as sanity checks, closing doors on the shuttle, stunning mobs, etc
|
||||
/obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/S1)
|
||||
. = canDock(S1)
|
||||
if(.)
|
||||
throw EXCEPTION("dock(): shuttle cannot dock")
|
||||
return .
|
||||
/obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/S1, force=FALSE)
|
||||
// Crashing this ship with NO SURVIVORS
|
||||
if(!force)
|
||||
if(!check_dock(S1))
|
||||
return -1
|
||||
|
||||
if(canMove())
|
||||
return -1
|
||||
if(canMove())
|
||||
return -1
|
||||
|
||||
// //rotate transit docking ports, so we don't need zillions of variants
|
||||
// if(istype(S1, /obj/docking_port/stationary/transit))
|
||||
@@ -367,9 +424,7 @@
|
||||
A0.contents += T0
|
||||
|
||||
//move or squish anything in the way ship at destination
|
||||
roadkill(L1, S1.dir)
|
||||
|
||||
var/list/door_unlock_list = list()
|
||||
roadkill(L0, L1, S1.dir)
|
||||
|
||||
for(var/i in 1 to L0.len)
|
||||
var/turf/T0 = L0[i]
|
||||
@@ -388,49 +443,8 @@
|
||||
Ts1.copy_air_with_tile(T0)
|
||||
|
||||
//move mobile to new location
|
||||
|
||||
|
||||
|
||||
for(var/atom/movable/AM in T0)
|
||||
if(rotation)
|
||||
AM.shuttleRotate(rotation)
|
||||
|
||||
if(istype(AM,/obj))
|
||||
var/obj/O = AM
|
||||
if(istype(O, /obj/docking_port/stationary))
|
||||
continue
|
||||
O.forceMove(T1)
|
||||
|
||||
//close open doors
|
||||
if(istype(O, /obj/machinery/door))
|
||||
var/obj/machinery/door/Door = O
|
||||
spawn(-1)
|
||||
if(Door)
|
||||
if(istype(Door, /obj/machinery/door/airlock))
|
||||
var/obj/machinery/door/airlock/A = Door
|
||||
A.close(0,1)
|
||||
if(A.id_tag == "s_docking_airlock")
|
||||
A.lock()
|
||||
door_unlock_list += A
|
||||
else
|
||||
Door.close()
|
||||
else if(istype(AM,/mob))
|
||||
var/mob/M = AM
|
||||
if(!M.move_on_shuttle)
|
||||
continue
|
||||
M.forceMove(T1)
|
||||
|
||||
//docking turbulence
|
||||
if(M.client)
|
||||
spawn(0)
|
||||
if(M.buckled)
|
||||
shake_camera(M, 2, 1) // turn it down a bit come on
|
||||
else
|
||||
shake_camera(M, 7, 1)
|
||||
if(istype(M, /mob/living/carbon))
|
||||
if(!M.buckled)
|
||||
M.Weaken(3)
|
||||
|
||||
AM.onShuttleMove(T1, rotation)
|
||||
|
||||
if(rotation)
|
||||
T1.shuttleRotate(rotation)
|
||||
@@ -448,40 +462,39 @@
|
||||
|
||||
for(var/A1 in L1)
|
||||
var/turf/T1 = A1
|
||||
T1.postDock()
|
||||
T1.postDock(S1)
|
||||
for(var/atom/movable/M in T1)
|
||||
M.postDock()
|
||||
M.postDock(S1)
|
||||
|
||||
loc = S1.loc
|
||||
dir = S1.dir
|
||||
|
||||
unlockPortDoors(S1)
|
||||
if(S1 && !S1.lock_shuttle_doors)
|
||||
for(var/obj/machinery/door/airlock/A in door_unlock_list)
|
||||
spawn(-1)
|
||||
A.unlock()
|
||||
|
||||
/*
|
||||
if(istype(S1, /obj/docking_port/stationary/transit))
|
||||
var/d = turn(dir, 180 + travelDir)
|
||||
for(var/turf/space/transit/T in S1.return_ordered_turfs())
|
||||
T.pushdirection = d
|
||||
T.update_icon()
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/obj/docking_port/mobile/proc/findTransitDock()
|
||||
var/obj/docking_port/stationary/transit/T = shuttle_master.getDock("[id]_transit")
|
||||
if(T && !canDock(T))
|
||||
if(T && check_dock(T))
|
||||
return T
|
||||
/* commented out due to issues with rotation
|
||||
for(var/obj/docking_port/stationary/transit/S in shuttle_master.transit)
|
||||
if(S.id)
|
||||
continue
|
||||
if(!canDock(S))
|
||||
return S
|
||||
*/
|
||||
|
||||
|
||||
/obj/docking_port/mobile/proc/findRoundstartDock()
|
||||
var/obj/docking_port/stationary/D
|
||||
D = shuttle_master.getDock(roundstart_move)
|
||||
|
||||
if(D)
|
||||
return D
|
||||
|
||||
/obj/docking_port/mobile/proc/dockRoundstart()
|
||||
// Instead of spending a lot of time trying to work out where to place
|
||||
// our shuttle, just create it somewhere empty and send it to where
|
||||
// it should go
|
||||
var/obj/docking_port/stationary/D = findRoundstartDock()
|
||||
return dock(D)
|
||||
|
||||
/obj/effect/landmark/shuttle_import
|
||||
name = "Shuttle Import"
|
||||
|
||||
|
||||
|
||||
//shuttle-door closing is handled in the dock() proc whilst looping through turfs
|
||||
@@ -506,41 +519,43 @@
|
||||
if(A.locked)
|
||||
A.unlock()
|
||||
|
||||
/obj/docking_port/mobile/proc/roadkill(list/L, dir, x, y)
|
||||
for(var/turf/T in L)
|
||||
for(var/atom/movable/AM in T)
|
||||
if(ismob(AM))
|
||||
if(istype(AM, /mob/living))
|
||||
var/mob/living/M = AM
|
||||
M.Paralyse(10)
|
||||
M.take_organ_damage(80)
|
||||
M.anchored = 0
|
||||
else
|
||||
continue
|
||||
/obj/docking_port/mobile/proc/roadkill(list/L0, list/L1, dir)
|
||||
var/list/hurt_mobs = list()
|
||||
for(var/i in 1 to L0.len)
|
||||
var/turf/T0 = L0[i]
|
||||
var/turf/T1 = L1[i]
|
||||
if(!T0 || !T1)
|
||||
continue
|
||||
|
||||
for(var/atom/movable/AM in T1)
|
||||
if(AM.pulledby)
|
||||
AM.pulledby.stop_pulling()
|
||||
if(ismob(AM))
|
||||
var/mob/M = AM
|
||||
if(M.buckled)
|
||||
M.buckled.unbuckle_mob(M, 1)
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
L.stop_pulling()
|
||||
if(L.anchored)
|
||||
L.gib()
|
||||
else
|
||||
if(!(L in hurt_mobs))
|
||||
hurt_mobs |= L
|
||||
L.visible_message("<span class='warning'>[L] is hit by \
|
||||
a hyperspace ripple[L.anchored ? "":" and is thrown clear"]!</span>",
|
||||
"<span class='userdanger'>You feel an immense \
|
||||
crushing pressure as the space around you ripples.</span>")
|
||||
L.Paralyse(10)
|
||||
L.ex_act(2)
|
||||
|
||||
// Move unanchored atoms
|
||||
if(!AM.anchored)
|
||||
step(AM, dir)
|
||||
else
|
||||
if(AM.simulated) //lighting overlays are static
|
||||
if(AM.simulated) // Don't qdel lighting overlays, they are static
|
||||
qdel(AM)
|
||||
/*
|
||||
//used to check if atom/A is within the shuttle's bounding box
|
||||
/obj/docking_port/mobile/proc/onShuttleCheck(atom/A)
|
||||
var/turf/T = get_turf(A)
|
||||
if(!T)
|
||||
return 0
|
||||
|
||||
var/list/L = return_coords()
|
||||
if(L[1] > L[3])
|
||||
L.Swap(1,3)
|
||||
if(L[2] > L[4])
|
||||
L.Swap(2,4)
|
||||
|
||||
if(L[1] <= T.x && T.x <= L[3])
|
||||
if(L[2] <= T.y && T.y <= L[4])
|
||||
return 1
|
||||
return 0
|
||||
*/
|
||||
//used by shuttle subsystem to check timers
|
||||
/obj/docking_port/mobile/proc/check()
|
||||
var/timeLeft = timeLeft(1)
|
||||
@@ -667,7 +682,7 @@
|
||||
for(var/obj/docking_port/stationary/S in shuttle_master.stationary)
|
||||
if(!options.Find(S.id))
|
||||
continue
|
||||
if(M.canDock(S))
|
||||
if(!M.check_dock(S))
|
||||
continue
|
||||
destination_found = 1
|
||||
dat += "<A href='?src=[UID()];move=[S.id]'>Send to [S.name]</A><br>"
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
|
||||
/obj/machinery/shuttle_manipulator
|
||||
name = "shuttle manipulator"
|
||||
desc = "I shall be telling this with a sigh\n\
|
||||
Somewhere ages and ages hence:\n\
|
||||
Two roads diverged in a wood, and I,\n\
|
||||
I took the one less traveled by,\n\
|
||||
And that has made all the difference."
|
||||
|
||||
icon = 'icons/obj/machines/shuttle_manipulator.dmi'
|
||||
icon_state = "holograph_on"
|
||||
|
||||
var/selected_menu_key = "stat"
|
||||
var/busy
|
||||
// UI state variables
|
||||
var/datum/map_template/shuttle/selected
|
||||
|
||||
var/obj/docking_port/mobile/existing_shuttle
|
||||
|
||||
var/obj/docking_port/mobile/preview_shuttle
|
||||
var/datum/map_template/shuttle/preview_template
|
||||
var/list/templates = list()
|
||||
var/list/shuttle_data = list()
|
||||
|
||||
/obj/machinery/shuttle_manipulator/New()
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/shuttle_manipulator/update_icon()
|
||||
overlays.Cut()
|
||||
var/image/hologram_projection = image(icon, "hologram_on")
|
||||
hologram_projection.pixel_y = 22
|
||||
var/image/hologram_ship = image(icon, "hologram_whiteship")
|
||||
hologram_ship.pixel_y = 27
|
||||
overlays += hologram_projection
|
||||
overlays += hologram_ship
|
||||
|
||||
/obj/machinery/shuttle_manipulator/attack_ghost(user as mob)
|
||||
attack_hand(user)
|
||||
|
||||
/proc/shuttlemode2str(mode)
|
||||
switch(mode)
|
||||
if(SHUTTLE_IDLE)
|
||||
. = "idle"
|
||||
if(SHUTTLE_RECALL)
|
||||
. = "recalled"
|
||||
if(SHUTTLE_CALL)
|
||||
. = "called"
|
||||
if(SHUTTLE_DOCKED)
|
||||
. = "docked"
|
||||
if(SHUTTLE_STRANDED)
|
||||
. = "stranded"
|
||||
if(SHUTTLE_ESCAPE)
|
||||
. = "escape"
|
||||
if(!.)
|
||||
throw EXCEPTION("shuttlemode2str(): invalid mode")
|
||||
|
||||
/obj/machinery/shuttle_manipulator/attack_hand(mob/user)
|
||||
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, null, null, admin_state)
|
||||
// when the ui is first opened this is the data it will use
|
||||
// open the new ui window
|
||||
ui.open()
|
||||
// auto update every Master Controller tick
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/shuttle_manipulator/ui_data(mob/user, datum/topic_state/state)
|
||||
var/data[0]
|
||||
data["selectedMenuKey"] = selected_menu_key
|
||||
|
||||
data["templates"] = list()
|
||||
var/list/templates = data["templates"]
|
||||
data["templates_tabs"] = list()
|
||||
data["selected"] = null
|
||||
|
||||
for(var/shuttle_id in shuttle_templates)
|
||||
var/datum/map_template/shuttle/S = shuttle_templates[shuttle_id]
|
||||
|
||||
if(!templates[S.port_id])
|
||||
data["templates_tabs"] += S.port_id
|
||||
// The first found shuttle type will be our default
|
||||
if(!existing_shuttle)
|
||||
existing_shuttle = shuttle_master.getShuttle(S.port_id)
|
||||
templates[S.port_id] = list(
|
||||
"port_id" = S.port_id,
|
||||
"templates" = list()
|
||||
)
|
||||
|
||||
var/list/L = list()
|
||||
L["name"] = S.name
|
||||
L["shuttle_id"] = S.shuttle_id
|
||||
L["port_id"] = S.port_id
|
||||
L["description"] = S.description
|
||||
L["admin_notes"] = S.admin_notes
|
||||
|
||||
if(selected == S)
|
||||
data["selected"] = L
|
||||
|
||||
templates[S.port_id]["templates"] += list(L)
|
||||
|
||||
data["templates_tabs"] = sortList(data["templates_tabs"])
|
||||
|
||||
data["existing_shuttle"] = null
|
||||
|
||||
// Status panel
|
||||
data["shuttles"] = list()
|
||||
for(var/i in shuttle_master.mobile)
|
||||
var/obj/docking_port/mobile/M = i
|
||||
var/list/L = list()
|
||||
L["name"] = M.name
|
||||
L["id"] = M.id
|
||||
L["timer"] = M.timer
|
||||
L["timeleft"] = M.getTimerStr()
|
||||
var/can_fast_travel = FALSE
|
||||
if(M.timer && M.timeLeft() >= 50)
|
||||
can_fast_travel = TRUE
|
||||
L["can_fast_travel"] = can_fast_travel
|
||||
L["mode"] = capitalize(shuttlemode2str(M.mode))
|
||||
L["status"] = M.getStatusText()
|
||||
if(M == existing_shuttle)
|
||||
data["existing_shuttle"] = L
|
||||
|
||||
data["shuttles"] += list(L)
|
||||
return data
|
||||
|
||||
/obj/machinery/shuttle_manipulator/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
var/mob/user = usr
|
||||
|
||||
// Preload some common parameters
|
||||
var/shuttle_id = href_list["shuttle_id"]
|
||||
var/datum/map_template/shuttle/S = shuttle_templates[shuttle_id]
|
||||
|
||||
|
||||
if(href_list["selectMenuKey"])
|
||||
selected_menu_key = href_list["selectMenuKey"]
|
||||
return 1 // return 1 forces an update to all Nano uis attached to src
|
||||
|
||||
if(href_list["select_template_category"])
|
||||
var/chosen_shuttle_id = href_list["select_template_category"]
|
||||
selected = null
|
||||
existing_shuttle = shuttle_master.getShuttle(chosen_shuttle_id)
|
||||
return 1
|
||||
|
||||
if(href_list["select_template"])
|
||||
if(S)
|
||||
existing_shuttle = shuttle_master.getShuttle(S.port_id)
|
||||
selected = S
|
||||
. = TRUE
|
||||
|
||||
if(href_list["jump_to"])
|
||||
|
||||
if(href_list["type"] == "mobile")
|
||||
for(var/i in shuttle_master.mobile)
|
||||
var/obj/docking_port/mobile/M = i
|
||||
if(M.id == href_list["id"])
|
||||
user.forceMove(get_turf(M))
|
||||
. = TRUE
|
||||
break
|
||||
|
||||
|
||||
if(href_list["fast_travel"])
|
||||
|
||||
for(var/i in shuttle_master.mobile)
|
||||
var/obj/docking_port/mobile/M = i
|
||||
if(M.id == href_list["id"] && M.timer && M.timeLeft() >= 50)
|
||||
M.setTimer(50)
|
||||
. = TRUE
|
||||
message_admins("[key_name_admin(usr)] fast travelled \
|
||||
[M]")
|
||||
log_admin("[key_name(usr)] fast travelled [M]")
|
||||
feedback_add_details("shuttle_fasttravel", M.name)
|
||||
break
|
||||
|
||||
|
||||
if(href_list["preview"])
|
||||
if(S)
|
||||
. = TRUE
|
||||
unload_preview()
|
||||
load_template(S)
|
||||
if(preview_shuttle)
|
||||
preview_template = S
|
||||
user.forceMove(get_turf(preview_shuttle))
|
||||
|
||||
if(href_list["load"])
|
||||
if(existing_shuttle == shuttle_master.backup_shuttle)
|
||||
// TODO make the load button disabled
|
||||
WARNING("The shuttle that the selected shuttle will replace \
|
||||
is the backup shuttle. Backup shuttle is required to be \
|
||||
intact for round sanity.")
|
||||
else if(S)
|
||||
. = TRUE
|
||||
// If successful, returns the mobile docking port
|
||||
var/obj/docking_port/mobile/mdp = action_load(S)
|
||||
if(mdp)
|
||||
user.forceMove(get_turf(mdp))
|
||||
message_admins("[key_name_admin(usr)] loaded [mdp] \
|
||||
with the shuttle manipulator.")
|
||||
log_admin("[key_name(usr)] loaded [mdp] with the \
|
||||
shuttle manipulator.</span>")
|
||||
feedback_add_details("shuttle_manipulator", mdp.name)
|
||||
|
||||
|
||||
/obj/machinery/shuttle_manipulator/proc/action_load(datum/map_template/shuttle/loading_template)
|
||||
// Check for an existing preview
|
||||
if(preview_shuttle && (loading_template != preview_template))
|
||||
preview_shuttle.jumpToNullSpace()
|
||||
preview_shuttle = null
|
||||
preview_template = null
|
||||
|
||||
if(!preview_shuttle)
|
||||
load_template(loading_template)
|
||||
preview_template = loading_template
|
||||
|
||||
// get the existing shuttle information, if any
|
||||
var/timer = 0
|
||||
var/mode = SHUTTLE_IDLE
|
||||
var/obj/docking_port/stationary/D
|
||||
|
||||
if(existing_shuttle)
|
||||
timer = existing_shuttle.timer
|
||||
mode = existing_shuttle.mode
|
||||
D = existing_shuttle.get_docked()
|
||||
else
|
||||
D = preview_shuttle.findRoundstartDock()
|
||||
|
||||
if(!D)
|
||||
var/m = "No dock found for preview shuttle, aborting."
|
||||
WARNING(m)
|
||||
throw EXCEPTION(m)
|
||||
|
||||
var/result = preview_shuttle.canDock(D)
|
||||
// truthy value means that it cannot dock for some reason
|
||||
// but we can ignore the someone else docked error because we'll
|
||||
// be moving into their place shortly
|
||||
if((result != SHUTTLE_CAN_DOCK) && (result != SHUTTLE_SOMEONE_ELSE_DOCKED))
|
||||
|
||||
var/m = "Unsuccessful dock of [preview_shuttle] ([result])."
|
||||
message_admins("[m]")
|
||||
WARNING(m)
|
||||
return
|
||||
|
||||
existing_shuttle.jumpToNullSpace()
|
||||
|
||||
preview_shuttle.dock(D)
|
||||
. = preview_shuttle
|
||||
|
||||
// Shuttle state involves a mode and a timer based on world.time, so
|
||||
// plugging the existing shuttles old values in works fine.
|
||||
preview_shuttle.timer = timer
|
||||
preview_shuttle.mode = mode
|
||||
|
||||
preview_shuttle.register()
|
||||
|
||||
// TODO indicate to the user that success happened, rather than just
|
||||
// blanking the modification tab
|
||||
existing_shuttle = null
|
||||
preview_shuttle = null
|
||||
preview_template = null
|
||||
selected = null
|
||||
|
||||
return preview_shuttle
|
||||
|
||||
/obj/machinery/shuttle_manipulator/proc/load_template(datum/map_template/shuttle/S)
|
||||
// load shuttle template, centred at shuttle import landmark,
|
||||
var/turf/landmark_turf = get_turf(locate("landmark*Shuttle Import"))
|
||||
S.load(landmark_turf, centered = TRUE)
|
||||
|
||||
var/affected = S.get_affected_turfs(landmark_turf, centered=TRUE)
|
||||
|
||||
var/found = 0
|
||||
// Search the turfs for docking ports
|
||||
// - We need to find the mobile docking port because that is the heart of
|
||||
// the shuttle.
|
||||
// - We need to check that no additional ports have slipped in from the
|
||||
// template, because that causes unintended behaviour.
|
||||
for(var/T in affected)
|
||||
for(var/obj/docking_port/P in T)
|
||||
if(istype(P, /obj/docking_port/mobile))
|
||||
var/obj/docking_port/mobile/M = P
|
||||
found++
|
||||
if(found > 1)
|
||||
qdel(P, force=TRUE)
|
||||
world.log << "Map warning: Shuttle Template [S.mappath] \
|
||||
has multiple mobile docking ports."
|
||||
else if(!M.timid)
|
||||
// The shuttle template we loaded isn't "timid" which means
|
||||
// it's already registered with the shuttles subsystem.
|
||||
// This is a bad thing.
|
||||
var/m = "Template [S] is non-timid! Unloading."
|
||||
WARNING(m)
|
||||
M.jumpToNullSpace()
|
||||
return
|
||||
else
|
||||
preview_shuttle = P
|
||||
if(istype(P, /obj/docking_port/stationary))
|
||||
world.log << "Map warning: Shuttle Template [S.mappath] has a \
|
||||
stationary docking port."
|
||||
if(!found)
|
||||
var/msg = "load_template(): Shuttle Template [S.mappath] has no \
|
||||
mobile docking port. Aborting import."
|
||||
for(var/T in affected)//wot do?
|
||||
var/turf/T0 = T
|
||||
T0.contents = null
|
||||
|
||||
message_admins(msg)
|
||||
WARNING(msg)
|
||||
|
||||
/obj/machinery/shuttle_manipulator/proc/unload_preview()
|
||||
if(preview_shuttle)
|
||||
preview_shuttle.jumpToNullSpace()
|
||||
preview_shuttle = null
|
||||
@@ -631,11 +631,11 @@
|
||||
data["can_launch"] = !shuttle_master.supply.canMove()
|
||||
return data
|
||||
|
||||
/obj/machinery/computer/supplycomp/proc/is_authorized(user)
|
||||
/obj/machinery/computer/supplycomp/proc/is_authorized(mob/user)
|
||||
if(allowed(user))
|
||||
return 1
|
||||
|
||||
if(isobserver(user) && check_rights(R_ADMIN, 0))
|
||||
if(user.can_admin_interact())
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
Reference in New Issue
Block a user