Merge pull request #11358 from Couls/diagonal-movement

[Ready for Testmerge] Implement Diagonal Movement
This commit is contained in:
variableundefined
2019-07-12 17:12:57 +08:00
committed by GitHub
60 changed files with 3513 additions and 4172 deletions
+4
View File
@@ -185,3 +185,7 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","
else
send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])")
return admin_number_present
/client/proc/get_adminhelp()
var/msg = input(src, "Please describe your problem concisely and an admin will help as soon as they're able.", "Adminhelp contents") as text|null
adminhelp(msg)
+4
View File
@@ -17,6 +17,10 @@
feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/get_admin_say()
var/msg = input(src, null, "asay \"text\"") as text|null
cmd_admin_say(msg)
/client/proc/cmd_mentor_say(msg as text)
set category = "Admin"
set name = "Msay"
+4
View File
@@ -43,3 +43,7 @@
say_dead_direct("<span class='name'>[prefix]</span> says, <span class='message'>\"[msg]\"</span>")
feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/get_dead_say()
var/msg = input(src, null, "dsay \"text\"") as text
dsay(msg)
+1 -1
View File
@@ -116,7 +116,7 @@
a_right.holder_movement()
/obj/item/assembly_holder/Move()
..()
. = ..()
process_movement()
return
+1 -1
View File
@@ -106,7 +106,7 @@
/obj/item/assembly/infra/Move()
var/t = dir
..()
. = ..()
dir = t
qdel(first)
+7 -3
View File
@@ -1,4 +1,8 @@
/client
//////////////////////
//BLACK MAGIC THINGS//
//////////////////////
parent_type = /datum
////////////////
//ADMIN THINGS//
////////////////
@@ -24,7 +28,7 @@
var/adminhelped = 0
var/gc_destroyed //Time when this object was destroyed.
// var/gc_destroyed //Time when this object was destroyed. [Inherits from datum]
#ifdef TESTING
var/running_find_references
@@ -89,5 +93,5 @@
// If set to true, this client can interact with atoms such as buttons and doors on top of regular machinery interaction
var/advanced_admin_interaction = FALSE
// Has the client been varedited by an admin?
var/var_edited = FALSE
// Has the client been varedited by an admin? [Inherits from datum now]
// var/var_edited = FALSE
+9 -14
View File
@@ -297,14 +297,14 @@
return null
if(byond_build < config.minimum_client_build)
alert(src, "You are using a byond build which is not supported by this server. Please use a build version of atleast [config.minimum_client_build].", "Incorrect build", "OK")
del(src)
qdel(src)
return
if(byond_version < SUGGESTED_CLIENT_VERSION) // Update is suggested, but not required.
to_chat(src,"<span class='userdanger'>Your BYOND client (v: [byond_version]) is out of date. This can cause glitches. We highly suggest you download the latest client from http://www.byond.com/ before playing. </span>")
if(IsGuestKey(key))
alert(src,"This server doesn't allow guest accounts to play. Please go to http://www.byond.com/ and register for a key.","Guest","OK")
del(src)
qdel(src)
return
// Change the way they should download resources.
@@ -317,7 +317,6 @@
GLOB.clients += src
GLOB.directory[ckey] = src
//Admin Authorisation
// Automatically makes localhost connection an admin
if(!config.disable_localhost_admin)
@@ -365,11 +364,14 @@
. = ..() //calls mob.Login()
if(ckey in clientmessages)
for(var/message in clientmessages[ckey])
to_chat(src, message)
clientmessages.Remove(ckey)
if(SSinput.initialized)
set_macros()
donator_check()
check_ip_intel()
@@ -404,8 +406,7 @@
if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them.
to_chat(src, "<span class='warning'>Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.</span>")
//This is down here because of the browse() calls in tooltip/New()
if(!tooltips)
tooltips = new /datum/tooltip(src)
@@ -664,7 +665,7 @@
sleep(10) // Since browse is non-instant, and kinda async
to_chat(src, "<pre class=\"system system\">you're a huge nerd. wakka wakka doodle doop nobody's ever gonna see this, the chat system shouldn't be online by this point</pre>")
del(src)
qdel(src)
return TRUE
else
if (!topic || !topic["token"] || !tokens[ckey] || topic["token"] != tokens[ckey])
@@ -675,7 +676,7 @@
tokens[ckey] = cid_check_reconnect()
sleep(10) //browse is queued, we don't want them to disconnect before getting the browse() command.
del(src)
qdel(src)
return TRUE
// We DO have their cached CID handy - compare it, now
if(oldcid != computer_id)
@@ -693,7 +694,7 @@
log_adminwarn("Failed Login: [key] [computer_id] [address] - CID randomizer confirmed (oldcid: [oldcid])")
del(src)
qdel(src)
return TRUE
else
// don't shoot, I'm innocent
@@ -795,18 +796,15 @@
/* Mainwindow */
winset(src, "mainwindow.saybutton", "background-color=#40628a;text-color=#FFFFFF")
winset(src, "mainwindow.mebutton", "background-color=#40628a;text-color=#FFFFFF")
winset(src, "mainwindow.hotkey_toggle", "background-color=#40628a;text-color=#FFFFFF")
///// UI ELEMENTS /////
/* Mainwindow */
winset(src, "mainwindow", "background-color=#272727")
winset(src, "mainwindow.mainvsplit", "background-color=#272727")
winset(src, "mainwindow.tooltip", "background-color=#272727")
/* Outputwindow */
winset(src, "outputwindow.outputwindow", "background-color=#272727")
winset(src, "outputwindow.browseroutput", "background-color=#272727")
/* Rpane */
winset(src, "rpane", "background-color=#272727")
winset(src, "rpane.rpane", "background-color=#272727")
winset(src, "rpane.rpanewindow", "background-color=#272727")
/* Browserwindow */
winset(src, "browserwindow", "background-color=#272727")
@@ -830,18 +828,15 @@
/* Mainwindow */
winset(src, "mainwindow.saybutton", "background-color=none;text-color=#000000")
winset(src, "mainwindow.mebutton", "background-color=none;text-color=#000000")
winset(src, "mainwindow.hotkey_toggle", "background-color=none;text-color=#000000")
///// UI ELEMENTS /////
/* Mainwindow */
winset(src, "mainwindow", "background-color=none")
winset(src, "mainwindow.mainvsplit", "background-color=none")
winset(src, "mainwindow.tooltip", "background-color=none")
/* Outputwindow */
winset(src, "outputwindow.outputwindow", "background-color=none")
winset(src, "outputwindow.browseroutput", "background-color=none")
/* Rpane */
winset(src, "rpane", "background-color=none")
winset(src, "rpane.rpane", "background-color=none")
winset(src, "rpane.rpanewindow", "background-color=none")
/* Browserwindow */
winset(src, "browserwindow", "background-color=none")
@@ -241,7 +241,31 @@
to_chat(usr, "<span class='notice'>You have enabled text popup limiting.")
else
to_chat(usr, "<span class='notice'>You have disabled text popup limiting.")
return
/client/verb/numpad_target()
set name = "Toggle Numpad targetting"
set category = "Preferences"
set desc = "This button will allow you to enable or disable Numpad Targetting"
prefs.toggles ^= NUMPAD_TARGET
prefs.save_preferences(src)
if (prefs.toggles & NUMPAD_TARGET)
to_chat(usr, "<span class='notice'>You have enabled Numpad Targetting.")
else
to_chat(usr, "<span class='notice'>You have disabled Numpad Targetting.")
return
/client/verb/azerty_toggle()
set name = "Toggle QWERTY/AZERTY"
set category = "Preferences"
set desc = "This button will switch you between QWERTY and AZERTY control sets"
prefs.toggles ^= AZERTY
prefs.save_preferences(src)
if (prefs.toggles & AZERTY)
to_chat(usr, "<span class='notice'>You are now in AZERTY mode.")
else
to_chat(usr, "<span class='notice'>You are now in QWERTY mode.")
return
/client/verb/toggle_ghost_pda()
set name = "Show/Hide GhostPDA"
set category = "Preferences"
@@ -249,4 +273,4 @@
prefs.toggles ^= CHAT_GHOSTPDA
to_chat(src, "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTPDA) ? "see all PDA messages" : "no longer see PDA messages"].")
prefs.save_preferences(src)
feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -0,0 +1,24 @@
/datum/admins/key_down(_key, client/user)
switch(_key)
if("F3")
user.get_admin_say()
return
if("F5")
user.admin_ghost()
return
if("F6")
player_panel_new()
return
if("F7")
user.togglebuildmodeself()
return
if("F8")
if(user.keys_held["Ctrl"])
user.stealth()
else
user.invisimin()
return
if("F10")
user.get_dead_say()
return
..()
+21
View File
@@ -0,0 +1,21 @@
// You might be wondering why this isn't client level. If focus is null, we don't want you to move.
// Only way to do that is to tie the behavior into the focus's keyLoop().
/atom/movable/keyLoop(client/user)
if(!user.keys_held["Ctrl"])
var/movement_dir = NONE
var/list/movement = SSinput.movement_keys
if(user.prefs.toggles & AZERTY)
movement = SSinput.alt_movement_keys
for(var/_key in user.keys_held)
movement_dir = movement_dir | movement[_key]
if(user.next_move_dir_add)
movement_dir |= user.next_move_dir_add
if(user.next_move_dir_sub)
movement_dir &= ~user.next_move_dir_sub
// Sanity checks in case you hold left and right and up to make sure you only go up
if((movement_dir & NORTH) && (movement_dir & SOUTH))
movement_dir &= ~(NORTH|SOUTH)
if((movement_dir & EAST) && (movement_dir & WEST))
movement_dir &= ~(EAST|WEST)
user.Move(get_step(src, movement_dir), movement_dir)
@@ -0,0 +1,18 @@
/mob/living/carbon/key_down(_key, client/user)
switch(_key)
if("R", "Southwest") // Southwest is End
toggle_throw_mode()
return
if("1")
a_intent_change("help")
return
if("2")
a_intent_change("disarm")
return
if("3")
a_intent_change("grab")
return
if("4")
a_intent_change("harm")
return
return ..()
@@ -0,0 +1,53 @@
// Clients aren't datums so we have to define these procs indpendently.
// These verbs are called for all key press and release events
/client/verb/keyDown(_key as text)
set instant = TRUE
set hidden = TRUE
keys_held[_key] = world.time
var/movement = SSinput.movement_keys[_key]
if (prefs.toggles & AZERTY) movement = SSinput.alt_movement_keys[_key]
if(!(next_move_dir_sub & movement) && !keys_held["Ctrl"])
next_move_dir_add |= movement
// Client-level keybindings are ones anyone should be able to do at any time
// Things like taking screenshots, hitting tab, and adminhelps.
switch(_key)
if("F1")
if(keys_held["Ctrl"] && keys_held["Shift"]) // Is this command ever used?
winset(src, null, "command=.options")
else
get_adminhelp()
return
if("F2") // Screenshot. Hold shift to choose a name and location to save in
winset(src, null, "command=.screenshot [!keys_held["shift"] ? "auto" : ""]")
return
if("F12") // Toggles minimal HUD
mob.button_pressed_F12()
return
if(holder)
holder.key_down(_key, src)
if(mob.focus)
mob.focus.key_down(_key, src)
/client/verb/keyUp(_key as text)
set instant = TRUE
set hidden = TRUE
keys_held -= _key
var/movement = SSinput.movement_keys[_key]
if (prefs.toggles & AZERTY) movement = SSinput.alt_movement_keys[_key]
if(!(next_move_dir_add & movement))
next_move_dir_sub |= movement
if(holder)
holder.key_up(_key, src)
if(mob.focus)
mob.focus.key_up(_key, src)
// Called every game tick
/client/keyLoop()
if(holder)
holder.keyLoop(src)
if(mob.focus)
mob.focus.keyLoop(src)
@@ -0,0 +1,80 @@
/mob/living/carbon/human/key_down(_key, client/user)
if(_key == "H")
var/obj/item/clothing/accessory/holster/H = null
if(istype(w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/S = w_uniform
if(S.accessories.len)
H = locate() in S.accessories
if (!H)
return
if(!H.holstered)
if(!istype(get_active_hand(), /obj/item/gun))
to_chat(usr, "<span class='warning'>You need your gun equiped to holster it.</span>")
return
var/obj/item/gun/W = get_active_hand()
H.holster(W, usr)
else
H.unholster(usr)
if(client.keys_held["Shift"])
switch(_key)
if("E") // Put held thing in belt or take out most recent thing from belt
quick_equip() // Implementing the storage component is going to take way too long
// var/obj/item/thing = get_active_hand()
// var/obj/item/equipped_belt = get_item_by_slot(slot_belt)
// if(!equipped_belt) // We also let you equip a belt like this
// if(!thing)
// to_chat(user, "<span class='notice'>You have no belt to take something out of.</span>")
// return
// if(equip_to_slot_if_possible(thing, slot_belt))
// update_inv_r_hand()
// update_inv_l_hand()
// return
// if(!SEND_SIGNAL(equipped_belt, COMSIG_CONTAINS_STORAGE)) // not a storage item
// if(!thing)
// equipped_belt.attack_hand(src)
// else
// to_chat(user, "<span class='notice'>You can't fit anything in.</span>")
// return
// if(thing) // put thing in belt
// if(!SEND_SIGNAL(equipped_belt, COMSIG_TRY_STORAGE_INSERT, thing, user.mob))
// to_chat(user, "<span class='notice'>You can't fit anything in.</span>")
// return
// if(!equipped_belt.contents.len) // nothing to take out
// to_chat(user, "<span class='notice'>There's nothing in your belt to take out.</span>")
// return
// var/obj/item/stored = equipped_belt.contents[equipped_belt.contents.len]
// if(!stored || stored.on_found(src))
// return
// stored.attack_hand(src) // take out thing from belt
// return
/* if("B") // Put held thing in backpack or take out most recent thing from backpack
var/obj/item/thing = get_active_hand()
var/obj/item/equipped_back = get_item_by_slot(slot_back)
if(!equipped_back) // We also let you equip a backpack like this
if(!thing)
to_chat(user, "<span class='notice'>You have no backpack to take something out of.</span>")
return
if(equip_to_slot_if_possible(thing, slot_back))
update_inv_r_hand()
update_inv_l_hand()
return
if(!SEND_SIGNAL(equipped_back, COMSIG_CONTAINS_STORAGE)) // not a storage item
if(!thing)
equipped_back.attack_hand(src)
else
to_chat(user, "<span class='notice'>You can't fit anything in.</span>")
return
if(thing) // put thing in backpack
if(!SEND_SIGNAL(equipped_back, COMSIG_TRY_STORAGE_INSERT, thing, user.mob))
to_chat(user, "<span class='notice'>You can't fit anything in.</span>")
return
if(!equipped_back.contents.len) // nothing to take out
to_chat(user, "<span class='notice'>There's nothing in your backpack to take out.</span>")
return
var/obj/item/stored = equipped_back.contents[equipped_back.contents.len]
if(!stored || stored.on_found(src))
return
stored.attack_hand(src) // take out thing from backpack
return */
return ..()
@@ -0,0 +1,7 @@
/mob/living/key_down(_key, client/user)
switch(_key)
if("B")
resist()
return
return ..()
+132
View File
@@ -0,0 +1,132 @@
// Technically the client argument is unncessary here since that SHOULD be src.client but let's not assume things
// All it takes is one badmin setting their focus to someone else's client to mess things up
// Or we can have NPC's send actual keypresses and detect that by seeing no client
/mob/key_down(_key, client/user)
if(user.prefs.toggles & AZERTY)
switch(_key)
if("Delete")
if(!pulling)
to_chat(src, "<span class='notice'>You are not pulling anything.</span>")
else
stop_pulling()
return
if("Insert", "G")
a_intent_change(INTENT_HOTKEY_RIGHT)
return
if("F")
a_intent_change(INTENT_HOTKEY_LEFT)
return
if("X", "Northeast") // Northeast is Page-up
swap_hand()
return
if("Y", "W", "Southeast") // Southeast is Page-down
mode() // attack_self(). No idea who came up with "mode()"
return
if("A", "Northwest") // Northwest is Home
var/obj/item/I = get_active_hand()
if(!I)
to_chat(src, "<span class='warning'>You have nothing to drop in your hand!</span>")
else
drop_item(I)
return
if("E")
quick_equip()
return
if("Alt")
toggle_move_intent()
return
else
switch(_key)
if("Delete")
if(!pulling)
to_chat(src, "<span class='notice'>You are not pulling anything.</span>")
else
stop_pulling()
return
if("Insert", "G")
a_intent_change(INTENT_HOTKEY_RIGHT)
return
if("F")
a_intent_change(INTENT_HOTKEY_LEFT)
return
if("X", "Northeast") // Northeast is Page-up
swap_hand()
return
if("Y", "Z", "Southeast") // Southeast is Page-down
mode() // attack_self(). No idea who came up with "mode()"
return
if("Q", "Northwest") // Northwest is Home
var/obj/item/I = get_active_hand()
if(!I)
to_chat(src, "<span class='warning'>You have nothing to drop in your hand!</span>")
else
drop_item(I)
return
if("E")
quick_equip()
return
if("Alt")
toggle_move_intent()
return
//Bodypart selections
if(client.prefs.toggles & NUMPAD_TARGET)
switch(_key)
if("Numpad8")
user.body_toggle_head()
return
if("Numpad4")
user.body_r_arm()
return
if("Numpad5")
user.body_chest()
return
if("Numpad6")
user.body_l_arm()
return
if("Numpad1")
user.body_r_leg()
return
if("Numpad2")
user.body_groin()
return
if("Numpad3")
user.body_l_leg()
return
if(client.keys_held["Ctrl"] && client.prefs.toggles & AZERTY)
switch(SSinput.alt_movement_keys[_key])
if(NORTH)
northface()
return
if(SOUTH)
southface()
return
if(WEST)
westface()
return
if(EAST)
eastface()
return
else if(client.keys_held["Ctrl"])
switch(SSinput.movement_keys[_key])
if(NORTH)
northface()
return
if(SOUTH)
southface()
return
if(WEST)
westface()
return
if(EAST)
eastface()
return
return ..()
/mob/key_up(_key, client/user)
switch(_key)
if("Alt")
toggle_move_intent()
return
return ..()
@@ -0,0 +1,12 @@
/mob/living/silicon/robot/key_down(_key, client/user)
switch(_key)
if("1", "2", "3")
toggle_module(text2num(_key))
return
if("4")
a_intent_change(INTENT_HOTKEY_LEFT)
return
if("Q")
uneq_active()
return
return ..()
+8
View File
@@ -0,0 +1,8 @@
/mob
var/datum/focus //What receives our keyboard inputs. src by default
/mob/proc/set_focus(datum/new_focus)
if(focus == new_focus)
return
focus = new_focus
reset_perspective(focus) //Maybe this should be done manually? You figure it out, reader
+42
View File
@@ -0,0 +1,42 @@
# In-code keypress handling system
This whole system is heavily based off of forum_account's keyboard library.
Thanks to forum_account for saving the day, the library can be found
[here](https://secure.byond.com/developer/Forum_account/Keyboard)!
.dmf macros have some very serious shortcomings. For example, they do not allow reusing parts
of one macro in another, so giving cyborgs their own shortcuts to swap active module couldn't
inherit the movement that all mobs should have anyways. The webclient only supports one macro,
so having more than one was problematic. Additionally each keybind has to call an actual
verb, which meant a lot of hidden verbs that just call one other proc. Also our existing
macro was really bad and tied unrelated behavior into `Northeast()`, `Southeast()`, `Northwest()`,
and `Southwest()`.
The basic premise of this system is to not screw with .dmf macro setup at all and handle
pressing those keys in the code instead. We have every key call `client.keyDown()`
or `client.keyUp()` with the pressed key as an argument. Certain keys get processed
directly by the client because they should be doable at any time, then we call
`keyDown()` or `keyUp()` on the client's holder and the client's mob's focus.
By default `mob.focus` is the mob itself, but you can set it to any datum to give control of a
client's keypresses to another object. This would be a good way to handle a menu or driving
a mech. You can also set it to null to disregard input from a certain user.
Movement is handled by having each client call `client.keyLoop()` every game tick.
As above, this calls holder and `focus.keyLoop()`. `atom/movable/keyLoop()` handles movement
Try to keep the calculations in this proc light. It runs every tick for every client after all!
You can also tell which keys are being held down now. Each client a list of keys pressed called
`keys_held`. Each entry is a key as a text string associated with the world.time when it was
pressed.
No client-set keybindings at this time, but it shouldn't be too hard if someone wants.
Notes about certain keys:
* `Tab` has client-sided behavior but acts normally
* `T`, `O`, and `M` move focus to the input when pressed. This fires the keyUp macro right away.
* `\` needs to be escaped in the dmf so any usage is `\\`
You cannot `TICK_CHECK` or check `world.tick_usage` inside of procs called by key down and up
events. They happen outside of a byond tick and have no meaning there. Key looping
works correctly since it's part of a subsystem, not direct input.
+46
View File
@@ -0,0 +1,46 @@
/client
var/list/keys_held = list() // A list of any keys held currently
// These next two vars are to apply movement for keypresses and releases made while move delayed.
// Because discarding that input makes the game less responsive.
var/next_move_dir_add // On next move, add this dir to the move that would otherwise be done
var/next_move_dir_sub // On next move, subtract this dir from the move that would otherwise be done
// Set a client's focus to an object and override these procs on that object to let it handle keypresses
/datum/proc/key_down(key, client/user) // Called when a key is pressed down initially
return
/datum/proc/key_up(key, client/user) // Called when a key is released
return
/datum/proc/keyLoop(client/user) // Called once every frame
set waitfor = FALSE
return
// removes all the existing macros
/client/proc/erase_all_macros()
var/list/macro_sets = params2list(winget(src, null, "macros"))
var/erase_output = ""
for(var/i in 1 to macro_sets.len)
var/setname = macro_sets[i]
var/list/macro_set = params2list(winget(src, "[setname].*", "command")) // The third arg doesnt matter here as we're just removing them all
for(var/k in 1 to macro_set.len)
var/list/split_name = splittext(macro_set[k], ".")
var/macro_name = "[split_name[1]].[split_name[2]]" // [3] is "command"
erase_output = "[erase_output];[macro_name].parent=null"
winset(src, null, erase_output)
/client/proc/set_macros()
set waitfor = FALSE
erase_all_macros()
var/list/macro_sets = SSinput.macro_sets
for(var/i in 1 to macro_sets.len)
var/setname = macro_sets[i]
if(setname != "default")
winclone(src, "default", setname)
var/list/macro_set = macro_sets[setname]
for(var/k in 1 to macro_set.len)
var/key = macro_set[k]
var/command = macro_set[key]
winset(src, "[setname]-\ref[key]", "parent=[setname];name=[key];command=[command]")
winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=default")
+43 -43
View File
@@ -1,44 +1,44 @@
/****************Explorer's Suit and Mask****************/
/obj/item/clothing/suit/hooded/explorer
name = "explorer suit"
desc = "An armoured suit for exploring harsh environments."
icon_state = "explorer"
item_state = "explorer"
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
hoodtype = /obj/item/clothing/head/hooded/explorer
armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50)
allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe)
resistance_flags = FIRE_PROOF
hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran")
sprite_sheets = list(
"Vox" = 'icons/mob/species/vox/suit.dmi',
"Drask" = 'icons/mob/species/drask/suit.dmi',
"Tajaran" = 'icons/mob/species/tajaran/suit.dmi',
"Unathi" = 'icons/mob/species/unathi/suit.dmi',
"Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi'
)
/obj/item/clothing/head/hooded/explorer
name = "explorer hood"
desc = "An armoured hood for exploring harsh environments."
icon_state = "explorer"
item_state = "explorer"
body_parts_covered = HEAD
flags = BLOCKHAIR | NODROP
flags_cover = HEADCOVERSEYES
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT
armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50)
resistance_flags = FIRE_PROOF
sprite_sheets = list(
"Vox" = 'icons/mob/species/vox/head.dmi',
"Drask" = 'icons/mob/species/drask/head.dmi',
"Grey" = 'icons/mob/species/grey/head.dmi',
"Skrell" = 'icons/mob/species/skrell/head.dmi'
/****************Explorer's Suit and Mask****************/
/obj/item/clothing/suit/hooded/explorer
name = "explorer suit"
desc = "An armoured suit for exploring harsh environments."
icon_state = "explorer"
item_state = "explorer"
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
hoodtype = /obj/item/clothing/head/hooded/explorer
armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50)
allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe)
resistance_flags = FIRE_PROOF
hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran")
sprite_sheets = list(
"Vox" = 'icons/mob/species/vox/suit.dmi',
"Drask" = 'icons/mob/species/drask/suit.dmi',
"Tajaran" = 'icons/mob/species/tajaran/suit.dmi',
"Unathi" = 'icons/mob/species/unathi/suit.dmi',
"Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi'
)
/obj/item/clothing/head/hooded/explorer
name = "explorer hood"
desc = "An armoured hood for exploring harsh environments."
icon_state = "explorer"
item_state = "explorer"
body_parts_covered = HEAD
flags = BLOCKHAIR | NODROP
flags_cover = HEADCOVERSEYES
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT
armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50)
resistance_flags = FIRE_PROOF
sprite_sheets = list(
"Vox" = 'icons/mob/species/vox/head.dmi',
"Drask" = 'icons/mob/species/drask/head.dmi',
"Grey" = 'icons/mob/species/grey/head.dmi',
"Skrell" = 'icons/mob/species/skrell/head.dmi'
)
+9
View File
@@ -6,6 +6,14 @@
if(hand) return l_hand
else return r_hand
/mob/verb/quick_equip()
set name = "quick-equip"
set hidden = 1
var/obj/item/I = get_active_hand()
if(I)
I.equip_to_best_slot(src)
/mob/proc/is_in_active_hand(obj/item/I)
var/obj/item/item_to_test = get_active_hand()
@@ -254,3 +262,4 @@
if(slot_r_hand)
return r_hand
return null
+1
View File
@@ -975,6 +975,7 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
to_chat(src, "<span class='notice'>You [slipVerb]ped on [description]!</span>")
playsound(src.loc, 'sound/misc/slip.ogg', 50, 1, -3)
// Something something don't run with scissors
moving_diagonally = 0 //If this was part of diagonal move slipping will stop it.
Stun(stun)
Weaken(weaken)
return 1
@@ -1,11 +1,3 @@
/mob/living/carbon/human/verb/quick_equip()
set name = "quick-equip"
set hidden = 1
var/obj/item/I = get_active_hand()
if(I)
I.equip_to_best_slot(src)
/mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1)
for(var/slot in slots)
if(equip_to_slot_if_possible(W, slots[slot], del_on_fail = 0))
+1 -1
View File
@@ -528,7 +528,7 @@
return
var/pull_dir = get_dir(src, pulling)
if(get_dist(src, pulling) > 1 || ((pull_dir - 1) & pull_dir)) // puller and pullee more than one tile away or in diagonal position
if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position
if(isliving(pulling))
var/mob/living/M = pulling
if(M.lying && !M.buckled && (prob(M.getBruteLoss() * 200 / M.maxHealth)))
@@ -2,6 +2,3 @@
..()
regenerate_icons()
show_laws(0)
var/datum/hotkey_mode/cyborg/C = new(src)
C.set_winset_values()
@@ -1,265 +1,265 @@
#define MINER_DASH_RANGE 4
/*
BLOOD-DRUNK MINER
Effectively a highly aggressive miner, the blood-drunk miner has very few attacks but compensates by being highly aggressive.
The blood-drunk miner's attacks are as follows
- If not in KA range, it will rapidly dash at its target
- If in KA range, it will fire its kinetic accelerator
- If in melee range, will rapidly attack, akin to an actual player
- After any of these attacks, may transform its cleaving saw:
Untransformed, it attacks very rapidly for smaller amounts of damage
Transformed, it attacks at normal speed for higher damage and cleaves enemies hit
When the blood-drunk miner dies, it leaves behind the cleaving saw it was using and its kinetic accelerator.
Difficulty: Medium
*/
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner
name = "blood-drunk miner"
desc = "A miner destined to wander forever, engaged in an endless hunt."
health = 900
maxHealth = 900
icon_state = "miner"
icon_living = "miner"
icon = 'icons/mob/alienqueen.dmi'
light_color = "#E4C7C5"
speak_emote = list("roars")
speed = 1
move_to_delay = 3
projectiletype = /obj/item/projectile/kinetic/miner
projectilesound = 'sound/weapons/kenetic_accel.ogg'
ranged = 1
ranged_cooldown_time = 16
pixel_x = -16
loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator)
wander = FALSE
del_on_death = TRUE
blood_volume = BLOOD_VOLUME_NORMAL
var/obj/item/melee/energy/cleaving_saw/miner/miner_saw
var/time_until_next_transform = 0
var/dashing = FALSE
var/dash_cooldown = 15
var/guidance = FALSE
deathmessage = "falls to the ground, decaying into glowing particles."
death_sound = "bodyfall"
/obj/item/gps/internal/miner
icon_state = null
gpstag = "Resonant Signal"
desc = "The sweet blood, oh, it sings to me."
invisibility = 100
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance
guidance = TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget()
. = ..()
if(. && prob(12))
INVOKE_ASYNC(src, .proc/dash)
/obj/item/melee/energy/cleaving_saw/miner //nerfed saw because it is very murdery
force = 6
force_on = 10
/obj/item/melee/energy/cleaving_saw/miner/attack(mob/living/target, mob/living/carbon/human/user)
target.add_stun_absorption("miner", 10, INFINITY)
..()
target.stun_absorption -= "miner"
/obj/item/projectile/kinetic/miner
damage = 20
speed = 0.9
icon_state = "ka_tracer"
range = MINER_DASH_RANGE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize()
. = ..()
miner_saw = new(src)
internal_gps = new/obj/item/gps/internal/miner(src)
// Add a zone selection UI; otherwise the mob can't melee attack properly.
zone_sel = new /obj/screen/zone_sel()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
var/adjustment_amount = amount * 0.1
if(world.time + adjustment_amount > next_move)
changeNext_move(adjustment_amount) //attacking it interrupts it attacking, but only briefly
. = ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/death()
if(health > 0)
return
new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir)
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc)
if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid!
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/ex_act(severity, target)
if(dash())
return
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/AttackingTarget()
if(QDELETED(target))
return
if(next_move > world.time || !Adjacent(target)) //some cheating
INVOKE_ASYNC(src, .proc/quick_attack_loop)
return
face_atom(target)
if(isliving(target))
var/mob/living/L = target
if(L.stat == DEAD)
visible_message("<span class='danger'>[src] butchers [L]!</span>",
"<span class='userdanger'>You butcher [L], restoring your health!</span>")
if(!is_station_level(z) || client) //NPC monsters won't heal while on station
if(guidance)
adjustHealth(-L.maxHealth)
else
adjustHealth(-(L.maxHealth * 0.5))
L.gib()
return TRUE
changeNext_move(CLICK_CD_MELEE)
miner_saw.melee_attack_chain(src, target)
if(guidance)
adjustHealth(-2)
transform_weapon()
INVOKE_ASYNC(src, .proc/quick_attack_loop)
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!used_item && !isturf(A))
used_item = miner_saw
..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/GiveTarget(new_target)
var/targets_the_same = (new_target == target)
. = ..()
if(. && target && !targets_the_same)
wander = TRUE
transform_weapon()
INVOKE_ASYNC(src, .proc/quick_attack_loop)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire()
Goto(target, move_to_delay, minimum_distance)
if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time)
INVOKE_ASYNC(src, .proc/dash, target)
else
shoot_ka()
transform_weapon()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka()
if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target))
ranged_cooldown = world.time + ranged_cooldown_time
visible_message("<span class='danger'>[src] fires the proto-kinetic accelerator!</span>")
face_atom(target)
new /obj/effect/temp_visual/dir_setting/firing_effect(loc, dir)
Shoot(target)
changeNext_move(CLICK_CD_RANGE)
//I'm still of the belief that this entire proc needs to be wiped from existence.
// do not take my touching of it to be endorsement of it. ~mso
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop()
while(!QDELETED(target) && next_move <= world.time) //this is done this way because next_move can change to be sooner while we sleep.
stoplag(1)
sleep((next_move - world.time) * 1.5) //but don't ask me what the fuck this is about
if(QDELETED(target))
return
if(dashing || next_move > world.time || !Adjacent(target))
if(dashing && next_move <= world.time)
next_move = world.time + 1
INVOKE_ASYNC(src, .proc/quick_attack_loop) //lets try that again.
return
AttackingTarget()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash(atom/dash_target)
if(world.time < dash_cooldown)
return
var/list/accessable_turfs = list()
var/self_dist_to_target = 0
var/turf/own_turf = get_turf(src)
if(!QDELETED(dash_target))
self_dist_to_target += get_dist(dash_target, own_turf)
for(var/turf/simulated/O in RANGE_TURFS(MINER_DASH_RANGE, own_turf))
var/turf_dist_to_target = 0
if(!QDELETED(dash_target))
turf_dist_to_target += get_dist(dash_target, O)
if(get_dist(src, O) >= MINER_DASH_RANGE && turf_dist_to_target <= self_dist_to_target && !islava(O) && !ischasm(O))
var/valid = TRUE
for(var/turf/T in getline(own_turf, O))
if(is_blocked_turf(T, TRUE))
valid = FALSE
continue
if(valid)
accessable_turfs[O] = turf_dist_to_target
var/turf/target_turf
if(!QDELETED(dash_target))
var/closest_dist = MINER_DASH_RANGE
for(var/t in accessable_turfs)
if(accessable_turfs[t] < closest_dist)
closest_dist = accessable_turfs[t]
for(var/t in accessable_turfs)
if(accessable_turfs[t] != closest_dist)
accessable_turfs -= t
if(!LAZYLEN(accessable_turfs))
return
dash_cooldown = world.time + initial(dash_cooldown)
target_turf = pick(accessable_turfs)
var/turf/step_back_turf = get_step(target_turf, get_cardinal_dir(target_turf, own_turf))
var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf))
new /obj/effect/temp_visual/small_smoke/halfsecond(step_back_turf)
new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc, src)
animate(D, alpha = 0, time = 5)
forceMove(step_back_turf)
playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
dashing = TRUE
alpha = 0
animate(src, alpha = 255, time = 5)
sleep(2)
D.forceMove(step_forward_turf)
forceMove(target_turf)
playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
sleep(1)
dashing = FALSE
shoot_ka()
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/transform_weapon()
if(time_until_next_transform <= world.time)
miner_saw.transform_cooldown = 0
miner_saw.transform_weapon(src, TRUE)
icon_state = "miner[miner_saw.active ? "_transformed":""]"
icon_living = "miner[miner_saw.active ? "_transformed":""]"
time_until_next_transform = world.time + rand(50, 100)
/obj/effect/temp_visual/dir_setting/miner_death
icon_state = "miner_death"
duration = 15
/obj/effect/temp_visual/dir_setting/miner_death/Initialize(mapload, set_dir)
. = ..()
INVOKE_ASYNC(src, .proc/fade_out)
/obj/effect/temp_visual/dir_setting/miner_death/proc/fade_out()
var/matrix/M = new
M.Turn(pick(90, 270))
var/final_dir = dir
if(dir & (EAST|WEST)) //Facing east or west
final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass
animate(src, transform = M, pixel_y = -6, dir = final_dir, time = 2, easing = EASE_IN|EASE_OUT)
sleep(5)
animate(src, color = list("#A7A19E", "#A7A19E", "#A7A19E", list(0, 0, 0)), time = 10, easing = EASE_IN, flags = ANIMATION_PARALLEL)
sleep(4)
animate(src, alpha = 0, time = 6, easing = EASE_OUT, flags = ANIMATION_PARALLEL)
#define MINER_DASH_RANGE 4
/*
BLOOD-DRUNK MINER
Effectively a highly aggressive miner, the blood-drunk miner has very few attacks but compensates by being highly aggressive.
The blood-drunk miner's attacks are as follows
- If not in KA range, it will rapidly dash at its target
- If in KA range, it will fire its kinetic accelerator
- If in melee range, will rapidly attack, akin to an actual player
- After any of these attacks, may transform its cleaving saw:
Untransformed, it attacks very rapidly for smaller amounts of damage
Transformed, it attacks at normal speed for higher damage and cleaves enemies hit
When the blood-drunk miner dies, it leaves behind the cleaving saw it was using and its kinetic accelerator.
Difficulty: Medium
*/
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner
name = "blood-drunk miner"
desc = "A miner destined to wander forever, engaged in an endless hunt."
health = 900
maxHealth = 900
icon_state = "miner"
icon_living = "miner"
icon = 'icons/mob/alienqueen.dmi'
light_color = "#E4C7C5"
speak_emote = list("roars")
speed = 1
move_to_delay = 3
projectiletype = /obj/item/projectile/kinetic/miner
projectilesound = 'sound/weapons/kenetic_accel.ogg'
ranged = 1
ranged_cooldown_time = 16
pixel_x = -16
loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator)
wander = FALSE
del_on_death = TRUE
blood_volume = BLOOD_VOLUME_NORMAL
var/obj/item/melee/energy/cleaving_saw/miner/miner_saw
var/time_until_next_transform = 0
var/dashing = FALSE
var/dash_cooldown = 15
var/guidance = FALSE
deathmessage = "falls to the ground, decaying into glowing particles."
death_sound = "bodyfall"
/obj/item/gps/internal/miner
icon_state = null
gpstag = "Resonant Signal"
desc = "The sweet blood, oh, it sings to me."
invisibility = 100
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance
guidance = TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget()
. = ..()
if(. && prob(12))
INVOKE_ASYNC(src, .proc/dash)
/obj/item/melee/energy/cleaving_saw/miner //nerfed saw because it is very murdery
force = 6
force_on = 10
/obj/item/melee/energy/cleaving_saw/miner/attack(mob/living/target, mob/living/carbon/human/user)
target.add_stun_absorption("miner", 10, INFINITY)
..()
target.stun_absorption -= "miner"
/obj/item/projectile/kinetic/miner
damage = 20
speed = 0.9
icon_state = "ka_tracer"
range = MINER_DASH_RANGE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize()
. = ..()
miner_saw = new(src)
internal_gps = new/obj/item/gps/internal/miner(src)
// Add a zone selection UI; otherwise the mob can't melee attack properly.
zone_sel = new /obj/screen/zone_sel()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
var/adjustment_amount = amount * 0.1
if(world.time + adjustment_amount > next_move)
changeNext_move(adjustment_amount) //attacking it interrupts it attacking, but only briefly
. = ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/death()
if(health > 0)
return
new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir)
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc)
if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid!
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/ex_act(severity, target)
if(dash())
return
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/AttackingTarget()
if(QDELETED(target))
return
if(next_move > world.time || !Adjacent(target)) //some cheating
INVOKE_ASYNC(src, .proc/quick_attack_loop)
return
face_atom(target)
if(isliving(target))
var/mob/living/L = target
if(L.stat == DEAD)
visible_message("<span class='danger'>[src] butchers [L]!</span>",
"<span class='userdanger'>You butcher [L], restoring your health!</span>")
if(!is_station_level(z) || client) //NPC monsters won't heal while on station
if(guidance)
adjustHealth(-L.maxHealth)
else
adjustHealth(-(L.maxHealth * 0.5))
L.gib()
return TRUE
changeNext_move(CLICK_CD_MELEE)
miner_saw.melee_attack_chain(src, target)
if(guidance)
adjustHealth(-2)
transform_weapon()
INVOKE_ASYNC(src, .proc/quick_attack_loop)
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!used_item && !isturf(A))
used_item = miner_saw
..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/GiveTarget(new_target)
var/targets_the_same = (new_target == target)
. = ..()
if(. && target && !targets_the_same)
wander = TRUE
transform_weapon()
INVOKE_ASYNC(src, .proc/quick_attack_loop)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire()
Goto(target, move_to_delay, minimum_distance)
if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time)
INVOKE_ASYNC(src, .proc/dash, target)
else
shoot_ka()
transform_weapon()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka()
if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target))
ranged_cooldown = world.time + ranged_cooldown_time
visible_message("<span class='danger'>[src] fires the proto-kinetic accelerator!</span>")
face_atom(target)
new /obj/effect/temp_visual/dir_setting/firing_effect(loc, dir)
Shoot(target)
changeNext_move(CLICK_CD_RANGE)
//I'm still of the belief that this entire proc needs to be wiped from existence.
// do not take my touching of it to be endorsement of it. ~mso
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop()
while(!QDELETED(target) && next_move <= world.time) //this is done this way because next_move can change to be sooner while we sleep.
stoplag(1)
sleep((next_move - world.time) * 1.5) //but don't ask me what the fuck this is about
if(QDELETED(target))
return
if(dashing || next_move > world.time || !Adjacent(target))
if(dashing && next_move <= world.time)
next_move = world.time + 1
INVOKE_ASYNC(src, .proc/quick_attack_loop) //lets try that again.
return
AttackingTarget()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash(atom/dash_target)
if(world.time < dash_cooldown)
return
var/list/accessable_turfs = list()
var/self_dist_to_target = 0
var/turf/own_turf = get_turf(src)
if(!QDELETED(dash_target))
self_dist_to_target += get_dist(dash_target, own_turf)
for(var/turf/simulated/O in RANGE_TURFS(MINER_DASH_RANGE, own_turf))
var/turf_dist_to_target = 0
if(!QDELETED(dash_target))
turf_dist_to_target += get_dist(dash_target, O)
if(get_dist(src, O) >= MINER_DASH_RANGE && turf_dist_to_target <= self_dist_to_target && !islava(O) && !ischasm(O))
var/valid = TRUE
for(var/turf/T in getline(own_turf, O))
if(is_blocked_turf(T, TRUE))
valid = FALSE
continue
if(valid)
accessable_turfs[O] = turf_dist_to_target
var/turf/target_turf
if(!QDELETED(dash_target))
var/closest_dist = MINER_DASH_RANGE
for(var/t in accessable_turfs)
if(accessable_turfs[t] < closest_dist)
closest_dist = accessable_turfs[t]
for(var/t in accessable_turfs)
if(accessable_turfs[t] != closest_dist)
accessable_turfs -= t
if(!LAZYLEN(accessable_turfs))
return
dash_cooldown = world.time + initial(dash_cooldown)
target_turf = pick(accessable_turfs)
var/turf/step_back_turf = get_step(target_turf, get_cardinal_dir(target_turf, own_turf))
var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf))
new /obj/effect/temp_visual/small_smoke/halfsecond(step_back_turf)
new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc, src)
animate(D, alpha = 0, time = 5)
forceMove(step_back_turf)
playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
dashing = TRUE
alpha = 0
animate(src, alpha = 255, time = 5)
sleep(2)
D.forceMove(step_forward_turf)
forceMove(target_turf)
playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
sleep(1)
dashing = FALSE
shoot_ka()
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/transform_weapon()
if(time_until_next_transform <= world.time)
miner_saw.transform_cooldown = 0
miner_saw.transform_weapon(src, TRUE)
icon_state = "miner[miner_saw.active ? "_transformed":""]"
icon_living = "miner[miner_saw.active ? "_transformed":""]"
time_until_next_transform = world.time + rand(50, 100)
/obj/effect/temp_visual/dir_setting/miner_death
icon_state = "miner_death"
duration = 15
/obj/effect/temp_visual/dir_setting/miner_death/Initialize(mapload, set_dir)
. = ..()
INVOKE_ASYNC(src, .proc/fade_out)
/obj/effect/temp_visual/dir_setting/miner_death/proc/fade_out()
var/matrix/M = new
M.Turn(pick(90, 270))
var/final_dir = dir
if(dir & (EAST|WEST)) //Facing east or west
final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass
animate(src, transform = M, pixel_y = -6, dir = final_dir, time = 2, easing = EASE_IN|EASE_OUT)
sleep(5)
animate(src, color = list("#A7A19E", "#A7A19E", "#A7A19E", list(0, 0, 0)), time = 10, easing = EASE_IN, flags = ANIMATION_PARALLEL)
sleep(4)
animate(src, alpha = 0, time = 6, easing = EASE_OUT, flags = ANIMATION_PARALLEL)
#undef MINER_DASH_RANGE
+3 -1
View File
@@ -2,6 +2,7 @@
GLOB.mob_list -= src
GLOB.dead_mob_list -= src
GLOB.living_mob_list -= src
focus = null
QDEL_NULL(hud_used)
if(mind && mind.current == src)
spellremove(src)
@@ -30,6 +31,7 @@
GLOB.dead_mob_list += src
else
GLOB.living_mob_list += src
set_focus(src)
prepare_huds()
..()
@@ -50,7 +52,6 @@
/mob/proc/GetAltName()
return ""
/mob/proc/Cell()
set category = "Admin"
set hidden = 1
@@ -173,6 +174,7 @@
return 0
/mob/proc/Life(seconds, times_fired)
set waitfor = FALSE
if(forced_look)
if(!isnum(forced_look))
var/atom/A = locateUID(forced_look)
-3
View File
@@ -399,9 +399,6 @@ var/list/intents = list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM)
set name = "Rest"
set category = "IC"
if(world.time < client.move_delay)
return
if(!resting)
client.move_delay = world.time + 20
to_chat(src, "<span class='notice'>You are now resting.</span>")
+145 -105
View File
@@ -15,102 +15,6 @@
return 1
return (!mover.density || !density || lying)
//The byond version of these verbs wait for the next tick before acting.
// instant verbs however can run mid tick or even during the time between ticks.
#define DO_MOVE(this_dir) var/final_dir = turn(this_dir, -dir2angle(dir)); Move(get_step(mob, final_dir), final_dir);
/client/verb/moveup()
set name = ".moveup"
set instant = 1
DO_MOVE(NORTH)
/client/verb/movedown()
set name = ".movedown"
set instant = 1
DO_MOVE(SOUTH)
/client/verb/moveright()
set name = ".moveright"
set instant = 1
DO_MOVE(EAST)
/client/verb/moveleft()
set name = ".moveleft"
set instant = 1
DO_MOVE(WEST)
#undef DO_MOVE
/client/Northeast()
swap_hand()
return
/client/Southeast()
attack_self()
return
/client/Southwest()
if(iscarbon(usr))
var/mob/living/carbon/C = usr
C.toggle_throw_mode()
else if(isrobot(usr))
var/mob/living/silicon/robot/R = usr
var/module = R.get_selected_module()
if(!module)
to_chat(usr, "<span class='warning'>You have no module selected.</span>")
return
R.cycle_modules()
R.uneq_numbered(module)
else
to_chat(usr, "<span class='warning'>This mob type cannot throw items.</span>")
return
/client/Northwest()
if(iscarbon(usr))
var/mob/living/carbon/C = usr
if(!C.get_active_hand())
to_chat(usr, "<span class='warning'>You have nothing to drop in your hand.</span>")
return
drop_item()
else if(isrobot(usr))
var/mob/living/silicon/robot/R = usr
if(!R.get_selected_module())
to_chat(usr, "<span class='warning'>You have no module selected.</span>")
return
R.deselect_module(R.get_selected_module())
else
to_chat(usr, "<span class='warning'>This mob type cannot drop items.</span>")
return
//This gets called when you press the delete button.
/client/verb/delete_key_pressed()
set hidden = 1
if(!usr.pulling)
to_chat(usr, "<span class='notice'>You are not pulling anything.</span>")
return
usr.stop_pulling()
/client/verb/swap_hand()
set hidden = 1
if(istype(mob, /mob/living/carbon))
mob:swap_hand()
if(istype(mob,/mob/living/silicon/robot))
var/mob/living/silicon/robot/R = mob
R.cycle_modules()
return
/client/verb/attack_self()
set hidden = 1
if(mob)
mob.mode()
return
/client/verb/toggle_throw_mode()
set hidden = 1
@@ -128,7 +32,7 @@
return
/client/Center()
/* /client/Center()
/* No 3D movement in 2D spessman game. dir 16 is Z Up
if(isobj(mob.loc))
var/obj/O = mob.loc
@@ -136,7 +40,7 @@
return O.relaymove(mob, 16)
*/
return
*/
/client/proc/Move_object(direct)
@@ -155,11 +59,17 @@
/client/Move(n, direct)
if(world.time < move_delay)
return
else
next_move_dir_add = 0
next_move_dir_sub = 0
var/old_move_delay = move_delay
move_delay = world.time + world.tick_lag //this is here because Move() can now be called multiple times per tick
if(!mob || !mob.loc)
return 0
if(!n || !direct) // why did we never check this before?
return FALSE
if(mob.notransform)
return 0 //This is sota the goto stop mobs from moving var
@@ -226,9 +136,9 @@
moving = 1
var/delay = mob.movement_delay()
if(old_move_delay + (delay * MOVEMENT_DELAY_BUFFER_DELTA) + MOVEMENT_DELAY_BUFFER > world.time)
move_delay = old_move_delay + delay
move_delay = old_move_delay
else
move_delay = delay + world.time
move_delay = world.time
mob.last_movement = world.time
if(locate(/obj/item/grab, mob))
@@ -264,19 +174,29 @@
return
else if(mob.confused)
step(mob, pick(cardinal))
else
. = ..()
var/newdir = 0
if(mob.confused > 40)
newdir = pick(alldirs)
else if(prob(mob.confused * 1.5))
newdir = angle2dir(dir2angle(direct) + pick(90, -90))
else if(prob(mob.confused * 3))
newdir = angle2dir(dir2angle(direct) + pick(45, -45))
if(newdir)
direct = newdir
n = get_step(mob, direct)
. = ..()
mob.setDir(direct)
for(var/obj/item/grab/G in mob)
if(G.state == GRAB_NECK)
mob.setDir(reverse_dir[direct])
mob.setDir(angle2dir((dir2angle(direct) + 202.5) % 365))
G.adjust_position()
for(var/obj/item/grab/G in mob.grabbed_by)
G.adjust_position()
if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully
delay = mob.movement_delay() * 2
move_delay += delay
moving = 0
if(mob && .)
if(mob.throwing)
@@ -461,3 +381,123 @@
/mob/proc/update_gravity()
return
/client/proc/check_has_body_select()
return mob && mob.hud_used && mob.zone_sel && istype(mob.zone_sel, /obj/screen/zone_sel)
/client/verb/body_toggle_head()
set name = "body-toggle-head"
set hidden = 1
if(!check_has_body_select())
return
var/next_in_line
switch(mob.zone_sel.selecting)
if(BODY_ZONE_HEAD)
next_in_line = BODY_ZONE_PRECISE_EYES
if(BODY_ZONE_PRECISE_EYES)
next_in_line = BODY_ZONE_PRECISE_MOUTH
else
next_in_line = BODY_ZONE_HEAD
var/obj/screen/zone_sel/selector = mob.zone_sel
selector.set_selected_zone(next_in_line, mob)
/client/verb/body_r_arm()
set name = "body-r-arm"
set hidden = 1
if(!check_has_body_select())
return
var/next_in_line
if(mob.zone_sel.selecting == BODY_ZONE_R_ARM)
next_in_line = BODY_ZONE_PRECISE_R_HAND
else
next_in_line = BODY_ZONE_R_ARM
var/obj/screen/zone_sel/selector = mob.zone_sel
selector.set_selected_zone(next_in_line, mob)
/client/verb/body_chest()
set name = "body-chest"
set hidden = 1
if(!check_has_body_select())
return
var/obj/screen/zone_sel/selector = mob.zone_sel
selector.set_selected_zone(BODY_ZONE_CHEST, mob)
/client/verb/body_l_arm()
set name = "body-l-arm"
set hidden = 1
if(!check_has_body_select())
return
var/next_in_line
if(mob.zone_sel.selecting == BODY_ZONE_L_ARM)
next_in_line = BODY_ZONE_PRECISE_L_HAND
else
next_in_line = BODY_ZONE_L_ARM
var/obj/screen/zone_sel/selector = mob.zone_sel
selector.set_selected_zone(next_in_line, mob)
/client/verb/body_r_leg()
set name = "body-r-leg"
set hidden = 1
if(!check_has_body_select())
return
var/next_in_line
if(mob.zone_sel.selecting == BODY_ZONE_R_LEG)
next_in_line = BODY_ZONE_PRECISE_R_FOOT
else
next_in_line = BODY_ZONE_R_LEG
var/obj/screen/zone_sel/selector = mob.zone_sel
selector.set_selected_zone(next_in_line, mob)
/client/verb/body_groin()
set name = "body-groin"
set hidden = 1
if(!check_has_body_select())
return
var/obj/screen/zone_sel/selector = mob.zone_sel
selector.set_selected_zone(BODY_ZONE_PRECISE_GROIN, mob)
/client/verb/body_l_leg()
set name = "body-l-leg"
set hidden = 1
if(!check_has_body_select())
return
var/next_in_line
if(mob.zone_sel.selecting == BODY_ZONE_L_LEG)
next_in_line = BODY_ZONE_PRECISE_L_FOOT
else
next_in_line = BODY_ZONE_L_LEG
var/obj/screen/zone_sel/selector = mob.zone_sel
selector.set_selected_zone(next_in_line, mob)
/client/verb/toggle_walk_run()
set name = "toggle-walk-run"
set hidden = TRUE
set instant = TRUE
if(mob)
mob.toggle_move_intent(usr)
/mob/proc/toggle_move_intent(mob/user)
if(m_intent == MOVE_INTENT_RUN)
m_intent = MOVE_INTENT_WALK
else
m_intent = MOVE_INTENT_RUN
if(hud_used && hud_used.static_inventory)
for(var/obj/screen/mov_intent/selector in hud_used.static_inventory)
selector.update_icon(src)
+1 -1
View File
@@ -46,7 +46,7 @@ var/const/GRAV_NEEDS_WRENCH = 3
// You aren't allowed to move.
/obj/machinery/gravity_generator/Move()
..()
. = ..()
qdel(src)
/obj/machinery/gravity_generator/proc/set_broken()
@@ -144,7 +144,7 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin
/obj/structure/particle_accelerator/Move()
..()
. = ..()
if(master && master.active)
master.toggle_power()
investigate_log("was moved whilst active; it <font color='red'>powered down</font>.","singulo")
+1 -1
View File
@@ -1037,7 +1037,7 @@ obj/spacepod/proc/add_equipment(mob/user, var/obj/item/spacepod_equipment/SPE, v
return stop()
/obj/spacepod/relaymove(mob/user, direction)
if(usr != src.pilot)
if(user != src.pilot)
return
handlerelaymove(user, direction)