This commit is contained in:
Fordoxia
2024-05-12 14:37:38 +01:00
104 changed files with 1071 additions and 400 deletions

View File

@@ -15,10 +15,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- id: create_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
- run: echo "GH_TOKEN=${{ steps.create_token.outputs.token }}" >> "$GITHUB_ENV"
- run: echo "FAIL_NOTIFIED=false" >> "$GITHUB_ENV"
- name: Like the comment
env:
BASE_REPOSITORY: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
gh api \
--method POST \
@@ -43,13 +51,13 @@ jobs:
with:
repository: ${{ env.PR_REPO }}
ref: ${{ env.PR_BRANCH }}
token: ${{ github.token }}
token: ${{ env.GH_TOKEN }}
- name: Build MILLA
env:
BASE_BRANCH: ${{ github.event.repository.default_branch }}
BASE_REPOSITORY: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
GH_TOKEN: ${{ env.GH_TOKEN }}
run: |
# Get the code.
git config user.name paradisess13[bot]
@@ -74,20 +82,10 @@ jobs:
cp target/i686-unknown-linux-gnu/release/libmilla.so ../tools/ci/libmilla_ci.so
cp target/i686-pc-windows-gnu/release/milla.dll ../milla.dll
# Check if a workflow file would be modified by the merge (permissions prevent pushes if so)
latest_workflow_commit=$(git log -n 1 --pretty=format:"%H" upstream/$BASE_BRANCH -- .github/workflows)
if ! git branch --contains $latest_workflow_commit | grep -q "$(git rev-parse --abbrev-ref HEAD)"; then
gh pr comment ${{ github.event.issue.html_url }} --body "GitHub Actions can not push to this branch as workflow files have been changed since your branch was last updated. Please update your branch past https://github.com/$BASE_REPOSITORY/commit/$latest_workflow_commit before using this command again."
echo "FAIL_NOTIFIED=true" >> "$GITHUB_ENV"
exit 1
fi
git commit -a -m "Build MILLA" --allow-empty
git push origin
- name: Notify Failure
if: failure() && env.FAIL_NOTIFIED != 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr comment ${{ github.event.issue.html_url }} -b 'Building MILLA failed, see the action run log for details: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}'

View File

@@ -49,6 +49,8 @@ jobs:
python tools/ci/illegal_dme_files.py ${GITHUB_WORKSPACE}
python tools/ci/define_sanity.py
python tools/ci/restrict_file_types.py
python tools/ci/verify_sql_version.py
python tools/ci/no_duplicate_definitions.py
python -m tools.ci.check_icon_conflicts
python -m tools.ci.check_icon_dupenames
python -m tools.maplint.source --github

View File

@@ -25,12 +25,14 @@ jobs:
- run: echo "FAIL_NOTIFIED=false" >> "$GITHUB_ENV"
- name: Like the comment
env:
BASE_REPOSITORY: ${{ github.repository }}
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/ParadiseSS13/Paradise/issues/comments/${{ github.event.comment.id }}/reactions \
/repos/$BASE_REPOSITORY/issues/comments/${{ github.event.comment.id }}/reactions \
-f content='+1'
- name: PR Data

View File

@@ -528,7 +528,7 @@
id = "syndbaseigniter";
pixel_y = 20
},
/turf/simulated/floor/engine/air,
/turf/simulated/floor/engine/vacuum,
/area/ruin/unpowered/syndicate_space_base/atmos)
"de" = (
/obj/machinery/light{
@@ -1064,7 +1064,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction{
dir = 4
},
/turf/simulated/floor/engine/air,
/turf/simulated/floor/engine/vacuum,
/area/ruin/unpowered/syndicate_space_base/atmos)
"fP" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
@@ -1860,7 +1860,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 10
},
/turf/simulated/floor/engine/air,
/turf/simulated/floor/engine/vacuum,
/area/ruin/unpowered/syndicate_space_base/atmos)
"kh" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
@@ -2541,7 +2541,7 @@
/obj/machinery/atmospherics/air_sensor{
autolink_id = "syndiebase_mix_sensor"
},
/turf/simulated/floor/engine/air,
/turf/simulated/floor/engine/vacuum,
/area/ruin/unpowered/syndicate_space_base/atmos)
"nI" = (
/obj/machinery/atmospherics/binary/pump{
@@ -3350,7 +3350,7 @@
autolink_id = "syndiebase_mix_out";
dir = 8
},
/turf/simulated/floor/engine/air,
/turf/simulated/floor/engine/vacuum,
/area/ruin/unpowered/syndicate_space_base/atmos)
"sF" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
@@ -7440,7 +7440,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 5
},
/turf/simulated/floor/engine/air,
/turf/simulated/floor/engine/vacuum,
/area/ruin/unpowered/syndicate_space_base/atmos)
"QB" = (
/obj/machinery/optable,
@@ -7740,7 +7740,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 9
},
/turf/simulated/floor/engine/air,
/turf/simulated/floor/engine/vacuum,
/area/ruin/unpowered/syndicate_space_base/atmos)
"Su" = (
/obj/structure/table,
@@ -7988,7 +7988,7 @@
autolink_id = "syndiebase_mix_in";
dir = 8
},
/turf/simulated/floor/engine/air,
/turf/simulated/floor/engine/vacuum,
/area/ruin/unpowered/syndicate_space_base/atmos)
"Tp" = (
/obj/machinery/light{

View File

@@ -3314,6 +3314,12 @@
icon_state = "darkfull"
},
/area/mine/outpost/airlock)
"iU" = (
/obj/structure/shuttle/engine/propulsion/burst{
dir = 8
},
/turf/simulated/wall/mineral/titanium/interior,
/area/lavaland/surface/outdoors)
"iW" = (
/turf/simulated/floor/plasteel{
dir = 4;
@@ -5760,6 +5766,14 @@
icon_state = "darkred"
},
/area/mine/laborcamp/security)
"wb" = (
/obj/structure/girder,
/turf/simulated/floor/plating{
nitrogen = 14;
oxygen = 8;
temperature = 500
},
/area/lavaland/surface/outdoors)
"wd" = (
/obj/structure/stone_tile/cracked{
dir = 4
@@ -6320,6 +6334,12 @@
},
/turf/simulated/floor/catwalk,
/area/mine/outpost/hallway/west)
"zm" = (
/obj/structure/closet/walllocker/emerglocker/north,
/obj/item/storage/toolbox/mechanical,
/obj/item/flashlight/flare,
/turf/simulated/floor/mineral/titanium/blue,
/area/lavaland/surface/outdoors)
"zn" = (
/obj/structure/lattice/catwalk/mining,
/obj/effect/decal/cleanable/dirt,
@@ -7819,6 +7839,10 @@
"IC" = (
/turf/simulated/wall,
/area/mine/outpost/hallway/east)
"ID" = (
/obj/structure/door_assembly/door_assembly_silver,
/turf/simulated/floor/mineral/titanium/blue,
/area/lavaland/surface/outdoors)
"IE" = (
/obj/machinery/atmospherics/unary/vent_pump/on,
/obj/structure/sign/poster/official/help_others{
@@ -7984,6 +8008,18 @@
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"JD" = (
/obj/structure/grille/broken,
/obj/structure/grille/broken,
/obj/effect/mob_spawn/human/corpse/skeleton,
/obj/effect/decal/cleanable/glass,
/obj/item/shard,
/turf/simulated/floor/plating{
nitrogen = 14;
oxygen = 8;
temperature = 500
},
/area/lavaland/surface/outdoors)
"JF" = (
/obj/effect/spawner/window/reinforced/grilled,
/obj/machinery/door/poddoor/preopen{
@@ -8443,6 +8479,14 @@
icon_state = "darkyellowcorners"
},
/area/mine/outpost/hallway/west)
"MQ" = (
/obj/item/stack/ore/iron,
/turf/simulated/floor/plating{
nitrogen = 14;
oxygen = 8;
temperature = 500
},
/area/lavaland/surface/outdoors)
"MS" = (
/obj/structure/stone_tile{
dir = 1
@@ -8952,6 +8996,14 @@
icon_state = "darkfull"
},
/area/mine/laborcamp/security)
"PZ" = (
/obj/structure/grille/broken,
/turf/simulated/floor/plating{
nitrogen = 14;
oxygen = 8;
temperature = 500
},
/area/lavaland/surface/outdoors)
"Qa" = (
/obj/structure/closet,
/obj/effect/spawner/lootdrop/maintenance,
@@ -9759,6 +9811,13 @@
icon_state = "tranquillite"
},
/area/mine/outpost/cafeteria)
"VE" = (
/obj/item/mounted/frame/intercom,
/obj/item/stack/cable_coil{
amount = 1
},
/turf/simulated/floor/mineral/titanium/blue,
/area/lavaland/surface/outdoors)
"VF" = (
/obj/machinery/atmospherics/pipe/simple/visible/cyan{
dir = 6
@@ -9926,6 +9985,9 @@
/obj/machinery/atmospherics/pipe/simple/hidden,
/turf/simulated/floor/catwalk,
/area/mine/laborcamp/security)
"Wy" = (
/turf/simulated/wall/mineral/titanium,
/area/lavaland/surface/outdoors)
"WD" = (
/obj/structure/lattice/catwalk/mining,
/obj/structure/railing{
@@ -12443,9 +12505,9 @@ an
an
an
an
an
an
an
KQ
Aw
Aw
an
an
an
@@ -12461,8 +12523,8 @@ an
an
an
an
an
an
Aw
Aw
an
an
an
@@ -12478,8 +12540,8 @@ an
an
an
an
an
an
Aw
Aw
an
an
an
@@ -12700,9 +12762,9 @@ an
an
an
an
an
an
an
ab
ab
ab
an
an
an
@@ -12957,9 +13019,9 @@ an
an
an
an
an
an
an
iU
ID
iU
an
an
an
@@ -13214,9 +13276,9 @@ an
an
an
an
an
an
an
Wy
zm
PZ
an
an
an
@@ -13471,9 +13533,9 @@ an
an
an
an
an
an
an
MQ
VE
Wy
an
an
an
@@ -13728,8 +13790,8 @@ an
an
an
an
an
an
wb
JD
an
an
an

View File

@@ -725,6 +725,7 @@
dir = 6;
color = "#63009c"
},
/obj/effect/landmark/spawner/roundstart_observer,
/turf/simulated/floor/wood,
/area/wizard_station)
"cO" = (
@@ -1369,6 +1370,7 @@
dir = 4;
color = "#63009c"
},
/obj/effect/landmark/spawner/roundstart_observer,
/turf/simulated/floor/wood,
/area/wizard_station)
"fp" = (
@@ -1432,6 +1434,7 @@
/obj/structure/chair/sofa/right{
color = "#63009c"
},
/obj/effect/landmark/spawner/roundstart_observer,
/turf/simulated/floor/wood,
/area/wizard_station)
"fx" = (
@@ -5407,7 +5410,6 @@
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/assault_pod)
"sh" = (
/obj/effect/spawner/window/reinforced,
/obj/machinery/door/poddoor/shutters{
density = 0;
dir = 2;
@@ -5416,6 +5418,7 @@
name = "Privacy Shutters";
opacity = 0
},
/obj/effect/spawner/window/shuttle,
/turf/simulated/floor/mineral/titanium/blue,
/area/shuttle/trade/sol)
"sj" = (
@@ -8159,6 +8162,7 @@
name = "wizard council throne";
dir = 1
},
/obj/effect/landmark/spawner/roundstart_observer,
/turf/simulated/floor/carpet/red,
/area/wizard_station)
"CY" = (
@@ -12077,6 +12081,7 @@
/obj/structure/chair/sofa{
color = "#63009c"
},
/obj/effect/landmark/spawner/roundstart_observer,
/turf/simulated/floor/wood,
/area/wizard_station)
"Qm" = (
@@ -14263,6 +14268,7 @@
/obj/structure/chair/comfy/purp{
name = "wizard council throne"
},
/obj/effect/landmark/spawner/roundstart_observer,
/turf/simulated/floor/carpet/red,
/area/wizard_station)
"Yp" = (

View File

@@ -53839,7 +53839,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 5
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"eiv" = (
/obj/effect/spawner/window/reinforced/plasma/grilled,
@@ -59935,7 +59935,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 4
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"gZn" = (
/obj/effect/spawner/random_spawners/cobweb_left_rare,
@@ -63601,7 +63601,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 10
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"iUF" = (
/obj/structure/cable{
@@ -64813,7 +64813,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 9
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"jzT" = (
/obj/machinery/door/airlock/command{
@@ -69802,7 +69802,7 @@
/area/station/maintenance/apmaint2)
"lVB" = (
/obj/machinery/atmospherics/unary/passive_vent,
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"lVK" = (
/obj/machinery/door/airlock/maintenance,
@@ -73550,7 +73550,7 @@
id = "toxinsigniter";
pixel_x = -20
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"nTm" = (
/obj/machinery/sparker{
@@ -74563,7 +74563,7 @@
dir = 6
},
/obj/effect/landmark/spawner/nukedisc_respawn,
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"opg" = (
/obj/item/seeds/potato,
@@ -76565,7 +76565,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction{
dir = 1
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"plJ" = (
/obj/structure/cable{
@@ -77521,7 +77521,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 6
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"pMv" = (
/obj/machinery/light_switch{

View File

@@ -21636,7 +21636,7 @@
/obj/machinery/atmospherics/unary/outlet_injector/on{
dir = 4
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"clR" = (
/obj/machinery/atmospherics/pipe/simple/visible{
@@ -21840,7 +21840,7 @@
/obj/machinery/light{
dir = 1
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"cnm" = (
/obj/structure/sign/poster/random{
@@ -62432,7 +62432,7 @@
/obj/machinery/atmospherics/unary/vent_pump/siphon/on{
dir = 4
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"mQh" = (
/obj/machinery/firealarm{
@@ -64492,7 +64492,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 6
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"nyv" = (
/obj/machinery/door/airlock/maintenance{
@@ -70056,7 +70056,7 @@
/obj/machinery/igniter{
id = "Incinerator"
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"pow" = (
/obj/machinery/economy/vending/artvend,
@@ -83107,7 +83107,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 5
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"txQ" = (
/obj/machinery/atmospherics/unary/vent_pump/on,
@@ -95114,7 +95114,7 @@
/area/station/maintenance/port)
"xbh" = (
/obj/machinery/atmospherics/pipe/simple/heat_exchanging,
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"xbm" = (
/obj/structure/disposalpipe/trunk,

View File

@@ -50104,7 +50104,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 6
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"cKw" = (
/obj/structure/table,
@@ -51924,7 +51924,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 10
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"cRz" = (
/obj/structure/girder,
@@ -71280,7 +71280,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 4
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"hsE" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
@@ -72445,7 +72445,7 @@
/area/station/engineering/control)
"ieu" = (
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction,
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"iev" = (
/obj/machinery/door/window/classic/reversed{
@@ -74635,7 +74635,7 @@
/obj/machinery/atmospherics/unary/passive_vent{
dir = 1
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"jAs" = (
/obj/machinery/door/firedoor,
@@ -79593,19 +79593,6 @@
icon_state = "dark"
},
/area/station/security/prison/cell_block)
"mBa" = (
/obj/effect/spawner/window/reinforced,
/obj/structure/cable{
d1 = 4;
d2 = 8;
icon_state = "4-8"
},
/obj/structure/cable{
d2 = 8;
icon_state = "0-8"
},
/turf/simulated/floor/plating,
/area/station/engineering/smes)
"mBy" = (
/obj/structure/bed,
/obj/item/bedsheet/green,
@@ -86252,7 +86239,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 9
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"qwZ" = (
/obj/machinery/door/poddoor{
@@ -90537,7 +90524,7 @@
/obj/machinery/atmospherics/pipe/simple/heat_exchanging{
dir = 5
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"sMn" = (
/obj/structure/table/glass,
@@ -95131,7 +95118,7 @@
/obj/machinery/atmospherics/air_sensor{
autolink_id = "burn_sensor"
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"vBT" = (
/obj/machinery/door/window/brigdoor{
@@ -97380,7 +97367,7 @@
autolink_id = "air_in";
dir = 1
},
/turf/simulated/floor/engine,
/turf/simulated/floor/engine/vacuum,
/area/station/science/toxins/mixing)
"wSw" = (
/obj/machinery/economy/vending/cola,
@@ -127352,7 +127339,7 @@ bwP
fsO
oNV
ucB
mBa
fsO
clv
bQr
bQr

View File

@@ -0,0 +1,7 @@
// These are used to convery what kind of debris should spawn from being hit by something
#define DEBRIS_SPARKS "spark"
#define DEBRIS_WOOD "wood"
#define DEBRIS_ROCK "rock"
#define DEBRIS_GLASS "glass"
#define DEBRIS_LEAF "leaf"
#define DEBRIS_SNOW "snow"

View File

@@ -445,9 +445,9 @@ GLOBAL_LIST_EMPTY(bicon_cache)
* * frame - what frame of the icon_state's animation for the icon being used
* * moving - whether or not to use a moving state for the given icon
* * sourceonly - if TRUE, only generate the asset and send back the asset url, instead of tags that display the icon to players
* * extra_clases - string of extra css classes to use when returning the icon string
* * extra_classes - string of extra css classes to use when returning the icon string
*/
/proc/icon2html(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null)
/proc/icon2asset(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null)
if(!thing)
return
@@ -516,17 +516,15 @@ GLOBAL_LIST_EMPTY(bicon_cache)
SSassets.transport.register_asset(key, rsc_ref, file_hash, icon_path)
for(var/client_target in targets)
SSassets.transport.send_assets(client_target, key)
if(sourceonly)
return SSassets.transport.get_asset_url(key)
return "<img class='icon icon-[icon_state]' src='[SSassets.transport.get_asset_url(key)]'>"
return key
/// Costlier version of icon2html() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs.
/proc/costly_icon2html(thing, target, sourceonly = FALSE)
/// Costlier version of icon2asset() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs.
/proc/costly_icon2asset(thing, target, sourceonly = FALSE)
if(!thing)
return
if(isicon(thing))
return icon2html(thing, target)
return icon2asset(thing, target)
var/icon/I = getFlatIcon(thing)
return icon2html(I, target, sourceonly = sourceonly)
return icon2asset(I, target)

View File

@@ -346,35 +346,23 @@
new_text += copytext(text, i, i+1)
return new_text
//This proc strips html properly, but it's not lazy like the other procs.
//This means that it doesn't just remove < and > and call it a day.
//Also limit the size of the input, if specified.
/proc/strip_html_properly(input, max_length = MAX_MESSAGE_LEN, allow_lines = 0)
/// Strips HTML tags (and only tags) from the input.
/// The result may still include HTML entities, like &#39; for '
/proc/strip_html_tags(input, max_length = MAX_MESSAGE_LEN, allow_lines = 0)
if(!input)
return
var/opentag = 1 //These store the position of < and > respectively.
var/closetag = 1
while(1)
opentag = findtext(input, "<")
closetag = findtext(input, ">")
if(closetag && opentag)
if(closetag < opentag)
input = copytext(input, (closetag + 1))
else
input = copytext(input, 1, opentag) + copytext(input, (closetag + 1))
else if(closetag || opentag)
if(opentag)
input = copytext(input, 1, opentag)
else
input = copytext(input, (closetag + 1))
else
break
return ""
var/static/regex/tags = regex("<\[^>]*>", "g")
if(!tags)
tags = regex("<\[^>]*>", "g")
input = tags.Replace(input, "")
if(max_length)
input = copytext_char(input, 1, max_length)
return sanitize(input, allow_lines ? list("\t" = " ") : list("\n" = " ", "\t" = " "))
if(allow_lines)
return sanitize_simple(input, list("\t" = " "))
return sanitize_simple(input, list("\n" = " ", "\t" = " "))
/proc/trim_strip_html_properly(input, max_length = MAX_MESSAGE_LEN, allow_lines = 0)
return trim(strip_html_properly(input, max_length, allow_lines))
/proc/trim_strip_html_tags(input, max_length = MAX_MESSAGE_LEN, allow_lines = 0)
return trim(strip_html_tags(input, max_length, allow_lines))
//Used in preferences' SetFlavorText and human's set_flavor verb
//Previews a string of len or less length
@@ -742,11 +730,6 @@
return null
// Removes HTML tags, preserving text
/proc/strip_html_tags(the_text)
var/static/regex/html_replacer = regex("<\[^>]*>", "g")
return html_replacer.Replace(the_text, "")
/proc/starts_with_vowel(text)
var/start_char = copytext(text, 1, 2)
switch(lowertext(start_char))

View File

@@ -38,6 +38,7 @@ GLOBAL_LIST_EMPTY(nukedisc_respawn)
GLOBAL_LIST_EMPTY(ninjastart)
GLOBAL_LIST_EMPTY(carplist) //list of all carp-spawn landmarks
GLOBAL_LIST_EMPTY(syndicateofficer)
GLOBAL_LIST_EMPTY(roundstart_observer_start)
//List of preloaded templates
GLOBAL_LIST_EMPTY(map_templates)

View File

@@ -112,11 +112,10 @@ SUBSYSTEM_DEF(statpanels)
/// Set the atoms we're meant to display
var/datum/object_window_info/obj_window = istype(target.obj_window) ? target.obj_window : new(target)
obj_window.atoms_to_show = atoms_to_display
START_PROCESSING(SSobj_tab_items, obj_window)
refresh_client_obj_view(target)
refresh_client_obj_view(target, obj_window.min_index, obj_window.max_index)
/datum/controller/subsystem/statpanels/proc/refresh_client_obj_view(client/refresh)
var/list/turf_items = return_object_images(refresh)
/datum/controller/subsystem/statpanels/proc/refresh_client_obj_view(client/refresh, min_index = 0, max_index = 30)
var/list/turf_items = return_object_images(refresh, min_index, max_index)
if(!length(turf_items) || !refresh.mob?.listed_turf)
return
refresh.stat_panel.send_message("update_listedturf", turf_items)
@@ -125,7 +124,7 @@ SUBSYSTEM_DEF(statpanels)
/// Returns all our ready object tab images
/// Returns a list in the form list(list(object_name, object_ref, loaded_image), ...)
/datum/controller/subsystem/statpanels/proc/return_object_images(client/load_from)
/datum/controller/subsystem/statpanels/proc/return_object_images(client/load_from, min_index, max_index)
// You might be inclined to think that this is a waste of cpu time, since we
// A: Double iterate over atoms in the build case, or
// B: Generate these lists over and over in the refresh case
@@ -137,10 +136,18 @@ SUBSYSTEM_DEF(statpanels)
return list()
var/datum/object_window_info/obj_window = load_from.obj_window
if(!obj_window)
return list()
var/list/already_seen = obj_window.atoms_to_images
var/list/to_make = obj_window.atoms_to_imagify
var/list/turf_items = list()
var/i = 0
for(var/atom/turf_item as anything in obj_window.atoms_to_show)
// Limit what we send to the client's rendered section.
i++
if(i <= min_index || i > max_index)
continue
// First, we fill up the list of refs to display
// If we already have one, just use that
var/existing_image = already_seen[turf_item]
@@ -148,12 +155,17 @@ SUBSYSTEM_DEF(statpanels)
continue
// We already have it. Success!
if(existing_image)
turf_items[++turf_items.len] = list("[turf_item.name]", turf_item.UID(), existing_image)
turf_items["[i]"] = list("[turf_item.name]", turf_item.UID(), SSassets.transport.get_asset_url(existing_image), existing_image)
continue
// Now, we're gonna queue image generation out of those refs
to_make += turf_item
already_seen[turf_item] = OBJ_IMAGE_LOADING
obj_window.RegisterSignal(turf_item, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/datum/object_window_info, viewing_atom_deleted), override = TRUE) // we reset cache if anything in it gets deleted
turf_items["total"] = i
obj_window.min_index = min_index
obj_window.max_index = max_index
if(length(to_make))
START_PROCESSING(SSobj_tab_items, obj_window)
return turf_items
#undef OBJ_IMAGE_LOADING
@@ -219,6 +231,10 @@ SUBSYSTEM_DEF(statpanels)
var/client/parent
/// Are we currently tracking a turf?
var/actively_tracking = FALSE
/// The minimum index currently sent to the client.
var/min_index = 0
/// The maximum index currently sent to the client.
var/max_index = 30
/datum/object_window_info/New(client/parent)
. = ..()
@@ -245,9 +261,9 @@ SUBSYSTEM_DEF(statpanels)
var/generated_string
if(ismob(thing) || length(thing.overlays) > 2)
generated_string = costly_icon2html(thing, parent, sourceonly=TRUE)
generated_string = costly_icon2asset(thing, parent)
else
generated_string = icon2html(thing, parent, sourceonly=TRUE)
generated_string = icon2asset(thing, parent)
newly_seen[thing] = generated_string
if(TICK_CHECK)
@@ -257,7 +273,7 @@ SUBSYSTEM_DEF(statpanels)
// If we've not cut yet, do it now
if(index)
to_make.Cut(1, index + 1)
SSstatpanels.refresh_client_obj_view(parent)
SSstatpanels.refresh_client_obj_view(parent, min_index, max_index)
if(!length(to_make))
return PROCESS_KILL
@@ -302,6 +318,9 @@ SUBSYSTEM_DEF(statpanels)
if(listed_turf)
client.stat_panel.send_message("create_listedturf", listed_turf.name)
client.obj_window.start_turf_tracking()
client.obj_window.min_index = 0
client.obj_window.max_index = 30
SSstatpanels.set_turf_examine_tab(client, src)
else
client.stat_panel.send_message("remove_listedturf")
client.obj_window.stop_turf_tracking()

View File

@@ -852,17 +852,17 @@ SUBSYSTEM_DEF(ticker)
if(length(SSticker.mode.blob_overminds))
switch(outcome)
if(ROUND_END_NUCLEAR)
SSblackbox.record_feedback("tally", "Biohazard nuclear victories", 1, "Blob")
SSblackbox.record_feedback("tally", "Blob nuclear victories", 1, "Blob")
if(ROUND_END_CREW_TRANSFER)
SSblackbox.record_feedback("tally", "Biohazard survives to normal round end", 1, "Blob")
SSblackbox.record_feedback("tally", "Blob survives to normal round end", 1, "Blob")
if(ROUND_END_FORCED)
SSblackbox.record_feedback("tally", "Biohazard survives to admin round end", 1, "Blob")
SSblackbox.record_feedback("tally", "Blob survives to admin round end", 1, "Blob")
else
switch(outcome)
if(ROUND_END_NUCLEAR)
SSblackbox.record_feedback("tally", "Biohazard dies station nuked", 1, "Blob")
SSblackbox.record_feedback("tally", "Blob dies station nuked", 1, "Blob")
if(ROUND_END_CREW_TRANSFER)
SSblackbox.record_feedback("tally", "Biohazard dies normal end", 1, "Blob")
SSblackbox.record_feedback("tally", "Blob dies normal end", 1, "Blob")
if(ROUND_END_FORCED)
SSblackbox.record_feedback("tally", "Biohazard dies admin round end", 1, "Blob")
SSblackbox.record_feedback("tally", "Blob dies admin round end", 1, "Blob")

View File

@@ -738,7 +738,7 @@ UI STUFF
for(var/datum/ticket_response/TR in T.ticket_responses)
var/list/this_response = list()
this_response["ckey"] = TR.response_user
this_response["text"] = strip_html_tags(TR.response_text) // Dont want to save HTML tags in the thing
this_response["text"] = html_decode(strip_html_tags(TR.response_text)) // Dont want to save HTML stuff to the DB
this_response["time"] = TR.response_time
raw_responses += list(this_response)

View File

@@ -0,0 +1,93 @@
/*
* In this file you can find the particle component for bullet hits
* Originally from https://github.com/tgstation/TerraGov-Marine-Corps/pull/12752
*/
/particles/debris
icon = 'icons/effects/particles/generic_particles.dmi'
width = 500
height = 500
count = 10
spawning = 10
lifespan = 0.5 SECONDS
fade = 0.3 SECONDS
drift = generator("circle", 0, 7)
scale = 0.3
velocity = list(50, 0)
friction = generator("num", 0.1, 0.15)
spin = generator("num", -20, 20)
/particles/impact_smoke
icon = 'icons/effects/effects.dmi'
icon_state = "smoke"
width = 500
height = 500
count = 20
spawning = 20
lifespan = 0.8 SECONDS
fade = 10 SECONDS
grow = 0.1
scale = 0.2
spin = generator("num", -20, 20)
velocity = list(50, 0)
friction = generator("num", 0.1, 0.5)
/datum/component/debris
/// Icon state of debris when impacted by a projectile
var/debris
/// Velocity of debris particles
var/debris_velocity = -15
/// Amount of debris particles
var/debris_amount = 8
/// Scale of particle debris
var/debris_scale = 0.7
/datum/component/debris/Initialize(_debris_icon_state, _debris_velocity = -15, _debris_amount = 8, _debris_scale = 0.7)
. = ..()
debris = _debris_icon_state
debris_velocity = _debris_velocity
debris_amount = _debris_amount
debris_scale = _debris_scale
RegisterSignal(parent, COMSIG_ATOM_BULLET_ACT, PROC_REF(register_for_impact))
/datum/component/debris/Destroy(force)
. = ..()
if(parent)
UnregisterSignal(parent, COMSIG_ATOM_BULLET_ACT)
/datum/component/debris/proc/register_for_impact(datum/source, obj/item/projectile/proj)
SIGNAL_HANDLER // COMSIG_ATOM_BULLET_ACT
INVOKE_ASYNC(src, PROC_REF(on_impact), proj)
/datum/component/debris/proc/on_impact(obj/item/projectile/P)
var/angle = !isnull(P.Angle) ? P.Angle : round(get_angle(P.starting, parent), 1)
var/x_component = sin(angle) * debris_velocity
var/y_component = cos(angle) * debris_velocity
var/x_component_smoke = sin(angle) * -15
var/y_component_smoke = cos(angle) * -15
var/obj/effect/abstract/particle_holder/debris_visuals
var/obj/effect/abstract/particle_holder/smoke_visuals
var/position_offset = rand(-6, 6)
smoke_visuals = new(parent, /particles/impact_smoke)
smoke_visuals.particles.position = list(position_offset, position_offset)
smoke_visuals.particles.velocity = list(x_component_smoke, y_component_smoke)
if(debris && P.damage_type == BRUTE)
debris_visuals = new(parent, /particles/debris)
debris_visuals.particles.position = generator("circle", position_offset, position_offset)
debris_visuals.particles.velocity = list(x_component, y_component)
debris_visuals.layer = ABOVE_OBJ_LAYER + 0.02
debris_visuals.particles.icon_state = debris
debris_visuals.particles.count = debris_amount
debris_visuals.particles.spawning = debris_amount
debris_visuals.particles.scale = debris_scale
smoke_visuals.layer = ABOVE_OBJ_LAYER + 0.01
addtimer(CALLBACK(src, PROC_REF(remove_ping), src, smoke_visuals, debris_visuals), 0.5 SECONDS)
/datum/component/debris/proc/remove_ping(hit, obj/effect/abstract/particle_holder/smoke_visuals, obj/effect/abstract/particle_holder/debris_visuals)
QDEL_NULL(smoke_visuals)
if(debris_visuals)
QDEL_NULL(debris_visuals)

View File

@@ -84,7 +84,7 @@ GLOBAL_DATUM_INIT(discord_manager, /datum/discord_manager, new())
else
alerttext = "| **NO MENTORS ONLINE**"
var/message = "[content] [alerttext][add_ping ? handle_mentor_ping() : ""]"
var/message = "[html_decode(strip_html_tags(content))] [alerttext][add_ping ? handle_mentor_ping() : ""]"
var/datum/discord_webhook_payload/dwp = new()
dwp.webhook_content = "**\[[GLOB.configuration.system.instance_id]]** [message]"

View File

@@ -42,6 +42,9 @@
// Semi-permanent clown mask while in last stage of infection
if(locate(/obj/item/clothing/mask/gas/clown_hat) in H)
return
if(!istype(H)) // Xenos don't have masks. They can still feel silly though
return
if(!H.has_organ_for_slot(SLOT_HUD_WEAR_MASK) || !H.canUnEquip(H.get_item_by_slot(SLOT_HUD_WEAR_MASK)))
return

View File

@@ -58,6 +58,8 @@
/datum/disease/wizarditis/proc/spawn_wizard_clothes()
var/mob/living/carbon/human/H = affected_mob
if(!istype(H))
return // Woe, wizard xeno upon ye
// Which slots can we replace?
var/list/eligible_slot_IDs = new

View File

@@ -277,6 +277,9 @@
/// A lazy list of user mobs to a list of strip menu keys that they're interacting with
var/list/interactions
/// Associated list of "[icon][icon_state]" = base64 representation of icon. Used for PERFORMANCE.
var/static/list/base64_cache = list()
/datum/strip_menu/New(atom/movable/owner, datum/element/strippable/strippable)
. = ..()
src.owner = owner
@@ -341,7 +344,10 @@
LAZYINITLIST(result)
result["icon"] = icon2base64(icon(item.icon, item.icon_state, dir = SOUTH, frame = 1, moving = FALSE))
var/key = "[item.icon],[item.icon_state]"
if(!(key in base64_cache))
base64_cache[key] = icon2base64(icon(item.icon, item.icon_state, dir = SOUTH, frame = 1, moving = FALSE))
result["icon"] = base64_cache[key]
result["name"] = item.name
var/real_alts = item_data.get_alternate_actions(owner, user)

View File

@@ -36,8 +36,8 @@
return
var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
vampire.bloodusable -= calculate_blood_cost(vampire)
var/blood_cost = calculate_blood_cost(vampire)
vampire.subtract_usable_blood(blood_cost)
/datum/spell_handler/vampire/proc/calculate_blood_cost(datum/antagonist/vampire/vampire)
var/blood_cost_modifier = 1 + vampire.nullified / 100

View File

@@ -388,7 +388,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/explosives/plastic_explosives
name = "Composition C-4"
desc = "C-4 is plastic explosive of the common variety Composition C. Reliably destroys the object it's placed on, assuming it isn't bomb resistant. Does not stick to crewmembers. Will only destroy station floors if placed directly on it. It has a modifiable timer with a minimum setting of 10 seconds."
desc = "C-4 is plastic explosive of the common variety Composition C. Reliably destroys the object it's placed on, assuming it isn't bomb resistant. Remarkably good for disposing bodies, or tired crewmates. Will only destroy station floors if placed directly on it. It has a modifiable timer with a minimum setting of 10 seconds."
reference = "C4"
item = /obj/item/grenade/plastic/c4
cost = 5

View File

@@ -663,6 +663,7 @@
/datum/uplink_item/stealthy_tools/chameleon/nuke
reference = "NCHAM"
item = /obj/item/storage/box/syndie_kit/chameleon/nuke
cost = 30
excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)

View File

@@ -468,7 +468,16 @@
While the mask is active, your voice will sound unrecognizable to others."
reference = "CVMM"
item = /obj/item/clothing/mask/gas/voice_modulator/chameleon
cost = 8
cost = 5
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/stealthy_tools/voice_changer
name = "Chameleon Voice Changer Mask"
desc = "A syndicate gas mask equipped with chameleon technology and a voice changer for disguising your voice. \
Use it to impersonate or obfuscate your identity when talking and make nobody the wiser!"
reference = "CVCM"
item = /obj/item/clothing/mask/chameleon/voice_change
cost = 10
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/stealthy_tools/silicon_cham_suit
@@ -692,10 +701,10 @@
/datum/uplink_item/stealthy_tools/chameleon
name = "Chameleon Kit"
desc = "A set of items that contain chameleon technology allowing you to disguise as pretty much anything on the station, and more! \
Due to budget cuts, the shoes don't provide protection against slipping. The set comes with a complementary chameleon stamp."
Due to budget cuts, the shoes don't provide protection against slipping."
reference = "CHAM"
item = /obj/item/storage/box/syndie_kit/chameleon
cost = 20
cost = 10
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/stealthy_tools/syndigaloshes

View File

@@ -45,7 +45,7 @@ GLOBAL_DATUM_INIT(major_announcement, /datum/announcer, new(config_type = /datum
var/message_sound2 = new_sound2 ? sound(new_sound2) : null
if(!msg_sanitized)
message = trim_strip_html_properly(message, allow_lines = TRUE)
message = html_encode(message)
var/datum/language/message_language = GLOB.all_languages[msg_language ? msg_language : language]

View File

@@ -18,7 +18,7 @@
icon_state = "yellow"
/area/ruin/powered/golem_ship
name = "Free Golem Landing"
name = "Free Golem Ship"
icon_state = "yellow"
/area/ruin/powered/greed

View File

@@ -356,6 +356,8 @@ structure_check() searches for nearby cultist structures required for the invoca
H.uncuff()
H.Silence(6 SECONDS) //Prevent "HALP MAINT CULT" before you realise you're converted
if(H.reagents?.has_reagent("holywater"))
H.reagents.del_reagent("holywater") // Also prevent fill stomach with holy water and "forgot" about it after converting
var/obj/item/melee/cultblade/dagger/D = new(get_turf(src))
if(H.equip_to_slot_if_possible(D, SLOT_HUD_IN_BACKPACK, FALSE, TRUE))

View File

@@ -96,15 +96,15 @@
/obj/item/pinpointer/proc/scandisk()
if(!the_disk)
the_disk = locate()
the_disk = locate() in GLOB.poi_list
/obj/item/pinpointer/proc/scanbomb()
if(!syndicate)
if(!the_bomb)
the_bomb = locate()
the_bomb = locate() in GLOB.poi_list
else
if(!the_s_bomb)
the_s_bomb = locate()
the_s_bomb = locate() in GLOB.poi_list
/obj/item/pinpointer/proc/point_at_target(atom/target)
if(!target)

View File

@@ -65,8 +65,7 @@
return FALSE
/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user, obj/item/spellbook/book) //return point value or -1 for failure
var/area/wizard_station/A = locate()
if(!(user in A.contents))
if(!istype(get_area(user), /area/wizard_station))
to_chat(user, "<span class='warning'>You can only refund spells at the wizard lair.</span>")
return -1
if(!S) //This happens when the spell's source is from another spellbook, from loadouts, or adminery, this create a new template temporary spell
@@ -1152,7 +1151,7 @@
/obj/item/spellbook/oneuse/random/initialize()
. = ..()
var/static/banned_spells = list(/obj/item/spellbook/oneuse/mime, /obj/item/spellbook/oneuse/mime/fingergun, /obj/item/spellbook/oneuse/mime/fingergun/fake, /obj/item/spellbook/oneuse/mime/greaterwall)
var/static/list/banned_spells = typesof(/obj/item/spellbook/oneuse/mime, /obj/item/spellbook/oneuse/emp)
var/real_type = pick(subtypesof(/obj/item/spellbook/oneuse) - banned_spells)
new real_type(loc)
qdel(src)

View File

@@ -255,7 +255,7 @@
Nuke_request(input, ui.user)
to_chat(ui.user, "<span class='notice'>Request sent.</span>")
log_game("[key_name(ui.user)] has requested the nuclear codes from Centcomm")
GLOB.major_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [ui.user]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested", 'sound/AI/nuke_codes.ogg')
GLOB.major_announcement.Announce("The codes for the on-station nuclear self-destruction device have been requested by [ui.user]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested", 'sound/AI/nuke_codes.ogg')
centcomm_message_cooldown = world.time + 6000 // 10 minutes
setMenuState(ui.user, COMM_SCREEN_MAIN)
@@ -562,7 +562,7 @@
return
if(!sanitized)
reason = trim_strip_html_properly(reason, allow_lines = TRUE)
reason = trim_strip_html_tags(reason, allow_lines = TRUE)
SSshuttle.requestEvac(user, reason)
log_game("[key_name(user)] has called the shuttle.")

View File

@@ -396,8 +396,7 @@
//Select Your Name
if("Sender")
customsender = input("Please enter the sender's name.")
customsender = trim_strip_html_properly(customsender)
customsender = clean_input("Please enter the sender's name.")
//Select Receiver
if("Recepient")
@@ -416,13 +415,11 @@
//Enter custom job
if("RecJob")
customjob = input("Please enter the sender's job.")
customjob = trim_strip_html_properly(customjob)
customjob = clean_input("Please enter the sender's job.")
//Enter message
if("Message")
custommessage = input("Please enter your message.")
custommessage = trim_strip_html_properly(custommessage)
custommessage = clean_input("Please enter your message.")
//Send message
if("Send")

View File

@@ -27,6 +27,10 @@
//The list of directions to block a projectile from
var/list/directional_list = list()
/obj/structure/barricade/Initialize(mapload)
. = ..()
AddComponent(/datum/component/debris, DEBRIS_WOOD, -20, 10)
/obj/structure/barricade/deconstruct(disassembled = TRUE)
if(!(flags & NODECONSTRUCT))
make_debris()

View File

@@ -45,6 +45,9 @@
ME = new /obj/item/mecha_parts/mecha_equipment/thrusters
ME.attach(src)
/obj/mecha/combat/marauder/add_cell()
cell = new /obj/item/stock_parts/cell/bluespace(src)
/obj/mecha/combat/marauder/ares
name = "Ares"
desc = "Heavy-duty, combat exosuit, adapted from rejected early versions of the Marauder to serve as a biohazard containment exosuit. This model, albeit rare, can be found among civilian populations."
@@ -85,9 +88,6 @@
force = 80
max_equip = 8
/obj/mecha/combat/marauder/seraph/add_cell()
cell = new /obj/item/stock_parts/cell/bluespace(src)
/obj/mecha/combat/marauder/seraph/loaded/Initialize(mapload)
. = ..() //Let it equip whatever is needed.
var/obj/item/mecha_parts/mecha_equipment/ME

View File

@@ -148,3 +148,67 @@
/obj/effect/decal/proc/on_scoop()
return
/// These effects can be added to anything to hold particles, which is useful because Byond only allows a single particle per atom
/obj/effect/abstract/particle_holder
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = ABOVE_ALL_MOB_LAYER
vis_flags = VIS_INHERIT_PLANE
invisibility = FALSE
///typepath of the last location we're in, if it's different when moved then we need to update vis contents
var/last_attached_location_type
/// The main item we're attached to at the moment, particle holders hold particles for something
var/atom/movable/parent
/// The mob that is holding our item
var/mob/holding_parent
/obj/effect/abstract/particle_holder/Initialize(mapload, particle_path = null)
. = ..()
if(!loc)
stack_trace("particle holder was created with no loc!")
return INITIALIZE_HINT_QDEL
parent = loc
if(ismovable(parent))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(on_qdel))
particles = new particle_path
update_visual_contents(parent)
/obj/effect/abstract/particle_holder/Destroy(force)
if(parent)
UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
QDEL_NULL(particles)
parent.vis_contents -= src
return ..()
///signal called when parent is moved
/obj/effect/abstract/particle_holder/proc/on_move(atom/movable/attached, atom/oldloc, direction)
SIGNAL_HANDLER
if(parent.loc.type != last_attached_location_type)
update_visual_contents(attached)
///signal called when parent is deleted
/obj/effect/abstract/particle_holder/proc/on_qdel(atom/movable/attached, force)
SIGNAL_HANDLER
attached.vis_contents -= src
qdel(src)//our parent is gone and we need to be as well
///logic proc for particle holders, aka where they move.
///subtypes of particle holders can override this for particles that should always be turf level or do special things when repositioning.
///this base subtype has some logic for items, as the loc of items becomes mobs very often hiding the particles
/obj/effect/abstract/particle_holder/proc/update_visual_contents(atom/movable/attached_to)
// Remove old
if(holding_parent && !(QDELETED(holding_parent)))
holding_parent.vis_contents -= src
// Add new
if(isitem(attached_to) && ismob(attached_to.loc)) //special case we want to also be emitting from the mob
var/mob/particle_mob = attached_to.loc
last_attached_location_type = attached_to.loc
particle_mob.vis_contents += src
// Readd to ourselves
attached_to.vis_contents |= src

View File

@@ -235,6 +235,14 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
/obj/effect/landmark/spawner/nuke_code
name = "nukecode"
/obj/effect/landmark/spawner/roundstart_observer
name = "Roundstart Observer"
icon_state = "spooky"
/obj/effect/landmark/spawner/roundstart_observer/Initialize(mapload)
spawner_list = GLOB.roundstart_observer_start
return ..()
/obj/effect/landmark/Destroy()
GLOB.landmarks_list -= src
..()
@@ -414,7 +422,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
name = "Warden"
icon_state = "Warden"
/obj/effect/landmark/start/set_tag()
tag = "start*[name]"

View File

@@ -149,7 +149,8 @@
/obj/item/clothing/under/chameleon = 2,
/obj/item/stamp/chameleon = 2,
/obj/item/clothing/shoes/chameleon/noslip = 5,
/obj/item/clothing/mask/chameleon = 2,
/obj/item/clothing/mask/chameleon = 4,
/obj/item/clothing/mask/chameleon/voice_change = 2,
/obj/item/clothing/mask/gas/voice_modulator = 2,
/obj/item/clothing/mask/gas/voice_modulator/chameleon = 2,
/obj/item/dnascrambler = 1,

View File

@@ -10,7 +10,6 @@
* Toy Nuke
* Card Deck
* Therapy dolls
* Toddler doll
* Inflatable duck
* Foam armblade
* Mini Gibber
@@ -438,14 +437,6 @@
item_state = "egg3" // It's the green egg in items_left/righthand
item_color = "green"
/obj/item/toddler
icon_state = "toddler"
name = "toddler"
desc = "This baby looks almost real. Wait, did it just burp?"
force = 5
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_FLAG_BACK
//This should really be somewhere else but I don't know where. w/e

View File

@@ -1,8 +1,8 @@
/obj/item/stack/spacecash
name = "1 Credit"
desc = "Money money money."
icon = 'icons/goonstation/objects/money.dmi'
icon_state = "cashgreen"
icon = 'icons/obj/money.dmi'
icon_state = "cash"
hitsound = "swing_hit"
force = 1
throwforce = 1
@@ -38,18 +38,18 @@
/obj/item/stack/spacecash/update_icon_state()
name = "[amount] Credit[amount > 1 ? "s" : ""]"
if(amount >= 1 && amount < 10)
icon_state = "cashgreen"
else if(amount >= 10 && amount < 50)
icon_state = "spacecash"
else if(amount >= 50 && amount < 500)
icon_state = "cashblue"
if(amount >= 1 && amount < 50)
icon_state = "cash"
else if(amount >= 50 && amount < 100)
icon_state = "cash2"
else if(amount >= 100 && amount < 500)
icon_state = "cash3"
else if(amount >= 500 && amount < 1000)
icon_state = "cashindi"
else if(amount >= 1000 && amount <= 10000)
icon_state = "cashpurp"
icon_state = "cash4"
else if(amount >= 1000 && amount < 10000)
icon_state = "cash5"
else
icon_state = "cashrbow"
icon_state = "cash6"
/obj/item/stack/spacecash/c5
amount = 5

View File

@@ -67,7 +67,7 @@
if(r)
r.droplimb(0, DROPLIMB_SHARP)
/obj/item/stack/cyborg_mine //Malf module
/obj/item/stack/caution/proximity_sign/malf //Malf module
name = "proximity mine dispenser"
icon = 'icons/obj/janitor.dmi'
icon_state = "caution"

View File

@@ -63,25 +63,25 @@
det_time = newtime
to_chat(user, "Timer set for [det_time] seconds.")
/obj/item/grenade/plastic/afterattack(atom/movable/AM, mob/user, flag)
/obj/item/grenade/plastic/afterattack(mob/AM, mob/user, flag)
if(!flag)
return
if(iscarbon(AM))
to_chat(user, "<span class='warning'>You can't get the [src] to stick to [AM]!</span>")
if(ismob(AM) && AM.stat == CONSCIOUS)
to_chat(user, "<span class='warning'>You can't get the [src] to stick to [AM]! Perhaps if [AM] was asleep or dead you could attach it?</span>")
return
if(isobserver(AM))
to_chat(user, "<span class='warning'>Your hand just phases through [AM]!</span>")
return
to_chat(user, "<span class='notice'>You start planting [src].[isnull(nadeassembly) ? " The timer is set to [det_time]..." : ""]</span>")
if(do_after(user, 50 * toolspeed, target = AM))
if(do_after(user, 5 SECONDS * toolspeed, target = AM))
if(!user.unEquip(src))
return
target = AM
loc = null
if(notify_admins)
message_admins("[ADMIN_LOOKUPFLW(user)] planted [src.name] on [target.name] at ([target.x],[target.y],[target.z] - <a href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[target.x];Y=[target.y];Z=[target.z]'>JMP</a>) with [det_time] second fuse", 0, 1)
message_admins("[ADMIN_LOOKUPFLW(user)] planted [name] on [target.name] at ([target.x],[target.y],[target.z] - <a href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[target.x];Y=[target.y];Z=[target.z]'>JMP</a>) with [det_time] second fuse", 0, 1)
log_game("[key_name(user)] planted [name] on [target.name] at ([target.x],[target.y],[target.z]) with [det_time] second fuse")
AddComponent(/datum/component/persistent_overlay, image_overlay, target)

View File

@@ -112,7 +112,7 @@
/obj/item/storage/backpack/clown/syndie/populate_contents()
new /obj/item/clothing/under/rank/civilian/clown(src)
new /obj/item/clothing/shoes/magboots/clown(src)
new /obj/item/clothing/mask/chameleon(src)
new /obj/item/clothing/mask/chameleon/voice_change(src)
new /obj/item/radio/headset/headset_service(src)
new /obj/item/pda/clown(src)
new /obj/item/storage/box/survival(src)

View File

@@ -39,7 +39,7 @@
storage_slots = 30
max_combined_w_class = 30
can_hold = list() // any
cant_hold = list(/obj/item/disk/nuclear)
cant_hold = list(/obj/item/disk/nuclear, /obj/item/grown/bananapeel/traitorpeel)
/obj/item/storage/bag/trash/proc/update_weight()
if(!length(contents))
@@ -213,6 +213,7 @@
/obj/item/grown,
/obj/item/food/snacks/grown/ash_flora,
/obj/item/food/snacks/honeycomb)
cant_hold = list(/obj/item/grown/bananapeel/traitorpeel)
resistance_flags = FLAMMABLE
/obj/item/storage/bag/plants/portaseeder

View File

@@ -144,7 +144,7 @@
/obj/item/clothing/gloves/combat, // accounted in belt + toolbox
/obj/item/flashlight/emp) // 4TC
/// 168TC + Telekinesis
/// 170TC + Telekinesis
/obj/item/storage/box/syndie_kit/bundle/darklord
name = "Dark Lord Bundle"
desc = "Turn your anger into hate and your hate into suffering with a mix of energy swords and magical powers. DO IT."
@@ -155,7 +155,7 @@
/obj/item/dnainjector/telemut/darkbundle, // ?TC
/obj/item/clothing/suit/hooded/chaplain_hoodie, // 0TC
/obj/item/clothing/glasses/meson/engine/tray, // 0TC
/obj/item/clothing/mask/chameleon, // 8TC
/obj/item/clothing/mask/chameleon/voice_change, // 10TC
/obj/item/card/id/syndicate, // 10TC
/obj/item/clothing/shoes/chameleon/noslip, // 10TC
/obj/item/encryptionkey/syndicate) // 10TC
@@ -403,11 +403,26 @@
new /obj/item/clothing/shoes/chameleon(src)
new /obj/item/clothing/glasses/chameleon(src)
new /obj/item/clothing/head/chameleon(src)
new /obj/item/clothing/mask/chameleon(src)
new /obj/item/storage/backpack/chameleon(src)
new /obj/item/radio/headset/chameleon(src)
new /obj/item/stamp/chameleon(src)
new /obj/item/pda/chameleon(src)
new /obj/item/clothing/mask/chameleon(src)
/obj/item/storage/box/syndie_kit/chameleon/nuke
name = "operative's chameleon kit"
/obj/item/storage/box/syndie_kit/chameleon/nuke/populate_contents()
new /obj/item/clothing/under/chameleon(src)
new /obj/item/clothing/suit/chameleon(src)
new /obj/item/clothing/gloves/chameleon(src)
new /obj/item/clothing/shoes/chameleon(src)
new /obj/item/clothing/glasses/chameleon(src)
new /obj/item/clothing/head/chameleon(src)
new /obj/item/storage/backpack/chameleon(src)
new /obj/item/radio/headset/chameleon(src)
new /obj/item/pda/chameleon(src)
new /obj/item/stamp/chameleon(src)
new /obj/item/clothing/mask/chameleon/voice_change(src)
/obj/item/storage/box/syndie_kit/dart_gun
name = "dart gun kit"

View File

@@ -14,6 +14,10 @@
var/metalUsed = 2 //used to determine amount returned in deconstruction
var/metal_type = /obj/item/stack/sheet/metal
/obj/structure/girder/Initialize(mapload)
. = ..()
AddComponent(/datum/component/debris, DEBRIS_SPARKS, -20, 10)
/obj/structure/girder/examine(mob/user)
. = ..()
switch(state)

View File

@@ -27,6 +27,7 @@
. = ..()
initial_state = icon_state
air_update_turf(1)
AddComponent(/datum/component/debris, DEBRIS_SPARKS, -20, 10)
/obj/structure/mineral_door/Destroy()
density = FALSE
@@ -211,3 +212,7 @@
resistance_flags = FLAMMABLE
max_integrity = 200
rad_insulation = RAD_VERY_LIGHT_INSULATION
/obj/structure/mineral_door/wood/Initialize()
. = ..()
AddComponent(/datum/component/debris, DEBRIS_WOOD, -20, 10)

View File

@@ -42,6 +42,7 @@
var/turf/T = get_step(src, dir)
if(istype(T, /turf/simulated/mineral/random))
Spread(T)
AddComponent(/datum/component/debris, DEBRIS_ROCK, -20, 10, 1)
/turf/simulated/mineral/proc/Spread(turf/T)
T.ChangeTurf(type)

View File

@@ -61,6 +61,7 @@
underlay_appearance.icon_state = fixed_underlay["icon_state"]
fixed_underlay = string_assoc_list(fixed_underlay)
underlays += underlay_appearance
AddComponent(/datum/component/debris, DEBRIS_SPARKS, -20, 10, 1)
/turf/simulated/wall/BeforeChange()
for(var/obj/effect/overlay/wall_rot/WR in src)

View File

@@ -2,10 +2,10 @@
name = "mineral wall"
desc = "This shouldn't exist"
icon_state = ""
var/last_event = 0
var/active = FALSE
smoothing_flags = SMOOTH_BITMASK
canSmoothWith = null
var/last_event = 0
var/active = FALSE
/turf/simulated/wall/mineral/shuttleRotate(rotation)
return //This override is needed to properly rotate the object when on a shuttle that is rotated.

View File

@@ -104,12 +104,8 @@
if(handle_spam_prevention(msg, MUTE_ADMINHELP, OOC_COOLDOWN))
return
//clean the message if it's not sent by a high-rank admin
if(!check_rights(R_SERVER|R_DEBUG,0))
msg = sanitize_simple(copytext_char(msg, 1, MAX_MESSAGE_LEN))
if(!msg)
return
else
// Let high-rank admins use advanced pencode.
if(check_rights(R_SERVER|R_DEBUG, 0))
msg = admin_pencode_to_html(msg)
var/send_span

View File

@@ -51,6 +51,7 @@
venom_per_bite = 3
speak_chance = 0
wander = 0
gold_core_spawnable = NO_SPAWN
/// To check and gib the spider when dead, then remove only one of the counter for the changeling owner
var/gibbed = FALSE

View File

@@ -54,7 +54,7 @@
new /obj/item/clothing/under/chameleon(src)
new /obj/item/clothing/suit/chameleon(src)
new /obj/item/clothing/head/chameleon(src)
new /obj/item/clothing/mask/chameleon(src)
new /obj/item/clothing/mask/chameleon/voice_change(src)
new /obj/item/card/id/syndicate(src)
new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
new /obj/item/lighter/zippo(src)

View File

@@ -228,7 +228,7 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
return
if(bloodusable >= 10) //burn through your blood to tank the light for a little while
to_chat(owner.current, "<span class='warning'>The starlight saps your strength!</span>")
bloodusable -= 10
subtract_usable_blood(10)
vamp_burn(10)
else //You're in trouble, get out of the sun NOW
to_chat(owner.current, "<span class='userdanger'>Your body is turning to ash, get out of the light now!</span>")
@@ -278,6 +278,13 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
REMOVE_TRAIT(owner.current, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
owner.current.alpha = 204 // 255 * 0.80
/**
* Handles unique drain ID checks and increases vampire's total and usable blood by blood_amount. Checks for ability upgrades.
*
* Arguments:
** C: victim [/mob/living/carbon] that is being drained form.
** blood_amount: amount of blood to add to vampire's usable and total pools.
*/
/datum/antagonist/vampire/proc/adjust_blood(mob/living/carbon/C, blood_amount = 0)
if(C)
var/unique_suck_id = C.UID()
@@ -293,6 +300,15 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
if(S.action)
S.action.UpdateButtons()
/**
* Safely subtract vampire's bloodusable. Clamped between 0 and bloodtotal.
*
* Arguments:
** blood_amount: amount of blood to subtract.
*/
/datum/antagonist/vampire/proc/subtract_usable_blood(blood_amount)
bloodusable = clamp(bloodusable - blood_amount, 0, bloodtotal)
/datum/antagonist/vampire/proc/vamp_burn(burn_chance)
if(prob(burn_chance) && owner.current.health >= 50)
switch(owner.current.health)

View File

@@ -33,7 +33,7 @@
handle_enthrall(user, target)
var/datum/spell_handler/vampire/V = custom_handler
var/blood_cost = V.calculate_blood_cost(vampire)
vampire.bloodusable -= blood_cost //we take the blood after enthralling, not before
vampire.subtract_usable_blood(blood_cost) //we take the blood after enthralling, not before
else
revert_cast(user)
to_chat(user, "<span class='warning'>You or your target moved.</span>")

View File

@@ -199,7 +199,7 @@
var/datum/spell_handler/vampire/V = custom_handler
var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
var/blood_cost = V.calculate_blood_cost(vampire)
vampire.bloodusable -= blood_cost
vampire.subtract_usable_blood(blood_cost)
start_turf = null
should_recharge_after_cast = FALSE

View File

@@ -181,7 +181,7 @@
var/datum/spell_handler/vampire/V = custom_handler
var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
var/blood_cost = V.calculate_blood_cost(vampire)
vampire.bloodusable = clamp(vampire.bloodusable - blood_cost, 0, vampire.bloodusable)// Vampires get a coupon if they have less than the normal blood cost
vampire.subtract_usable_blood(blood_cost)// Vampires get a coupon if they have less than the normal blood cost
/proc/shadow_to_animation(turf/start_turf, turf/end_turf, mob/user)
var/x_difference = end_turf.x - start_turf.x

View File

@@ -29,7 +29,7 @@
if(listening)
if(findtext(msg, "</span>"))
recorded = strip_html_properly(msg)
recorded = strip_html_tags(msg)
else
recorded = msg
recorded_type = type

View File

@@ -1230,6 +1230,18 @@
if("Set-Tab")
stat_tab = payload["tab"]
SSstatpanels.immediate_send_stat_data(src)
if("Listedturf-Scroll")
if(payload["min"] == payload["max"])
// Not properly loaded yet, send the default set.
SSstatpanels.refresh_client_obj_view(src)
else
SSstatpanels.refresh_client_obj_view(src, payload["min"], payload["max"])
// Uncomment to enable log_debug in stat panel code.
// Disabled normally due to HREF exploit concerns.
//if("Statpanel-Debug")
// log_debug(payload)
if("Resend-Asset")
SSassets.transport.send_assets(src, list(payload))
if("Debug-Stat-Entry")
var/stat_item = locateUID(payload["stat_item_uid"])
if(!check_rights(R_DEBUG | R_VIEWRUNTIMES) || !stat_item)

View File

@@ -301,7 +301,7 @@
disable_message = "You will no longer see runechat."
blackbox_message = "Toggle Runechat"
/datum/preference_toggle/toggle_runechat
/datum/preference_toggle/toggle_ghost_death_notifs
name = "Toggle Ghost Death Notifications"
description = "Toggle a notification when a player dies"
preftoggle_bitflag = PREFTOGGLE_2_DEATHMESSAGE

View File

@@ -469,8 +469,6 @@
"Grey" = 'icons/mob/clothing/species/grey/mask.dmi'
)
var/obj/item/voice_changer/voice_changer
var/datum/action/item_action/chameleon/change/chameleon_action
/obj/item/clothing/mask/chameleon/Initialize(mapload)
@@ -482,10 +480,7 @@
chameleon_action.chameleon_blacklist = list()
chameleon_action.initialize_disguises()
voice_changer = new(src)
/obj/item/clothing/mask/chameleon/Destroy()
QDEL_NULL(voice_changer)
QDEL_NULL(chameleon_action)
return ..()
@@ -497,6 +492,23 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/mask/chameleon/voice_change
name = "gas mask"
desc = "A face-covering mask that can be connected to an air supply. While good for concealing your identity, it isn't good for blocking gas flow."
icon_state = "swat"
item_state = "swat"
var/obj/item/voice_changer/voice_changer
/obj/item/clothing/mask/chameleon/voice_change/Destroy()
QDEL_NULL(voice_changer)
return ..()
/obj/item/clothing/mask/chameleon/voice_change/Initialize(mapload)
. = ..()
voice_changer = new(src)
/obj/item/clothing/shoes/chameleon
name = "black shoes"
icon_state = "black"

View File

@@ -124,12 +124,6 @@
cost = 500
category = MERCH_CAT_TOY
/datum/merch_item/baby
name = "Toddler"
desc = "This baby looks almost real. Wait, did it just burp?"
typepath = /obj/item/toddler
cost = 500
category = MERCH_CAT_TOY
/datum/merch_item/flag_slime
name = "Slime People Flag"

View File

@@ -38,6 +38,8 @@
/obj/structure/blob/core/Destroy()
if(overmind)
if(overmind.mind)
SSticker.mode.blob_overminds -= overmind.mind
overmind.blob_core = null
overmind = null
STOP_PROCESSING(SSobj, src)

View File

@@ -116,7 +116,7 @@
status_tab_data[++status_tab_data.len] = list("Power Stored:", "[blob_points]/[max_blob_points]")
/mob/camera/blob/Move(NewLoc, Dir = 0)
var/obj/structure/blob/B = locate() in range("3x3", NewLoc)
var/obj/structure/blob/B = locate() in range(3, NewLoc)
if(B)
loc = NewLoc
else

View File

@@ -141,7 +141,7 @@ GLOBAL_LIST_EMPTY(event_last_fired)
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Mundane News", /datum/event/mundane_news, 300),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Vermin Infestation",/datum/event/infestation, 100, list(ASSIGNMENT_JANITOR = 100)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Sentience", /datum/event/sentience, 50),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Wallrot", /datum/event/wallrot, 0, list(ASSIGNMENT_ENGINEER = 30, ASSIGNMENT_GARDENER = 50)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Wallrot", /datum/event/wallrot, 0, list(ASSIGNMENT_ENGINEER = 30)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Fungal Growth", /datum/event/wallrot/fungus, 50, list(ASSIGNMENT_CHEMIST = 50)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Koi School", /datum/event/carp_migration/koi, 80),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Camera Failure", /datum/event/camera_failure, 100, list(ASSIGNMENT_ENGINEER = 10)),

View File

@@ -64,6 +64,7 @@
active_with_role["Cyborg"] = 0
active_with_role["Janitor"] = 0
active_with_role["Botanist"] = 0
active_with_role["Chemist"] = 0
active_with_role["Any"] = length(GLOB.player_list)
for(var/mob/M in GLOB.player_list)
@@ -105,6 +106,9 @@
if(M.mind.assigned_role == "Botanist")
active_with_role["Botanist"]++
if(M.mind.assigned_role == "Chemist")
active_with_role["Chemist"]++
return active_with_role
/datum/event/proc/num_players()

View File

@@ -27,9 +27,9 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol"))
/datum/traders/nian)
if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been denied docking permission due to the heightened security alert aboard [station_name()].", "Trader Shuttle Docking Request Refused")
GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been denied docking permission due to the heightened security alert aboard [station_name()].", "Trader Shuttle Docking Request Refused", 'sound/AI/traderdeny.ogg')
return
GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been granted docking permission at [station_name()] arrivals port 4.", "Trader Shuttle Docking Request Accepted")
GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been granted docking permission at [station_name()] arrivals port 4.", "Trader Shuttle Docking Request Accepted", 'sound/AI/tradergranted.ogg')
/datum/event/traders/start()
@@ -48,7 +48,7 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol"))
/datum/traders/nian)
if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been denied docking permission due to the heightened security alert aboard [station_name()].", "Trader Shuttle Docking Request Refused")
GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been denied docking permission due to the heightened security alert aboard [station_name()].", "Trader Shuttle Docking Request Refused", 'sound/AI/traderdeny.ogg')
// if the docking request was refused, fire another major event in 60 seconds
var/datum/event_container/EC = SSevents.event_containers[EVENT_LEVEL_MAJOR]
EC.next_event_time = world.time + (60 * 10)
@@ -102,7 +102,7 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol"))
greet_trader(M, T)
success_spawn = TRUE
if(success_spawn)
GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been granted docking permission at [station_name()] arrivals port 4.", "Trader Shuttle Docking Request Accepted")
GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been granted docking permission at [station_name()] arrivals port 4.", "Trader Shuttle Docking Request Accepted", 'sound/AI/tradergranted.ogg')
else
GLOB.unused_trade_stations += station // Return the station to the list of usable stations.

View File

@@ -135,35 +135,43 @@
// Middle Eastern //
//////////////////////
/obj/item/food/snacks/human/kabob
name = "-kabob"
icon_state = "kabob"
desc = "Human meat, on a stick."
trash = /obj/item/stack/rods
filling_color = "#A85340"
list_reagents = list("nutriment" = 8)
/obj/item/food/snacks/monkeykabob
name = "meat-kabob"
icon_state = "kabob"
/obj/item/food/snacks/meatkebab
name = "meat-kebab"
desc = "Delicious meat, on a stick."
icon_state = "kebab"
trash = /obj/item/stack/rods
filling_color = "#A85340"
list_reagents = list("nutriment" = 8)
goal_difficulty = FOOD_GOAL_NORMAL
/obj/item/food/snacks/tofukabob
name = "tofu-kabob"
icon_state = "kabob"
/obj/item/food/snacks/syntikebab
name = "synthimeat-kebab"
desc = "Delicious synthetic meat, on a stick."
icon_state = "kebab"
trash = /obj/item/stack/rods
filling_color = "#A85340"
list_reagents = list("nutriment" = 8)
/obj/item/food/snacks/human/kebab
name = "-kebab"
desc = "Human meat, on a stick."
icon_state = "kebab"
trash = /obj/item/stack/rods
filling_color = "#A85340"
list_reagents = list("nutriment" = 8)
/obj/item/food/snacks/tofukebab
name = "tofu-kebab"
desc = "Vegan meat, on a stick."
icon_state = "kebab"
trash = /obj/item/stack/rods
filling_color = "#FFFEE0"
list_reagents = list("nutriment" = 8)
goal_difficulty = FOOD_GOAL_NORMAL
/obj/item/food/snacks/picoss_kabob
name = "picoss-kabob"
desc = "A popular Moghes streetfood consisting of vinegar-marinated space carp on a skewer with onion and chillis."
/obj/item/food/snacks/picoss_kebab
name = "picoss-kebab"
desc = "A popular Moghes street food consisting of vinegar-marinated space carp on a skewer with onion and chillis."
icon_state = "picoss_skewer"
trash = /obj/item/stack/rods
list_reagents = list("protein" = 8, "vitamin" = 4, "vinegar" = 1, "capsaicin" = 1)

View File

@@ -69,7 +69,7 @@
reagents = list("psilocybin" = 5, "sugar" = 10)
items = list(
/obj/item/food/snacks/dough,
/obj/item/food/snacks/dough,
/obj/item/food/snacks/dough
)
result = /obj/item/food/snacks/rofflewaffles
@@ -77,14 +77,14 @@
items = list(
/obj/item/food/snacks/breadslice,
/obj/item/food/snacks/breadslice,
/obj/item/food/snacks/cheesewedge,
/obj/item/food/snacks/cheesewedge
)
result = /obj/item/food/snacks/grilledcheese
/datum/recipe/grill/sausage
items = list(
/obj/item/food/snacks/meatball,
/obj/item/food/snacks/cutlet,
/obj/item/food/snacks/cutlet
)
result = /obj/item/food/snacks/sausage
@@ -92,7 +92,7 @@
reagents = list("flour" = 10)
items = list(
/obj/item/food/snacks/egg,
/obj/item/food/snacks/carpmeat,
/obj/item/food/snacks/carpmeat
)
result = /obj/item/food/snacks/fishfingers
@@ -113,7 +113,7 @@
/obj/item/food/snacks/egg,
/obj/item/food/snacks/egg,
/obj/item/food/snacks/cheesewedge,
/obj/item/food/snacks/cheesewedge,
/obj/item/food/snacks/cheesewedge
)
result = /obj/item/food/snacks/omelette
@@ -124,39 +124,39 @@
)
result = /obj/item/food/snacks/wingfangchu
/datum/recipe/grill/human/kabob
/datum/recipe/grill/human/kebab
items = list(
/obj/item/stack/rods,
/obj/item/food/snacks/meat/human,
/obj/item/food/snacks/meat/human,
/obj/item/food/snacks/meat/human
)
result = /obj/item/food/snacks/human/kabob
result = /obj/item/food/snacks/human/kebab
/datum/recipe/grill/monkeykabob
items = list(
/obj/item/stack/rods,
/obj/item/food/snacks/meat/monkey,
/obj/item/food/snacks/meat/monkey,
)
result = /obj/item/food/snacks/monkeykabob
/datum/recipe/grill/syntikabob
/datum/recipe/grill/syntikebab
items = list(
/obj/item/stack/rods,
/obj/item/food/snacks/meat/syntiflesh,
/obj/item/food/snacks/meat/syntiflesh,
/obj/item/food/snacks/meat/syntiflesh
)
result = /obj/item/food/snacks/monkeykabob
result = /obj/item/food/snacks/syntikebab
/datum/recipe/grill/tofukabob
/datum/recipe/grill/meatkeb // Do not put this above the other meat kebab variants or it will override them.
items = list(
/obj/item/stack/rods,
/obj/item/food/snacks/meat,
/obj/item/food/snacks/meat
)
result = /obj/item/food/snacks/meatkebab
/datum/recipe/grill/tofukebab
items = list(
/obj/item/stack/rods,
/obj/item/food/snacks/tofu,
/obj/item/food/snacks/tofu,
/obj/item/food/snacks/tofu
)
result = /obj/item/food/snacks/tofukabob
result = /obj/item/food/snacks/tofukebab
/datum/recipe/grill/picoss_kabob
/datum/recipe/grill/picoss_kebab
reagents = list("vinegar" = 5)
items = list(
/obj/item/food/snacks/carpmeat,
@@ -165,10 +165,10 @@
/obj/item/food/snacks/grown/chili,
/obj/item/stack/rods
)
result = /obj/item/food/snacks/picoss_kabob
result = /obj/item/food/snacks/picoss_kebab
/datum/recipe/grill/picoss_kabob/make_food(obj/container)
var/obj/item/food/snacks/picoss_kabob/being_cooked = ..()
/datum/recipe/grill/picoss_kebab/make_food(obj/container)
var/obj/item/food/snacks/picoss_kebab/being_cooked = ..()
being_cooked.reagents.del_reagent("carpotoxin")
return being_cooked
@@ -272,7 +272,7 @@
/obj/item/food/snacks/shrimp,
/obj/item/food/snacks/shrimp,
/obj/item/food/snacks/shrimp,
/obj/item/stack/rods,
/obj/item/stack/rods
)
result = /obj/item/food/snacks/shrimp_skewer
@@ -281,7 +281,7 @@
items = list(
/obj/item/food/snacks/salmonmeat,
/obj/item/food/snacks/salmonmeat,
/obj/item/stack/rods,
/obj/item/stack/rods
)
result = /obj/item/food/snacks/fish_skewer

View File

@@ -13,6 +13,7 @@
/obj/structure/fermenting_barrel/Initialize()
create_reagents(300) //Bluespace beakers, but without the portability or efficiency in circuits.
AddComponent(/datum/component/debris, DEBRIS_WOOD, -20, 10)
. = ..()
/obj/structure/fermenting_barrel/examine(mob/user)

View File

@@ -205,9 +205,7 @@
add_attack_logs(user, target, "[what_done] ([reagent_str] | [genes_str])")
/obj/item/food/snacks/grown/extinguish_light(force = FALSE)
if(!force)
return
/obj/item/food/snacks/grown/extinguish_light(force)
if(seed.get_gene(/datum/plant_gene/trait/glow/shadow))
return
set_light(0)

View File

@@ -315,6 +315,11 @@
update_icon()
/obj/machinery/hydroponics/extinguish_light(force)
if(!force)
return
set_light(0)
/obj/machinery/hydroponics/update_overlays()
. = ..()
if(self_sustaining && !istype(src, /obj/machinery/hydroponics/soil))

View File

@@ -85,8 +85,6 @@
desc = "Hostile Environment Cross-Kinetic Helmet: A helmet designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner."
icon_state = "hostile_env"
item_state = "hostile_env"
body_parts_covered = HEAD
flags = BLOCKHAIR
w_class = WEIGHT_CLASS_NORMAL
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
heat_protection = HEAD

View File

@@ -167,6 +167,10 @@
harvest_message_high = "You finish mining the rock."
delete_on_harvest = TRUE
/obj/structure/flora/ash/rock/Initialize(mapload)
. = ..()
AddComponent(/datum/component/debris, DEBRIS_ROCK, -20, 10)
/obj/structure/flora/ash/rock/style_2
icon_state = "basalt2"

View File

@@ -174,16 +174,16 @@
/mob/proc/hear_sleep(message)
var/heard = ""
if(prob(15))
message = strip_html_properly(message)
message = html_decode(strip_html_tags(message))
var/list/punctuation = list(",", "!", ".", ";", "?")
var/list/messages = splittext(message, " ")
if(length(messages) > 0)
var/R = rand(1, length(messages))
var/heardword = messages[R]
if(copytext(heardword,1, 1) in punctuation)
heardword = copytext(heardword,2)
heardword = html_encode(copytext(heardword, 2))
if(copytext(heardword,-1) in punctuation)
heardword = copytext(heardword,1,length(heardword))
heardword = html_encode(copytext(heardword, 1, length(heardword)))
heard = "<span class='game say'>...<i>You hear something about<i>... '[heardword]'...</span>"
else
heard = "<span class='game say'>...<i>You almost hear something...</i>...</span>"

View File

@@ -311,7 +311,7 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key)
return name
/mob/living/whisper(message as text)
message = trim_strip_html_properly(message)
message = trim_strip_html_tags(message)
//parse the language code and consume it
var/list/message_pieces = parse_languages(message)

View File

@@ -517,7 +517,7 @@
)
emag_override_modules = list(/obj/item/reagent_containers/spray/cyborg_lube)
emag_modules = list(/obj/item/reagent_containers/spray/cyborg_facid, /obj/item/malfbroom)
malf_modules = list(/obj/item/stack/cyborg_mine)
malf_modules = list(/obj/item/stack/caution/proximity_sign/malf)
special_rechargables = list(
/obj/item/lightreplacer,
/obj/item/reagent_containers/spray/cyborg_lube,

View File

@@ -96,7 +96,7 @@
light_color = LIGHT_COLOR_LAVA
projectiletype = /obj/item/projectile/temp/basilisk/magmawing
crusher_loot = /obj/item/crusher_trophy/blaster_tubes/magma_wing
crusher_drop_mod = 60
crusher_drop_mod = 100 //These things are extremely rare (1/133 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/icewing
name = "icewing watcher"
@@ -110,7 +110,7 @@
projectiletype = /obj/item/projectile/temp/basilisk/icewing
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/bone = 1) //No sinew; the wings are too fragile to be usable
crusher_loot = /obj/item/crusher_trophy/watcher_wing/ice_wing
crusher_drop_mod = 30
crusher_drop_mod = 100 //These things are extremely rare (1/400 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
/obj/item/projectile/temp/basilisk/magmawing
name = "scorching blast"

View File

@@ -112,7 +112,7 @@
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide) //A throwback to the asteroid days
butcher_results = list(/obj/item/food/snacks/monstermeat/goliath= 2, /obj/item/stack/sheet/bone = 2)
crusher_loot = /obj/item/crusher_trophy/goliath_tentacle/ancient
crusher_drop_mod = 30
crusher_drop_mod = 100 //These things are rare (1/100 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
wander = FALSE
var/list/cached_tentacle_turfs
var/turf/last_location

View File

@@ -185,9 +185,14 @@
to_chat(src, "<span class='notice'>As you observed [period_human_readable], you can freely toggle antag-hud without losing respawnability.</span>")
observer.started_as_observer = 1
close_spawn_windows()
var/obj/O = locate("landmark*Observer-Start")
var/obj/spawn_point
if(SSticker.current_state < GAME_STATE_PLAYING)
spawn_point = pick(GLOB.roundstart_observer_start)
else
spawn_point = locate("landmark*Observer-Start")
to_chat(src, "<span class='notice'>Now teleporting.</span>")
observer.forceMove(O.loc)
observer.forceMove(get_turf(spawn_point))
observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly.
client.prefs.active_character.update_preview_icon(1)
observer.icon = client.prefs.active_character.preview_icon

View File

@@ -200,7 +200,7 @@
var/obj/item/paper/crumpled/P = new(loc)
P.name = name
if(info) // Something written on the paper.
/*var/new_text = strip_html_properly(info, MAX_PAPER_MESSAGE_LEN, TRUE) // Don't want HTML stuff getting gibberished.
/*var/new_text = strip_html_tags(info, MAX_PAPER_MESSAGE_LEN, TRUE) // Don't want HTML stuff getting gibberished.
P.info = Gibberish(new_text, 100)*/
P.info = "<i>Whatever was once written here has been made completely illegible by a combination of chew marks and saliva.</i>"
message_ending = ", the drool making it an unreadable mess!"

View File

@@ -112,7 +112,7 @@
//WT550//
/obj/item/gun/projectile/automatic/wt550
name = "WT-550 PDW"
name = "\improper WT-550 PDW"
desc = "An outdated personal defense weapon utilized by law enforcement. Chambered in 4.6x30mm."
icon_state = "wt550"
item_state = "wt550"

View File

@@ -1020,7 +1020,10 @@
if(ishuman(M) && prob(5))
var/mob/living/carbon/human/H = M
if(!H.get_int_organ(/obj/item/organ/internal/bone_tumor))
new/obj/item/organ/internal/bone_tumor(H)
if(isslimeperson(H))
new /obj/item/organ/internal/bone_tumor/slime_tumor(H)
else
new /obj/item/organ/internal/bone_tumor(H)
return ..()

View File

@@ -1,3 +1,5 @@
GLOBAL_LIST_EMPTY(occupants_by_key)
/obj/effect/mob_spawn/human/alive/ghost_bar
name = "ghastly rejuvenator"
mob_name = "ghost bar occupant"
@@ -83,6 +85,11 @@
implant.insert(H)
log_game("[ckey] has entered the ghost bar")
playsound(src, 'sound/machines/wooden_closet_open.ogg', 50)
var/mob/old_mob = GLOB.occupants_by_key["[H.ckey]"]
if(old_mob)
qdel(old_mob)
GLOB.occupants_by_key["[H.ckey]"] = H
RegisterSignal(H, COMSIG_PARENT_QDELETING, PROC_REF(clear_references_to_owner))
/obj/effect/mob_spawn/human/alive/ghost_bar/proc/equip_item(mob/living/carbon/human/H, path, slot)
var/obj/item/I = new path(H)
@@ -90,6 +97,10 @@
H.speaks_ooc = TRUE
return I
/obj/effect/mob_spawn/human/alive/ghost_bar/proc/clear_references_to_owner(mob/mob_to_obliterate)
SIGNAL_HANDLER // COMSIG_PARENT_QDELETING
GLOB.occupants_by_key -= mob_to_obliterate.ckey
/obj/structure/ghost_bar_cryopod
name = "returning sarcophagus"
desc = "Returns you back to the world of the dead."

View File

@@ -177,11 +177,11 @@
return CARGO_PREVENT_SHUTTLE
var/sellable = SEND_SIGNAL(src, COMSIG_CARGO_CHECK_SELL, AM)
manifest.items_to_sell[AM] = sellable
if(top_level && !(sellable & COMSIG_CARGO_IS_SECURED))
manifest.loose_cargo = TRUE
if(sellable & COMSIG_CARGO_SELL_PRIORITY)
if(top_level)
return CARGO_OK
if(top_level)
if(!AM.anchored && !(sellable & COMSIG_CARGO_IS_SECURED))
manifest.loose_cargo = TRUE
return CARGO_OK
if(found_priority || sellable & COMSIG_CARGO_SELL_PRIORITY)
return CARGO_HAS_PRIORITY
return CARGO_OK
@@ -220,9 +220,6 @@
if(E.is_cleanable())
return CARGO_OK
if(AM.anchored && !istype(AM, /obj/mecha/working))
return CARGO_SKIP_ATOM
return CARGO_OK
@@ -253,7 +250,7 @@
SSeconomy.sold_atoms += "[AM.name](mess)"
qdel_atoms += AM
continue
else if(!(sellable & COMSIG_CARGO_SELL_SKIP))
else if(!AM.anchored && !(sellable & COMSIG_CARGO_SELL_SKIP))
manifest.sent_trash = TRUE
rescue_atoms += AM
@@ -784,7 +781,7 @@
/datum/economy/simple_seller/mechs/check_sell(obj/docking_port/mobile/supply/S, atom/movable/AM)
if(istype(AM, /obj/mecha/working))
return COMSIG_CARGO_SELL_NORMAL | COMSIG_CARGO_IS_SECURED
return COMSIG_CARGO_SELL_NORMAL
// Skip mech parts to avoid complaining about them.

View File

@@ -67,16 +67,11 @@
return copy
/datum/secondary_goal_progress/random_bulk_reagent/update(atom/movable/AM, datum/economy/cargo_shuttle_manifest/manifest = null)
// Not a reagent container? Ignore.
if(!istype(AM, /obj/item/reagent_containers))
return
// Not in a matching personal crate? Ignore.
if(!check_personal_crate(AM))
return
var/obj/item/reagent_containers/container = AM
var/amount = container.reagents?.get_reagent_amount(initial(reagent_type.id))
var/amount = AM.reagents?.get_reagent_amount(initial(reagent_type.id))
if(!amount)
return
sent += amount

View File

@@ -56,9 +56,8 @@
return copy
/datum/secondary_goal_progress/random_ripley/update(atom/movable/AM, datum/economy/cargo_shuttle_manifest/manifest = null)
var/datum/component/label/goal/label = AM.GetComponent(/datum/component/label/goal)
// Not labelled for this goal? Ignore.
if(!istype(label) || label.label_name != goal_requester)
if(!check_personal_crate(AM))
return
if(!istype(AM, /obj/mecha/working/ripley))
return

View File

@@ -98,12 +98,16 @@
return FALSE
/datum/secondary_goal_progress/proc/check_personal_crate(atom/movable/AM)
// Accept stuff that is properly labelled with a hand labeller.
var/datum/component/label/goal/label = AM.GetComponent(/datum/component/label/goal)
if(istype(label))
return !goal_requester || label.label_name == goal_requester
// Accept stuff in matching personal crates.
var/obj/structure/closet/crate/secure/personal/PC = get_atom_on_turf(AM, /obj/structure/closet/crate/secure/personal)
if(!istype(PC))
return FALSE
if(goal_requester && PC.registered_name != goal_requester)
return FALSE
return TRUE
return !goal_requester || PC.registered_name == goal_requester
/datum/secondary_goal_progress/proc/three_way_reward(datum/economy/cargo_shuttle_manifest/manifest, department, department_account, reward, message)
SSblackbox.record_feedback("nested tally", "secondary goals", 1, list(goal_name, "payments made"))

View File

@@ -45,27 +45,22 @@
return copy
/datum/secondary_goal_progress/variety_reagent/update(atom/movable/AM, datum/economy/cargo_shuttle_manifest/manifest = null)
// Not a reagent container? Ignore.
if(!istype(AM, /obj/item/reagent_containers))
return
// Not in a matching personal crate? Ignore.
if(!check_personal_crate(AM))
return
var/obj/item/reagent_containers/container = AM
// No reagents? Ignore.
if(!container.reagents.reagent_list)
if(!AM.reagents?.reagent_list)
return
var/datum/reagent/reagent = container.reagents?.get_master_reagent()
var/datum/reagent/reagent = AM.reagents.get_master_reagent()
// Make sure it's for our department.
if(!reagent || reagent.goal_department != department)
return
// Isolated reagents only, please.
if(length(container.reagents.reagent_list) != 1)
if(length(AM.reagents.reagent_list) != 1)
if(!manifest)
return COMSIG_CARGO_SELL_WRONG
SSblackbox.record_feedback("nested tally", "secondary goals", 1, list(goal_name, "mixed reagents"))

View File

@@ -27,3 +27,7 @@
for(var/obj/item/organ/internal/I in other_groin_organs)
I.receive_damage(rand(5, 15) + tumor_damage_modifier)
/obj/item/organ/internal/bone_tumor/slime_tumor
name = "crystalized slime jelly"

View File

@@ -158,19 +158,24 @@ td {
pointer-events: none;
}
.link {
display: inline;
.link,
.listedturf_link {
background: none;
border: none;
padding: 7px 14px;
color: black;
text-decoration: none;
cursor: pointer;
font-size: 13px;
}
.link {
display: inline;
padding: 7px 14px;
margin: 2px 2px;
}
.link:hover {
.link:hover,
.listedturf_link:hover {
text-decoration: underline;
}
@@ -225,7 +230,8 @@ body.dark {
color: #b2c4dd;
}
.dark .link {
.dark .link,
.dark .listedturf_link{
color: #abc6ec;
}
@@ -268,7 +274,8 @@ body.ntos {
color: #b2c4dd;
}
.ntos .link {
.ntos .link,
.ntos .listedturf_link{
color: #abc6ec;
}
@@ -311,7 +318,8 @@ body.paradise {
color: #dec5bd;
}
.paradise .link {
.paradise .link,
.paradise .listedturf_link{
color: #edc1b2;
}
@@ -354,6 +362,7 @@ body.syndicate {
color: #debdbd;
}
.syndicate .link {
.syndicate .link,
.syndicate .listedturf_link {
color: #edb2b2;
}

View File

@@ -14,6 +14,13 @@ if (!String.prototype.trim) {
};
}
// For sending BYOND debug logs -----------------------------------------------
// If you use this, you'll need to uncomment the Statpanel-Debug message
// handling, currently in code/modules/client/client_procs.dm
function log_debug(data) {
Byond.sendMessage("Statpanel-Debug", JSON.stringify(data));
}
// Status panel implementation ------------------------------------------------
var status_tab_parts = [["Loading...", ""]];
var current_tab = null;
@@ -22,14 +29,20 @@ var href_token = null;
var verb_tabs = [];
var verbs = [["", ""]]; // list with a list inside
var permanent_tabs = []; // tabs that won't be cleared by wipes
var turfcontents = [];
var turf_row_inner_height = 33;
var turf_row_outer_height = 35;
var turf_rows = {};
var turf_incomplete_rows = {};
var turf_size = 0;
var turf_image_errors = {};
var turfcontents = {"total": 0};
var turfname = "";
var imageFirstRetryDelay = 50;
var imageRetryDelay = 500;
var imageRetryLimit = 50;
var menu = document.getElementById('menu');
var under_menu = document.getElementById('under_menu');
var statcontentdiv = document.getElementById('statcontent');
var storedimages = [];
var split_admin_tabs = false;
// Any BYOND commands that could result in the client's focus changing go through this
@@ -371,79 +384,217 @@ function draw_mc() {
document.getElementById("statcontent").appendChild(table);
}
function iconError(e) {
if(current_tab != turfname) {
function listedturf_add_row(table, table_index, true_index) {
let row = table.insertRow(table_index);
row.style.height = turf_row_inner_height + "px"
row.style.padding = "0px"
row.style.margin = "0px"
turf_rows[true_index] = row;
turf_incomplete_rows[true_index] = true_index + 1;
}
function listedturf_fill_row(row, item_index) {
let object_info = turfcontents["" + item_index];
if(!object_info) {
return false;
}
let cell = document.createElement("td");
cell.style.height = turf_row_inner_height + "px"
cell.style.padding = "0px"
cell.style.margin = "0px"
row.appendChild(cell)
var button = document.createElement("div");
button.className = "listedturf_link";
var clickcatcher = "";
button.onmousedown = function (object_info) {
// The outer function is used to close over a fresh "object_info"
// variable, rather than every onmousedown getting the "object_info"
// of the last entry.
return function (e) {
e.preventDefault();
clickcatcher = "?src=" + object_info[1];
switch (e.button) {
case 1:
clickcatcher += ";statpanel_item_click=middle"
break;
case 2:
clickcatcher += ";statpanel_item_click=right"
break;
default:
clickcatcher += ";statpanel_item_click=left"
}
if (e.shiftKey) {
clickcatcher += ";statpanel_item_shiftclick=1";
}
if (e.ctrlKey) {
clickcatcher += ";statpanel_item_ctrlclick=1";
}
if (e.altKey) {
clickcatcher += ";statpanel_item_altclick=1";
}
window.location.href = clickcatcher;
}
}(object_info);
cell.appendChild(button);
let img = document.createElement("img");
img.id = object_info[1];
img.src = object_info[2];
img.style.verticalAlign = "middle";
img.onerror = function (object_info) {
return function () {
let delay = imageRetryDelay;
if (!turf_image_errors[object_info[3]]) {
turf_image_errors[object_info[3]] = 0;
delay = imageFirstRetryDelay;
}
turf_image_errors[object_info[3]]++;
if (turf_image_errors[object_info[3]] > imageRetryLimit) {
return;
}
Byond.sendMessage("Resend-Asset", object_info[3]);
setTimeout(function () {
// Use the failure count as a cachebreaker to force-reload.
let img = document.getElementById(object_info[1]);
img.src = object_info[2] + "?" + turf_image_errors[object_info[3]];
}, imageRetryDelay);
}
}(object_info);
button.appendChild(img);
var label = document.createElement("span");
label.style.marginLeft = "5px";
label.textContent = object_info[0];
button.appendChild(label);
return true;
}
function listedturf_fill_all() {
for(let i in turf_incomplete_rows) {
let item_index = turf_incomplete_rows[i];
if(!turf_rows[i] || listedturf_fill_row(turf_rows[i], item_index)) {
delete turf_incomplete_rows[i];
}
}
}
var suppress_next_scroll_message = false;
/* We keep a sliding "window" of listedturf items loded. On scroll, we add and
* remove table rows to maintain that window, and update the size of the
* padding row at the top of the table to keep them in the right spot.
*/
function listedturf_scrolled() {
let top_edge = document.documentElement.scrollTop;
let height = document.documentElement.clientHeight;
let bottom_edge = top_edge + height;
let total = document.documentElement.scrollHeight;
let table = document.getElementById("listedturf_table");
let padding = document.getElementById("listedturf_padding");
if (!turf_rows.initialized) {
turf_rows = {
initialized: true,
min_row: 0,
max_row: 0,
};
}
if (turf_size === 0) {
return;
}
setTimeout(function () {
var node = e.target;
var current_attempts = Number(node.getAttribute("data-attempts")) || 0
if (current_attempts > imageRetryLimit) {
return;
let desired_min_row = Math.min(turf_size, Math.max(0, Math.floor(top_edge / turf_row_outer_height) - 10));
let desired_max_row = Math.min(turf_size, desired_min_row + Math.ceil(height / turf_row_outer_height) + 21);
padding.style.height = (desired_min_row * turf_row_outer_height) + "px";
if(desired_min_row == turf_rows.min_row && desired_max_row == turf_rows.max_row) {
listedturf_fill_all();
suppress_next_scroll_message = false;
return;
}
if (desired_min_row < turf_rows.min_row) {
for (let i = desired_min_row; i < turf_rows.min_row; i++) {
listedturf_add_row(table, i - desired_min_row + 1, i);
}
var src = node.src;
node.src = null;
node.src = src + '#' + current_attempts;
node.setAttribute("data-attempts", current_attempts + 1)
draw_listedturf();
}, imageRetryDelay);
} else if (desired_min_row > turf_rows.min_row) {
for (let i = turf_rows.min_row; i < desired_min_row && i < turf_rows.max_row; i++) {
if(turf_rows[i]) {
turf_rows[i].remove();
delete turf_rows[i];
}
}
}
turf_rows.min_row = desired_min_row;
padding.style.height = turf_rows.min_row * turf_row_outer_height + "px"
if (desired_max_row < turf_rows.max_row) {
for (let i = Math.max(desired_max_row, turf_rows.min_row); i < turf_rows.max_row; i++) {
if(turf_rows[i]) {
turf_rows[i].remove();
delete turf_rows[i];
}
}
} else if (desired_max_row > turf_rows.max_row) {
for (let i = Math.max(turf_rows.min_row, turf_rows.max_row); i < desired_max_row; i++) {
listedturf_add_row(table, i - turf_rows.min_row + 1, i);
}
}
turf_rows.max_row = desired_max_row;
listedturf_fill_all();
if (!suppress_next_scroll_message) {
Byond.sendMessage("Listedturf-Scroll", {"min": turf_rows.min_row, "max": turf_rows.max_row})
}
suppress_next_scroll_message = false;
}
function draw_listedturf() {
statcontentdiv.textContent = "";
var table = document.createElement("table");
for (var i = 0; i < turfcontents.length; i++) {
var part = turfcontents[i];
if (storedimages[part[1]] == null && part[2]) {
var img = document.createElement("img");
img.src = part[2];
img.id = part[1];
storedimages[part[1]] = part[2];
img.onerror = iconError;
table.appendChild(img);
} else {
var img = document.createElement("img");
img.onerror = iconError;
img.src = storedimages[part[1]];
img.id = part[1];
table.appendChild(img);
}
var b = document.createElement("div");
var clickcatcher = "";
b.className = "link";
b.onmousedown = function (part) {
// The outer function is used to close over a fresh "part" variable,
// rather than every onmousedown getting the "part" of the last entry.
return function (e) {
e.preventDefault();
clickcatcher = "?src=" + part[1];
switch (e.button) {
case 1:
clickcatcher += ";statpanel_item_click=middle"
break;
case 2:
clickcatcher += ";statpanel_item_click=right"
break;
default:
clickcatcher += ";statpanel_item_click=left"
}
if (e.shiftKey) {
clickcatcher += ";statpanel_item_shiftclick=1";
}
if (e.ctrlKey) {
clickcatcher += ";statpanel_item_ctrlclick=1";
}
if (e.altKey) {
clickcatcher += ";statpanel_item_altclick=1";
}
window.location.href = clickcatcher;
}
}(part);
b.textContent = part[0];
table.appendChild(b);
table.appendChild(document.createElement("br"));
if(document.getElementById("listedturf_div")) {
let div = document.getElementById("listedturf_div");
div.style.height = (turf_row_outer_height * turf_size) + "px";
suppress_next_scroll_message = true;
listedturf_scrolled();
return
}
document.getElementById("statcontent").appendChild(table);
statcontentdiv.textContent = "";
turf_rows = {};
window.onscroll = function() { listedturf_scrolled(); };
let div = document.createElement("div");
div.id = "listedturf_div";
div.style.height = (turf_row_outer_height * turf_size) + "px";
document.getElementById("statcontent").appendChild(div);
let table = document.createElement("table");
table.id = "listedturf_table";
table.style.width = "100%"
table.style.height = "100%"
div.appendChild(table)
let padding = document.createElement("tr");
padding.id = "listedturf_padding";
padding.style.height = "0px";
padding.style.padding = "0px"
padding.style.margin = "0px"
table.appendChild(padding);
let end_flex = document.createElement("tr");
end_flex.id = "listedturf_end_flex";
end_flex.style.height = "100%";
end_flex.style.padding = "0px"
end_flex.style.margin = "0px"
table.appendChild(end_flex);
suppress_next_scroll_message = true;
listedturf_scrolled();
}
function remove_listedturf() {
@@ -452,6 +603,14 @@ function remove_listedturf() {
if (current_tab == turfname) {
tab_change("Status");
}
if(document.getElementById("listedturf_div")) {
document.getElementById("listedturf_div").remove();
}
turf_rows = {};
turf_incomplete_rows = {};
turf_size = 0;
turfcontents = {"total": 0};
turfname = "";
}
function remove_mc() {
@@ -738,6 +897,7 @@ Byond.subscribeTo('remove_mc_tab', function (removeHref) {
Byond.subscribeTo('update_listedturf', function (TC) {
turfcontents = TC;
turf_size = TC["total"];
if (current_tab == turfname) {
draw_listedturf();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 621 KiB

After

Width:  |  Height:  |  Size: 621 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

BIN
icons/obj/money.dmi Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -92,6 +92,7 @@
#include "code\__DEFINES\muzzle_flash.dm"
#include "code\__DEFINES\nanomob_defines.dm"
#include "code\__DEFINES\newscaster_defines.dm"
#include "code\__DEFINES\particle_defines.dm"
#include "code\__DEFINES\pda.dm"
#include "code\__DEFINES\pipes.dm"
#include "code\__DEFINES\power_defines.dm"
@@ -402,6 +403,7 @@
#include "code\datums\components\corpse_description.dm"
#include "code\datums\components\cult_held_body.dm"
#include "code\datums\components\deadchat_control.dm"
#include "code\datums\components\debris.dm"
#include "code\datums\components\decal.dm"
#include "code\datums\components\defibrillator.dm"
#include "code\datums\components\ducttape.dm"

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More