[READY]Ports /tg/station pixel projectiles, processing subsystems, timer subsystems, and some misc stuff to make it all work

This commit is contained in:
Neerti
2019-01-26 04:25:17 -05:00
committed by Novacat
parent 1b2116bf80
commit 3256202407
86 changed files with 4372 additions and 1777 deletions

View File

@@ -36,3 +36,4 @@
#define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, null) #define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, null)
#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, null) #define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, null)

View File

@@ -52,3 +52,5 @@
__BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\ __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\
LIST.Insert(__BIN_MID, IN);\ LIST.Insert(__BIN_MID, IN);\
} }
#define islist(L) istype(L, /list)

View File

@@ -4,16 +4,42 @@
#define NONE 0 #define NONE 0
//for convenience //for convenience
#define ENABLE_BITFIELD(variable, flag) (variable |= (flag)) #define ENABLE_BITFIELD(variable, flag) (variable |= (flag))
#define DISABLE_BITFIELD(variable, flag) (variable &= ~(flag)) #define DISABLE_BITFIELD(variable, flag) (variable &= ~(flag))
#define CHECK_BITFIELD(variable, flag) (variable & flag) #define CHECK_BITFIELD(variable, flag) (variable & (flag))
//check if all bitflags specified are present //check if all bitflags specified are present
#define CHECK_MULTIPLE_BITFIELDS(flagvar, flags) ((flagvar & (flags)) == flags) #define CHECK_MULTIPLE_BITFIELDS(flagvar, flags) ((flagvar & (flags)) == flags)
GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768)) GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768))
// datum_flags // datum_flags
#define DF_VAR_EDITED (1<<0) #define DF_VAR_EDITED (1<<0)
#define DF_ISPROCESSING (1<<1) #define DF_ISPROCESSING (1<<1)
// /atom/movable movement_type
#define UNSTOPPABLE (1<<0) //Can not be stopped from moving from Cross(), CanPass(), or Uncross() failing. Still bumps everything it passes through, though.
// Flags bitmasks. - Used in /atom/var/flags
#define NOBLUDGEON (1<<0) // When an item has this it produces no "X has been hit by Y with Z" message with the default handler.
#define CONDUCT (1<<1) // Conducts electricity. (metal etc.)
#define ON_BORDER (1<<2) // Item has priority to check when entering or leaving.
#define NOBLOODY (1<<3) // Used for items if they don't want to get a blood overlay.
#define OPENCONTAINER (1<<4) // Is an open container for chemistry purposes.
#define PHORONGUARD (1<<5) // Does not get contaminated by phoron.
#define NOREACT (1<<6) // Reagents don't react inside this container.
#define PROXMOVE (1<<7)// Does this object require proximity checking in Enter()?
#define OVERLAY_QUEUED (1<<8)// Atom queued to SSoverlay for COMPILE_OVERLAYS
//Flags for items (equipment) - Used in /obj/item/var/item_flags
#define THICKMATERIAL (1<<0) // Prevents syringes, parapens and hyposprays if equipped to slot_suit or slot_head.
#define AIRTIGHT (1<<1) // Functions with internals.
#define NOSLIP (1<<2) // Prevents from slipping on wet floors, in space, etc.
#define BLOCK_GAS_SMOKE_EFFECT (1<<3) // Blocks the effect that chemical clouds would have on a mob -- glasses, mask and helmets ONLY! (NOTE: flag shared with ONESIZEFITSALL)
#define FLEXIBLEMATERIAL (1<<4) // At the moment, masks with this flag will not prevent eating even if they are covering your face.
// Flags for pass_flags. - Used in /atom/var/pass_flags
#define PASSTABLE (1<<0)
#define PASSGLASS (1<<1)
#define PASSGRILLE (1<<2)
#define PASSBLOB (1<<3)

View File

@@ -40,6 +40,7 @@
#define ACCESSORY_SLOT_TORSO (ACCESSORY_SLOT_UTILITY|ACCESSORY_SLOT_WEAPON) #define ACCESSORY_SLOT_TORSO (ACCESSORY_SLOT_UTILITY|ACCESSORY_SLOT_WEAPON)
<<<<<<< HEAD
// Flags bitmasks. - Used in /atom/var/flags // Flags bitmasks. - Used in /atom/var/flags
#define NOBLUDGEON 0x1 // When an item has this it produces no "X has been hit by Y with Z" message with the default handler. #define NOBLUDGEON 0x1 // When an item has this it produces no "X has been hit by Y with Z" message with the default handler.
#define CONDUCT 0x2 // Conducts electricity. (metal etc.) #define CONDUCT 0x2 // Conducts electricity. (metal etc.)
@@ -64,6 +65,8 @@
#define PASSGRILLE 0x4 #define PASSGRILLE 0x4
#define PASSBLOB 0x8 #define PASSBLOB 0x8
=======
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
// Bitmasks for the /obj/item/var/flags_inv variable. These determine when a piece of clothing hides another, i.e. a helmet hiding glasses. // Bitmasks for the /obj/item/var/flags_inv variable. These determine when a piece of clothing hides another, i.e. a helmet hiding glasses.
// WARNING: The following flags apply only to the external suit! // WARNING: The following flags apply only to the external suit!
#define HIDEGLOVES 0x1 #define HIDEGLOVES 0x1

View File

@@ -21,4 +21,4 @@
#define QUANTIZE(variable) (round(variable,0.0001)) #define QUANTIZE(variable) (round(variable,0.0001))
#define TICKS_IN_DAY (TICKS_IN_SECOND * 60 * 60 * 24) #define TICKS_IN_DAY (TICKS_IN_SECOND * 60 * 60 * 24)
#define TICKS_IN_SECOND (world.fps) #define TICKS_IN_SECOND (world.fps)

View File

@@ -290,6 +290,11 @@ var/global/list/##LIST_NAME = list();\
#define IS_WIRECUTTER "wirecutter" #define IS_WIRECUTTER "wirecutter"
#define IS_WRENCH "wrench" #define IS_WRENCH "wrench"
// Diagonal movement
#define FIRST_DIAG_STEP 1
#define SECOND_DIAG_STEP 2
// RCD modes. Used on the RCD, and gets passed to an object's rcd_act() when an RCD is used on it, to determine what happens. // RCD modes. Used on the RCD, and gets passed to an object's rcd_act() when an RCD is used on it, to determine what happens.
#define RCD_FLOORWALL "Floor / Wall" // Builds plating on space/ground/open tiles. Builds a wall when on floors. Finishes walls when used on girders. #define RCD_FLOORWALL "Floor / Wall" // Builds plating on space/ground/open tiles. Builds a wall when on floors. Finishes walls when used on girders.
#define RCD_AIRLOCK "Airlock" // Builds an airlock on the tile if one isn't already there. #define RCD_AIRLOCK "Airlock" // Builds an airlock on the tile if one isn't already there.
@@ -300,7 +305,6 @@ var/global/list/##LIST_NAME = list();\
#define RCD_VALUE_DELAY "delay" #define RCD_VALUE_DELAY "delay"
#define RCD_VALUE_COST "cost" #define RCD_VALUE_COST "cost"
#define RCD_SHEETS_PER_MATTER_UNIT 4 // Each physical material sheet is worth four matter units. #define RCD_SHEETS_PER_MATTER_UNIT 4 // Each physical material sheet is worth four matter units.
#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT #define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT

View File

@@ -84,6 +84,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
#define FIRE_PRIORITY_DEFAULT 50 #define FIRE_PRIORITY_DEFAULT 50
#define FIRE_PRIORITY_PLANETS 75 #define FIRE_PRIORITY_PLANETS 75
#define FIRE_PRIORITY_MACHINES 100 #define FIRE_PRIORITY_MACHINES 100
#define FIRE_PRIORITY_PROJECTILES 150
#define FIRE_PRIORITY_OVERLAYS 500 #define FIRE_PRIORITY_OVERLAYS 500
// Macro defining the actual code applying our overlays lists to the BYOND overlays list. (I guess a macro for speed) // Macro defining the actual code applying our overlays lists to the BYOND overlays list. (I guess a macro for speed)

View File

1516
code/_helpers/_lists.dm Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,758 +0,0 @@
/*
* Holds procs to help with list operations
* Contains groups:
* Misc
* Sorting
*/
/*
* Misc
*/
//Returns a list in plain english as a string
/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
switch(input.len)
if(0) return nothing_text
if(1) return "[input[1]]"
if(2) return "[input[1]][and_text][input[2]]"
else return "[jointext(input, comma_text, 1, -1)][final_comma_text][and_text][input[input.len]]"
//Returns list element or null. Should prevent "index out of bounds" error.
proc/listgetindex(var/list/list,index)
if(istype(list) && list.len)
if(isnum(index))
if(InRange(index,1,list.len))
return list[index]
else if(index in list)
return list[index]
return
//Return either pick(list) or null if list is not of type /list or is empty
proc/safepick(list/list)
if(!islist(list) || !list.len)
return
return pick(list)
//Checks if the list is empty
proc/isemptylist(list/list)
if(!list.len)
return 1
return 0
//Checks for specific types in a list
/proc/is_type_in_list(var/atom/A, var/list/L)
for(var/type in L)
if(istype(A, type))
return 1
return 0
//Checks for specific paths in a list
/proc/is_path_in_list(var/atom/A, var/list/L)
for(var/path in L)
if(ispath(A, path))
return 1
return 0
//////////////////////////////////////////////////////
// "typecache" utilities - Making and searching them
//////////////////////////////////////////////////////
//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
/proc/is_type_in_typecache(atom/A, list/L)
if(!LAZYLEN(L) || !A)
return FALSE
return L[A.type]
//returns a new list with only atoms that are in typecache L
/proc/typecache_filter_list(list/atoms, list/typecache)
. = list()
for(var/thing in atoms)
var/atom/A = thing
if(typecache[A.type])
. += A
/proc/typecache_filter_list_reverse(list/atoms, list/typecache)
. = list()
for(var/thing in atoms)
var/atom/A = thing
if(!typecache[A.type])
. += A
/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude)
. = list()
for(var/thing in atoms)
var/atom/A = thing
if(typecache_include[A.type] && !typecache_exclude[A.type])
. += A
//Like typesof() or subtypesof(), but returns a typecache instead of a list
/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
if(ispath(path))
var/list/types = list()
if(only_root_path)
types = list(path)
else
types = ignore_root_path ? subtypesof(path) : typesof(path)
var/list/L = list()
for(var/T in types)
L[T] = TRUE
return L
else if(islist(path))
var/list/pathlist = path
var/list/L = list()
if(ignore_root_path)
for(var/P in pathlist)
for(var/T in subtypesof(P))
L[T] = TRUE
else
for(var/P in pathlist)
if(only_root_path)
L[P] = TRUE
else
for(var/T in typesof(P))
L[T] = TRUE
return L
//////////////////////////////////////////////////////
//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
proc/clearlist(list/list)
if(istype(list))
list.len = 0
return
//Removes any null entries from the list
proc/listclearnulls(list/list)
if(istype(list))
while(null in list)
list -= null
return
/*
* Returns list containing all the entries from first list that are not present in second.
* If skiprep = 1, repeated elements are treated as one.
* If either of arguments is not a list, returns null
*/
/proc/difflist(var/list/first, var/list/second, var/skiprep=0)
if(!islist(first) || !islist(second))
return
var/list/result = new
if(skiprep)
for(var/e in first)
if(!(e in result) && !(e in second))
result += e
else
result = first - second
return result
/*
* Returns list containing entries that are in either list but not both.
* If skipref = 1, repeated elements are treated as one.
* If either of arguments is not a list, returns null
*/
/proc/uniquemergelist(var/list/first, var/list/second, var/skiprep=0)
if(!islist(first) || !islist(second))
return
var/list/result = new
if(skiprep)
result = difflist(first, second, skiprep)+difflist(second, first, skiprep)
else
result = first ^ second
return result
//Pretends to pick an element based on its weight but really just seems to pick a random element.
/proc/pickweight(list/L)
var/total = 0
var/item
for (item in L)
if (!L[item])
L[item] = 1
total += L[item]
total = rand(1, total)
for (item in L)
total -=L [item]
if (total <= 0)
return item
return null
//Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/listfrom)
if (listfrom.len > 0)
var/picked = pick(listfrom)
listfrom -= picked
return picked
return null
//Returns the top(last) element from the list and removes it from the list (typical stack function)
/proc/pop(list/listfrom)
if (listfrom.len > 0)
var/picked = listfrom[listfrom.len]
listfrom.len--
return picked
return null
//Returns the next element in parameter list after first appearance of parameter element. If it is the last element of the list or not present in list, returns first element.
/proc/next_in_list(element, list/L)
for(var/i=1, i<L.len, i++)
if(L[i] == element)
return L[i+1]
return L[1]
/*
* Sorting
*/
//Reverses the order of items in the list
/proc/reverselist(list/L)
var/list/output = list()
if(L)
for(var/i = L.len; i >= 1; i--)
output += L[i]
return output
//Randomize: Return the list in a random order
/proc/shuffle(var/list/L)
if(!L)
return
L = L.Copy()
for(var/i=1; i<L.len; i++)
L.Swap(i, rand(i,L.len))
return L
//same, but returns nothing and acts on list in place
/proc/shuffle_inplace(list/L)
if(!L)
return
for(var/i=1, i<L.len, ++i)
L.Swap(i,rand(i,L.len))
//Return a list with no duplicate entries
/proc/uniquelist(var/list/L)
. = list()
for(var/i in L)
. |= i
//same, but returns nothing and acts on list in place (also handles associated values properly)
/proc/uniqueList_inplace(list/L)
var/temp = L.Copy()
L.len = 0
for(var/key in temp)
if (isnum(key))
L |= key
else
L[key] = temp[key]
//Mergesort: divides up the list into halves to begin the sort
/proc/sortKey(var/list/client/L, var/order = 1)
if(isnull(L) || L.len < 2)
return L
var/middle = L.len / 2 + 1
return mergeKey(sortKey(L.Copy(0,middle)), sortKey(L.Copy(middle)), order)
//Mergsort: does the actual sorting and returns the results back to sortAtom
/proc/mergeKey(var/list/client/L, var/list/client/R, var/order = 1)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
var/client/rL = L[Li]
var/client/rR = R[Ri]
if(sorttext(rL.ckey, rR.ckey) == order)
result += L[Li++]
else
result += R[Ri++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
//Mergesort: divides up the list into halves to begin the sort
/proc/sortAtom(var/list/atom/L, var/order = 1)
if(isnull(L) || L.len < 2)
return L
var/middle = L.len / 2 + 1
return mergeAtoms(sortAtom(L.Copy(0,middle)), sortAtom(L.Copy(middle)), order)
//Mergsort: does the actual sorting and returns the results back to sortAtom
/proc/mergeAtoms(var/list/atom/L, var/list/atom/R, var/order = 1)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
var/atom/rL = L[Li]
var/atom/rR = R[Ri]
if(sorttext(rL.name, rR.name) == order)
result += L[Li++]
else
result += R[Ri++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
//Mergesort: Specifically for record datums in a list.
/proc/sortRecord(var/list/datum/data/record/L, var/field = "name", var/order = 1)
if(isnull(L))
return list()
if(L.len < 2)
return L
var/middle = L.len / 2 + 1
return mergeRecordLists(sortRecord(L.Copy(0, middle), field, order), sortRecord(L.Copy(middle), field, order), field, order)
//Mergsort: does the actual sorting and returns the results back to sortRecord
/proc/mergeRecordLists(var/list/datum/data/record/L, var/list/datum/data/record/R, var/field = "name", var/order = 1)
var/Li=1
var/Ri=1
var/list/result = new()
if(!isnull(L) && !isnull(R))
while(Li <= L.len && Ri <= R.len)
var/datum/data/record/rL = L[Li]
if(isnull(rL))
L -= rL
continue
var/datum/data/record/rR = R[Ri]
if(isnull(rR))
R -= rR
continue
if(sorttext(rL.fields[field], rR.fields[field]) == order)
result += L[Li++]
else
result += R[Ri++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
//Mergesort: any value in a list
/proc/sortList(var/list/L)
if(L.len < 2)
return L
var/middle = L.len / 2 + 1 // Copy is first,second-1
return mergeLists(sortList(L.Copy(0,middle)), sortList(L.Copy(middle))) //second parameter null = to end of list
//Mergsorge: uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
/proc/sortNames(var/list/L)
var/list/Q = new()
for(var/atom/x in L)
Q[x.name] = x
return sortList(Q)
/proc/mergeLists(var/list/L, var/list/R)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
if(sorttext(L[Li], R[Ri]) < 1)
result += R[Ri++]
else
result += L[Li++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
// List of lists, sorts by element[key] - for things like crew monitoring computer sorting records by name.
/proc/sortByKey(var/list/L, var/key)
if(L.len < 2)
return L
var/middle = L.len / 2 + 1
return mergeKeyedLists(sortByKey(L.Copy(0, middle), key), sortByKey(L.Copy(middle), key), key)
/proc/mergeKeyedLists(var/list/L, var/list/R, var/key)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
if(sorttext(L[Li][key], R[Ri][key]) < 1)
// Works around list += list2 merging lists; it's not pretty but it works
result += "temp item"
result[result.len] = R[Ri++]
else
result += "temp item"
result[result.len] = L[Li++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
//Mergesort: any value in a list, preserves key=value structure
/proc/sortAssoc(var/list/L)
if(L.len < 2)
return L
var/middle = L.len / 2 + 1 // Copy is first,second-1
return mergeAssoc(sortAssoc(L.Copy(0,middle)), sortAssoc(L.Copy(middle))) //second parameter null = to end of list
/proc/mergeAssoc(var/list/L, var/list/R)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
if(sorttext(L[Li], R[Ri]) < 1)
result += R&R[Ri++]
else
result += L&L[Li++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
// Macros to test for bits in a bitfield. Note, that this is for use with indexes, not bit-masks!
#define BITTEST(bitfield,index) ((bitfield) & (1 << (index)))
#define BITSET(bitfield,index) (bitfield) |= (1 << (index))
#define BITRESET(bitfield,index) (bitfield) &= ~(1 << (index))
#define BITFLIP(bitfield,index) (bitfield) ^= (1 << (index))
//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
/proc/bitfield2list(bitfield = 0, list/wordlist)
var/list/r = list()
if(istype(wordlist,/list))
var/max = min(wordlist.len,16)
var/bit = 1
for(var/i=1, i<=max, i++)
if(bitfield & bit)
r += wordlist[i]
bit = bit << 1
else
for(var/bit=1, bit<=65535, bit = bit << 1)
if(bitfield & bit)
r += bit
return r
// Returns the key based on the index
/proc/get_key_by_index(var/list/L, var/index)
var/i = 1
for(var/key in L)
if(index == i)
return key
i++
return null
// Returns the key based on the index
/proc/get_key_by_value(var/list/L, var/value)
for(var/key in L)
if(L[key] == value)
return key
/proc/count_by_type(var/list/L, type)
var/i = 0
for(var/T in L)
if(istype(T, type))
i++
return i
//Don't use this on lists larger than half a dozen or so
/proc/insertion_sort_numeric_list_ascending(var/list/L)
//world.log << "ascending len input: [L.len]"
var/list/out = list(pop(L))
for(var/entry in L)
if(isnum(entry))
var/success = 0
for(var/i=1, i<=out.len, i++)
if(entry <= out[i])
success = 1
out.Insert(i, entry)
break
if(!success)
out.Add(entry)
//world.log << " output: [out.len]"
return out
/proc/insertion_sort_numeric_list_descending(var/list/L)
//world.log << "descending len input: [L.len]"
var/list/out = insertion_sort_numeric_list_ascending(L)
//world.log << " output: [out.len]"
return reverselist(out)
/proc/dd_sortedObjectList(var/list/L, var/cache=list())
if(L.len < 2)
return L
var/middle = L.len / 2 + 1 // Copy is first,second-1
return dd_mergeObjectList(dd_sortedObjectList(L.Copy(0,middle), cache), dd_sortedObjectList(L.Copy(middle), cache), cache) //second parameter null = to end of list
/proc/dd_mergeObjectList(var/list/L, var/list/R, var/list/cache)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
var/LLi = L[Li]
var/RRi = R[Ri]
var/LLiV = cache[LLi]
var/RRiV = cache[RRi]
if(!LLiV)
LLiV = LLi:dd_SortValue()
cache[LLi] = LLiV
if(!RRiV)
RRiV = RRi:dd_SortValue()
cache[RRi] = RRiV
if(LLiV < RRiV)
result += L[Li++]
else
result += R[Ri++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
// Insert an object into a sorted list, preserving sortedness
/proc/dd_insertObjectList(var/list/L, var/O)
var/min = 1
var/max = L.len
var/Oval = O:dd_SortValue()
while(1)
var/mid = min+round((max-min)/2)
if(mid == max)
L.Insert(mid, O)
return
var/Lmid = L[mid]
var/midval = Lmid:dd_SortValue()
if(Oval == midval)
L.Insert(mid, O)
return
else if(Oval < midval)
max = mid
else
min = mid+1
/*
proc/dd_sortedObjectList(list/incoming)
/*
Use binary search to order by dd_SortValue().
This works by going to the half-point of the list, seeing if the node in
question is higher or lower cost, then going halfway up or down the list
and checking again. This is a very fast way to sort an item into a list.
*/
var/list/sorted_list = new()
var/low_index
var/high_index
var/insert_index
var/midway_calc
var/current_index
var/current_item
var/current_item_value
var/current_sort_object_value
var/list/list_bottom
var/current_sort_object
for (current_sort_object in incoming)
low_index = 1
high_index = sorted_list.len
while (low_index <= high_index)
// Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.)
midway_calc = (low_index + high_index) / 2
current_index = round(midway_calc)
if (midway_calc > current_index)
current_index++
current_item = sorted_list[current_index]
current_item_value = current_item:dd_SortValue()
current_sort_object_value = current_sort_object:dd_SortValue()
if (current_sort_object_value < current_item_value)
high_index = current_index - 1
else if (current_sort_object_value > current_item_value)
low_index = current_index + 1
else
// current_sort_object == current_item
low_index = current_index
break
// Insert before low_index.
insert_index = low_index
// Special case adding to end of list.
if (insert_index > sorted_list.len)
sorted_list += current_sort_object
continue
// Because BYOND lists don't support insert, have to do it by:
// 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list.
list_bottom = sorted_list.Copy(insert_index)
sorted_list.Cut(insert_index)
sorted_list += current_sort_object
sorted_list += list_bottom
return sorted_list
*/
proc/dd_sortedtextlist(list/incoming, case_sensitive = 0)
// Returns a new list with the text values sorted.
// Use binary search to order by sortValue.
// This works by going to the half-point of the list, seeing if the node in question is higher or lower cost,
// then going halfway up or down the list and checking again.
// This is a very fast way to sort an item into a list.
var/list/sorted_text = new()
var/low_index
var/high_index
var/insert_index
var/midway_calc
var/current_index
var/current_item
var/list/list_bottom
var/sort_result
var/current_sort_text
for (current_sort_text in incoming)
low_index = 1
high_index = sorted_text.len
while (low_index <= high_index)
// Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.)
midway_calc = (low_index + high_index) / 2
current_index = round(midway_calc)
if (midway_calc > current_index)
current_index++
current_item = sorted_text[current_index]
if (case_sensitive)
sort_result = sorttextEx(current_sort_text, current_item)
else
sort_result = sorttext(current_sort_text, current_item)
switch(sort_result)
if (1)
high_index = current_index - 1 // current_sort_text < current_item
if (-1)
low_index = current_index + 1 // current_sort_text > current_item
if (0)
low_index = current_index // current_sort_text == current_item
break
// Insert before low_index.
insert_index = low_index
// Special case adding to end of list.
if (insert_index > sorted_text.len)
sorted_text += current_sort_text
continue
// Because BYOND lists don't support insert, have to do it by:
// 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list.
list_bottom = sorted_text.Copy(insert_index)
sorted_text.Cut(insert_index)
sorted_text += current_sort_text
sorted_text += list_bottom
return sorted_text
proc/dd_sortedTextList(list/incoming)
var/case_sensitive = 1
return dd_sortedtextlist(incoming, case_sensitive)
/datum/proc/dd_SortValue()
return "[src]"
/obj/machinery/dd_SortValue()
return "[sanitize_old(name)]"
/obj/machinery/camera/dd_SortValue()
return "[c_tag]"
/datum/alarm/dd_SortValue()
return "[sanitize_old(last_name)]"
/proc/subtypesof(prototype)
return (typesof(prototype) - prototype)
//creates every subtype of prototype (excluding prototype) and adds it to list L.
//if no list/L is provided, one is created.
/proc/init_subtypes(prototype, list/L)
if(!istype(L)) L = list()
for(var/path in subtypesof(prototype))
L += new path()
return L
//creates every subtype of prototype (excluding prototype) and adds it to list L as a type/instance pair.
//if no list/L is provided, one is created.
/proc/init_subtypes_assoc(prototype, list/L)
if(!istype(L)) L = list()
for(var/path in subtypesof(prototype))
L[path] = new path()
return L
//Move a single element from position fromIndex within a list, to position toIndex
//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
//fromIndex and toIndex must be in the range [1,L.len+1]
//This will preserve associations ~Carnie
/proc/moveElement(list/L, fromIndex, toIndex)
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
return
if(fromIndex > toIndex)
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex+1)
//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
//Same as moveElement but for ranges of elements
//This will preserve associations ~Carnie
/proc/moveRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(fromIndex <= toIndex)
return //no need to move
fromIndex += len //we want to shift left instead of right
for(var/i=0, i<distance, ++i)
L.Insert(fromIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(toIndex, toIndex+1)
else
if(fromIndex > toIndex)
fromIndex += len
for(var/i=0, i<len, ++i)
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex+1)
//replaces reverseList ~Carnie
/proc/reverseRange(list/L, start=1, end=0)
if(L.len)
start = start % L.len
end = end % (L.len+1)
if(start <= 0)
start += L.len
if(end <= 0)
end += L.len + 1
--end
while(start < end)
L.Swap(start++,end--)
return L
//Copies a list, and all lists inside it recusively
//Does not copy any other reference type
/proc/deepCopyList(list/l)
if(!islist(l))
return l
. = l.Copy()
for(var/i = 1 to l.len)
if(islist(.[i]))
.[i] = .(.[i])
//Return a list with no duplicate entries
/proc/uniqueList(list/L)
. = list()
for(var/i in L)
. |= i

View File

@@ -285,16 +285,3 @@ Proc for attack log creation, because really why not
else else
. = getCompoundIcon(desired) . = getCompoundIcon(desired)
cached_character_icons[cachekey] = . cached_character_icons[cachekey] = .
/proc/getviewsize(view)
var/viewX
var/viewY
if(isnum(view))
var/totalviewrange = 1 + 2 * view
viewX = totalviewrange
viewY = totalviewrange
else
var/list/viewrangelist = splittext(view,"x")
viewX = text2num(viewrangelist[1])
viewY = text2num(viewrangelist[2])
return list(viewX, viewY)

View File

@@ -19,6 +19,9 @@
/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b) /proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b)
return a.priority - b.priority return a.priority - b.priority
/proc/cmp_timer(datum/timedevent/a, datum/timedevent/b)
return a.timeToRun - b.timeToRun
// Sorts qdel statistics recorsd by time and count // Sorts qdel statistics recorsd by time and count
/proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B) /proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B)
. = B.hard_delete_time - A.hard_delete_time . = B.hard_delete_time - A.hard_delete_time

View File

@@ -1403,6 +1403,20 @@ var/mob/dview/dview_mob = new
#undef NOT_FLAG #undef NOT_FLAG
#undef HAS_FLAG #undef HAS_FLAG
//datum may be null, but it does need to be a typed var
#define NAMEOF(datum, X) (#X || ##datum.##X)
#define VARSET_LIST_CALLBACK(target, var_name, var_value) CALLBACK(GLOBAL_PROC, /proc/___callbackvarset, ##target, ##var_name, ##var_value)
//dupe code because dm can't handle 3 level deep macros
#define VARSET_CALLBACK(datum, var, var_value) CALLBACK(GLOBAL_PROC, /proc/___callbackvarset, ##datum, NAMEOF(##datum, ##var), ##var_value)
/proc/___callbackvarset(list_or_datum, var_name, var_value)
if(length(list_or_datum))
list_or_datum[var_name] = var_value
return
var/datum/D = list_or_datum
D.vars[var_name] = var_value
// Returns direction-string, rounded to multiples of 22.5, from the first parameter to the second // Returns direction-string, rounded to multiples of 22.5, from the first parameter to the second
// N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW // N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW
/proc/get_adir(var/turf/A, var/turf/B) /proc/get_adir(var/turf/A, var/turf/B)

12
code/_helpers/view.dm Normal file
View File

@@ -0,0 +1,12 @@
/proc/getviewsize(view)
var/viewX
var/viewY
if(isnum(view))
var/totalviewrange = 1 + 2 * view
viewX = totalviewrange
viewY = totalviewrange
else
var/list/viewrangelist = splittext(view,"x")
viewX = text2num(viewrangelist[1])
viewY = text2num(viewrangelist[2])
return list(viewX, viewY)

View File

@@ -76,7 +76,7 @@
if(!locate(/turf) in list(A, A.loc)) // Prevents inventory from being drilled if(!locate(/turf) in list(A, A.loc)) // Prevents inventory from being drilled
return return
var/obj/mecha/M = loc var/obj/mecha/M = loc
return M.click_action(A, src) return M.click_action(A, src, params)
if(restrained()) if(restrained())
setClickCooldown(10) setClickCooldown(10)
@@ -282,10 +282,10 @@
Laser Eyes: as the name implies, handles this since nothing else does currently Laser Eyes: as the name implies, handles this since nothing else does currently
face_atom: turns the mob towards what you clicked on face_atom: turns the mob towards what you clicked on
*/ */
/mob/proc/LaserEyes(atom/A) /mob/proc/LaserEyes(atom/A, params)
return return
/mob/living/LaserEyes(atom/A) /mob/living/LaserEyes(atom/A, params)
setClickCooldown(4) setClickCooldown(4)
var/turf/T = get_turf(src) var/turf/T = get_turf(src)
@@ -293,8 +293,11 @@
LE.icon = 'icons/effects/genetics.dmi' LE.icon = 'icons/effects/genetics.dmi'
LE.icon_state = "eyelasers" LE.icon_state = "eyelasers"
playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1) playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1)
LE.launch(A) LE.firer = src
/mob/living/carbon/human/LaserEyes() LE.preparePixelProjectile(A, src, params)
LE.fire()
/mob/living/carbon/human/LaserEyes(atom/A, params)
if(nutrition>0) if(nutrition>0)
..() ..()
nutrition = max(nutrition - rand(1,5),0) nutrition = max(nutrition - rand(1,5),0)

View File

@@ -82,7 +82,7 @@
/datum/action/proc/Deactivate() /datum/action/proc/Deactivate()
return return
/datum/action/proc/Process() /datum/action/process()
return return
/datum/action/proc/CheckRemoval(mob/living/user) // 1 if action is no longer valid for this mob and should be removed /datum/action/proc/CheckRemoval(mob/living/user) // 1 if action is no longer valid for this mob and should be removed

View File

@@ -0,0 +1,16 @@
PROCESSING_SUBSYSTEM_DEF(projectiles)
name = "Projectiles"
wait = 1
stat_tag = "PP"
priority = FIRE_PRIORITY_PROJECTILES
flags = SS_NO_INIT|SS_TICKER
var/global_max_tick_moves = 10
var/global_pixel_speed = 2
var/global_iterations_per_move = 16
/datum/controller/subsystem/processing/projectiles/proc/set_pixel_speed(new_speed)
global_pixel_speed = new_speed
for(var/i in processing)
var/obj/item/projectile/P = i
if(istype(P)) //there's non projectiles on this too.
P.set_pixel_speed(new_speed)

View File

@@ -29,6 +29,7 @@
qdel(timer) qdel(timer)
weakref = null // Clear this reference to ensure it's kept for as brief duration as possible. weakref = null // Clear this reference to ensure it's kept for as brief duration as possible.
tag = null tag = null
SSnanoui.close_uis(src) SSnanoui.close_uis(src)
return QDEL_HINT_QUEUE return QDEL_HINT_QUEUE

View File

@@ -0,0 +1,227 @@
//Designed for things that need precision trajectories like projectiles.
//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels.
//You might see places where it does - 16 - 1. This is intentionally 17 instead of 16, because of how byond's tiles work and how not doing it will result in rounding errors like things getting put on the wrong turf.
#define RETURN_PRECISE_POSITION(A) new /datum/position(A)
#define RETURN_PRECISE_POINT(A) new /datum/point(A)
#define RETURN_POINT_VECTOR(ATOM, ANGLE, SPEED) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED)}
#define RETURN_POINT_VECTOR_INCREMENT(ATOM, ANGLE, SPEED, AMT) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED, AMT)}
/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
var/x = 0
var/y = 0
var/z = 0
var/pixel_x = 0
var/pixel_y = 0
/datum/position/proc/valid()
return x && y && z && !isnull(pixel_x) && !isnull(pixel_y)
/datum/position/New(_x = 0, _y = 0, _z = 0, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/point.
if(istype(_x, /datum/point))
var/datum/point/P = _x
var/turf/T = P.return_turf()
_x = T.x
_y = T.y
_z = T.z
_pixel_x = P.return_px()
_pixel_y = P.return_py()
else if(istype(_x, /atom))
var/atom/A = _x
_x = A.x
_y = A.y
_z = A.z
_pixel_x = A.pixel_x
_pixel_y = A.pixel_y
x = _x
y = _y
z = _z
pixel_x = _pixel_x
pixel_y = _pixel_y
/datum/position/proc/return_turf()
return locate(x, y, z)
/datum/position/proc/return_px()
return pixel_x
/datum/position/proc/return_py()
return pixel_y
/datum/position/proc/return_point()
return new /datum/point(src)
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
var/datum/point/P = new
P.x = a.x + (b.x - a.x) / 2
P.y = a.y + (b.y - a.y) / 2
P.z = a.z
return P
/proc/pixel_length_between_points(datum/point/a, datum/point/b)
return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
/proc/angle_between_points(datum/point/a, datum/point/b)
return ATAN2((b.y - a.y), (b.x - a.x))
/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
var/x = 0
var/y = 0
var/z = 0
/datum/point/proc/valid()
return x && y && z
/datum/point/proc/copy_to(datum/point/p = new)
p.x = x
p.y = y
p.z = z
return p
/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/position or /atom.
if(istype(_x, /datum/position))
var/datum/position/P = _x
_x = P.x
_y = P.y
_z = P.z
_pixel_x = P.pixel_x
_pixel_y = P.pixel_y
else if(istype(_x, /atom))
var/atom/A = _x
_x = A.x
_y = A.y
_z = A.z
_pixel_x = A.pixel_x
_pixel_y = A.pixel_y
initialize_location(_x, _y, _z, _pixel_x, _pixel_y)
/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
if(!isnull(tile_x))
x = ((tile_x - 1) * world.icon_size) + world.icon_size / 2 + p_x + 1
if(!isnull(tile_y))
y = ((tile_y - 1) * world.icon_size) + world.icon_size / 2 + p_y + 1
if(!isnull(tile_z))
z = tile_z
/datum/point/proc/debug_out()
var/turf/T = return_turf()
return "\ref[src] aX [x] aY [y] aZ [z] pX [return_px()] pY [return_py()] mX [T.x] mY [T.y] mZ [T.z]"
/datum/point/proc/move_atom_to_src(atom/movable/AM)
AM.forceMove(return_turf())
AM.pixel_x = return_px()
AM.pixel_y = return_py()
/datum/point/proc/return_turf()
return locate(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
/datum/point/proc/return_coordinates() //[turf_x, turf_y, z]
return list(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
/datum/point/proc/return_position()
return new /datum/position(src)
/datum/point/proc/return_px()
return MODULUS(x, world.icon_size) - 16 - 1
/datum/point/proc/return_py()
return MODULUS(y, world.icon_size) - 16 - 1
/datum/point/vector
var/speed = 32 //pixels per iteration
var/iteration = 0
var/angle = 0
var/mpx = 0 //calculated x/y movement amounts to prevent having to do trig every step.
var/mpy = 0
var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location().
var/starting_y = 0
var/starting_z = 0
/datum/point/vector/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0, _angle, _speed, initial_increment = 0)
..()
initialize_trajectory(_speed, _angle)
if(initial_increment)
increment(initial_increment)
/datum/point/vector/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
. = ..()
starting_x = x
starting_y = y
starting_z = z
/datum/point/vector/copy_to(datum/point/vector/v = new)
..(v)
v.speed = speed
v.iteration = iteration
v.angle = angle
v.mpx = mpx
v.mpy = mpy
v.starting_x = starting_x
v.starting_y = starting_y
v.starting_z = starting_z
return v
/datum/point/vector/proc/initialize_trajectory(pixel_speed, new_angle)
if(!isnull(pixel_speed))
speed = pixel_speed
set_angle(new_angle)
/datum/point/vector/proc/set_angle(new_angle) //calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270.
if(isnull(angle))
return
angle = new_angle
update_offsets()
/datum/point/vector/proc/update_offsets()
mpx = sin(angle) * speed
mpy = cos(angle) * speed
/datum/point/vector/proc/set_speed(new_speed)
if(isnull(new_speed) || speed == new_speed)
return
speed = new_speed
update_offsets()
/datum/point/vector/proc/increment(multiplier = 1)
iteration++
x += mpx * multiplier
y += mpy * multiplier
/datum/point/vector/proc/return_vector_after_increments(amount = 7, multiplier = 1, force_simulate = FALSE)
var/datum/point/vector/v = copy_to()
if(force_simulate)
for(var/i in 1 to amount)
v.increment(multiplier)
else
v.increment(multiplier * amount)
return v
/datum/point/vector/proc/on_z_change()
return
/datum/point/vector/processed //pixel_speed is per decisecond.
var/last_process = 0
var/last_move = 0
var/paused = FALSE
/datum/point/vector/processed/Destroy()
STOP_PROCESSING(SSprojectiles, src)
return ..()
/datum/point/vector/processed/proc/start()
last_process = world.time
last_move = world.time
START_PROCESSING(SSprojectiles, src)
/datum/point/vector/processed/process()
if(paused)
last_move += world.time - last_process
last_process = world.time
return
var/needed_time = world.time - last_move
last_process = world.time
last_move = world.time
increment(needed_time / SSprojectiles.wait)

View File

@@ -8,13 +8,14 @@
var/list/blood_DNA var/list/blood_DNA
var/was_bloodied var/was_bloodied
var/blood_color var/blood_color
var/last_bumped = 0
var/pass_flags = 0 var/pass_flags = 0
var/throwpass = 0 var/throwpass = 0
var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom.
var/simulated = 1 //filter for actions - used by lighting overlays var/simulated = 1 //filter for actions - used by lighting overlays
var/fluorescent // Shows up under a UV light. var/fluorescent // Shows up under a UV light.
var/last_bumped = 0
///Chemistry. ///Chemistry.
var/datum/reagents/reagents = null var/datum/reagents/reagents = null
@@ -98,7 +99,7 @@
return -1 return -1
/atom/proc/Bumped(AM as mob|obj) /atom/proc/Bumped(AM as mob|obj)
return set waitfor = FALSE
// Convenience proc to see if a container is open for chemistry handling // Convenience proc to see if a container is open for chemistry handling
// returns true if open // returns true if open
@@ -496,7 +497,7 @@
if(!istype(drop_destination) || drop_destination == destination) if(!istype(drop_destination) || drop_destination == destination)
return forceMove(destination) return forceMove(destination)
destination = drop_destination destination = drop_destination
return forceMove(null) return moveToNullspace()
/atom/proc/onDropInto(var/atom/movable/AM) /atom/proc/onDropInto(var/atom/movable/AM)
return // If onDropInto returns null, then dropInto will forceMove AM into us. return // If onDropInto returns null, then dropInto will forceMove AM into us.

View File

@@ -4,6 +4,7 @@
var/last_move = null var/last_move = null
var/anchored = 0 var/anchored = 0
// var/elevation = 2 - not used anywhere // var/elevation = 2 - not used anywhere
var/moving_diagonally
var/move_speed = 10 var/move_speed = 10
var/l_move_time = 1 var/l_move_time = 1
var/m_flag = 1 var/m_flag = 1
@@ -21,6 +22,7 @@
var/old_y = 0 var/old_y = 0
var/datum/riding/riding_datum //VOREStation Add - Moved from /obj/vehicle var/datum/riding/riding_datum //VOREStation Add - Moved from /obj/vehicle
var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P) var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P)
var/movement_type = NONE
/atom/movable/Destroy() /atom/movable/Destroy()
. = ..() . = ..()
@@ -33,7 +35,7 @@
if(opacity && isturf(loc)) if(opacity && isturf(loc))
un_opaque = loc un_opaque = loc
loc = null moveToNullspace()
if(un_opaque) if(un_opaque)
un_opaque.recalc_atom_opacity() un_opaque.recalc_atom_opacity()
if (pulledby) if (pulledby)
@@ -42,56 +44,234 @@
pulledby = null pulledby = null
QDEL_NULL(riding_datum) //VOREStation Add QDEL_NULL(riding_datum) //VOREStation Add
/atom/movable/vv_edit_var(var_name, var_value) ////////////////////////////////////////
if(GLOB.VVpixelmovement[var_name]) //Pixel movement is not yet implemented, changing this will break everything irreversibly. // Here's where we rewrite how byond handles movement except slightly different
return FALSE // To be removed on step_ conversion
return ..() // All this work to prevent a second bump
/atom/movable/Move(atom/newloc, direct=0)
/atom/movable/Bump(var/atom/A, yes) . = FALSE
if(src.throwing) if(!newloc || newloc == loc)
src.throw_impact(A)
src.throwing = 0
spawn(0)
if ((A && yes))
A.last_bumped = world.time
A.Bumped(src)
return return
..()
if(!direct)
direct = get_dir(src, newloc)
set_dir(direct)
if(!loc.Exit(src, newloc))
return
if(!newloc.Enter(src, src.loc))
return
// Past this is the point of no return
var/atom/oldloc = loc
var/area/oldarea = get_area(oldloc)
var/area/newarea = get_area(newloc)
loc = newloc
. = TRUE
oldloc.Exited(src, newloc)
if(oldarea != newarea)
oldarea.Exited(src, newloc)
for(var/i in oldloc)
if(i == src) // Multi tile objects
continue
var/atom/movable/thing = i
thing.Uncrossed(src)
newloc.Entered(src, oldloc)
if(oldarea != newarea)
newarea.Entered(src, oldloc)
for(var/i in loc)
if(i == src) // Multi tile objects
continue
var/atom/movable/thing = i
thing.Crossed(src)
//
////////////////////////////////////////
/atom/movable/Move(atom/newloc, direct)
if(!loc || !newloc)
return FALSE
var/atom/oldloc = loc
if(loc != newloc)
if (!(direct & (direct - 1))) //Cardinal move
. = ..()
else //Diagonal move, split it into cardinal moves
moving_diagonally = FIRST_DIAG_STEP
var/first_step_dir
// The `&& moving_diagonally` checks are so that a forceMove taking
// place due to a Crossed, Bumped, etc. call will interrupt
// the second half of the diagonal movement, or the second attempt
// at a first half if step() fails because we hit something.
if (direct & NORTH)
if (direct & EAST)
if (step(src, NORTH) && moving_diagonally)
first_step_dir = NORTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, EAST)
else if (moving_diagonally && step(src, EAST))
first_step_dir = EAST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, NORTH)
else if (direct & WEST)
if (step(src, NORTH) && moving_diagonally)
first_step_dir = NORTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, WEST)
else if (moving_diagonally && step(src, WEST))
first_step_dir = WEST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, NORTH)
else if (direct & SOUTH)
if (direct & EAST)
if (step(src, SOUTH) && moving_diagonally)
first_step_dir = SOUTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, EAST)
else if (moving_diagonally && step(src, EAST))
first_step_dir = EAST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, SOUTH)
else if (direct & WEST)
if (step(src, SOUTH) && moving_diagonally)
first_step_dir = SOUTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, WEST)
else if (moving_diagonally && step(src, WEST))
first_step_dir = WEST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, SOUTH)
if(moving_diagonally == SECOND_DIAG_STEP)
if(!.)
set_dir(first_step_dir)
//else if (!inertia_moving)
// inertia_next_move = world.time + inertia_move_delay
// newtonian_move(direct)
moving_diagonally = 0
return
if(!loc || (loc == oldloc && oldloc != newloc))
last_move = 0
return
if(.)
Moved(oldloc, direct)
//Polaris stuff
move_speed = world.time - l_move_time
l_move_time = world.time
m_flag = 1
//End
last_move = direct
set_dir(direct)
if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s)
return FALSE
//Called after a successful Move(). By this point, we've already moved
/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE)
//if (!inertia_moving)
// inertia_next_move = world.time + inertia_move_delay
// newtonian_move(Dir)
//if (length(client_mobs_in_contents))
// update_parallax_contents()
return TRUE
// Make sure you know what you're doing if you call this, this is intended to only be called by byond directly.
// You probably want CanPass()
/atom/movable/Cross(atom/movable/AM)
. = TRUE
return CanPass(AM, loc)
//oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called!
/atom/movable/Crossed(atom/movable/AM, oldloc)
return return
/atom/movable/Uncross(atom/movable/AM, atom/newloc)
. = ..()
if(isturf(newloc) && !CheckExit(AM, newloc))
return FALSE
/atom/movable/Bump(atom/A)
if(!A)
CRASH("Bump was called with no argument.")
. = ..()
if(throwing)
throw_impact(A)
throwing = 0
if(QDELETED(A))
return
A.Bumped(src)
A.last_bumped = world.time
/atom/movable/proc/forceMove(atom/destination) /atom/movable/proc/forceMove(atom/destination)
if(loc == destination) . = FALSE
return 0
var/is_origin_turf = isturf(loc)
var/is_destination_turf = isturf(destination)
// It is a new area if:
// Both the origin and destination are turfs with different areas.
// When either origin or destination is a turf and the other is not.
var/is_new_area = (is_origin_turf ^ is_destination_turf) || (is_origin_turf && is_destination_turf && loc.loc != destination.loc)
var/atom/origin = loc
loc = destination
if(origin)
origin.Exited(src, destination)
if(is_origin_turf)
for(var/atom/movable/AM in origin)
AM.Uncrossed(src)
if(is_new_area && is_origin_turf)
origin.loc.Exited(src, destination)
if(destination) if(destination)
destination.Entered(src, origin) . = doMove(destination)
if(is_destination_turf) // If we're entering a turf, cross all movable atoms else
for(var/atom/movable/AM in loc) CRASH("No valid destination passed into forceMove")
if(AM != src)
AM.Crossed(src)
if(is_new_area && is_destination_turf)
destination.loc.Entered(src, origin)
Moved(origin) /atom/movable/proc/moveToNullspace()
return 1 return doMove(null)
/atom/movable/proc/doMove(atom/destination)
. = FALSE
if(destination)
if(pulledby)
pulledby.stop_pulling()
var/atom/oldloc = loc
var/same_loc = oldloc == destination
var/area/old_area = get_area(oldloc)
var/area/destarea = get_area(destination)
loc = destination
moving_diagonally = 0
if(!same_loc)
if(oldloc)
oldloc.Exited(src, destination)
if(old_area && old_area != destarea)
old_area.Exited(src, destination)
for(var/atom/movable/AM in oldloc)
AM.Uncrossed(src)
var/turf/oldturf = get_turf(oldloc)
var/turf/destturf = get_turf(destination)
var/old_z = (oldturf ? oldturf.z : null)
var/dest_z = (destturf ? destturf.z : null)
if (old_z != dest_z)
onTransitZ(old_z, dest_z)
destination.Entered(src, oldloc)
if(destarea && old_area != destarea)
destarea.Entered(src, oldloc)
for(var/atom/movable/AM in destination)
if(AM == src)
continue
AM.Crossed(src, oldloc)
Moved(oldloc, NONE, TRUE)
. = TRUE
//If no destination, move the atom into nullspace (don't do this unless you know what you're doing)
else
. = TRUE
if (loc)
var/atom/oldloc = loc
var/area/old_area = get_area(oldloc)
oldloc.Exited(src, null)
if(old_area)
old_area.Exited(src, null)
loc = null
/atom/movable/proc/onTransitZ(old_z,new_z)
GLOB.z_moved_event.raise_event(src, old_z, new_z)
for(var/item in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care.
var/atom/movable/AM = item
AM.onTransitZ(old_z,new_z)
/////////////////////////////////////////////////////////////////
//called when src is thrown into hit_atom //called when src is thrown into hit_atom
/atom/movable/proc/throw_impact(atom/hit_atom, var/speed) /atom/movable/proc/throw_impact(atom/hit_atom, var/speed)
@@ -295,15 +475,12 @@
/atom/movable/proc/adjust_rotation(new_rotation) /atom/movable/proc/adjust_rotation(new_rotation)
icon_rotation = new_rotation icon_rotation = new_rotation
<<<<<<< HEAD
update_transform()
=======
update_transform() update_transform()
// Called when touching a lava tile. // Called when touching a lava tile.
/atom/movable/proc/lava_act() /atom/movable/proc/lava_act()
fire_act(null, 10000, 1000) fire_act(null, 10000, 1000)
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
// Called when something changes z-levels.
/atom/movable/proc/on_z_change(old_z, new_z)
GLOB.z_moved_event.raise_event(src, old_z, new_z)
for(var/item in src) // Notify contents of Z-transition. This can be overriden IF we know the items contents do not care.
var/atom/movable/AM = item
AM.on_z_change(old_z, new_z)

View File

@@ -532,7 +532,8 @@ proc/findNullRod(var/atom/target)
/obj/item/weapon/spell/construct/projectile/on_ranged_cast(atom/hit_atom, mob/living/user) /obj/item/weapon/spell/construct/projectile/on_ranged_cast(atom/hit_atom, mob/living/user)
if(set_up(hit_atom, user)) if(set_up(hit_atom, user))
var/obj/item/projectile/new_projectile = make_projectile(spell_projectile, user) var/obj/item/projectile/new_projectile = make_projectile(spell_projectile, user)
new_projectile.launch(hit_atom) new_projectile.old_style_target(hit_atom)
new_projectile.fire()
log_and_message_admins("has casted [src] at \the [hit_atom].") log_and_message_admins("has casted [src] at \the [hit_atom].")
if(fire_sound) if(fire_sound)
playsound(get_turf(src), fire_sound, 75, 1) playsound(get_turf(src), fire_sound, 75, 1)
@@ -597,9 +598,9 @@ proc/findNullRod(var/atom/target)
light_power = -2 light_power = -2
light_color = "#FFFFFF" light_color = "#FFFFFF"
muzzle_type = /obj/effect/projectile/inversion/muzzle muzzle_type = /obj/effect/projectile/muzzle/inversion
tracer_type = /obj/effect/projectile/inversion/tracer tracer_type = /obj/effect/projectile/tracer/inversion
impact_type = /obj/effect/projectile/inversion/impact impact_type = /obj/effect/projectile/impact/inversion
//Harvester Pain Orb //Harvester Pain Orb

View File

@@ -75,9 +75,10 @@
H.update_action_buttons() H.update_action_buttons()
..() ..()
/obj/item/clothing/suit/armor/tesla/proc/shoot_lightning(var/mob/target, var/power) /obj/item/clothing/suit/armor/tesla/proc/shoot_lightning(mob/target, power)
var/obj/item/projectile/beam/lightning/lightning = new(src) var/obj/item/projectile/beam/lightning/lightning = new(get_turf(src))
lightning.power = power lightning.power = power
lightning.launch(target) lightning.old_style_target(target)
lightning.fire()
visible_message("<span class='danger'>\The [src] strikes \the [target] with lightning!</span>") visible_message("<span class='danger'>\The [src] strikes \the [target] with lightning!</span>")
playsound(get_turf(src), 'sound/weapons/gauss_shoot.ogg', 75, 1) playsound(get_turf(src), 'sound/weapons/gauss_shoot.ogg', 75, 1)

View File

@@ -165,14 +165,15 @@
while(i) while(i)
var/obj/item/projectile/beam/lightning/energy_siphon/lightning = new(get_turf(source)) var/obj/item/projectile/beam/lightning/energy_siphon/lightning = new(get_turf(source))
lightning.firer = user lightning.firer = user
lightning.launch(user) lightning.old_style_target(user)
lightning.fire()
i-- i--
sleep(3) sleep(3)
/obj/item/projectile/beam/lightning/energy_siphon /obj/item/projectile/beam/lightning/energy_siphon
name = "energy stream" name = "energy stream"
icon_state = "lightning" icon_state = "lightning"
kill_count = 6 // Backup plan in-case the effect somehow misses the Technomancer. range = 6 // Backup plan in-case the effect somehow misses the Technomancer.
power = 5 // This fires really fast, so this may add up if someone keeps standing in the beam. power = 5 // This fires really fast, so this may add up if someone keeps standing in the beam.
penetrating = 5 penetrating = 5

View File

@@ -22,6 +22,6 @@
/obj/item/projectile/beam/blue /obj/item/projectile/beam/blue
damage = 30 damage = 30
muzzle_type = /obj/effect/projectile/laser_blue/muzzle muzzle_type = /obj/effect/projectile/muzzle/laser_blue
tracer_type = /obj/effect/projectile/laser_blue/tracer tracer_type = /obj/effect/projectile/tracer/laser_blue
impact_type = /obj/effect/projectile/laser_blue/impact impact_type = /obj/effect/projectile/impact/laser_blue

View File

@@ -26,9 +26,9 @@
nodamage = 1 nodamage = 1
damage_type = HALLOSS damage_type = HALLOSS
muzzle_type = /obj/effect/projectile/lightning/muzzle muzzle_type = /obj/effect/projectile/muzzle/lightning
tracer_type = /obj/effect/projectile/lightning/tracer tracer_type = /obj/effect/projectile/tracer/lightning
impact_type = /obj/effect/projectile/lightning/impact impact_type = /obj/effect/projectile/impact/lightning
var/bounces = 3 //How many times it 'chains'. Note that the first hit is not counted as it counts /bounces/. var/bounces = 3 //How many times it 'chains'. Note that the first hit is not counted as it counts /bounces/.
var/list/hit_mobs = list() //Mobs which were already hit. var/list/hit_mobs = list() //Mobs which were already hit.

View File

@@ -26,9 +26,9 @@
nodamage = 1 nodamage = 1
damage_type = HALLOSS damage_type = HALLOSS
muzzle_type = /obj/effect/projectile/lightning/muzzle muzzle_type = /obj/effect/projectile/muzzle/lightning
tracer_type = /obj/effect/projectile/lightning/tracer tracer_type = /obj/effect/projectile/tracer/lightning
impact_type = /obj/effect/projectile/lightning/impact impact_type = /obj/effect/projectile/impact/lightning
var/power = 60 //How hard it will hit for with electrocute_act(). var/power = 60 //How hard it will hit for with electrocute_act().

View File

@@ -12,7 +12,8 @@
/obj/item/weapon/spell/projectile/on_ranged_cast(atom/hit_atom, mob/living/user) /obj/item/weapon/spell/projectile/on_ranged_cast(atom/hit_atom, mob/living/user)
if(set_up(hit_atom, user)) if(set_up(hit_atom, user))
var/obj/item/projectile/new_projectile = make_projectile(spell_projectile, user) var/obj/item/projectile/new_projectile = make_projectile(spell_projectile, user)
new_projectile.launch(hit_atom) new_projectile.old_style_target(hit_atom)
new_projectile.fire()
log_and_message_admins("has casted [src] at \the [hit_atom].") log_and_message_admins("has casted [src] at \the [hit_atom].")
if(fire_sound) if(fire_sound)
playsound(get_turf(src), fire_sound, 75, 1) playsound(get_turf(src), fire_sound, 75, 1)

View File

@@ -98,6 +98,7 @@
return 1 return 1
/obj/machinery/door/Bumped(atom/AM) /obj/machinery/door/Bumped(atom/AM)
. = ..()
if(p_open || operating) if(p_open || operating)
return return
if(ismob(AM)) if(ismob(AM))
@@ -133,9 +134,6 @@
open() open()
else else
do_animate("deny") do_animate("deny")
return
return
/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) /obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group) return !block_air_zones if(air_group) return !block_air_zones

View File

@@ -68,33 +68,26 @@
if(istype(bot)) if(istype(bot))
if(density && src.check_access(bot.botcard)) if(density && src.check_access(bot.botcard))
open() open()
sleep(50) addtimer(CALLBACK(src, .proc/close), 50)
close()
else if(istype(AM, /obj/mecha)) else if(istype(AM, /obj/mecha))
var/obj/mecha/mecha = AM var/obj/mecha/mecha = AM
if(density) if(density)
if(mecha.occupant && src.allowed(mecha.occupant)) if(mecha.occupant && src.allowed(mecha.occupant))
open() open()
sleep(50) addtimer(CALLBACK(src, .proc/close), 50)
close()
return return
if (!( ticker )) if (!( ticker ))
return return
if (src.operating) if (src.operating)
return return
if (src.density && src.allowed(AM)) if (density && allowed(AM))
open() open()
if(src.check_access(null)) addtimer(CALLBACK(src, .proc/close), check_access(null)? 50 : 20)
sleep(50)
else //secure doors close faster
sleep(20)
close()
return
/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) /obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height, air_group)
if(istype(mover) && mover.checkpass(PASSGLASS)) if(istype(mover) && mover.checkpass(PASSGLASS))
return 1 return 1
if(get_dir(loc, target) == dir) //Make sure looking at appropriate border if(get_dir(mover, loc) == turn(dir, 180)) //Make sure looking at appropriate border
if(air_group) return 0 if(air_group) return 0
return !density return !density
else else
@@ -109,7 +102,7 @@
return 1 return 1
/obj/machinery/door/window/open() /obj/machinery/door/window/open()
if (operating == 1) //doors can still open when emag-disabled if (operating == 1 || !density) //doors can still open when emag-disabled
return 0 return 0
if (!ticker) if (!ticker)
return 0 return 0
@@ -129,20 +122,20 @@
return 1 return 1
/obj/machinery/door/window/close() /obj/machinery/door/window/close()
if (operating) if(operating || density)
return 0 return FALSE
src.operating = 1 operating = TRUE
flick(text("[]closing", src.base_state), src) flick(text("[]closing", src.base_state), src)
playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1) playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1)
density = 1 density = TRUE
update_icon() update_icon()
explosion_resistance = initial(explosion_resistance) explosion_resistance = initial(explosion_resistance)
update_nearby_tiles() update_nearby_tiles()
sleep(10) sleep(10)
operating = 0 operating = FALSE
return 1 return TRUE
/obj/machinery/door/window/take_damage(var/damage) /obj/machinery/door/window/take_damage(var/damage)
src.health = max(0, src.health - damage) src.health = max(0, src.health - damage)

View File

@@ -17,9 +17,12 @@
/datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) /datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param)
return return
/datum/computer/file/embedded_program/process() <<<<<<< HEAD
/datum/computer/file/embedded_program/proc/process()
return return
=======
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
/datum/computer/file/embedded_program/proc/post_signal(datum/signal/signal, comm_line) /datum/computer/file/embedded_program/proc/post_signal(datum/signal/signal, comm_line)
if(master) if(master)
master.post_signal(signal, comm_line) master.post_signal(signal, comm_line)

View File

@@ -722,7 +722,10 @@ var/list/turret_icons
def_zone = pick(BP_TORSO, BP_GROIN) def_zone = pick(BP_TORSO, BP_GROIN)
//Shooting Code: //Shooting Code:
A.launch(target, def_zone) A.firer = src
A.old_style_target(target)
A.def_zone = def_zone
A.fire()
// Reset the time needed to go back down, since we just tried to shoot at someone. // Reset the time needed to go back down, since we just tried to shoot at someone.
timeout = 10 timeout = 10

View File

@@ -19,7 +19,7 @@
return 0 return 0
return ..() return ..()
/obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target) /obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target, params)
if(!action_checks(target)) if(!action_checks(target))
return return
var/turf/curloc = chassis.loc var/turf/curloc = chassis.loc
@@ -39,7 +39,7 @@
playsound(chassis, fire_sound, fire_volume, 1) playsound(chassis, fire_sound, fire_volume, 1)
projectiles-- projectiles--
var/P = new projectile(curloc) var/P = new projectile(curloc)
Fire(P, target) Fire(P, target, params)
if(i == 1) if(i == 1)
set_ready_state(0) set_ready_state(0)
if(fire_cooldown) if(fire_cooldown)
@@ -60,11 +60,12 @@
return return
/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target) /obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, params)
var/obj/item/projectile/P = A var/obj/item/projectile/P = A
P.dispersion = deviation P.dispersion = deviation
process_accuracy(P, chassis.occupant, target) process_accuracy(P, chassis.occupant, target)
P.launch(target) P.preparePixelProjectile(target, chassis.occupant, params)
P.fire()
/obj/item/mecha_parts/mecha_equipment/weapon/proc/process_accuracy(obj/projectile, mob/living/user, atom/target) /obj/item/mecha_parts/mecha_equipment/weapon/proc/process_accuracy(obj/projectile, mob/living/user, atom/target)
var/obj/item/projectile/P = projectile var/obj/item/projectile/P = projectile

View File

@@ -317,7 +317,7 @@
// return ..() // return ..()
*/ */
/obj/mecha/proc/click_action(atom/target,mob/user) /obj/mecha/proc/click_action(atom/target,mob/user, params)
if(!src.occupant || src.occupant != user ) return if(!src.occupant || src.occupant != user ) return
if(user.stat) return if(user.stat) return
if(state) if(state)
@@ -339,7 +339,7 @@
if(selected && selected.is_ranged()) if(selected && selected.is_ranged())
selected.action(target) selected.action(target)
else if(selected && selected.is_melee()) else if(selected && selected.is_melee())
selected.action(target) selected.action(target, params)
else else
src.melee_action(target) src.melee_action(target)
return return

View File

@@ -195,6 +195,7 @@
else else
L.set_dir(dir) L.set_dir(dir)
return TRUE return TRUE
<<<<<<< HEAD
/atom/movable/Move(atom/newloc, direct = 0) /atom/movable/Move(atom/newloc, direct = 0)
. = ..() . = ..()
@@ -205,3 +206,5 @@
riding_datum.handle_vehicle_layer() riding_datum.handle_vehicle_layer()
riding_datum.handle_vehicle_offsets() riding_datum.handle_vehicle_offsets()
//VOREStation Add End //VOREStation Add End
=======
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles

View File

@@ -0,0 +1,81 @@
/obj/effect/projectile/impact
name = "beam impact"
icon = 'icons/obj/projectiles_impact.dmi'
/obj/effect/projectile/impact/laser_pulse
icon_state = "impact_u_laser"
light_range = 2
light_power = 0.5
light_color = "#0066FF"
/obj/effect/projectile/impact/laser_heavy
icon_state = "impact_beam_heavy"
light_range = 3
light_power = 1
light_color = "#FF0D00"
/obj/effect/projectile/impact/xray
icon_state = "impact_xray"
light_range = 2
light_power = 0.5
light_color = "#00CC33"
/obj/effect/projectile/impact/laser_omni
icon_state = "impact_omni"
light_range = 2
light_power = 0.5
light_color = "#00C6FF"
/obj/effect/projectile/impact/laser
icon_state = "impact_laser"
light_range = 2
light_power = 0.5
light_color = "#FF0D00"
/obj/effect/projectile/impact/laser_blue
icon_state = "impact_blue"
light_range = 2
light_power = 0.5
light_color = "#0066FF"
/obj/effect/projectile/impact/emitter
icon_state = "impact_emitter"
light_range = 2
light_power = 0.5
light_color = "#00CC33"
/obj/effect/projectile/impact/stun
icon_state = "impact_stun"
light_range = 2
light_power = 0.5
light_color = "#FFFFFF"
/obj/effect/projectile/impact/lightning
icon_state = "impact_lightning"
light_range = 2
light_power = 0.5
light_color = "#00C6FF"
/obj/effect/projectile/impact/darkmatterstun
icon_state = "impact_darkt"
light_range = 2
light_power = 0.5
light_color = "#8837A3"
/obj/effect/projectile/impact/inversion
icon_state = "impact_invert"
light_range = 2
light_power = -2
light_color = "#FFFFFF"
/obj/effect/projectile/impact/darkmatter
icon_state = "impact_darkb"
light_range = 2
light_power = 0.5
light_color = "#8837A3"
/obj/effect/projectile/tungsten/impact
icon_state = "impact_mhd_laser"
light_range = 4
light_power = 3
light_color = "#3300ff"

View File

@@ -0,0 +1,93 @@
/obj/effect/projectile/muzzle
name = "muzzle flash"
icon = 'icons/obj/projectiles_muzzle.dmi'
/obj/effect/projectile/muzzle/emitter
icon_state = "muzzle_emitter"
light_range = 2
light_power = 0.5
light_color = "#00CC33"
/obj/effect/projectile/muzzle/laser_pulse
icon_state = "muzzle_u_laser"
light_range = 2
light_power = 0.5
light_color = "#0066FF"
/obj/effect/projectile/muzzle/pulse
icon_state = "muzzle_pulse"
light_range = 2
light_power = 0.5
light_color = "#0066FF"
/obj/effect/projectile/muzzle/stun
icon_state = "muzzle_stun"
light_range = 2
light_power = 0.5
light_color = "#FFFFFF"
/obj/effect/projectile/muzzle/bullet
icon_state = "muzzle_bullet"
light_range = 2
light_power = 0.5
light_color = "#FFFFFF"
/obj/effect/projectile/muzzle/laser_heavy
icon_state = "muzzle_beam_heavy"
light_range = 3
light_power = 1
light_color = "#FF0D00"
/obj/effect/projectile/muzzle/lightning
icon_state = "muzzle_lightning"
light_range = 2
light_power = 0.5
light_color = "#00C6FF"
/obj/effect/projectile/muzzle/darkmatterstun
icon_state = "muzzle_darkt"
light_range = 2
light_power = 0.5
light_color = "#8837A3"
/obj/effect/projectile/muzzle/laser_blue
icon_state = "muzzle_blue"
light_range = 2
light_power = 0.5
light_color = "#0066FF"
/obj/effect/projectile/muzzle/darkmatter
icon_state = "muzzle_darkb"
light_range = 2
light_power = 0.5
light_color = "#8837A3"
/obj/effect/projectile/muzzle/inversion
icon_state = "muzzle_invert"
light_range = 2
light_power = -2
light_color = "#FFFFFF"
/obj/effect/projectile/muzzle/xray
icon_state = "muzzle_xray"
light_range = 2
light_power = 0.5
light_color = "#00CC33"
/obj/effect/projectile/muzzle/laser_omni
icon_state = "muzzle_omni"
light_range = 2
light_power = 0.5
light_color = "#00C6FF"
/obj/effect/projectile/muzzle/laser
icon_state = "muzzle_laser"
light_range = 2
light_power = 0.5
light_color = "#FF0D00"
/obj/effect/projectile/tungsten/muzzle
icon_state = "muzzle_mhd_laser"
light_range = 4
light_power = 3
light_color = "#3300ff"

View File

@@ -0,0 +1,60 @@
/obj/effect/projectile
name = "pew"
icon = 'icons/obj/projectiles.dmi'
icon_state = "nothing"
layer = ABOVE_MOB_LAYER
anchored = TRUE
mouse_opacity = 0
appearance_flags = 0
/obj/effect/projectile/singularity_pull()
return
/obj/effect/projectile/singularity_act()
return
/obj/effect/projectile/proc/scale_to(nx,ny,override=TRUE)
var/matrix/M
if(!override)
M = transform
else
M = new
M.Scale(nx,ny)
transform = M
/obj/effect/projectile/proc/turn_to(angle,override=TRUE)
var/matrix/M
if(!override)
M = transform
else
M = new
M.Turn(angle)
transform = M
/obj/effect/projectile/New(angle_override, p_x, p_y, color_override, scaling = 1)
if(angle_override && p_x && p_y && color_override && scaling)
apply_vars(angle_override, p_x, p_y, color_override, scaling)
return ..()
/obj/effect/projectile/proc/apply_vars(angle_override, p_x = 0, p_y = 0, color_override, scaling = 1, new_loc, increment = 0)
var/mutable_appearance/look = new(src)
look.pixel_x = p_x
look.pixel_y = p_y
if(color_override)
look.color = color_override
appearance = look
scale_to(1,scaling, FALSE)
turn_to(angle_override, FALSE)
if(!isnull(new_loc)) //If you want to null it just delete it...
forceMove(new_loc)
for(var/i in 1 to increment)
pixel_x += round((sin(angle_override)+16*sin(angle_override)*2), 1)
pixel_y += round((cos(angle_override)+16*cos(angle_override)*2), 1)
/obj/effect/projectile_lighting
var/owner
/obj/effect/projectile_lighting/New(loc, color, range, intensity, owner_key)
. = ..()
set_light(range, intensity, color)
owner = owner_key

View File

@@ -0,0 +1,109 @@
/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported!
if(!istype(starting) || !istype(ending) || !ispath(beam_type))
return
var/datum/point/midpoint = point_midpoint_points(starting, ending)
var/obj/effect/projectile/tracer/PB = new beam_type
if(isnull(light_color_override))
light_color_override = color
PB.apply_vars(angle_between_points(starting, ending), midpoint.return_px(), midpoint.return_py(), color, pixel_length_between_points(starting, ending) / world.icon_size, midpoint.return_turf(), 0)
. = PB
if(isnull(light_intensity) && !isnull(PB.light_power))
light_intensity = PB.light_power
if(isnull(light_range) && !isnull(PB.light_range))
light_range = PB.light_range
if(isnull(light_color_override) && !isnull(PB.light_color))
light_color_override = PB.light_color
if(light_range > 0 && light_intensity > 0)
var/list/turf/line = getline(starting.return_turf(), ending.return_turf())
tracing_line:
for(var/i in line)
var/turf/T = i
for(var/obj/effect/projectile_lighting/PL in T)
if(PL.owner == instance_key)
continue tracing_line
QDEL_IN(new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity, instance_key), qdel_in > 0? qdel_in : 5)
line = null
if(qdel_in)
QDEL_IN(PB, qdel_in)
/obj/effect/projectile/tracer
name = "beam"
icon = 'icons/obj/projectiles_tracer.dmi'
/obj/effect/projectile/tracer/stun
icon_state = "stun"
light_range = 2
light_power = 0.5
light_color = "#FFFFFF"
/obj/effect/projectile/tracer/lightning
icon_state = "lightning"
light_range = 2
light_power = 0.5
light_color = "#00C6FF"
/obj/effect/projectile/tracer/laser_pulse
icon_state = "u_laser"
light_range = 2
light_power = 0.5
light_color = "#0066FF"
/obj/effect/projectile/tracer/emitter
icon_state = "emitter"
light_range = 2
light_power = 0.5
light_color = "#00CC33"
/obj/effect/projectile/tracer/darkmatterstun
icon_state = "darkt"
light_range = 2
light_power = 0.5
light_color = "#8837A3"
/obj/effect/projectile/tracer/laser_omni
icon_state = "beam_omni"
light_range = 2
light_power = 0.5
light_color = "#00C6FF"
/obj/effect/projectile/tracer/xray
icon_state = "xray"
light_range = 2
light_power = 0.5
light_color = "#00CC33"
/obj/effect/projectile/tracer/laser_heavy
icon_state = "beam_heavy"
light_range = 3
light_power = 1
light_color = "#FF0D00"
/obj/effect/projectile/tracer/darkmatter
icon_state = "darkb"
light_range = 2
light_power = 0.5
light_color = "#8837A3"
/obj/effect/projectile/tracer/inversion
icon_state = "invert"
light_range = 2
light_power = -2
light_color = "#FFFFFF"
/obj/effect/projectile/tracer/laser
icon_state = "beam"
light_range = 2
light_power = 0.5
light_color = "#FF0D00"
/obj/effect/projectile/tracer/laser_blue
icon_state = "beam_blue"
light_range = 2
light_power = 0.5
light_color = "#0066FF"
/obj/effect/projectile/tungsten/tracer
icon_state = "mhd_laser"
light_range = 4
light_power = 3
light_color = "#3300ff"

View File

@@ -7,14 +7,17 @@
mouse_opacity = 0 mouse_opacity = 0
var/duration = 10 //in deciseconds var/duration = 10 //in deciseconds
var/randomdir = TRUE var/randomdir = TRUE
var/timerid
/obj/effect/temp_visual/Initialize() /obj/effect/temp_visual/initialize()
. = ..() . = ..()
if(randomdir) if(randomdir)
set_dir(pick(cardinal)) dir = pick(list(NORTH, SOUTH, EAST, WEST))
timerid = QDEL_IN(src, duration)
spawn(duration) /obj/effect/temp_visual/Destroy()
qdel(src) . = ..()
deltimer(timerid)
/obj/effect/temp_visual/singularity_act() /obj/effect/temp_visual/singularity_act()
return return
@@ -25,12 +28,12 @@
/obj/effect/temp_visual/ex_act() /obj/effect/temp_visual/ex_act()
return return
/*
/obj/effect/temp_visual/dir_setting /obj/effect/temp_visual/dir_setting
randomdir = FALSE randomdir = FALSE
/obj/effect/temp_visual/dir_setting/Initialize(mapload, set_dir) /obj/effect/temp_visual/dir_setting/Initialize(loc, set_dir)
if(set_dir) if(set_dir)
setDir(set_dir) dir = set_dir
. = ..() . = ..()
*/ //More tg stuff that might be useful later

View File

@@ -50,9 +50,10 @@
var/fragment_type = pickweight(fragtypes) var/fragment_type = pickweight(fragtypes)
var/obj/item/projectile/bullet/pellet/fragment/P = new fragment_type(T) var/obj/item/projectile/bullet/pellet/fragment/P = new fragment_type(T)
P.pellets = fragments_per_projectile P.pellets = fragments_per_projectile
P.shot_from = src.name P.shot_from = name
P.launch(O) P.old_style_target(O)
P.fire()
//Make sure to hit any mobs in the source turf //Make sure to hit any mobs in the source turf
for(var/mob/living/M in T) for(var/mob/living/M in T)

View File

@@ -61,7 +61,8 @@
var/obj/item/projectile/P = new shot_type(T) var/obj/item/projectile/P = new shot_type(T)
P.shot_from = src.name P.shot_from = src.name
P.launch(O) P.old_style_target(O)
P.fire()
//Make sure to hit any mobs in the source turf //Make sure to hit any mobs in the source turf
for(var/mob/living/M in T) for(var/mob/living/M in T)

View File

@@ -69,9 +69,10 @@
else else
to_chat(user, "It is full.") to_chat(user, "It is full.")
/obj/structure/closet/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) /obj/structure/closet/CanPass(atom/movable/mover, turf/target, height, air_group)
if(air_group || (height==0 || wall_mounted)) return 1 if(wall_mounted)
return (!density) return TRUE
return ..()
/obj/structure/closet/proc/can_open() /obj/structure/closet/proc/can_open()
if(src.sealed) if(src.sealed)

View File

@@ -131,14 +131,20 @@
/obj/structure/window/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) /obj/structure/window/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(istype(mover) && mover.checkpass(PASSGLASS)) if(istype(mover) && mover.checkpass(PASSGLASS))
<<<<<<< HEAD
return 1 return 1
if(is_fulltile()) if(is_fulltile())
return 0 //full tile window, you can't move into it! return 0 //full tile window, you can't move into it!
if(get_dir(loc, target) & dir) if(get_dir(loc, target) & dir)
=======
return TRUE
if(is_fulltile())
return FALSE //full tile window, you can't move into it!
if((get_dir(loc, target) & dir) || (get_dir(mover, target) == turn(dir, 180)))
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
return !density return !density
else else
return 1 return TRUE
/obj/structure/window/CheckExit(atom/movable/O as mob|obj, target as turf) /obj/structure/window/CheckExit(atom/movable/O as mob|obj, target as turf)
if(istype(O) && O.checkpass(PASSGLASS)) if(istype(O) && O.checkpass(PASSGLASS))
@@ -147,7 +153,6 @@
return 0 return 0
return 1 return 1
/obj/structure/window/hitby(AM as mob|obj) /obj/structure/window/hitby(AM as mob|obj)
..() ..()
visible_message("<span class='danger'>[src] was hit by [AM].</span>") visible_message("<span class='danger'>[src] was hit by [AM].</span>")

View File

@@ -142,50 +142,6 @@ turf/attackby(obj/item/weapon/W as obj, mob/user as mob)
sleep(2) sleep(2)
O.update_transform() O.update_transform()
/turf/Enter(atom/movable/mover as mob|obj, atom/forget as mob|obj|turf|area)
if(movement_disabled && usr.ckey != movement_disabled_exception)
usr << "<span class='warning'>Movement is admin-disabled.</span>" //This is to identify lag problems
return
..()
if (!mover || !isturf(mover.loc))
return 1
//First, check objects to block exit that are not on the border
for(var/obj/obstacle in mover.loc)
if(!(obstacle.flags & ON_BORDER) && (mover != obstacle) && (forget != obstacle))
if(!obstacle.CheckExit(mover, src))
mover.Bump(obstacle, 1)
return 0
//Now, check objects to block exit that are on the border
for(var/obj/border_obstacle in mover.loc)
if((border_obstacle.flags & ON_BORDER) && (mover != border_obstacle) && (forget != border_obstacle))
if(!border_obstacle.CheckExit(mover, src))
mover.Bump(border_obstacle, 1)
return 0
//Next, check objects to block entry that are on the border
for(var/obj/border_obstacle in src)
if(border_obstacle.flags & ON_BORDER)
if(!border_obstacle.CanPass(mover, mover.loc, 1, 0) && (forget != border_obstacle))
mover.Bump(border_obstacle, 1)
return 0
//Then, check the turf itself
if (!src.CanPass(mover, src))
mover.Bump(src, 1)
return 0
//Finally, check objects/mobs to block entry that are not on the border
for(var/atom/movable/obstacle in src)
if(!(obstacle.flags & ON_BORDER))
if(!obstacle.CanPass(mover, mover.loc, 1, 0) && (forget != obstacle))
mover.Bump(obstacle, 1)
return 0
return 1 //Nothing found to block so return success!
var/const/enterloopsanity = 100 var/const/enterloopsanity = 100
/turf/Entered(atom/atom as mob|obj) /turf/Entered(atom/atom as mob|obj)
@@ -218,14 +174,74 @@ var/const/enterloopsanity = 100
var/objects = 0 var/objects = 0
if(A && (A.flags & PROXMOVE)) if(A && (A.flags & PROXMOVE))
for(var/atom/movable/thing in range(1)) for(var/atom/movable/thing in range(1))
if(objects > enterloopsanity) break if(objects++ > enterloopsanity) break
objects++
spawn(0) spawn(0)
if(A) //Runtime prevention if(A) //Runtime prevention
A.HasProximity(thing, 1) A.HasProximity(thing, 1)
if ((thing && A) && (thing.flags & PROXMOVE)) if ((thing && A) && (thing.flags & PROXMOVE))
thing.HasProximity(A, 1) thing.HasProximity(A, 1)
return
/turf/CanPass(atom/movable/mover, turf/target)
if(!target)
return FALSE
if(istype(mover)) // turf/Enter(...) will perform more advanced checks
return !density
crash_with("Non movable passed to turf CanPass : [mover]")
return FALSE
//There's a lot of QDELETED() calls here if someone can figure out how to optimize this but not runtime when something gets deleted by a Bump/CanPass/Cross call, lemme know or go ahead and fix this mess - kevinz000
/turf/Enter(atom/movable/mover, atom/oldloc)
if(movement_disabled && usr.ckey != movement_disabled_exception)
usr << "<span class='warning'>Movement is admin-disabled.</span>" //This is to identify lag problems
return
// Do not call ..()
// Byond's default turf/Enter() doesn't have the behaviour we want with Bump()
// By default byond will call Bump() on the first dense object in contents
// Here's hoping it doesn't stay like this for years before we finish conversion to step_
var/atom/firstbump
var/CanPassSelf = CanPass(mover, src)
if(CanPassSelf || CHECK_BITFIELD(mover.movement_type, UNSTOPPABLE))
for(var/i in contents)
if(QDELETED(mover))
return FALSE //We were deleted, do not attempt to proceed with movement.
if(i == mover || i == mover.loc) // Multi tile objects and moving out of other objects
continue
var/atom/movable/thing = i
if(!thing.Cross(mover))
if(QDELETED(mover)) //Mover deleted from Cross/CanPass, do not proceed.
return FALSE
if(CHECK_BITFIELD(mover.movement_type, UNSTOPPABLE))
mover.Bump(thing)
continue
else
if(!firstbump || ((thing.layer > firstbump.layer || thing.flags & ON_BORDER) && !(firstbump.flags & ON_BORDER)))
firstbump = thing
if(QDELETED(mover)) //Mover deleted from Cross/CanPass/Bump, do not proceed.
return FALSE
if(!CanPassSelf) //Even if mover is unstoppable they need to bump us.
firstbump = src
if(firstbump)
mover.Bump(firstbump)
return !QDELETED(mover) && CHECK_BITFIELD(mover.movement_type, UNSTOPPABLE)
return TRUE
/turf/Exit(atom/movable/mover, atom/newloc)
. = ..()
if(!. || QDELETED(mover))
return FALSE
for(var/i in contents)
if(i == mover)
continue
var/atom/movable/thing = i
if(!thing.Uncross(mover, newloc))
if(thing.flags & ON_BORDER)
mover.Bump(thing)
if(!CHECK_BITFIELD(mover.movement_type, UNSTOPPABLE))
return FALSE
if(QDELETED(mover))
return FALSE //We were deleted.
/turf/proc/adjacent_fire_act(turf/simulated/floor/source, temperature, volume) /turf/proc/adjacent_fire_act(turf/simulated/floor/source, temperature, volume)
return return

639
code/game/world.dm Normal file
View File

@@ -0,0 +1,639 @@
#define RECOMMENDED_VERSION 501
/world/New()
world.log << "Map Loading Complete"
//logs
log_path += time2text(world.realtime, "YYYY/MM-Month/DD-Day/round-hh-mm-ss")
diary = file("[log_path].log")
href_logfile = file("[log_path]-hrefs.htm")
error_log = file("[log_path]-error.log")
debug_log = file("[log_path]-debug.log")
debug_log << "[log_end]\n[log_end]\nStarting up. [time_stamp()][log_end]\n---------------------[log_end]"
changelog_hash = md5('html/changelog.html') //used for telling if the changelog has changed recently
if(byond_version < RECOMMENDED_VERSION)
world.log << "Your server's byond version does not meet the recommended requirements for this server. Please update BYOND"
config.post_load()
if(config && config.server_name != null && config.server_suffix && world.port > 0)
// dumb and hardcoded but I don't care~
config.server_name += " #[(world.port % 1000) / 100]"
if(config && config.log_runtime)
log = file("data/logs/runtime/[time2text(world.realtime,"YYYY-MM-DD-(hh-mm-ss)")]-runtime.log")
GLOB.timezoneOffset = text2num(time2text(0,"hh")) * 36000
callHook("startup")
//Emergency Fix
load_mods()
//end-emergency fix
src.update_status()
. = ..()
#if UNIT_TEST
log_unit_test("Unit Tests Enabled. This will destroy the world when testing is complete.")
log_unit_test("If you did not intend to enable this please check code/__defines/unit_testing.dm")
#endif
// Set up roundstart seed list.
plant_controller = new()
// This is kinda important. Set up details of what the hell things are made of.
populate_material_list()
// Create frame types.
populate_frame_types()
// Create floor types.
populate_flooring_types()
// Create robolimbs for chargen.
populate_robolimb_list()
processScheduler = new
master_controller = new /datum/controller/game_controller()
processScheduler.deferSetupFor(/datum/controller/process/ticker)
processScheduler.setup()
Master.Initialize(10, FALSE)
spawn(1)
master_controller.setup()
#if UNIT_TEST
initialize_unit_tests()
#endif
spawn(3000) //so we aren't adding to the round-start lag
if(config.ToRban)
ToRban_autoupdate()
#undef RECOMMENDED_VERSION
return
var/world_topic_spam_protect_ip = "0.0.0.0"
var/world_topic_spam_protect_time = world.timeofday
/world/Topic(T, addr, master, key)
debug_log << "TOPIC: \"[T]\", from:[addr], master:[master], key:[key][log_end]"
if (T == "ping")
var/x = 1
for (var/client/C)
x++
return x
else if(T == "players")
var/n = 0
for(var/mob/M in player_list)
if(M.client)
n++
return n
else if (copytext(T,1,7) == "status")
var/input[] = params2list(T)
var/list/s = list()
s["version"] = game_version
s["mode"] = master_mode
s["respawn"] = config.abandon_allowed
s["enter"] = config.enter_allowed
s["vote"] = config.allow_vote_mode
s["ai"] = config.allow_ai
s["host"] = host ? host : null
// This is dumb, but spacestation13.com's banners break if player count isn't the 8th field of the reply, so... this has to go here.
s["players"] = 0
s["stationtime"] = stationtime2text()
s["roundduration"] = roundduration2text()
if(input["status"] == "2")
var/list/players = list()
var/list/admins = list()
for(var/client/C in GLOB.clients)
if(C.holder)
if(C.holder.fakekey)
continue
admins[C.key] = C.holder.rank
players += C.key
s["players"] = players.len
s["playerlist"] = list2params(players)
var/list/adm = get_admin_counts()
var/list/presentmins = adm["present"]
var/list/afkmins = adm["afk"]
s["admins"] = presentmins.len + afkmins.len //equivalent to the info gotten from adminwho
s["adminlist"] = list2params(admins)
else
var/n = 0
var/admins = 0
for(var/client/C in GLOB.clients)
if(C.holder)
if(C.holder.fakekey)
continue //so stealthmins aren't revealed by the hub
admins++
s["player[n]"] = C.key
n++
s["players"] = n
s["admins"] = admins
return list2params(s)
else if(T == "manifest")
var/list/positions = list()
var/list/set_names = list(
"heads" = command_positions,
"sec" = security_positions,
"eng" = engineering_positions,
"med" = medical_positions,
"sci" = science_positions,
"car" = cargo_positions,
"civ" = civilian_positions,
"bot" = nonhuman_positions
)
for(var/datum/data/record/t in data_core.general)
var/name = t.fields["name"]
var/rank = t.fields["rank"]
var/real_rank = make_list_rank(t.fields["real_rank"])
var/department = 0
for(var/k in set_names)
if(real_rank in set_names[k])
if(!positions[k])
positions[k] = list()
positions[k][name] = rank
department = 1
if(!department)
if(!positions["misc"])
positions["misc"] = list()
positions["misc"][name] = rank
// Synthetics don't have actual records, so we will pull them from here.
for(var/mob/living/silicon/ai/ai in mob_list)
if(!positions["bot"])
positions["bot"] = list()
positions["bot"][ai.name] = "Artificial Intelligence"
for(var/mob/living/silicon/robot/robot in mob_list)
// No combat/syndicate cyborgs, no drones.
if(robot.module && robot.module.hide_on_manifest)
continue
if(!positions["bot"])
positions["bot"] = list()
positions["bot"][robot.name] = "[robot.modtype] [robot.braintype]"
for(var/k in positions)
positions[k] = list2params(positions[k]) // converts positions["heads"] = list("Bob"="Captain", "Bill"="CMO") into positions["heads"] = "Bob=Captain&Bill=CMO"
return list2params(positions)
else if(T == "revision")
if(revdata.revision)
return list2params(list(branch = revdata.branch, date = revdata.date, revision = revdata.revision))
else
return "unknown"
else if(copytext(T,1,5) == "info")
var/input[] = params2list(T)
if(input["key"] != config.comms_password)
if(world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50)
spawn(50)
world_topic_spam_protect_time = world.time
return "Bad Key (Throttled)"
world_topic_spam_protect_time = world.time
world_topic_spam_protect_ip = addr
return "Bad Key"
var/list/search = params2list(input["info"])
var/list/ckeysearch = list()
for(var/text in search)
ckeysearch += ckey(text)
var/list/match = list()
for(var/mob/M in mob_list)
var/strings = list(M.name, M.ckey)
if(M.mind)
strings += M.mind.assigned_role
strings += M.mind.special_role
for(var/text in strings)
if(ckey(text) in ckeysearch)
match[M] += 10 // an exact match is far better than a partial one
else
for(var/searchstr in search)
if(findtext(text, searchstr))
match[M] += 1
var/maxstrength = 0
for(var/mob/M in match)
maxstrength = max(match[M], maxstrength)
for(var/mob/M in match)
if(match[M] < maxstrength)
match -= M
if(!match.len)
return "No matches"
else if(match.len == 1)
var/mob/M = match[1]
var/info = list()
info["key"] = M.key
info["name"] = M.name == M.real_name ? M.name : "[M.name] ([M.real_name])"
info["role"] = M.mind ? (M.mind.assigned_role ? M.mind.assigned_role : "No role") : "No mind"
var/turf/MT = get_turf(M)
info["loc"] = M.loc ? "[M.loc]" : "null"
info["turf"] = MT ? "[MT] @ [MT.x], [MT.y], [MT.z]" : "null"
info["area"] = MT ? "[MT.loc]" : "null"
info["antag"] = M.mind ? (M.mind.special_role ? M.mind.special_role : "Not antag") : "No mind"
info["hasbeenrev"] = M.mind ? M.mind.has_been_rev : "No mind"
info["stat"] = M.stat
info["type"] = M.type
if(isliving(M))
var/mob/living/L = M
info["damage"] = list2params(list(
oxy = L.getOxyLoss(),
tox = L.getToxLoss(),
fire = L.getFireLoss(),
brute = L.getBruteLoss(),
clone = L.getCloneLoss(),
brain = L.getBrainLoss()
))
else
info["damage"] = "non-living"
info["gender"] = M.gender
return list2params(info)
else
var/list/ret = list()
for(var/mob/M in match)
ret[M.key] = M.name
return list2params(ret)
else if(copytext(T,1,9) == "adminmsg")
/*
We got an adminmsg from IRC bot lets split the input then validate the input.
expected output:
1. adminmsg = ckey of person the message is to
2. msg = contents of message, parems2list requires
3. validatationkey = the key the bot has, it should match the gameservers commspassword in it's configuration.
4. sender = the ircnick that send the message.
*/
var/input[] = params2list(T)
if(input["key"] != config.comms_password)
if(world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50)
spawn(50)
world_topic_spam_protect_time = world.time
return "Bad Key (Throttled)"
world_topic_spam_protect_time = world.time
world_topic_spam_protect_ip = addr
return "Bad Key"
var/client/C
var/req_ckey = ckey(input["adminmsg"])
for(var/client/K in GLOB.clients)
if(K.ckey == req_ckey)
C = K
break
if(!C)
return "No client with that name on server"
var/rank = input["rank"]
if(!rank)
rank = "Admin"
var/message = "<font color='red'>IRC-[rank] PM from <b><a href='?irc_msg=[input["sender"]]'>IRC-[input["sender"]]</a></b>: [input["msg"]]</font>"
var/amessage = "<font color='blue'>IRC-[rank] PM from <a href='?irc_msg=[input["sender"]]'>IRC-[input["sender"]]</a> to <b>[key_name(C)]</b> : [input["msg"]]</font>"
C.received_irc_pm = world.time
C.irc_admin = input["sender"]
C << 'sound/effects/adminhelp.ogg'
C << message
for(var/client/A in admins)
if(A != C)
A << amessage
return "Message Successful"
else if(copytext(T,1,6) == "notes")
/*
We got a request for notes from the IRC Bot
expected output:
1. notes = ckey of person the notes lookup is for
2. validationkey = the key the bot has, it should match the gameservers commspassword in it's configuration.
*/
var/input[] = params2list(T)
if(input["key"] != config.comms_password)
if(world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50)
spawn(50)
world_topic_spam_protect_time = world.time
return "Bad Key (Throttled)"
world_topic_spam_protect_time = world.time
world_topic_spam_protect_ip = addr
return "Bad Key"
return show_player_info_irc(ckey(input["notes"]))
else if(copytext(T,1,4) == "age")
var/input[] = params2list(T)
if(input["key"] != config.comms_password)
if(world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50)
spawn(50)
world_topic_spam_protect_time = world.time
return "Bad Key (Throttled)"
world_topic_spam_protect_time = world.time
world_topic_spam_protect_ip = addr
return "Bad Key"
var/age = get_player_age(input["age"])
if(isnum(age))
if(age >= 0)
return "[age]"
else
return "Ckey not found"
else
return "Database connection failed or not set up"
/world/Reboot(reason = 0, fast_track = FALSE)
/*spawn(0)
world << sound(pick('sound/AI/newroundsexy.ogg','sound/misc/apcdestroyed.ogg','sound/misc/bangindonk.ogg')) // random end sounds!! - LastyBatsy
*/
if (reason || fast_track) //special reboot, do none of the normal stuff
if (usr)
log_admin("[key_name(usr)] Has requested an immediate world restart via client side debugging tools")
message_admins("[key_name_admin(usr)] Has requested an immediate world restart via client side debugging tools")
world << "<span class='boldannounce'>[key_name_admin(usr)] has requested an immediate world restart via client side debugging tools</span>"
else
world << "<span class='boldannounce'>Rebooting world immediately due to host request</span>"
else
processScheduler.stop()
Master.Shutdown() //run SS shutdowns
for(var/client/C in GLOB.clients)
if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[config.server]")
log_world("World rebooted at [time_stamp()]")
..()
/hook/startup/proc/loadMode()
world.load_mode()
return 1
/world/proc/load_mode()
if(!fexists("data/mode.txt"))
return
var/list/Lines = file2list("data/mode.txt")
if(Lines.len)
if(Lines[1])
master_mode = Lines[1]
log_misc("Saved mode is '[master_mode]'")
/world/proc/save_mode(var/the_mode)
var/F = file("data/mode.txt")
fdel(F)
F << the_mode
/hook/startup/proc/loadMOTD()
world.load_motd()
return 1
/world/proc/load_motd()
join_motd = file2text("config/motd.txt")
/proc/load_configuration()
config = new /datum/configuration()
config.load("config/config.txt")
config.load("config/game_options.txt","game_options")
config.loadsql("config/dbconfig.txt")
config.loadforumsql("config/forumdbconfig.txt")
/hook/startup/proc/loadMods()
world.load_mods()
world.load_mentors() // no need to write another hook.
return 1
/world/proc/load_mods()
if(config.admin_legacy_system)
var/text = file2text("config/moderators.txt")
if (!text)
error("Failed to load config/mods.txt")
else
var/list/lines = splittext(text, "\n")
for(var/line in lines)
if (!line)
continue
if (copytext(line, 1, 2) == ";")
continue
var/title = "Moderator"
var/rights = admin_ranks[title]
var/ckey = copytext(line, 1, length(line)+1)
var/datum/admins/D = new /datum/admins(title, rights, ckey)
D.associate(GLOB.directory[ckey])
/world/proc/load_mentors()
if(config.admin_legacy_system)
var/text = file2text("config/mentors.txt")
if (!text)
error("Failed to load config/mentors.txt")
else
var/list/lines = splittext(text, "\n")
for(var/line in lines)
if (!line)
continue
if (copytext(line, 1, 2) == ";")
continue
var/title = "Mentor"
var/rights = admin_ranks[title]
var/ckey = copytext(line, 1, length(line)+1)
var/datum/admins/D = new /datum/admins(title, rights, ckey)
D.associate(GLOB.directory[ckey])
/world/proc/update_status()
var/s = ""
if (config && config.server_name)
s += "<b>[config.server_name]</b> &#8212; "
s += "<b>[station_name()]</b>";
s += " ("
s += "<a href=\"http://\">" //Change this to wherever you want the hub to link to.
// s += "[game_version]"
s += "Default" //Replace this with something else. Or ever better, delete it and uncomment the game version.
s += "</a>"
s += ")"
var/list/features = list()
if(ticker)
if(master_mode)
features += master_mode
else
features += "<b>STARTING</b>"
if (!config.enter_allowed)
features += "closed"
features += config.abandon_allowed ? "respawn" : "no respawn"
if (config && config.allow_vote_mode)
features += "vote"
if (config && config.allow_ai)
features += "AI allowed"
var/n = 0
for (var/mob/M in player_list)
if (M.client)
n++
if (n > 1)
features += "~[n] players"
else if (n > 0)
features += "~[n] player"
if (config && config.hostedby)
features += "hosted by <b>[config.hostedby]</b>"
if (features)
s += ": [jointext(features, ", ")]"
/* does this help? I do not know */
if (src.status != s)
src.status = s
#define FAILED_DB_CONNECTION_CUTOFF 5
var/failed_db_connections = 0
var/failed_old_db_connections = 0
/hook/startup/proc/connectDB()
if(!config.sql_enabled)
world.log << "SQL connection disabled in config."
else if(!setup_database_connection())
world.log << "Your server failed to establish a connection with the feedback database."
else
world.log << "Feedback database connection established."
return 1
proc/setup_database_connection()
if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore.
return 0
if(!dbcon)
dbcon = new()
var/user = sqlfdbklogin
var/pass = sqlfdbkpass
var/db = sqlfdbkdb
var/address = sqladdress
var/port = sqlport
dbcon.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]")
. = dbcon.IsConnected()
if ( . )
failed_db_connections = 0 //If this connection succeeded, reset the failed connections counter.
else
failed_db_connections++ //If it failed, increase the failed connections counter.
world.log << dbcon.ErrorMsg()
return .
//This proc ensures that the connection to the feedback database (global variable dbcon) is established
proc/establish_db_connection()
if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF)
return 0
if(!dbcon || !dbcon.IsConnected())
return setup_database_connection()
else
return 1
/hook/startup/proc/connectOldDB()
if(!config.sql_enabled)
world.log << "SQL connection disabled in config."
else if(!setup_old_database_connection())
world.log << "Your server failed to establish a connection with the SQL database."
else
world.log << "SQL database connection established."
return 1
//These two procs are for the old database, while it's being phased out. See the tgstation.sql file in the SQL folder for more information.
proc/setup_old_database_connection()
if(failed_old_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore.
return 0
if(!dbcon_old)
dbcon_old = new()
var/user = sqllogin
var/pass = sqlpass
var/db = sqldb
var/address = sqladdress
var/port = sqlport
dbcon_old.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]")
. = dbcon_old.IsConnected()
if ( . )
failed_old_db_connections = 0 //If this connection succeeded, reset the failed connections counter.
else
failed_old_db_connections++ //If it failed, increase the failed connections counter.
world.log << dbcon.ErrorMsg()
return .
//This proc ensures that the connection to the feedback database (global variable dbcon) is established
proc/establish_old_db_connection()
if(failed_old_db_connections > FAILED_DB_CONNECTION_CUTOFF)
return 0
if(!dbcon_old || !dbcon_old.IsConnected())
return setup_old_database_connection()
else
return 1
// Things to do when a new z-level was just made.
/world/proc/max_z_changed()
if(!islist(GLOB.players_by_zlevel))
GLOB.players_by_zlevel = new /list(world.maxz, 0)
while(GLOB.players_by_zlevel.len < world.maxz)
GLOB.players_by_zlevel.len++
GLOB.players_by_zlevel[GLOB.players_by_zlevel.len] = list()
// Call this to make a new blank z-level, don't modify maxz directly.
/world/proc/increment_max_z()
maxz++
max_z_changed()
#undef FAILED_DB_CONNECTION_CUTOFF

27
code/global_init.dm Normal file
View File

@@ -0,0 +1,27 @@
/*
The initialization of the game happens roughly like this:
1. All global variables are initialized (including the global_init instance).
2. The map is initialized, and map objects are created.
3. world/New() runs, creating the process scheduler (and the old master controller) and spawning their setup.
4. processScheduler/setup() runs, creating all the processes. game_controller/setup() runs, calling initialize() on all movable atoms in the world.
5. The gameticker is created.
*/
var/global/datum/global_init/init = new ()
/*
Pre-map initialization stuff should go here.
*/
/datum/global_init/New()
makeDatumRefLists()
load_configuration()
initialize_integrated_circuits_list()
qdel(src) //we're done
/datum/global_init/Destroy()
global.init = null
return 2 // QDEL_HINT_IWILLGC

View File

@@ -432,7 +432,7 @@
P.pass_flags = initial(copy_projectile.pass_flags) P.pass_flags = initial(copy_projectile.pass_flags)
P.fire_sound = initial(copy_projectile.fire_sound) P.fire_sound = initial(copy_projectile.fire_sound)
P.hitscan = initial(copy_projectile.hitscan) P.hitscan = initial(copy_projectile.hitscan)
P.step_delay = initial(copy_projectile.step_delay) P.speed = initial(copy_projectile.speed)
P.muzzle_type = initial(copy_projectile.muzzle_type) P.muzzle_type = initial(copy_projectile.muzzle_type)
P.tracer_type = initial(copy_projectile.tracer_type) P.tracer_type = initial(copy_projectile.tracer_type)
P.impact_type = initial(copy_projectile.impact_type) P.impact_type = initial(copy_projectile.impact_type)

View File

@@ -68,8 +68,9 @@
playsound(loc, emagged ? 'sound/weapons/Laser.ogg' : 'sound/weapons/Taser.ogg', 50, 1) playsound(loc, emagged ? 'sound/weapons/Laser.ogg' : 'sound/weapons/Taser.ogg', 50, 1)
var/obj/item/projectile/P = new projectile(loc) var/obj/item/projectile/P = new projectile(loc)
P.launch(A) P.firer = src
return P.old_style_target(A)
P.fire()
// Assembly // Assembly

View File

@@ -348,12 +348,12 @@
if(alert(src,"You sure you want to sleep for a while?","Sleep","Yes","No") == "Yes") if(alert(src,"You sure you want to sleep for a while?","Sleep","Yes","No") == "Yes")
usr.sleeping = 20 //Short nap usr.sleeping = 20 //Short nap
/mob/living/carbon/Bump(var/atom/movable/AM, yes) /mob/living/carbon/Bump(atom/A)
if(now_pushing || !yes) if(now_pushing)
return return
..() ..()
if(istype(AM, /mob/living/carbon) && prob(10)) if(istype(A, /mob/living/carbon) && prob(10))
src.spread_disease_to(AM, "Contact") spread_disease_to(A, "Contact")
/mob/living/carbon/cannot_use_vents() /mob/living/carbon/cannot_use_vents()
return return

View File

@@ -142,7 +142,8 @@
visible_message("<span class='warning'>[src] spits [spit_name] at \the [A]!</span>", "<span class='alium'>You spit [spit_name] at \the [A].</span>") visible_message("<span class='warning'>[src] spits [spit_name] at \the [A]!</span>", "<span class='alium'>You spit [spit_name] at \the [A].</span>")
var/obj/item/projectile/P = new spit_projectile(get_turf(src)) var/obj/item/projectile/P = new spit_projectile(get_turf(src))
P.firer = src P.firer = src
P.launch(A) P.old_style_target(A)
P.fire()
playsound(loc, 'sound/weapons/pierce.ogg', 25, 0) playsound(loc, 'sound/weapons/pierce.ogg', 25, 0)
else else
..() ..()

View File

@@ -80,49 +80,23 @@ default behaviour is:
return 1 return 1
return 0 return 0
/mob/living/Bump(atom/movable/AM, yes) /mob/living/Bump(atom/movable/AM)
spawn(0) if(now_pushing || !loc)
if ((!( yes ) || now_pushing) || !loc) return
return now_pushing = 1
now_pushing = 1 if (istype(AM, /mob/living))
if (istype(AM, /mob/living)) var/mob/living/tmob = AM
var/mob/living/tmob = AM
//Even if we don't push/swap places, we "touched" them, so spread fire //Even if we don't push/swap places, we "touched" them, so spread fire
spread_fire(tmob) spread_fire(tmob)
for(var/mob/living/M in range(tmob, 1)) for(var/mob/living/M in range(tmob, 1))
if(tmob.pinned.len || ((M.pulling == tmob && ( tmob.restrained() && !( M.restrained() ) && M.stat == 0)) || locate(/obj/item/weapon/grab, tmob.grabbed_by.len)) ) if(tmob.pinned.len || ((M.pulling == tmob && ( tmob.restrained() && !( M.restrained() ) && M.stat == 0)) || locate(/obj/item/weapon/grab, tmob.grabbed_by.len)) )
if ( !(world.time % 5) ) if ( !(world.time % 5) )
to_chat(src, "<span class='warning'>[tmob] is restrained, you cannot push past</span>") to_chat(src, "<span class='warning'>[tmob] is restrained, you cannot push past</span>")
now_pushing = 0
return
if( tmob.pulling == M && ( M.restrained() && !( tmob.restrained() ) && tmob.stat == 0) )
if ( !(world.time % 5) )
to_chat(src, "<span class='warning'>[tmob] is restraining [M], you cannot push past</span>")
now_pushing = 0
return
//BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
var/dense = 0
if(loc.density)
dense = 1
for(var/atom/movable/A in loc)
if(A == src)
continue
if(A.density)
if(A.flags&ON_BORDER)
dense = !A.CanPass(src, src.loc)
else
dense = 1
if(dense) break
//Leaping mobs just land on the tile, no pushing, no anything.
if(status_flags & LEAPING)
loc = tmob.loc
status_flags &= ~LEAPING
now_pushing = 0 now_pushing = 0
return return
<<<<<<< HEAD
if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && !dense && can_move_mob(tmob, 1, 0)) // mutual brohugs all around! if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && !dense && can_move_mob(tmob, 1, 0)) // mutual brohugs all around!
var/turf/oldloc = loc var/turf/oldloc = loc
@@ -145,15 +119,58 @@ default behaviour is:
// VOREStation Edit - End // VOREStation Edit - End
tmob.forceMove(oldloc) tmob.forceMove(oldloc)
=======
if( tmob.pulling == M && ( M.restrained() && !( tmob.restrained() ) && tmob.stat == 0) )
if ( !(world.time % 5) )
to_chat(src, "<span class='warning'>[tmob] is restraining [M], you cannot push past</span>")
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
now_pushing = 0 now_pushing = 0
return return
if(!can_move_mob(tmob, 0, 0)) //BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
var/dense = 0
if(loc.density)
dense = 1
for(var/atom/movable/A in loc)
if(A == src)
continue
if(A.density)
if(A.flags&ON_BORDER)
dense = !A.CanPass(src, src.loc)
else
dense = 1
if(dense) break
//Leaping mobs just land on the tile, no pushing, no anything.
if(status_flags & LEAPING)
loc = tmob.loc
status_flags &= ~LEAPING
now_pushing = 0
return
if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && !dense && can_move_mob(tmob, 1, 0)) // mutual brohugs all around!
var/turf/oldloc = loc
forceMove(tmob.loc)
tmob.forceMove(oldloc)
now_pushing = 0
return
if(!can_move_mob(tmob, 0, 0))
now_pushing = 0
return
if(a_intent == I_HELP || src.restrained())
now_pushing = 0
return
if(istype(tmob, /mob/living/carbon/human) && (FAT in tmob.mutations))
if(prob(40) && !(FAT in src.mutations))
to_chat(src, "<span class='danger'>You fail to push [tmob]'s fat ass out of the way.</span>")
now_pushing = 0 now_pushing = 0
return return
if(a_intent == I_HELP || src.restrained()) if(tmob.r_hand && istype(tmob.r_hand, /obj/item/weapon/shield/riot))
if(prob(99))
now_pushing = 0 now_pushing = 0
return return
<<<<<<< HEAD
// VOREStation Edit - Begin // VOREStation Edit - Begin
// Plow that nerd. // Plow that nerd.
@@ -182,11 +199,19 @@ default behaviour is:
now_pushing = 0 now_pushing = 0
return return
if(!(tmob.status_flags & CANPUSH)) if(!(tmob.status_flags & CANPUSH))
=======
if(tmob.l_hand && istype(tmob.l_hand, /obj/item/weapon/shield/riot))
if(prob(99))
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
now_pushing = 0 now_pushing = 0
return return
if(!(tmob.status_flags & CANPUSH))
now_pushing = 0
return
tmob.LAssailant = src tmob.LAssailant = src
<<<<<<< HEAD
now_pushing = 0 now_pushing = 0
spawn(0) spawn(0)
..() ..()
@@ -203,27 +228,36 @@ default behaviour is:
src << ("<span class='warning'>You just [pick("ran", "slammed")] into \the [AM]!</span>") src << ("<span class='warning'>You just [pick("ran", "slammed")] into \the [AM]!</span>")
to_chat(src, "<span class='warning'>You just [pick("ran", "slammed")] into \the [AM]!</span>") to_chat(src, "<span class='warning'>You just [pick("ran", "slammed")] into \the [AM]!</span>")
*/ // VOREStation Removal End */ // VOREStation Removal End
=======
now_pushing = 0
. = ..()
if (!istype(AM, /atom/movable) || AM.anchored)
if(confused && prob(50) && m_intent=="run")
Weaken(2)
playsound(loc, "punch", 25, 1, -1)
visible_message("<span class='warning'>[src] [pick("ran", "slammed")] into \the [AM]!</span>")
src.apply_damage(5, BRUTE)
to_chat(src, "<span class='warning'>You just [pick("ran", "slammed")] into \the [AM]!</span>")
return
if (!now_pushing)
if(isobj(AM))
var/obj/I = AM
if(!can_pull_size || can_pull_size < I.w_class)
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
return return
if (!now_pushing) now_pushing = 1
if(isobj(AM))
var/obj/I = AM
if(!can_pull_size || can_pull_size < I.w_class)
return
now_pushing = 1
var/t = get_dir(src, AM) var/t = get_dir(src, AM)
if (istype(AM, /obj/structure/window)) if (istype(AM, /obj/structure/window))
for(var/obj/structure/window/win in get_step(AM,t)) for(var/obj/structure/window/win in get_step(AM,t))
now_pushing = 0
return
step(AM, t)
if(ishuman(AM) && AM:grabbed_by)
for(var/obj/item/weapon/grab/G in AM:grabbed_by)
step(G:assailant, get_dir(G:assailant, AM))
G.adjust_position()
now_pushing = 0 now_pushing = 0
return return
return step(AM, t)
if(ishuman(AM) && AM:grabbed_by)
for(var/obj/item/weapon/grab/G in AM:grabbed_by)
step(G:assailant, get_dir(G:assailant, AM))
G.adjust_position()
now_pushing = 0
/mob/living/CanPass(atom/movable/mover, turf/target) /mob/living/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /obj/structure/blob) && faction == "blob") //Blobs should ignore things on their faction. if(istype(mover, /obj/structure/blob) && faction == "blob") //Blobs should ignore things on their faction.

View File

@@ -1383,17 +1383,15 @@
if(target == start) if(target == start)
return return
var/obj/item/projectile/A = new projectiletype(user:loc) var/obj/item/projectile/A = new projectiletype(user.loc)
playsound(user, projectilesound, 100, 1) playsound(user, projectilesound, 100, 1)
if(!A) return if(!A) return
// if (!istype(target, /turf)) // if (!istype(target, /turf))
// qdel(A) // qdel(A)
// return // return
A.old_style_target(target)
A.firer = src A.fire()
A.launch(target)
return
//We can't see the target //We can't see the target
/mob/living/simple_mob/proc/LoseTarget() /mob/living/simple_mob/proc/LoseTarget()

View File

@@ -128,7 +128,8 @@
playsound(src, P.fire_sound ? P.fire_sound : projectilesound, 80, 1) playsound(src, P.fire_sound ? P.fire_sound : projectilesound, 80, 1)
P.firer = src // So we can't shoot ourselves. P.firer = src // So we can't shoot ourselves.
P.launch(A) P.old_style_target(A, src)
P.fire()
if(needs_reload) if(needs_reload)
reload_count++ reload_count++

View File

@@ -33,5 +33,6 @@
playsound(src, 'sound/weapons/thudswoosh.ogg', 100, 1) playsound(src, 'sound/weapons/thudswoosh.ogg', 100, 1)
if(!B) if(!B)
return return
B.launch(A) B.old_style_target(A, src)
B.fire()
set_AI_busy(FALSE) set_AI_busy(FALSE)

View File

@@ -1,261 +1,263 @@
// Stronger than a regular Dark Gygax, this one has three special attacks, based on intents. // Stronger than a regular Dark Gygax, this one has three special attacks, based on intents.
// First special attack launches three arcing rockets at the current target. // First special attack launches three arcing rockets at the current target.
// Second special attack fires a projectile that creates a short-lived microsingularity that pulls in everything nearby. Magboots can protect from this. // Second special attack fires a projectile that creates a short-lived microsingularity that pulls in everything nearby. Magboots can protect from this.
// Third special attack creates a dangerous electric field that causes escalating electric damage, before emitting a tesla shock and blinding anyone looking at the mecha. // Third special attack creates a dangerous electric field that causes escalating electric damage, before emitting a tesla shock and blinding anyone looking at the mecha.
// The AI will choose one every ten seconds. // The AI will choose one every ten seconds.
/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced /mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced
name = "advanced dark gygax" name = "advanced dark gygax"
desc = "An experimental exosuit that utilizes advanced materials to allow for greater protection while still being lightweight and fast. \ desc = "An experimental exosuit that utilizes advanced materials to allow for greater protection while still being lightweight and fast. \
It also is armed with an array of next-generation weaponry." It also is armed with an array of next-generation weaponry."
icon_state = "darkgygax_adv" icon_state = "darkgygax_adv"
wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv
icon_scale = 1.5 icon_scale = 1.5
movement_shake_radius = 14 movement_shake_radius = 14
maxHealth = 450 maxHealth = 450
deflect_chance = 25 deflect_chance = 25
has_repair_droid = TRUE has_repair_droid = TRUE
armor = list( armor = list(
"melee" = 50, "melee" = 50,
"bullet" = 50, "bullet" = 50,
"laser" = 50, "laser" = 50,
"energy" = 30, "energy" = 30,
"bomb" = 30, "bomb" = 30,
"bio" = 100, "bio" = 100,
"rad" = 100 "rad" = 100
) )
special_attack_min_range = 1 special_attack_min_range = 1
special_attack_max_range = 7 special_attack_max_range = 7
special_attack_cooldown = 10 SECONDS special_attack_cooldown = 10 SECONDS
projectiletype = /obj/item/projectile/force_missile projectiletype = /obj/item/projectile/force_missile
projectilesound = 'sound/weapons/wave.ogg' projectilesound = 'sound/weapons/wave.ogg'
var/obj/effect/overlay/energy_ball/energy_ball = null var/obj/effect/overlay/energy_ball/energy_ball = null
/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/Destroy() /mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/Destroy()
if(energy_ball) if(energy_ball)
energy_ball.stop_orbit() energy_ball.stop_orbit()
qdel(energy_ball) qdel(energy_ball)
return ..() return ..()
/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/do_special_attack(atom/A) /mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/do_special_attack(atom/A)
. = TRUE // So we don't fire a laser as well. . = TRUE // So we don't fire a laser as well.
switch(a_intent) switch(a_intent)
if(I_DISARM) // Side gun if(I_DISARM) // Side gun
electric_defense(A) electric_defense(A)
if(I_HURT) // Rockets if(I_HURT) // Rockets
launch_rockets(A) launch_rockets(A)
if(I_GRAB) // Micro-singulo if(I_GRAB) // Micro-singulo
launch_microsingularity(A) launch_microsingularity(A)
#define ELECTRIC_ZAP_POWER 20000 #define ELECTRIC_ZAP_POWER 20000
// Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing. // Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing.
// It also briefly blinds anyone looking directly at the mech without flash protection. // It also briefly blinds anyone looking directly at the mech without flash protection.
/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/electric_defense(atom/target) /mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/electric_defense(atom/target)
set waitfor = FALSE set waitfor = FALSE
// Temporary immunity to shock to avoid killing themselves with their own attack. // Temporary immunity to shock to avoid killing themselves with their own attack.
var/old_shock_resist = shock_resist var/old_shock_resist = shock_resist
shock_resist = 1 shock_resist = 1
// Make the energy ball. This is purely visual since the tesla ball is hyper-deadly. // Make the energy ball. This is purely visual since the tesla ball is hyper-deadly.
energy_ball = new(loc) energy_ball = new(loc)
energy_ball.adjust_scale(0.5) energy_ball.adjust_scale(0.5)
energy_ball.orbit(src, 32, TRUE, 1 SECOND) energy_ball.orbit(src, 32, TRUE, 1 SECOND)
visible_message(span("warning", "\The [src] creates \an [energy_ball] around itself!")) visible_message(span("warning", "\The [src] creates \an [energy_ball] around itself!"))
playsound(src.loc, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30) playsound(src.loc, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30)
// Shock nearby things that aren't ourselves. // Shock nearby things that aren't ourselves.
for(var/i = 1 to 10) for(var/i = 1 to 10)
energy_ball.adjust_scale(0.5 + (i/10)) energy_ball.adjust_scale(0.5 + (i/10))
energy_ball.set_light(i/2, i/2, "#0000FF") energy_ball.set_light(i/2, i/2, "#0000FF")
for(var/thing in range(3, src)) for(var/thing in range(3, src))
// This is stupid because mechs are stupid and not mobs. // This is stupid because mechs are stupid and not mobs.
if(isliving(thing)) if(isliving(thing))
var/mob/living/L = thing var/mob/living/L = thing
if(L == src) if(L == src)
continue continue
if(L.stat) if(L.stat)
continue // Otherwise it can get pretty laggy if there's loads of corpses around. continue // Otherwise it can get pretty laggy if there's loads of corpses around.
L.inflict_shock_damage(i * 2) L.inflict_shock_damage(i * 2)
if(L && L.has_AI()) // Some mobs delete themselves when dying. if(L && L.has_AI()) // Some mobs delete themselves when dying.
L.ai_holder.react_to_attack(src) L.ai_holder.react_to_attack(src)
else if(istype(thing, /obj/mecha)) else if(istype(thing, /obj/mecha))
var/obj/mecha/M = thing var/obj/mecha/M = thing
M.take_damage(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative. M.take_damage(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative.
sleep(1 SECOND) sleep(1 SECOND)
// Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection. // Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection.
visible_message(span("warning", "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!")) visible_message(span("warning", "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!"))
playsound(src.loc, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30) playsound(src.loc, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30)
tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE) tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE)
for(var/mob/living/L in viewers(src)) for(var/mob/living/L in viewers(src))
if(L == src) if(L == src)
continue continue
var/dir_towards_us = get_dir(L, src) var/dir_towards_us = get_dir(L, src)
if(L.dir && L.dir & dir_towards_us) if(L.dir && L.dir & dir_towards_us)
to_chat(L, span("danger", "The flash of light blinds you briefly.")) to_chat(L, span("danger", "The flash of light blinds you briefly."))
L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE) L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE)
// Get rid of our energy ball. // Get rid of our energy ball.
energy_ball.stop_orbit() energy_ball.stop_orbit()
qdel(energy_ball) qdel(energy_ball)
sleep(1 SECOND) sleep(1 SECOND)
// Resist resistance to old value. // Resist resistance to old value.
shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something. shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something.
#undef ELECTRIC_ZAP_POWER #undef ELECTRIC_ZAP_POWER
/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_rockets(atom/target) /mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_rockets(atom/target)
set waitfor = FALSE set waitfor = FALSE
// Telegraph our next move. // Telegraph our next move.
Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY) Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY)
visible_message(span("warning", "\The [src] deploys a missile rack!")) visible_message(span("warning", "\The [src] deploys a missile rack!"))
playsound(src, 'sound/effects/turret/move1.wav', 50, 1) playsound(src, 'sound/effects/turret/move1.wav', 50, 1)
sleep(0.5 SECONDS) sleep(0.5 SECONDS)
for(var/i = 1 to 3) for(var/i = 1 to 3)
if(target) // Might get deleted in the meantime. if(target) // Might get deleted in the meantime.
var/turf/T = get_turf(target) var/turf/T = get_turf(target)
if(T) if(T)
visible_message(span("warning", "\The [src] fires a rocket into the air!")) visible_message(span("warning", "\The [src] fires a rocket into the air!"))
playsound(src, 'sound/weapons/rpg.ogg', 70, 1) playsound(src, 'sound/weapons/rpg.ogg', 70, 1)
face_atom(T) face_atom(T)
var/obj/item/projectile/arc/explosive_rocket/rocket = new(loc) var/obj/item/projectile/arc/explosive_rocket/rocket = new(loc)
rocket.launch(T) rocket.old_style_target(T, src)
sleep(1 SECOND) rocket.fire()
sleep(1 SECOND)
visible_message(span("warning", "\The [src] retracts the missile rack."))
playsound(src, 'sound/effects/turret/move2.wav', 50, 1) visible_message(span("warning", "\The [src] retracts the missile rack."))
playsound(src, 'sound/effects/turret/move2.wav', 50, 1)
// Arcing rocket projectile that produces a weak explosion when it lands.
// Shouldn't punch holes in the floor, but will still hurt. // Arcing rocket projectile that produces a weak explosion when it lands.
/obj/item/projectile/arc/explosive_rocket // Shouldn't punch holes in the floor, but will still hurt.
name = "rocket" /obj/item/projectile/arc/explosive_rocket
icon_state = "mortar" name = "rocket"
icon_state = "mortar"
/obj/item/projectile/arc/explosive_rocket/on_impact(turf/T)
new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. /obj/item/projectile/arc/explosive_rocket/on_impact(turf/T)
explosion(T, 0, 0, 2, adminlog = FALSE) new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently.
explosion(T, 0, 0, 2, adminlog = FALSE)
/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_microsingularity(atom/target)
var/turf/T = get_turf(target) /mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_microsingularity(atom/target)
visible_message(span("warning", "\The [src] fires an energetic sphere into the air!")) var/turf/T = get_turf(target)
playsound(src, 'sound/weapons/Laser.ogg', 50, 1) visible_message(span("warning", "\The [src] fires an energetic sphere into the air!"))
face_atom(T) playsound(src, 'sound/weapons/Laser.ogg', 50, 1)
var/obj/item/projectile/arc/microsingulo/sphere = new(loc) face_atom(T)
sphere.launch(T) var/obj/item/projectile/arc/microsingulo/sphere = new(loc)
sphere.old_style_target(T, src)
/obj/item/projectile/arc/microsingulo sphere.fire()
name = "micro singularity"
icon_state = "bluespace" /obj/item/projectile/arc/microsingulo
name = "micro singularity"
/obj/item/projectile/arc/microsingulo/on_impact(turf/T) icon_state = "bluespace"
new /obj/effect/temporary_effect/pulse/microsingulo(T)
/obj/item/projectile/arc/microsingulo/on_impact(turf/T)
new /obj/effect/temporary_effect/pulse/microsingulo(T)
/obj/effect/temporary_effect/pulse/microsingulo
name = "micro singularity"
desc = "It's sucking everything in!" /obj/effect/temporary_effect/pulse/microsingulo
icon = 'icons/obj/objects.dmi' name = "micro singularity"
icon_state = "bhole3" desc = "It's sucking everything in!"
light_range = 4 icon = 'icons/obj/objects.dmi'
light_power = 5 icon_state = "bhole3"
light_color = "#2ECCFA" light_range = 4
pulses_remaining = 10 light_power = 5
pulse_delay = 0.5 SECONDS light_color = "#2ECCFA"
var/pull_radius = 3 pulses_remaining = 10
var/pull_strength = STAGE_THREE pulse_delay = 0.5 SECONDS
var/pull_radius = 3
/obj/effect/temporary_effect/pulse/microsingulo/on_pulse() var/pull_strength = STAGE_THREE
for(var/atom/A in range(pull_radius, src))
A.singularity_pull(src, pull_strength) /obj/effect/temporary_effect/pulse/microsingulo/on_pulse()
for(var/atom/A in range(pull_radius, src))
A.singularity_pull(src, pull_strength)
// The Advanced Dark Gygax's AI.
// The mob has three special attacks, based on the current intent.
// This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet. // The Advanced Dark Gygax's AI.
/datum/ai_holder/simple_mob/intentional/adv_dark_gygax // The mob has three special attacks, based on the current intent.
conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese. // This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet.
var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them. /datum/ai_holder/simple_mob/intentional/adv_dark_gygax
conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese.
var/electric_defense_radius = 3 // How big to assume electric defense's area is. var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them.
var/microsingulo_radius = 3 // Same but for microsingulo pull.
var/rocket_explosive_radius = 2 // Explosion radius for the rockets. var/electric_defense_radius = 3 // How big to assume electric defense's area is.
var/microsingulo_radius = 3 // Same but for microsingulo pull.
var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable. var/rocket_explosive_radius = 2 // Explosion radius for the rockets.
var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target.
var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable.
// Used to control the mob's positioning based on which special attack it has done. var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target.
// Note that the intent will not change again until the next special attack is about to happen.
/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A) // Used to control the mob's positioning based on which special attack it has done.
// Make the AI backpeddle if using an AoE special attack. // Note that the intent will not change again until the next special attack is about to happen.
var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles. /datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A)
if(holder.a_intent in risky_intents) // Make the AI backpeddle if using an AoE special attack.
var/closest_distance = 1 var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles.
switch(holder.a_intent) // Plus one just in case. if(holder.a_intent in risky_intents)
if(I_HURT) var/closest_distance = 1
closest_distance = rocket_explosive_radius + 1 switch(holder.a_intent) // Plus one just in case.
if(I_GRAB) if(I_HURT)
closest_distance = microsingulo_radius + 1 closest_distance = rocket_explosive_radius + 1
if(I_GRAB)
if(get_dist(holder, A) <= closest_distance) closest_distance = microsingulo_radius + 1
holder.IMove(get_step_away(holder, A, closest_distance))
if(get_dist(holder, A) <= closest_distance)
// Otherwise get up close and personal. holder.IMove(get_step_away(holder, A, closest_distance))
else if(get_dist(holder, A) > closest_desired_distance)
holder.IMove(get_step_towards(holder, A)) // Otherwise get up close and personal.
else if(get_dist(holder, A) > closest_desired_distance)
// Changes the mob's intent, which controls which special attack is used. holder.IMove(get_step_towards(holder, A))
// I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage.
/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) // Changes the mob's intent, which controls which special attack is used.
if(isliving(A)) // I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage.
var/mob/living/target = A /datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A)
if(isliving(A))
// If we're surrounded, Electric Defense will quickly fix that. var/mob/living/target = A
var/tally = 0
var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. // If we're surrounded, Electric Defense will quickly fix that.
for(var/atom/movable/AM in potential_targets) var/tally = 0
if(get_dist(holder, AM) > electric_defense_radius) var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets.
continue for(var/atom/movable/AM in potential_targets)
if(!can_attack(AM)) if(get_dist(holder, AM) > electric_defense_radius)
continue continue
tally++ if(!can_attack(AM))
continue
// Should we shock them? tally++
if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius)
holder.a_intent = I_DISARM // Should we shock them?
return if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius)
holder.a_intent = I_DISARM
// Otherwise they're a fair distance away and we're not getting mobbed up close. return
// See if we should use missiles or microsingulo.
tally = 0 // Let's recycle the var. // Otherwise they're a fair distance away and we're not getting mobbed up close.
for(var/atom/movable/AM in potential_targets) // See if we should use missiles or microsingulo.
if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. tally = 0 // Let's recycle the var.
continue for(var/atom/movable/AM in potential_targets)
if(!can_attack(AM)) if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder.
continue continue
if(AM.anchored) // Microsingulo doesn't do anything to anchored things. if(!can_attack(AM))
tally-- continue
else if(AM.anchored) // Microsingulo doesn't do anything to anchored things.
tally++ tally--
else
// Lots of people means minisingulo would be more useful. tally++
if(tally >= microsingulo_threshold)
holder.a_intent = I_GRAB // Lots of people means minisingulo would be more useful.
else // Otherwise use rockets. if(tally >= microsingulo_threshold)
holder.a_intent = I_HURT holder.a_intent = I_GRAB
else // Otherwise use rockets.
else holder.a_intent = I_HURT
if(get_dist(holder, A) >= rocket_explosive_radius + 1)
holder.a_intent = I_HURT // Fire rockets if it's an obj/turf. else
else if(get_dist(holder, A) >= rocket_explosive_radius + 1)
holder.a_intent = I_DISARM // Electricity might not work but it's safe up close. holder.a_intent = I_HURT // Fire rockets if it's an obj/turf.
else
holder.a_intent = I_DISARM // Electricity might not work but it's safe up close.

View File

@@ -1,94 +1,94 @@
// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists. // These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists.
/mob/living/simple_mob/slime/feral /mob/living/simple_mob/slime/feral
name = "feral slime" name = "feral slime"
desc = "The result of slimes escaping containment from some xenobiology lab. \ desc = "The result of slimes escaping containment from some xenobiology lab. \
Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \ Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \
creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species." creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species."
description_info = "Note that processing this large slime will give six cores." description_info = "Note that processing this large slime will give six cores."
cores = 6 // Xenobio will love getting their hands on these. cores = 6 // Xenobio will love getting their hands on these.
icon_state = "slime adult" icon_state = "slime adult"
icon_living = "slime adult" icon_living = "slime adult"
icon_dead = "slime adult dead" icon_dead = "slime adult dead"
glow_range = 5 glow_range = 5
glow_intensity = 4 glow_intensity = 4
icon_scale = 2 // Twice as big as the xenobio variant. icon_scale = 2 // Twice as big as the xenobio variant.
pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help.
default_pixel_y = -10 // To prevent resetting above var. default_pixel_y = -10 // To prevent resetting above var.
maxHealth = 300 maxHealth = 300
movement_cooldown = 10 movement_cooldown = 10
melee_attack_delay = 0.5 SECONDS melee_attack_delay = 0.5 SECONDS
ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank
// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't). // Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't).
/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount) /mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount)
taunt(user, TRUE) taunt(user, TRUE)
// *********** // ***********
// *Dark Blue* // *Dark Blue*
// *********** // ***********
// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration. // Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration.
// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs. // They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs.
/mob/living/simple_mob/slime/feral/dark_blue /mob/living/simple_mob/slime/feral/dark_blue
name = "dark blue feral slime" name = "dark blue feral slime"
color = "#2398FF" color = "#2398FF"
glow_toggle = TRUE glow_toggle = TRUE
slime_color = "dark blue" slime_color = "dark blue"
coretype = /obj/item/slime_extract/dark_blue coretype = /obj/item/slime_extract/dark_blue
cold_resist = 1 // Complete immunity. cold_resist = 1 // Complete immunity.
minbodytemp = 0 minbodytemp = 0
cold_damage_per_tick = 0 cold_damage_per_tick = 0
projectiletype = /obj/item/projectile/icicle projectiletype = /obj/item/projectile/icicle
base_attack_cooldown = 2 SECONDS base_attack_cooldown = 2 SECONDS
ranged_attack_delay = 1 SECOND ranged_attack_delay = 1 SECOND
player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.<br>\ player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.<br>\
You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.<br>\ You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.<br>\
Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer."
/obj/item/projectile/icicle /obj/item/projectile/icicle
name = "icicle" name = "icicle"
icon_state = "ice_2" icon_state = "ice_2"
damage = 40 damage = 40
damage_type = BRUTE damage_type = BRUTE
check_armour = "melee" check_armour = "melee"
armor_penetration = 30 armor_penetration = 30
step_delay = 2 // Make it a bit easier to dodge since its not a bullet. speed = 2
icon_scale = 2 // It hits like a truck. icon_scale = 2 // It hits like a truck.
sharp = TRUE sharp = TRUE
/obj/item/projectile/icicle/on_impact(atom/A) /obj/item/projectile/icicle/on_impact(atom/A)
playsound(get_turf(A), "shatter", 70, 1) playsound(get_turf(A), "shatter", 70, 1)
return ..() return ..()
/obj/item/projectile/icicle/get_structure_damage() /obj/item/projectile/icicle/get_structure_damage()
return damage / 2 // They're really deadly against mobs, but less effective against solid things. return damage / 2 // They're really deadly against mobs, but less effective against solid things.
/mob/living/simple_mob/slime/feral/dark_blue/handle_special() /mob/living/simple_mob/slime/feral/dark_blue/handle_special()
if(stat != DEAD) if(stat != DEAD)
cold_aura() cold_aura()
..() ..()
/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura() /mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura()
for(var/mob/living/L in view(3, src)) for(var/mob/living/L in view(3, src))
if(L == src) if(L == src)
continue continue
chill(L) chill(L)
/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L) /mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L)
L.inflict_cold_damage(10) L.inflict_cold_damage(10)
if(L.get_cold_protection() < 1) if(L.get_cold_protection() < 1)
L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src) L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src)
if(L.has_AI()) // Other AIs should react to hostile auras. if(L.has_AI()) // Other AIs should react to hostile auras.
L.ai_holder.react_to_attack(src) L.ai_holder.react_to_attack(src)

View File

@@ -672,10 +672,12 @@
. = (is_client_active(10 MINUTES)) . = (is_client_active(10 MINUTES))
if(.) if(.)
if(statpanel("Status") && ticker && ticker.current_state != GAME_STATE_PREGAME) if(statpanel("Status"))
stat("Station Time", stationtime2text()) stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)")
stat("Station Date", stationdate2text()) if(ticker && ticker.current_state != GAME_STATE_PREGAME)
stat("Round Duration", roundduration2text()) stat("Station Time", stationtime2text())
stat("Station Date", stationdate2text())
stat("Round Duration", roundduration2text())
if(client.holder) if(client.holder)
if(statpanel("Status")) if(statpanel("Status"))
@@ -1192,6 +1194,8 @@ mob/proc/yank_out_object()
closeToolTip(usr) //No reason not to, really closeToolTip(usr) //No reason not to, really
..() ..()
<<<<<<< HEAD
=======
// Manages a global list of mobs with clients attached, indexed by z-level. // Manages a global list of mobs with clients attached, indexed by z-level.
/mob/proc/update_client_z(new_z) // +1 to register, null to unregister. /mob/proc/update_client_z(new_z) // +1 to register, null to unregister.
@@ -1205,6 +1209,7 @@ mob/proc/yank_out_object()
else else
registered_z = null registered_z = null
/mob/on_z_change(old_z, new_z) /mob/onTransitZ(old_z, new_z)
..() ..()
update_client_z(new_z) update_client_z(new_z)
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles

View File

@@ -8,7 +8,6 @@
return (!mover.density || !density || lying) return (!mover.density || !density || lying)
else else
return (!mover.density || !density || lying) return (!mover.density || !density || lying)
return
/mob/proc/setMoveCooldown(var/timeout) /mob/proc/setMoveCooldown(var/timeout)
move_delay = max(world.time + timeout, move_delay) move_delay = max(world.time + timeout, move_delay)
@@ -18,22 +17,6 @@
return FALSE // Need to wait more. return FALSE // Need to wait more.
return TRUE return TRUE
/client/North()
..()
/client/South()
..()
/client/West()
..()
/client/East()
..()
/client/proc/client_dir(input, direction=-1) /client/proc/client_dir(input, direction=-1)
return turn(input, direction*dir2angle(dir)) return turn(input, direction*dir2angle(dir))
@@ -126,6 +109,7 @@
*/ */
return return
<<<<<<< HEAD
//This proc should never be overridden elsewhere at /atom/movable to keep directions sane. //This proc should never be overridden elsewhere at /atom/movable to keep directions sane.
/atom/movable/Move(newloc, direct) /atom/movable/Move(newloc, direct)
if (direct & (direct - 1)) if (direct & (direct - 1))
@@ -182,6 +166,8 @@
/atom/movable/proc/Moved(atom/oldloc) /atom/movable/proc/Moved(atom/oldloc)
return return
=======
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
/client/proc/Move_object(direct) /client/proc/Move_object(direct)
if(mob && mob.control_object) if(mob && mob.control_object)
if(mob.control_object.density) if(mob.control_object.density)

View File

@@ -121,7 +121,7 @@
var/mob/living/carbon/human/dummy/mannequin = new() var/mob/living/carbon/human/dummy/mannequin = new()
client.prefs.dress_preview_mob(mannequin) client.prefs.dress_preview_mob(mannequin)
var/mob/observer/dead/observer = new(mannequin) var/mob/observer/dead/observer = new(mannequin)
observer.forceMove(null) //Let's not stay in our doomed mannequin observer.moveToNullspace() //Let's not stay in our doomed mannequin
qdel(mannequin) qdel(mannequin)
spawning = 1 spawning = 1

View File

@@ -75,7 +75,6 @@
return return
/obj/machinery/power/supermatter/shard/singularity_act() /obj/machinery/power/supermatter/shard/singularity_act()
src.forceMove(null)
qdel(src) qdel(src)
return 5000 return 5000
@@ -90,7 +89,6 @@
SetUniversalState(/datum/universal_state/supermatter_cascade) SetUniversalState(/datum/universal_state/supermatter_cascade)
log_admin("New super singularity made by eating a SM crystal [prints]. Last touched by [src.fingerprintslast].") log_admin("New super singularity made by eating a SM crystal [prints]. Last touched by [src.fingerprintslast].")
message_admins("New super singularity made by eating a SM crystal [prints]. Last touched by [src.fingerprintslast].") message_admins("New super singularity made by eating a SM crystal [prints]. Last touched by [src.fingerprintslast].")
src.forceMove(null)
qdel(src) qdel(src)
return 50000 return 50000

View File

@@ -142,7 +142,8 @@
var/obj/item/projectile/beam/emitter/A = get_emitter_beam() var/obj/item/projectile/beam/emitter/A = get_emitter_beam()
A.damage = round(power_per_shot/EMITTER_DAMAGE_POWER_TRANSFER) A.damage = round(power_per_shot/EMITTER_DAMAGE_POWER_TRANSFER)
A.launch( get_step(src.loc, src.dir) ) A.firer = src
A.fire(dir2angle(dir))
/obj/machinery/power/emitter/attackby(obj/item/W, mob/user) /obj/machinery/power/emitter/attackby(obj/item/W, mob/user)

View File

@@ -470,7 +470,8 @@
P.shot_from = src.name P.shot_from = src.name
P.silenced = silenced P.silenced = silenced
P.launch(target) P.old_style_target(target)
P.fire()
last_shot = world.time last_shot = world.time
@@ -645,24 +646,17 @@
/obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null) /obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null)
var/obj/item/projectile/P = projectile var/obj/item/projectile/P = projectile
if(!istype(P)) if(!istype(P))
return 0 //default behaviour only applies to true projectiles return FALSE //default behaviour only applies to true projectiles
if(params)
P.set_clickpoint(params)
//shooting while in shock //shooting while in shock
var/x_offset = 0 var/forcespread
var/y_offset = 0
if(istype(user, /mob/living/carbon)) if(istype(user, /mob/living/carbon))
var/mob/living/carbon/mob = user var/mob/living/carbon/mob = user
if(mob.shock_stage > 120) if(mob.shock_stage > 120)
y_offset = rand(-2,2) forcespread = rand(50, 50)
x_offset = rand(-2,2)
else if(mob.shock_stage > 70) else if(mob.shock_stage > 70)
y_offset = rand(-1,1) forcespread = rand(-25, 25)
x_offset = rand(-1,1) var/launched = !P.launch_from_gun(target, target_zone, user, params, null, forcespread, src)
var/launched = !P.launch_from_gun(target, user, src, target_zone, x_offset, y_offset)
if(launched) if(launched)
play_fire_sound(user, P) play_fire_sound(user, P)

View File

@@ -3,7 +3,7 @@
icon_state = "dart" icon_state = "dart"
damage = 5 damage = 5
var/reagent_amount = 15 var/reagent_amount = 15
kill_count = 15 //shorter range range = 15 //shorter range
muzzle_type = null muzzle_type = null

View File

@@ -79,9 +79,9 @@
damage_type = HALLOSS damage_type = HALLOSS
light_color = "#8837A3" light_color = "#8837A3"
muzzle_type = /obj/effect/projectile/darkmatterstun/muzzle muzzle_type = /obj/effect/projectile/muzzle/darkmatterstun
tracer_type = /obj/effect/projectile/darkmatterstun/tracer tracer_type = /obj/effect/projectile/tracer/darkmatterstun
impact_type = /obj/effect/projectile/darkmatterstun/impact impact_type = /obj/effect/projectile/impact/darkmatterstun
/obj/item/projectile/beam/darkmatter /obj/item/projectile/beam/darkmatter
name = "dark matter bolt" name = "dark matter bolt"
@@ -95,9 +95,9 @@
embed_chance = 0 embed_chance = 0
muzzle_type = /obj/effect/projectile/darkmatter/muzzle muzzle_type = /obj/effect/projectile/muzzle/darkmatter
tracer_type = /obj/effect/projectile/darkmatter/tracer tracer_type = /obj/effect/projectile/tracer/darkmatter
impact_type = /obj/effect/projectile/darkmatter/impact impact_type = /obj/effect/projectile/impact/darkmatter
/obj/item/projectile/energy/darkmatter /obj/item/projectile/energy/darkmatter
name = "dark matter pellet" name = "dark matter pellet"

View File

@@ -1,37 +1,84 @@
/* #define MOVES_HITSCAN -1 //Not actually hitscan but close as we get without actual hitscan.
#define BRUTE "brute" #define MUZZLE_EFFECT_PIXEL_INCREMENT 17 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
#define BURN "burn"
#define TOX "tox"
#define OXY "oxy"
#define CLONE "clone"
#define ADD "add"
#define SET "set"
*/
/obj/item/projectile /obj/item/projectile
name = "projectile" name = "projectile"
icon = 'icons/obj/projectiles.dmi' icon = 'icons/obj/projectiles.dmi'
icon_state = "bullet" icon_state = "bullet"
density = 1 density = FALSE
unacidable = 1 anchored = TRUE
anchored = 1 //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots. unacidable = TRUE
pass_flags = PASSTABLE pass_flags = PASSTABLE
mouse_opacity = 0 mouse_opacity = 0
var/bumped = 0 //Prevents it from hitting more than one guy at once
////TG PROJECTILE SYTSEM
//Projectile stuff
var/range = 50
var/originalRange
//Fired processing vars
var/fired = FALSE //Have we been fired yet
var/paused = FALSE //for suspending the projectile midair
var/last_projectile_move = 0
var/last_process = 0
var/time_offset = 0
var/datum/point/vector/trajectory
var/trajectory_ignore_forcemove = FALSE //instructs forceMove to NOT reset our trajectory to the new location!
var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
var/Angle = 0
var/original_angle = 0 //Angle at firing
var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
var/spread = 0 //amount (in degrees) of projectile spread
animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
var/ricochets = 0
var/ricochets_max = 2
var/ricochet_chance = 30
//Hitscan
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation.
var/datum/point/beam_index
var/turf/hitscan_last //last turf touched during hitscanning.
var/tracer_type
var/muzzle_type
var/impact_type
//Fancy hitscan lighting effects!
var/hitscan_light_intensity = 1.5
var/hitscan_light_range = 0.75
var/hitscan_light_color_override
var/muzzle_flash_intensity = 3
var/muzzle_flash_range = 1.5
var/muzzle_flash_color_override
var/impact_light_intensity = 3
var/impact_light_range = 2
var/impact_light_color_override
//Homing
var/homing = FALSE
var/atom/homing_target
var/homing_turn_speed = 10 //Angle per tick.
var/homing_inaccuracy_min = 0 //in pixels for these. offsets are set once when setting target.
var/homing_inaccuracy_max = 0
var/homing_offset_x = 0
var/homing_offset_y = 0
//Targetting
var/yo = null
var/xo = null
var/atom/original = null // the original target clicked
var/turf/starting = null // the projectile's starting turf
var/list/permutated = list() // we've passed through these atoms, don't try to hit them again
var/p_x = 16
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
//Misc/Polaris variables
var/def_zone = "" //Aiming at var/def_zone = "" //Aiming at
var/mob/firer = null//Who shot it var/mob/firer = null//Who shot it
var/silenced = 0 //Attack message var/silenced = 0 //Attack message
var/yo = null
var/xo = null
var/current = null
var/shot_from = "" // name of the object which shot us var/shot_from = "" // name of the object which shot us
var/atom/original = null // the target clicked (not necessarily where the projectile is headed). Should probably be renamed to 'target' or something.
var/turf/starting = null // the projectile's starting turf
var/list/permutated = list() // we've passed through these atoms, don't try to hit them again
var/p_x = 16
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
var/accuracy = 0 var/accuracy = 0
var/dispersion = 0.0 var/dispersion = 0.0
@@ -45,7 +92,6 @@
var/check_armour = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid var/check_armour = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid
var/projectile_type = /obj/item/projectile var/projectile_type = /obj/item/projectile
var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate() var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate()
var/kill_count = 50 //This will de-increment every process(). When 0, it will delete the projectile.
//Effects //Effects
var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech
var/flammability = 0 //Amount of fire stacks to add for the above. var/flammability = 0 //Amount of fire stacks to add for the above.
@@ -64,23 +110,127 @@
embed_chance = 0 //Base chance for a projectile to embed embed_chance = 0 //Base chance for a projectile to embed
var/hitscan = 0 // whether the projectile should be hitscan
var/step_delay = 1 // the delay between iterations if not a hitscan projectile
// effect types to be used
var/muzzle_type
var/tracer_type
var/impact_type
var/fire_sound = 'sound/weapons/Gunshot_old.ogg' // Can be overriden in gun.dm's fire_sound var. It can also be null but I don't know why you'd ever want to do that. -Ace var/fire_sound = 'sound/weapons/Gunshot_old.ogg' // Can be overriden in gun.dm's fire_sound var. It can also be null but I don't know why you'd ever want to do that. -Ace
var/vacuum_traversal = 1 //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum. var/vacuum_traversal = TRUE //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum.
var/datum/plot_vector/trajectory // used to plot the path of the projectile /obj/item/projectile/proc/Range()
var/datum/vector_loc/location // current location of the projectile in pixel space range--
var/matrix/effect_transform // matrix to rotate and scale projectile effects - putting it here so it doesn't if(range <= 0 && loc)
// have to be recreated multiple times on_range()
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
qdel(src)
/obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
if(!trajectory && isnull(forced_angle) && isnull(Angle))
return FALSE
var/datum/point/vector/current = trajectory
if(!current)
var/turf/T = get_turf(src)
current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, SSprojectiles.global_pixel_speed)
var/datum/point/vector/v = current.return_vector_after_increments(moves * SSprojectiles.global_iterations_per_move)
return v.return_turf()
/obj/item/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle)
var/turf/current = get_turf(src)
var/turf/ending = return_predicted_turf_after_moves(moves, forced_angle)
return getline(current, ending)
/obj/item/projectile/proc/set_pixel_speed(new_speed)
if(trajectory)
trajectory.set_speed(new_speed)
return TRUE
return FALSE
/obj/item/projectile/proc/record_hitscan_start(datum/point/pcache)
if(pcache)
beam_segments = list()
beam_index = pcache
beam_segments[beam_index] = null //record start.
/obj/item/projectile/proc/process_hitscan()
var/safety = range * 3
record_hitscan_start(RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1))
while(loc && !QDELETED(src))
if(paused)
stoplag(1)
continue
if(safety-- <= 0)
if(loc)
Bump(loc)
if(!QDELETED(src))
qdel(src)
return //Kill!
pixel_move(1, TRUE)
/obj/item/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE)
if(!loc || !trajectory)
return
last_projectile_move = world.time
if(homing)
process_homing()
var/forcemoved = FALSE
for(var/i in 1 to SSprojectiles.global_iterations_per_move)
if(QDELETED(src))
return
trajectory.increment(trajectory_multiplier)
var/turf/T = trajectory.return_turf()
if(!istype(T))
qdel(src)
return
if(T.z != loc.z)
var/old = loc
before_z_change(loc, T)
trajectory_ignore_forcemove = TRUE
forceMove(T)
trajectory_ignore_forcemove = FALSE
after_z_change(old, loc)
if(!hitscanning)
pixel_x = trajectory.return_px()
pixel_y = trajectory.return_py()
forcemoved = TRUE
hitscan_last = loc
else if(T != loc)
before_move()
step_towards(src, T)
hitscan_last = loc
after_move()
if(can_hit_target(original, permutated))
Bump(original)
if(!hitscanning && !forcemoved)
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
Range()
/obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it.
..()
if(isliving(AM) && (AM.density || AM == original))
Bump(AM)
/obj/item/projectile/proc/process_homing() //may need speeding up in the future performance wise.
if(!homing_target)
return FALSE
var/datum/point/PT = RETURN_PRECISE_POINT(homing_target)
PT.x += CLAMP(homing_offset_x, 1, world.maxx)
PT.y += CLAMP(homing_offset_y, 1, world.maxy)
var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT))
setAngle(Angle + CLAMP(angle, -homing_turn_speed, homing_turn_speed))
/obj/item/projectile/proc/set_homing_target(atom/A)
if(!A || (!isturf(A) && !isturf(A.loc)))
return FALSE
homing = TRUE
homing_target = A
homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max)
homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max)
if(prob(50))
homing_offset_x = -homing_offset_x
if(prob(50))
homing_offset_y = -homing_offset_y
<<<<<<< HEAD
//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not //TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not
/obj/item/projectile/proc/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) /obj/item/projectile/proc/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
if(blocked >= 100) return 0//Full block if(blocked >= 100) return 0//Full block
@@ -91,151 +241,247 @@
if(modifier_type_to_apply) if(modifier_type_to_apply)
L.add_modifier(modifier_type_to_apply, modifier_duration) L.add_modifier(modifier_type_to_apply, modifier_duration)
return 1 return 1
=======
/obj/item/projectile/process()
last_process = world.time
if(!loc || !fired || !trajectory)
fired = FALSE
return PROCESS_KILL
if(paused || !isturf(loc))
last_projectile_move += world.time - last_process //Compensates for pausing, so it doesn't become a hitscan projectile when unpaused from charged up ticks.
return
var/elapsed_time_deciseconds = (world.time - last_projectile_move) + time_offset
time_offset = 0
var/required_moves = speed > 0? FLOOR(elapsed_time_deciseconds / speed, 1) : MOVES_HITSCAN //Would be better if a 0 speed made hitscan but everyone hates those so I can't make it a universal system :<
if(required_moves == MOVES_HITSCAN)
required_moves = SSprojectiles.global_max_tick_moves
else
if(required_moves > SSprojectiles.global_max_tick_moves)
var/overrun = required_moves - SSprojectiles.global_max_tick_moves
required_moves = SSprojectiles.global_max_tick_moves
time_offset += overrun * speed
time_offset += MODULUS(elapsed_time_deciseconds, speed)
//called when the projectile stops flying because it collided with something for(var/i in 1 to required_moves)
/obj/item/projectile/proc/on_impact(var/atom/A) pixel_move(1, FALSE)
impact_effect(effect_transform) // generate impact effect
if(damage && damage_type == BURN) /obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides.
var/turf/T = get_turf(A) Angle = new_angle
if(T) if(!nondirectional_sprite)
T.hotspot_expose(700, 5) var/matrix/M = new
M.Turn(Angle)
transform = M
if(trajectory)
trajectory.set_angle(new_angle)
return TRUE
/obj/item/projectile/forceMove(atom/target)
if(!isloc(target) || !isloc(loc) || !z)
return ..()
var/zc = target.z != z
var/old = loc
if(zc)
before_z_change(old, target)
. = ..()
if(trajectory && !trajectory_ignore_forcemove && isturf(target))
if(hitscan)
finalize_hitscan_and_generate_tracers(FALSE)
trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
if(hitscan)
record_hitscan_start(RETURN_PRECISE_POINT(src))
if(zc)
after_z_change(old, target)
/obj/item/projectile/proc/fire(angle, atom/direct_target)
//If no angle needs to resolve it from xo/yo!
if(direct_target)
direct_target.bullet_act(src, def_zone)
qdel(src)
return
if(isnum(angle))
setAngle(angle)
var/turf/starting = get_turf(src)
if(isnull(Angle)) //Try to resolve through offsets if there's no angle set.
if(isnull(xo) || isnull(yo))
crash_with("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!")
qdel(src)
return
var/turf/target = locate(CLAMP(starting + xo, 1, world.maxx), CLAMP(starting + yo, 1, world.maxy), starting.z)
setAngle(Get_Angle(src, target))
if(dispersion)
setAngle(Angle + rand(-dispersion, dispersion))
original_angle = Angle
trajectory_ignore_forcemove = TRUE
forceMove(starting)
trajectory_ignore_forcemove = FALSE
trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed)
last_projectile_move = world.time
permutated = list()
originalRange = range
fired = TRUE
if(hitscan)
process_hitscan()
START_PROCESSING(SSprojectiles, src)
pixel_move(1, FALSE) //move it now!
/obj/item/projectile/proc/after_z_change(atom/olcloc, atom/newloc)
/obj/item/projectile/proc/before_z_change(atom/oldloc, atom/newloc)
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
/obj/item/projectile/proc/before_move()
return return
//Checks if the projectile is eligible for embedding. Not that it necessarily will. /obj/item/projectile/proc/after_move()
/obj/item/projectile/proc/can_embed() return
//embed must be enabled and damage type must be brute
if(embed_chance == 0 || damage_type != BRUTE)
return 0
return 1
/obj/item/projectile/proc/get_structure_damage() /obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
if(damage_type == BRUTE || damage_type == BURN) beam_segments[beam_index] = pcache
return damage beam_index = pcache
return 0 beam_segments[beam_index] = null
//return 1 if the projectile should be allowed to pass through after all, 0 if not. //Spread is FORCED!
/obj/item/projectile/proc/check_penetrate(var/atom/A) /obj/item/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0)
return 1 var/turf/curloc = get_turf(source)
var/turf/targloc = get_turf(target)
trajectory_ignore_forcemove = TRUE
forceMove(get_turf(source))
trajectory_ignore_forcemove = FALSE
starting = get_turf(source)
original = target
if(targloc || !params)
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc) + spread)
/obj/item/projectile/proc/check_fire(atom/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not. if(isliving(source) && params)
check_trajectory(target, user, pass_flags, flags) var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
p_x = calculated[2]
p_y = calculated[3]
//sets the click point of the projectile using mouse input params setAngle(calculated[1] + spread)
/obj/item/projectile/proc/set_clickpoint(var/params) else if(targloc)
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc) + spread)
else
crash_with("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!")
qdel(src)
/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params)
var/list/mouse_control = params2list(params) var/list/mouse_control = params2list(params)
var/p_x = 0
var/p_y = 0
var/angle = 0
if(mouse_control["icon-x"]) if(mouse_control["icon-x"])
p_x = text2num(mouse_control["icon-x"]) p_x = text2num(mouse_control["icon-x"])
if(mouse_control["icon-y"]) if(mouse_control["icon-y"])
p_y = text2num(mouse_control["icon-y"]) p_y = text2num(mouse_control["icon-y"])
if(mouse_control["screen-loc"])
//Split screen-loc up into X+Pixel_X and Y+Pixel_Y
var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",")
//randomize clickpoint a bit based on dispersion //Split X+Pixel_X up into list(X, Pixel_X)
if(dispersion) var/list/screen_loc_X = splittext(screen_loc_params[1],":")
var/radius = round((dispersion*0.443)*world.icon_size*0.8) //0.443 = sqrt(pi)/4 = 2a, where a is the side length of a square that shares the same area as a circle with diameter = dispersion
p_x = between(0, p_x + rand(-radius, radius), world.icon_size)
p_y = between(0, p_y + rand(-radius, radius), world.icon_size)
//called to launch a projectile //Split Y+Pixel_Y up into list(Y, Pixel_Y)
/obj/item/projectile/proc/launch(atom/target, var/target_zone, var/x_offset=0, var/y_offset=0, var/angle_offset=0) var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
var/turf/curloc = get_turf(src) var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32
var/turf/targloc = get_turf(target) var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32
if (!istype(targloc) || !istype(curloc))
return 1
if(combustion) //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average.
curloc.hotspot_expose(700, 5) var/list/screenview = user.client? getviewsize(user.client.view) : world.view
var/screenviewX = screenview[1] * world.icon_size
var/screenviewY = screenview[2] * world.icon_size
if(targloc == curloc) //Shooting something in the same turf var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x
target.bullet_act(src, target_zone) var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y
on_impact(target) angle = ATAN2(y - oy, x - ox)
qdel(src) return list(angle, p_x, p_y)
return 0
/obj/item/projectile/proc/redirect(x, y, starting, source)
old_style_target(locate(x, y, z), starting? get_turf(starting) : get_turf(source))
/obj/item/projectile/proc/old_style_target(atom/target, atom/source)
if(!source)
source = get_turf(src)
starting = source
original = target original = target
def_zone = target_zone setAngle(Get_Angle(source, target))
spawn() /obj/item/projectile/Destroy()
setup_trajectory(curloc, targloc, x_offset, y_offset, angle_offset) //plot the initial trajectory if(hitscan)
process() finalize_hitscan_and_generate_tracers()
STOP_PROCESSING(SSprojectiles, src)
cleanup_beam_segments()
qdel(trajectory)
return ..()
return 0 /obj/item/projectile/proc/cleanup_beam_segments()
QDEL_LIST_ASSOC(beam_segments)
beam_segments = list()
qdel(beam_index)
//called to launch a projectile from a gun /obj/item/projectile/proc/vol_by_damage()
/obj/item/projectile/proc/launch_from_gun(atom/target, mob/user, obj/item/weapon/gun/launcher, var/target_zone, var/x_offset=0, var/y_offset=0) if(damage)
if(user == target) //Shooting yourself return CLAMP((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100
user.bullet_act(src, target_zone)
on_impact(user)
qdel(src)
return 0
loc = get_turf(user) //move the projectile out into the world
firer = user
shot_from = launcher.name
silenced = launcher.silenced
return launch(target, target_zone, x_offset, y_offset)
//Used to change the direction of the projectile in flight.
/obj/item/projectile/proc/redirect(var/new_x, var/new_y, var/atom/starting_loc, var/mob/new_firer=null)
var/turf/new_target = locate(new_x, new_y, src.z)
original = new_target
if(new_firer)
firer = src
setup_trajectory(starting_loc, new_target)
//Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying.
/obj/item/projectile/proc/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
if(!istype(target_mob))
return
//roll to-hit
miss_modifier = max(15*(distance-2) - accuracy + miss_modifier + target_mob.get_evasion(), 0)
var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1 || original != target_mob)) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss
var/result = PROJECTILE_FORCE_MISS
if(hit_zone)
def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
result = target_mob.bullet_act(src, def_zone)
if(result == PROJECTILE_FORCE_MISS)
if(!silenced)
visible_message("<span class='notice'>\The [src] misses [target_mob] narrowly!</span>")
return 0
//hit messages
if(silenced)
to_chat(target_mob, "<span class='danger'>You've been hit in the [parse_zone(def_zone)] by \the [src]!</span>")
else else
visible_message("<span class='danger'>\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!</span>")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume.
//admin logs /obj/item/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE)
if(!no_attack_log) if(trajectory && beam_index)
if(istype(firer, /mob) && istype(target_mob)) var/datum/point/pcache = trajectory.copy_to()
add_attack_logs(firer,target_mob,"Shot with \a [src.type] projectile") beam_segments[beam_index] = pcache
generate_hitscan_tracers(null, null, impacting)
//sometimes bullet_act() will want the projectile to continue flying /obj/item/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3, impacting = TRUE)
if (result == PROJECTILE_CONTINUE) if(!length(beam_segments))
return 0 return
if(tracer_type)
var/tempref = "\ref[src]"
for(var/datum/point/p in beam_segments)
generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref)
if(muzzle_type && duration > 0)
var/datum/point/p = beam_segments[1]
var/atom/movable/thing = new muzzle_type
p.move_atom_to_src(thing)
var/matrix/M = new
M.Turn(original_angle)
thing.transform = M
thing.color = color
thing.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color)
QDEL_IN(thing, duration)
if(impacting && impact_type && duration > 0)
var/datum/point/p = beam_segments[beam_segments[beam_segments.len]]
var/atom/movable/thing = new impact_type
p.move_atom_to_src(thing)
var/matrix/M = new
M.Turn(Angle)
thing.transform = M
thing.color = color
thing.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color)
QDEL_IN(thing, duration)
if(cleanup)
cleanup_beam_segments()
return 1 //Returns true if the target atom is on our current turf and above the right layer
/obj/item/projectile/proc/can_hit_target(atom/target, var/list/passthrough)
return (target && ((target.layer >= TABLE_LAYER) || ismob(target)) && (loc == get_turf(target)) && (!(target in passthrough)))
/obj/item/projectile/Bump(atom/A as mob|obj|turf|area, forced=0) /obj/item/projectile/Bump(atom/A)
if(A == src) if(A in permutated)
return 0 //no return FALSE
if(firer && !reflected)
if(A == firer || (A == firer.loc && istype(A, /obj/mecha))) //cannot shoot yourself or your mech
trajectory_ignore_forcemove = TRUE
forceMove(get_turf(A))
trajectory_ignore_forcemove = FALSE
return FALSE
if(A == firer) var/distance = get_dist(starting, get_turf(src))
loc = A.loc var/turf/target_turf = get_turf(A)
return 0 //cannot shoot yourself var/passthrough = FALSE
if((bumped && !forced) || (A in permutated))
return 0
var/passthrough = 0 //if the projectile should continue flying
var/distance = get_dist(starting,loc)
bumped = 1
if(ismob(A)) if(ismob(A))
var/mob/M = A var/mob/M = A
if(istype(A, /mob/living)) if(istype(A, /mob/living))
@@ -246,13 +492,13 @@
var/shield_chance = min(80, (30 * (M.mob_size / 10))) //Small mobs have a harder time keeping a dead body as a shield than a human-sized one. Unathi would have an easier job, if they are made to be SIZE_LARGE in the future. -Mech var/shield_chance = min(80, (30 * (M.mob_size / 10))) //Small mobs have a harder time keeping a dead body as a shield than a human-sized one. Unathi would have an easier job, if they are made to be SIZE_LARGE in the future. -Mech
if(prob(shield_chance)) if(prob(shield_chance))
visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>") visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>")
if(Bump(G.affecting, forced=1)) if(Bump(G.affecting))
return return
else else
visible_message("<span class='danger'>\The [M] tries to use [G.affecting] as a shield, but fails!</span>") visible_message("<span class='danger'>\The [M] tries to use [G.affecting] as a shield, but fails!</span>")
else else
visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>") visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>")
if(Bump(G.affecting, forced=1)) if(Bump(G.affecting))
return //If Bump() returns 0 (keep going) then we continue on to attack M. return //If Bump() returns 0 (keep going) then we continue on to attack M.
passthrough = !attack_mob(M, distance) passthrough = !attack_mob(M, distance)
@@ -269,119 +515,83 @@
//penetrating projectiles can pass through things that otherwise would not let them //penetrating projectiles can pass through things that otherwise would not let them
if(!passthrough && penetrating > 0) if(!passthrough && penetrating > 0)
if(check_penetrate(A)) if(check_penetrate(A))
passthrough = 1 passthrough = TRUE
penetrating-- penetrating--
//the bullet passes through a dense object!
if(passthrough) if(passthrough)
//move ourselves onto A so we can continue on our way. trajectory_ignore_forcemove = TRUE
if(A) forceMove(target_turf)
if(istype(A, /turf)) permutated.Add(A)
loc = A trajectory_ignore_forcemove = FALSE
else return FALSE
loc = A.loc
permutated.Add(A)
bumped = 0 //reset bumped variable!
return 0
//stop flying
on_impact(A)
density = 0
invisibility = 101
if(A)
on_impact(A)
qdel(src) qdel(src)
return TRUE
//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not
/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, def_zone)
if(blocked >= 100) return 0//Full block
if(!isliving(target)) return 0
// if(isanimal(target)) return 0
var/mob/living/L = target
L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY!
if(modifier_type_to_apply)
L.add_modifier(modifier_type_to_apply, modifier_duration)
return 1 return 1
/obj/item/projectile/ex_act() //called when the projectile stops flying because it Bump'd with something
return //explosions probably shouldn't delete projectiles /obj/item/projectile/proc/on_impact(atom/A)
if(damage && damage_type == BURN)
var/turf/T = get_turf(A)
if(T)
T.hotspot_expose(700, 5)
/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) //Checks if the projectile is eligible for embedding. Not that it necessarily will.
/obj/item/projectile/proc/can_embed()
//embed must be enabled and damage type must be brute
if(embed_chance == 0 || damage_type != BRUTE)
return 0
return 1 return 1
/obj/item/projectile/process() /obj/item/projectile/proc/get_structure_damage()
var/first_step = 1 if(damage_type == BRUTE || damage_type == BURN)
return damage
return 0
spawn while(src && src.loc) //return 1 if the projectile should be allowed to pass through after all, 0 if not.
if(kill_count-- < 1) /obj/item/projectile/proc/check_penetrate(atom/A)
on_impact(src.loc) //for any final impact behaviours return 1
qdel(src)
return
if((!( current ) || loc == current))
current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z)
if((x == 1 || x == world.maxx || y == 1 || y == world.maxy))
qdel(src)
return
trajectory.increment() // increment the current location /obj/item/projectile/proc/check_fire(atom/target as mob, mob/living/user as mob) //Checks if you can hit them or not.
location = trajectory.return_location(location) // update the locally stored location data check_trajectory(target, user, pass_flags, flags)
update_light() //energy projectiles will look glowy and fun
if(!location) /obj/item/projectile/CanPass()
qdel(src) // if it's left the world... kill it return TRUE
return
if (is_below_sound_pressure(get_turf(src)) && !vacuum_traversal) //Deletes projectiles that aren't supposed to bein vacuum if they leave pressurised areas //Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying.
qdel(src) /obj/item/projectile/proc/attack_mob(mob/living/target_mob, distance, miss_modifier = 0)
return if(!istype(target_mob))
return
before_move() //roll to-hit
Move(location.return_turf()) miss_modifier = max(15*(distance-2) - accuracy + miss_modifier + target_mob.get_evasion(), 0)
after_move() var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1 || original != target_mob)) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss
if(!bumped && !isturf(original)) var/result = PROJECTILE_FORCE_MISS
if(loc == get_turf(original)) if(hit_zone)
if(!(original in permutated)) def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
if(Bump(original)) result = target_mob.bullet_act(src, def_zone)
return
if(first_step) if(result == PROJECTILE_FORCE_MISS)
muzzle_effect(effect_transform) if(!silenced)
first_step = 0 visible_message("<span class='notice'>\The [src] misses [target_mob] narrowly!</span>")
else if(!bumped) return FALSE
tracer_effect(effect_transform)
if(incendiary >= 2) //This should cover the bases of 'Why is there fuel here?' in a much cleaner way than previous. //hit messages
if(src && src.loc) //Safety.
if(!src.loc.density)
var/trail_volume = (flammability * 0.20)
new /obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(src.loc, trail_volume, src.dir)
if(!hitscan)
sleep(step_delay) //add delay between movement iterations if it's not a hitscan weapon
/obj/item/projectile/proc/before_move()
return
/obj/item/projectile/proc/after_move()
return
/obj/item/projectile/proc/setup_trajectory(turf/startloc, turf/targloc, var/x_offset = 0, var/y_offset = 0)
// setup projectile state
starting = startloc
current = startloc
yo = targloc.y - startloc.y + y_offset
xo = targloc.x - startloc.x + x_offset
// trajectory dispersion
var/offset = 0
if(dispersion)
var/radius = round(dispersion*9, 1)
offset = rand(-radius, radius)
// plot the initial trajectory
trajectory = new()
trajectory.setup(starting, original, pixel_x, pixel_y, angle_offset=offset)
// generate this now since all visual effects the projectile makes can use it
effect_transform = new()
effect_transform.Scale(trajectory.return_hypotenuse(), 1)
effect_transform.Turn(-trajectory.return_angle()) //no idea why this has to be inverted, but it works
transform = turn(transform, -(trajectory.return_angle() + 90)) //no idea why 90 needs to be added, but it works
/obj/item/projectile/proc/muzzle_effect(var/matrix/T)
if(silenced) if(silenced)
<<<<<<< HEAD
return return
if(ispath(muzzle_type)) if(ispath(muzzle_type))
@@ -455,13 +665,22 @@
var/turf/targloc = get_turf(target) var/turf/targloc = get_turf(target)
if(!curloc || !targloc) if(!curloc || !targloc)
return 0 return 0
=======
to_chat(target_mob, "<span class='danger'>You've been hit in the [parse_zone(def_zone)] by \the [src]!</span>")
else
visible_message("<span class='danger'>\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!</span>")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
original = target //admin logs
if(!no_attack_log)
if(istype(firer, /mob) && istype(target_mob))
add_attack_logs(firer,target_mob,"Shot with \a [src.type] projectile")
//plot the initial trajectory //sometimes bullet_act() will want the projectile to continue flying
setup_trajectory(curloc, targloc) if (result == PROJECTILE_CONTINUE)
return process(targloc) return FALSE
<<<<<<< HEAD
/obj/item/projectile/test/process(var/turf/targloc) /obj/item/projectile/test/process(var/turf/targloc)
while(src) //Loop on through! while(src) //Loop on through!
if(result) if(result)
@@ -469,12 +688,20 @@
// return (result - 1) // return (result - 1)
if((!( targloc ) || loc == targloc)) if((!( targloc ) || loc == targloc))
targloc = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge targloc = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge
=======
return TRUE
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
trajectory.increment() // increment the current location
location = trajectory.return_location(location) // update the locally stored location data
Move(location.return_turf()) /obj/item/projectile/proc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
original = target
def_zone = check_zone(target_zone)
firer = user
var/direct_target
if(get_turf(target) == get_turf(src))
direct_target = target
<<<<<<< HEAD
var/mob/living/M = locate() in get_turf(src) var/mob/living/M = locate() in get_turf(src)
if(istype(M)) //If there is someting living... if(istype(M)) //If there is someting living...
result_ref = M result_ref = M
@@ -489,14 +716,15 @@
/proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null) /proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null)
if(!istype(target) || !istype(firer)) if(!istype(target) || !istype(firer))
return 0 return 0
=======
preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread)
return fire(angle_override, direct_target)
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
var/obj/item/projectile/test/trace = new /obj/item/projectile/test(get_turf(firer)) //Making the test.... //called to launch a projectile from a gun
/obj/item/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/weapon/gun/launcher)
//Set the flags and pass flags to that of the real projectile... shot_from = launcher.name
if(!isnull(flags)) silenced = launcher.silenced
trace.flags = flags
trace.pass_flags = pass_flags
var/output = trace.launch(target) //Test it! return launch_projectile(target, target_zone, user, params, angle_override, forced_spread)
qdel(trace) //No need for it anymore
return output //Send it back to the gun!

View File

@@ -9,12 +9,16 @@
/obj/item/projectile/arc /obj/item/projectile/arc
name = "arcing shot" name = "arcing shot"
icon_state = "fireball" // WIP icon_state = "fireball" // WIP
step_delay = 2 // Travel a bit slower, to really sell the arc visuals. speed = 2 // Travel a bit slower, to really sell the arc visuals.
movement_type = UNSTOPPABLE
plane = ABOVE_PLANE // Since projectiles are 'in the air', they might visually overlap mobs while in flight, so the projectile needs to be above their plane. plane = ABOVE_PLANE // Since projectiles are 'in the air', they might visually overlap mobs while in flight, so the projectile needs to be above their plane.
var/target_distance = null // How many tiles the impact site is. var/target_distance = null // How many tiles the impact site is.
var/fired_dir = null // Which direction was the projectile fired towards. Needed to invert the projectile turning based on if facing left or right. var/fired_dir = null // Which direction was the projectile fired towards. Needed to invert the projectile turning based on if facing left or right.
var/obj/effect/projectile_shadow/shadow = null // Visual indicator for the projectile's 'true' position. Needed due to being bound to two dimensions in reality. var/obj/effect/projectile_shadow/shadow = null // Visual indicator for the projectile's 'true' position. Needed due to being bound to two dimensions in reality.
/obj/item/projectile/arc/Bump()
return
/obj/item/projectile/arc/Initialize() /obj/item/projectile/arc/Initialize()
shadow = new(get_turf(src)) shadow = new(get_turf(src))
return ..() return ..()
@@ -23,33 +27,32 @@
QDEL_NULL(shadow) QDEL_NULL(shadow)
return ..() return ..()
/obj/item/projectile/arc/Bump(atom/A, forced=0)
return 0
// if(get_turf(src) != original)
// return 0
// else
// return ..()
// This is a test projectile in the sense that its testing the code to make sure it works, // This is a test projectile in the sense that its testing the code to make sure it works,
// as opposed to a 'can I hit this thing' projectile. // as opposed to a 'can I hit this thing' projectile.
/obj/item/projectile/arc/test/on_impact(turf/T) /obj/item/projectile/arc/test/on_impact(turf/T)
new /obj/effect/explosion(T) new /obj/effect/explosion(T)
return ..() return ..()
/obj/item/projectile/arc/launch(atom/target, target_zone, x_offset=0, y_offset=0, angle_offset=0) /obj/item/projectile/arc/old_style_target(target, source)
var/expected_distance = get_dist(target, loc) var/source_loc = get_turf(source) || get_turf(src)
kill_count = expected_distance // So the projectile "hits the ground." var/expected_distance = get_dist(target, source_loc)
range = expected_distance // So the projectile "hits the ground."
target_distance = expected_distance target_distance = expected_distance
fired_dir = get_dir(loc, target) fired_dir = get_dir(source_loc, target)
..() // Does the regular launching stuff. ..()
if(fired_dir & EAST) if(fired_dir & EAST)
transform = turn(transform, -45) transform = turn(transform, -45)
else if(fired_dir & WEST) else if(fired_dir & WEST)
transform = turn(transform, 45) transform = turn(transform, 45)
/obj/item/projectile/arc/on_range()
on_impact(loc)
return ..()
// Visuals. // Visuals.
/obj/item/projectile/arc/after_move() /obj/item/projectile/arc/after_move()
if(QDELETED(src))
return
// Handle projectile turning in flight. // Handle projectile turning in flight.
// This won't turn if fired north/south, as it looks weird. // This won't turn if fired north/south, as it looks weird.
var/turn_per_step = 90 / target_distance var/turn_per_step = 90 / target_distance
@@ -73,7 +76,7 @@
var/projectile_position = arc_progress / target_distance var/projectile_position = arc_progress / target_distance
var/sine_position = projectile_position * 180 var/sine_position = projectile_position * 180
var/pixel_z_position = arc_max_height * sin(sine_position) var/pixel_z_position = arc_max_height * sin(sine_position)
animate(src, pixel_z = pixel_z_position, time = step_delay) animate(src, pixel_z = pixel_z_position, time = speed)
// Update our shadow. // Update our shadow.
shadow.forceMove(loc) shadow.forceMove(loc)

View File

@@ -15,9 +15,9 @@
light_power = 0.5 light_power = 0.5
light_color = "#FF0D00" light_color = "#FF0D00"
muzzle_type = /obj/effect/projectile/laser/muzzle muzzle_type = /obj/effect/projectile/muzzle/laser
tracer_type = /obj/effect/projectile/laser/tracer tracer_type = /obj/effect/projectile/tracer/laser
impact_type = /obj/effect/projectile/laser/impact impact_type = /obj/effect/projectile/impact/laser
/obj/item/projectile/beam/practice /obj/item/projectile/beam/practice
name = "laser" name = "laser"
@@ -54,9 +54,9 @@
light_power = 1 light_power = 1
light_color = "#FF0D00" light_color = "#FF0D00"
muzzle_type = /obj/effect/projectile/laser_heavy/muzzle muzzle_type = /obj/effect/projectile/muzzle/laser_heavy
tracer_type = /obj/effect/projectile/laser_heavy/tracer tracer_type = /obj/effect/projectile/tracer/laser_heavy
impact_type = /obj/effect/projectile/laser_heavy/impact impact_type = /obj/effect/projectile/impact/laser_heavy
/obj/item/projectile/beam/heavylaser/fakeemitter /obj/item/projectile/beam/heavylaser/fakeemitter
name = "emitter beam" name = "emitter beam"
@@ -64,9 +64,9 @@
fire_sound = 'sound/weapons/emitter.ogg' fire_sound = 'sound/weapons/emitter.ogg'
light_color = "#00CC33" light_color = "#00CC33"
muzzle_type = /obj/effect/projectile/emitter/muzzle muzzle_type = /obj/effect/projectile/muzzle/emitter
tracer_type = /obj/effect/projectile/emitter/tracer tracer_type = /obj/effect/projectile/tracer/emitter
impact_type = /obj/effect/projectile/emitter/impact impact_type = /obj/effect/projectile/impact/emitter
/obj/item/projectile/beam/heavylaser/cannon /obj/item/projectile/beam/heavylaser/cannon
damage = 80 damage = 80
@@ -81,9 +81,9 @@
armor_penetration = 50 armor_penetration = 50
light_color = "#00CC33" light_color = "#00CC33"
muzzle_type = /obj/effect/projectile/xray/muzzle muzzle_type = /obj/effect/projectile/muzzle/xray
tracer_type = /obj/effect/projectile/xray/tracer tracer_type = /obj/effect/projectile/tracer/xray
impact_type = /obj/effect/projectile/xray/impact impact_type = /obj/effect/projectile/impact/xray
/obj/item/projectile/beam/cyan /obj/item/projectile/beam/cyan
name = "cyan beam" name = "cyan beam"
@@ -91,9 +91,9 @@
damage = 40 damage = 40
light_color = "#00C6FF" light_color = "#00C6FF"
muzzle_type = /obj/effect/projectile/laser_omni/muzzle muzzle_type = /obj/effect/projectile/muzzle/laser_omni
tracer_type = /obj/effect/projectile/laser_omni/tracer tracer_type = /obj/effect/projectile/tracer/laser_omni
impact_type = /obj/effect/projectile/laser_omni/impact impact_type = /obj/effect/projectile/impact/laser_omni
/obj/item/projectile/beam/pulse /obj/item/projectile/beam/pulse
name = "pulse" name = "pulse"
@@ -103,9 +103,9 @@
armor_penetration = 100 armor_penetration = 100
light_color = "#0066FF" light_color = "#0066FF"
muzzle_type = /obj/effect/projectile/laser_pulse/muzzle muzzle_type = /obj/effect/projectile/muzzle/laser_pulse
tracer_type = /obj/effect/projectile/laser_pulse/tracer tracer_type = /obj/effect/projectile/tracer/laser_pulse
impact_type = /obj/effect/projectile/laser_pulse/impact impact_type = /obj/effect/projectile/impact/laser_pulse
/obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0) /obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0)
if(isturf(target)) if(isturf(target))
@@ -119,9 +119,9 @@
damage = 0 // The actual damage is computed in /code/modules/power/singularity/emitter.dm damage = 0 // The actual damage is computed in /code/modules/power/singularity/emitter.dm
light_color = "#00CC33" light_color = "#00CC33"
muzzle_type = /obj/effect/projectile/emitter/muzzle muzzle_type = /obj/effect/projectile/muzzle/emitter
tracer_type = /obj/effect/projectile/emitter/tracer tracer_type = /obj/effect/projectile/tracer/emitter
impact_type = /obj/effect/projectile/emitter/impact impact_type = /obj/effect/projectile/impact/emitter
/obj/item/projectile/beam/lastertag/blue /obj/item/projectile/beam/lastertag/blue
name = "lasertag beam" name = "lasertag beam"
@@ -134,9 +134,9 @@
combustion = FALSE combustion = FALSE
muzzle_type = /obj/effect/projectile/laser_blue/muzzle muzzle_type = /obj/effect/projectile/muzzle/laser_blue
tracer_type = /obj/effect/projectile/laser_blue/tracer tracer_type = /obj/effect/projectile/tracer/laser_blue
impact_type = /obj/effect/projectile/laser_blue/impact impact_type = /obj/effect/projectile/impact/laser_blue
/obj/item/projectile/beam/lastertag/blue/on_hit(var/atom/target, var/blocked = 0) /obj/item/projectile/beam/lastertag/blue/on_hit(var/atom/target, var/blocked = 0)
if(istype(target, /mob/living/carbon/human)) if(istype(target, /mob/living/carbon/human))
@@ -173,9 +173,9 @@
combustion = FALSE combustion = FALSE
muzzle_type = /obj/effect/projectile/laser_omni/muzzle muzzle_type = /obj/effect/projectile/muzzle/laser_omni
tracer_type = /obj/effect/projectile/laser_omni/tracer tracer_type = /obj/effect/projectile/tracer/laser_omni
impact_type = /obj/effect/projectile/laser_omni/impact impact_type = /obj/effect/projectile/impact/laser_omni
/obj/item/projectile/beam/lastertag/omni/on_hit(var/atom/target, var/blocked = 0) /obj/item/projectile/beam/lastertag/omni/on_hit(var/atom/target, var/blocked = 0)
if(istype(target, /mob/living/carbon/human)) if(istype(target, /mob/living/carbon/human))
@@ -192,9 +192,9 @@
armor_penetration = 10 armor_penetration = 10
light_color = "#00CC33" light_color = "#00CC33"
muzzle_type = /obj/effect/projectile/xray/muzzle muzzle_type = /obj/effect/projectile/muzzle/xray
tracer_type = /obj/effect/projectile/xray/tracer tracer_type = /obj/effect/projectile/tracer/xray
impact_type = /obj/effect/projectile/xray/impact impact_type = /obj/effect/projectile/impact/xray
/obj/item/projectile/beam/stun /obj/item/projectile/beam/stun
name = "stun beam" name = "stun beam"
@@ -208,9 +208,9 @@
combustion = FALSE combustion = FALSE
muzzle_type = /obj/effect/projectile/stun/muzzle muzzle_type = /obj/effect/projectile/muzzle/stun
tracer_type = /obj/effect/projectile/stun/tracer tracer_type = /obj/effect/projectile/tracer/stun
impact_type = /obj/effect/projectile/stun/impact impact_type = /obj/effect/projectile/impact/stun
/obj/item/projectile/beam/stun/weak /obj/item/projectile/beam/stun/weak
name = "weak stun beam" name = "weak stun beam"

View File

@@ -10,7 +10,7 @@
sharp = 1 sharp = 1
var/mob_passthrough_check = 0 var/mob_passthrough_check = 0
muzzle_type = /obj/effect/projectile/bullet/muzzle muzzle_type = /obj/effect/projectile/muzzle/bullet
/obj/item/projectile/bullet/on_hit(var/atom/target, var/blocked = 0) /obj/item/projectile/bullet/on_hit(var/atom/target, var/blocked = 0)
if (..(target, blocked)) if (..(target, blocked))
@@ -257,12 +257,12 @@
incendiary = 2 incendiary = 2
flammability = 4 flammability = 4
agony = 30 agony = 30
kill_count = 4 range = 4
vacuum_traversal = 0 vacuum_traversal = 0
/obj/item/projectile/bullet/incendiary/flamethrower/large /obj/item/projectile/bullet/incendiary/flamethrower/large
damage = 15 damage = 15
kill_count = 6 range = 6
/* Practice rounds and blanks */ /* Practice rounds and blanks */

View File

@@ -12,7 +12,7 @@
icon_state = "bullet" icon_state = "bullet"
fire_sound = 'sound/weapons/gunshot_pathetic.ogg' fire_sound = 'sound/weapons/gunshot_pathetic.ogg'
damage = 5 damage = 5
kill_count = 15 //if the shell hasn't hit anything after travelling this far it just explodes. range = 15 //if the shell hasn't hit anything after travelling this far it just explodes.
var/flash_range = 0 var/flash_range = 0
var/brightness = 7 var/brightness = 7
var/light_colour = "#ffffff" var/light_colour = "#ffffff"
@@ -165,7 +165,7 @@
icon_state = "plasma_stun" icon_state = "plasma_stun"
fire_sound = 'sound/weapons/blaster.ogg' fire_sound = 'sound/weapons/blaster.ogg'
armor_penetration = 10 armor_penetration = 10
kill_count = 4 range = 4
damage = 5 damage = 5
agony = 55 agony = 55
damage_type = BURN damage_type = BURN
@@ -212,25 +212,25 @@
light_color = "#0000FF" light_color = "#0000FF"
embed_chance = 0 embed_chance = 0
muzzle_type = /obj/effect/projectile/pulse/muzzle muzzle_type = /obj/effect/projectile/muzzle/pulse
/obj/item/projectile/energy/phase /obj/item/projectile/energy/phase
name = "phase wave" name = "phase wave"
icon_state = "phase" icon_state = "phase"
kill_count = 6 range = 6
damage = 5 damage = 5
SA_bonus_damage = 45 // 50 total on animals SA_bonus_damage = 45 // 50 total on animals
SA_vulnerability = SA_ANIMAL SA_vulnerability = SA_ANIMAL
/obj/item/projectile/energy/phase/light /obj/item/projectile/energy/phase/light
kill_count = 4 range = 4
SA_bonus_damage = 35 // 40 total on animals SA_bonus_damage = 35 // 40 total on animals
/obj/item/projectile/energy/phase/heavy /obj/item/projectile/energy/phase/heavy
kill_count = 8 range = 8
SA_bonus_damage = 55 // 60 total on animals SA_bonus_damage = 55 // 60 total on animals
/obj/item/projectile/energy/phase/heavy/cannon /obj/item/projectile/energy/phase/heavy/cannon
kill_count = 10 range = 10
damage = 15 damage = 15
SA_bonus_damage = 60 // 75 total on animals SA_bonus_damage = 60 // 75 total on animals

View File

@@ -9,7 +9,7 @@
var/beam_state = "b_beam" var/beam_state = "b_beam"
damage = 5 damage = 5
step_delay = 2 speed = 2
damage_type = BURN damage_type = BURN
check_armour = "energy" check_armour = "energy"
armor_penetration = 15 armor_penetration = 15
@@ -27,9 +27,9 @@
var/list/help_messages = list("slaps", "pokes", "nudges", "bumps", "pinches") var/list/help_messages = list("slaps", "pokes", "nudges", "bumps", "pinches")
var/done_mob_unique = FALSE // Has the projectile already done something to a mob? var/done_mob_unique = FALSE // Has the projectile already done something to a mob?
/obj/item/projectile/energy/hook/launch(atom/target, target_zone, x_offset=0, y_offset=0, angle_offset=0) /obj/item/projectile/energy/hook/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
var/expected_distance = get_dist(target, loc) var/expected_distance = get_dist(target, loc)
kill_count = expected_distance // So the hook hits the ground if no mob is hit. range = expected_distance // So the hook hits the ground if no mob is hit.
target_distance = expected_distance target_distance = expected_distance
if(firer) // Needed to ensure later checks in impact and on hit function. if(firer) // Needed to ensure later checks in impact and on hit function.
launcher_intent = firer.a_intent launcher_intent = firer.a_intent

View File

@@ -34,7 +34,7 @@
penetrating = 2 penetrating = 2
embed_chance = 0 embed_chance = 0
armor_penetration = 40 armor_penetration = 40
kill_count = 20 range = 20
var/searing = 0 //Does this fuelrod ignore shields? var/searing = 0 //Does this fuelrod ignore shields?
var/detonate_travel = 0 //Will this fuelrod explode when it reaches maximum distance? var/detonate_travel = 0 //Will this fuelrod explode when it reaches maximum distance?
@@ -50,7 +50,7 @@
if(energetic_impact) if(energetic_impact)
var/eye_coverage = 0 var/eye_coverage = 0
for(var/mob/living/carbon/M in viewers(world.view, location)) for(var/mob/living/carbon/M in viewers(world.view, get_turf(src)))
eye_coverage = 0 eye_coverage = 0
if(iscarbon(M)) if(iscarbon(M))
eye_coverage = M.eyecheck() eye_coverage = M.eyecheck()
@@ -103,7 +103,7 @@
armor_penetration = 100 armor_penetration = 100
penetrating = 100 //Theoretically, this shouldn't stop flying for a while, unless someone lines it up with a wall or fires it into a mountain. penetrating = 100 //Theoretically, this shouldn't stop flying for a while, unless someone lines it up with a wall or fires it into a mountain.
irradiate = 120 irradiate = 120
kill_count = 75 range = 75
searing = 1 searing = 1
detonate_travel = 1 detonate_travel = 1
detonate_mob = 1 detonate_mob = 1
@@ -116,6 +116,9 @@
return ..(target, blocked, def_zone) return ..(target, blocked, def_zone)
/obj/item/projectile/bullet/magnetic/fuelrod/supermatter/check_penetrate() /obj/item/projectile/bullet/magnetic/fuelrod/supermatter/check_penetrate()
<<<<<<< HEAD
return 1
=======
return 1 return 1
/obj/item/projectile/bullet/magnetic/bore /obj/item/projectile/bullet/magnetic/bore
@@ -127,7 +130,7 @@
penetrating = 0 penetrating = 0
check_armour = "melee" check_armour = "melee"
irradiate = 20 irradiate = 20
kill_count = 6 range = 6
/obj/item/projectile/bullet/magnetic/bore/Bump(atom/A, forced=0) /obj/item/projectile/bullet/magnetic/bore/Bump(atom/A, forced=0)
if(istype(A, /turf/simulated/mineral)) if(istype(A, /turf/simulated/mineral))
@@ -142,3 +145,4 @@
return 1 return 1
else else
..() ..()
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles

View File

@@ -9,10 +9,6 @@
var/base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone var/base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone
var/spread_step = 10 //higher means the pellets spread more across body parts with distance var/spread_step = 10 //higher means the pellets spread more across body parts with distance
/obj/item/projectile/bullet/pellet/Bumped()
. = ..()
bumped = 0 //can hit all mobs in a tile. pellets is decremented inside attack_mob so this should be fine.
/obj/item/projectile/bullet/pellet/proc/get_pellets(var/distance) /obj/item/projectile/bullet/pellet/proc/get_pellets(var/distance)
var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance
return max(pellets - pellet_loss, 1) return max(pellets - pellet_loss, 1)

View File

@@ -219,7 +219,7 @@
embed_chance = 0 // nope embed_chance = 0 // nope
nodamage = 1 nodamage = 1
damage_type = HALLOSS damage_type = HALLOSS
muzzle_type = /obj/effect/projectile/bullet/muzzle muzzle_type = /obj/effect/projectile/muzzle/bullet
/obj/item/projectile/bola /obj/item/projectile/bola
name = "bola" name = "bola"

View File

@@ -0,0 +1,38 @@
//Helper proc to check if you can hit them or not.
/proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null)
if(!istype(target) || !istype(firer))
return 0
var/obj/item/projectile/test/trace = new /obj/item/projectile/test(get_turf(firer)) //Making the test....
//Set the flags and pass flags to that of the real projectile...
if(!isnull(flags))
trace.flags = flags
trace.pass_flags = pass_flags
return trace.launch_projectile(target) //Test it!
/obj/item/projectile/proc/_check_fire(atom/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not.
check_trajectory(target, user, pass_flags, flags)
//"Tracing" projectile
/obj/item/projectile/test //Used to see if you can hit them.
invisibility = 101 //Nope! Can't see me!
hitscan = TRUE
nodamage = TRUE
damage = 0
var/list/hit = list()
/obj/item/projectile/test/process_hitscan()
. = ..()
if(!QDELING(src))
qdel(src)
return hit
/obj/item/projectile/test/Bump(atom/A)
if(A != src)
hit |= A
return ..()
/obj/item/projectile/test/attack_mob()
return

View File

@@ -467,7 +467,7 @@
H.vent_gas(loc) H.vent_gas(loc)
qdel(H) qdel(H)
/obj/machinery/disposal/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) /obj/machinery/disposal/CanPass(atom/movable/mover, turf/target, height, air_group)
if(istype(mover, /obj/item/projectile)) if(istype(mover, /obj/item/projectile))
return 1 return 1
if (istype(mover,/obj/item) && mover.throwing) if (istype(mover,/obj/item) && mover.throwing)

View File

@@ -7,7 +7,7 @@
var/spell/targeted/projectile/carried var/spell/targeted/projectile/carried
penetrating = 0 penetrating = 0
kill_count = 10 //set by the duration of the spell range = 10 //set by the duration of the spell
var/proj_trail = 0 //if it leaves a trail var/proj_trail = 0 //if it leaves a trail
var/proj_trail_lifespan = 0 //deciseconds var/proj_trail_lifespan = 0 //deciseconds

View File

@@ -29,12 +29,13 @@ If the spell_projectile is seeking, it will update its target every process and
projectile.shot_from = user //fired from the user projectile.shot_from = user //fired from the user
projectile.hitscan = !proj_step_delay projectile.hitscan = !proj_step_delay
projectile.step_delay = proj_step_delay projectile.speed = proj_step_delay
if(istype(projectile, /obj/item/projectile/spell_projectile)) if(istype(projectile, /obj/item/projectile/spell_projectile))
var/obj/item/projectile/spell_projectile/SP = projectile var/obj/item/projectile/spell_projectile/SP = projectile
SP.carried = src //casting is magical SP.carried = src //casting is magical
projectile.launch(target, target_zone="chest") projectile.def_zone = check_zone("chest")
return projectile.old_style_target(target)
projectile.fire()
/spell/targeted/projectile/proc/choose_prox_targets(mob/user = usr, var/atom/movable/spell_holder) /spell/targeted/projectile/proc/choose_prox_targets(mob/user = usr, var/atom/movable/spell_holder)
var/list/targets = list() var/list/targets = list()

View File

@@ -1,5 +1,5 @@
/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) /obj/structure/table/CanPass(atom/movable/mover, turf/target, height, air_group)
if(air_group || (height==0)) return 1 if(air_group || (height==0)) return 1
if(istype(mover,/obj/item/projectile)) if(istype(mover,/obj/item/projectile))
return (check_cover(mover,target)) return (check_cover(mover,target))

View File

@@ -32,7 +32,7 @@
var/uy = y var/uy = y
var/uz = z var/uz = z
var/udir = dir var/udir = dir
forceMove(null) moveToNullspace()
// These modifiers are used in relation to the origin // These modifiers are used in relation to the origin
// to place the system control panels and doors. // to place the system control panels and doors.

View File

@@ -90,9 +90,9 @@
// Probably for the best so that it doesn't harm the slime. // Probably for the best so that it doesn't harm the slime.
taser_effect = FALSE taser_effect = FALSE
muzzle_type = /obj/effect/projectile/laser_omni/muzzle muzzle_type = /obj/effect/projectile/muzzle/laser_omni
tracer_type = /obj/effect/projectile/laser_omni/tracer tracer_type = /obj/effect/projectile/tracer/laser_omni
impact_type = /obj/effect/projectile/laser_omni/impact impact_type = /obj/effect/projectile/impact/laser_omni
/obj/item/projectile/beam/stun/xeno/weak //Weaker variant for non-research equipment, turrets, or rapid fire types. /obj/item/projectile/beam/stun/xeno/weak //Weaker variant for non-research equipment, turrets, or rapid fire types.
agony = 3 agony = 3

View File

@@ -1,3 +1,4 @@
<<<<<<< HEAD
/* /*
The initialization of the game happens roughly like this: The initialization of the game happens roughly like this:
@@ -27,12 +28,16 @@ var/global/datum/global_init/init = new ()
global.init = null global.init = null
return 2 // QDEL_HINT_IWILLGC return 2 // QDEL_HINT_IWILLGC
=======
//Global init and the rest of world's code have been moved to code/global_init.dm and code/game/world.dm respectively.
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles
/world /world
mob = /mob/new_player mob = /mob/new_player
turf = /turf/space turf = /turf/space
area = /area/space area = /area/space
view = "15x15" view = "15x15"
cache_lifespan = 7 cache_lifespan = 7
<<<<<<< HEAD
@@ -679,3 +684,5 @@ proc/establish_old_db_connection()
max_z_changed() max_z_changed()
#undef FAILED_DB_CONNECTION_CUTOFF #undef FAILED_DB_CONNECTION_CUTOFF
=======
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -16,7 +16,11 @@
#include "code\_map_tests.dm" #include "code\_map_tests.dm"
#include "code\_unit_tests.dm" #include "code\_unit_tests.dm"
#include "code\global.dm" #include "code\global.dm"
<<<<<<< HEAD:vorestation.dme
#include "code\global_vr.dm" #include "code\global_vr.dm"
=======
#include "code\global_init.dm"
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles:polaris.dme
#include "code\hub.dm" #include "code\hub.dm"
#include "code\names.dm" #include "code\names.dm"
#include "code\stylesheet.dm" #include "code\stylesheet.dm"
@@ -89,6 +93,7 @@
#include "code\_global_vars\sensitive.dm" #include "code\_global_vars\sensitive.dm"
#include "code\_global_vars\lists\mapping.dm" #include "code\_global_vars\lists\mapping.dm"
#include "code\_helpers\_global_objects.dm" #include "code\_helpers\_global_objects.dm"
#include "code\_helpers\_lists.dm"
#include "code\_helpers\atmospherics.dm" #include "code\_helpers\atmospherics.dm"
#include "code\_helpers\events.dm" #include "code\_helpers\events.dm"
#include "code\_helpers\files.dm" #include "code\_helpers\files.dm"
@@ -96,8 +101,11 @@
#include "code\_helpers\global_lists.dm" #include "code\_helpers\global_lists.dm"
#include "code\_helpers\global_lists_vr.dm" #include "code\_helpers\global_lists_vr.dm"
#include "code\_helpers\icons.dm" #include "code\_helpers\icons.dm"
<<<<<<< HEAD:vorestation.dme
#include "code\_helpers\icons_vr.dm" #include "code\_helpers\icons_vr.dm"
#include "code\_helpers\lists.dm" #include "code\_helpers\lists.dm"
=======
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles:polaris.dme
#include "code\_helpers\logging.dm" #include "code\_helpers\logging.dm"
#include "code\_helpers\logging_vr.dm" #include "code\_helpers\logging_vr.dm"
#include "code\_helpers\matrices.dm" #include "code\_helpers\matrices.dm"
@@ -112,8 +120,12 @@
#include "code\_helpers\type2type.dm" #include "code\_helpers\type2type.dm"
#include "code\_helpers\type2type_vr.dm" #include "code\_helpers\type2type_vr.dm"
#include "code\_helpers\unsorted.dm" #include "code\_helpers\unsorted.dm"
<<<<<<< HEAD:vorestation.dme
#include "code\_helpers\unsorted_vr.dm" #include "code\_helpers\unsorted_vr.dm"
#include "code\_helpers\vector.dm" #include "code\_helpers\vector.dm"
=======
#include "code\_helpers\view.dm"
>>>>>>> 9ff8103... Merge pull request #5636 from kevinz000/pixel_projectiles:polaris.dme
#include "code\_helpers\sorts\__main.dm" #include "code\_helpers\sorts\__main.dm"
#include "code\_helpers\sorts\comparators.dm" #include "code\_helpers\sorts\comparators.dm"
#include "code\_helpers\sorts\TimSort.dm" #include "code\_helpers\sorts\TimSort.dm"
@@ -241,6 +253,7 @@
#include "code\controllers\subsystems\processing\fastprocess.dm" #include "code\controllers\subsystems\processing\fastprocess.dm"
#include "code\controllers\subsystems\processing\obj.dm" #include "code\controllers\subsystems\processing\obj.dm"
#include "code\controllers\subsystems\processing\processing.dm" #include "code\controllers\subsystems\processing\processing.dm"
#include "code\controllers\subsystems\processing\projectiles.dm"
#include "code\controllers\subsystems\processing\turfs.dm" #include "code\controllers\subsystems\processing\turfs.dm"
#include "code\datums\ai_law_sets.dm" #include "code\datums\ai_law_sets.dm"
#include "code\datums\ai_laws.dm" #include "code\datums\ai_laws.dm"
@@ -261,6 +274,7 @@
#include "code\datums\mutable_appearance.dm" #include "code\datums\mutable_appearance.dm"
#include "code\datums\orbit.dm" #include "code\datums\orbit.dm"
#include "code\datums\organs.dm" #include "code\datums\organs.dm"
#include "code\datums\position_point_vector.dm"
#include "code\datums\progressbar.dm" #include "code\datums\progressbar.dm"
#include "code\datums\recipe.dm" #include "code\datums\recipe.dm"
#include "code\datums\riding.dm" #include "code\datums\riding.dm"
@@ -430,6 +444,7 @@
#include "code\game\skincmd.dm" #include "code\game\skincmd.dm"
#include "code\game\sound.dm" #include "code\game\sound.dm"
#include "code\game\trader_visit.dm" #include "code\game\trader_visit.dm"
#include "code\game\world.dm"
#include "code\game\antagonist\_antagonist_setup.dm" #include "code\game\antagonist\_antagonist_setup.dm"
#include "code\game\antagonist\antagonist.dm" #include "code\game\antagonist\antagonist.dm"
#include "code\game\antagonist\antagonist_add.dm" #include "code\game\antagonist\antagonist_add.dm"
@@ -943,7 +958,11 @@
#include "code\game\objects\effects\spawners\bombspawner.dm" #include "code\game\objects\effects\spawners\bombspawner.dm"
#include "code\game\objects\effects\spawners\gibspawner.dm" #include "code\game\objects\effects\spawners\gibspawner.dm"
#include "code\game\objects\effects\temporary_visuals\miscellaneous.dm" #include "code\game\objects\effects\temporary_visuals\miscellaneous.dm"
#include "code\game\objects\effects\temporary_visuals\temproary_visual.dm" #include "code\game\objects\effects\temporary_visuals\temporary_visual.dm"
#include "code\game\objects\effects\temporary_visuals\projectiles\impact.dm"
#include "code\game\objects\effects\temporary_visuals\projectiles\muzzle.dm"
#include "code\game\objects\effects\temporary_visuals\projectiles\projectile_effects.dm"
#include "code\game\objects\effects\temporary_visuals\projectiles\tracer.dm"
#include "code\game\objects\items\antag_spawners.dm" #include "code\game\objects\items\antag_spawners.dm"
#include "code\game\objects\items\apc_frame.dm" #include "code\game\objects\items\apc_frame.dm"
#include "code\game\objects\items\blueprints.dm" #include "code\game\objects\items\blueprints.dm"
@@ -2595,7 +2614,6 @@
#include "code\modules\power\tesla\tesla_act.dm" #include "code\modules\power\tesla\tesla_act.dm"
#include "code\modules\projectiles\ammunition.dm" #include "code\modules\projectiles\ammunition.dm"
#include "code\modules\projectiles\dnalocking.dm" #include "code\modules\projectiles\dnalocking.dm"
#include "code\modules\projectiles\effects.dm"
#include "code\modules\projectiles\gun.dm" #include "code\modules\projectiles\gun.dm"
#include "code\modules\projectiles\projectile.dm" #include "code\modules\projectiles\projectile.dm"
#include "code\modules\projectiles\ammunition\magazines.dm" #include "code\modules\projectiles\ammunition\magazines.dm"
@@ -2657,6 +2675,7 @@
#include "code\modules\projectiles\projectile\magnetic.dm" #include "code\modules\projectiles\projectile\magnetic.dm"
#include "code\modules\projectiles\projectile\pellets.dm" #include "code\modules\projectiles\projectile\pellets.dm"
#include "code\modules\projectiles\projectile\special.dm" #include "code\modules\projectiles\projectile\special.dm"
#include "code\modules\projectiles\projectile\trace.dm"
#include "code\modules\projectiles\targeting\targeting_client.dm" #include "code\modules\projectiles\targeting\targeting_client.dm"
#include "code\modules\projectiles\targeting\targeting_gun.dm" #include "code\modules\projectiles\targeting\targeting_gun.dm"
#include "code\modules\projectiles\targeting\targeting_mob.dm" #include "code\modules\projectiles\targeting\targeting_mob.dm"