mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 09:54:52 +00:00
Sliding Puzzle
This commit is contained in:
47
_maps/RandomRuins/LavaRuins/lavaland_surface_puzzle.dmm
Normal file
47
_maps/RandomRuins/LavaRuins/lavaland_surface_puzzle.dmm
Normal file
@@ -0,0 +1,47 @@
|
||||
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
|
||||
"a" = (
|
||||
/turf/template_noop,
|
||||
/area/lavaland/surface/outdoors)
|
||||
"b" = (
|
||||
/obj/effect/sliding_puzzle/lavaland,
|
||||
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
|
||||
/area/lavaland/surface/outdoors)
|
||||
"c" = (
|
||||
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
|
||||
/area/lavaland/surface/outdoors)
|
||||
|
||||
(1,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
a
|
||||
c
|
||||
c
|
||||
c
|
||||
a
|
||||
"}
|
||||
(3,1,1) = {"
|
||||
a
|
||||
c
|
||||
b
|
||||
c
|
||||
a
|
||||
"}
|
||||
(4,1,1) = {"
|
||||
a
|
||||
c
|
||||
c
|
||||
c
|
||||
a
|
||||
"}
|
||||
(5,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
@@ -70,6 +70,7 @@
|
||||
#define ADMIN_PUNISHMENT_FIREBALL "Fireball"
|
||||
#define ADMIN_PUNISHMENT_ROD "Immovable Rod"
|
||||
#define ADMIN_PUNISHMENT_SUPPLYPOD "Supply Pod"
|
||||
#define ADMIN_PUNISHMENT_MAZING "Puzzle"
|
||||
|
||||
#define AHELP_ACTIVE 1
|
||||
#define AHELP_CLOSED 2
|
||||
|
||||
@@ -72,6 +72,9 @@
|
||||
if(check_click_intercept(params,A))
|
||||
return
|
||||
|
||||
if(notransform)
|
||||
return
|
||||
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["shift"] && modifiers["middle"])
|
||||
ShiftMiddleClickOn(A)
|
||||
|
||||
@@ -23,7 +23,7 @@ SUBSYSTEM_DEF(npcpool)
|
||||
var/mob/living/simple_animal/SA = currentrun[currentrun.len]
|
||||
--currentrun.len
|
||||
|
||||
if(!SA.ckey)
|
||||
if(!SA.ckey && !SA.notransform)
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_movement()
|
||||
if(SA.stat != DEAD)
|
||||
|
||||
@@ -215,3 +215,10 @@
|
||||
suffix = "lavaland_surface_random_ripley.dmm"
|
||||
allow_duplicates = FALSE
|
||||
cost = 5
|
||||
|
||||
/datum/map_template/ruin/lavaland/puzzle
|
||||
name = "Ancient Puzzle"
|
||||
id = "puzzle"
|
||||
description = "Mystery to be solved."
|
||||
suffix = "lavaland_surface_puzzle.dmm"
|
||||
cost = 5
|
||||
@@ -1313,7 +1313,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_ROD, ADMIN_PUNISHMENT_SUPPLYPOD)
|
||||
var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_ROD, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING)
|
||||
|
||||
var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list
|
||||
|
||||
@@ -1359,6 +1359,10 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
if(iscarbon(target))
|
||||
target.Stun(10)//takes 0.53 seconds for CentCom pod to land
|
||||
new /obj/effect/DPtarget(get_turf(target), delivery, POD_CENTCOM)
|
||||
if(ADMIN_PUNISHMENT_MAZING)
|
||||
if(!puzzle_imprison(target))
|
||||
to_chat(usr,"<span class='warning'>Imprisonment failed!</span>")
|
||||
return
|
||||
|
||||
var/msg = "[key_name_admin(usr)] punished [key_name_admin(target)] with [punishment]."
|
||||
message_admins(msg)
|
||||
|
||||
@@ -1315,3 +1315,18 @@
|
||||
for(var/t in RANGE_TURFS(1, T))
|
||||
var/obj/effect/temp_visual/hierophant/blast/B = new(t, user, friendly_fire_check)
|
||||
B.damage = 15 //keeps monster damage boost due to lower damage
|
||||
|
||||
|
||||
//Just some minor stuff
|
||||
/obj/structure/closet/crate/necropolis/puzzle
|
||||
name = "puzzling chest"
|
||||
|
||||
/obj/structure/closet/crate/necropolis/puzzle/PopulateContents()
|
||||
var/loot = rand(1,3)
|
||||
switch(loot)
|
||||
if(1)
|
||||
new /obj/item/soulstone/anybody(src)
|
||||
if(2)
|
||||
new /obj/item/wisp_lantern(src)
|
||||
if(3)
|
||||
new /obj/item/prisoncube(src)
|
||||
351
code/modules/ruins/lavalandruin_code/puzzle.dm
Normal file
351
code/modules/ruins/lavalandruin_code/puzzle.dm
Normal file
@@ -0,0 +1,351 @@
|
||||
/obj/effect/sliding_puzzle
|
||||
name = "Sliding puzzle generator"
|
||||
icon = 'icons/obj/items_and_weapons.dmi' //mapping
|
||||
icon_state = "syndballoon"
|
||||
invisibility = INVISIBILITY_ABSTRACT
|
||||
anchored = TRUE
|
||||
var/list/elements
|
||||
var/floor_type = /turf/open/floor/vault
|
||||
var/finished = FALSE
|
||||
var/reward_type = /obj/item/reagent_containers/food/snacks/cookie
|
||||
var/element_type = /obj/structure/puzzle_element
|
||||
var/auto_setup = TRUE
|
||||
var/empty_tile_id
|
||||
|
||||
//Gets the turf where the tile with given id should be
|
||||
/obj/effect/sliding_puzzle/proc/get_turf_for_id(id)
|
||||
var/turf/center = get_turf(src)
|
||||
switch(id)
|
||||
if(1)
|
||||
return get_step(center,NORTHWEST)
|
||||
if(2)
|
||||
return get_step(center,NORTH)
|
||||
if(3)
|
||||
return get_step(center,NORTHEAST)
|
||||
if(4)
|
||||
return get_step(center,WEST)
|
||||
if(5)
|
||||
return center
|
||||
if(6)
|
||||
return get_step(center,EAST)
|
||||
if(7)
|
||||
return get_step(center,SOUTHWEST)
|
||||
if(8)
|
||||
return get_step(center,SOUTH)
|
||||
if(9)
|
||||
return get_step(center,SOUTHEAST)
|
||||
|
||||
/obj/effect/sliding_puzzle/Initialize(mapload)
|
||||
..()
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/effect/sliding_puzzle/LateInitialize()
|
||||
if(auto_setup)
|
||||
setup()
|
||||
|
||||
/obj/effect/sliding_puzzle/proc/check_setup_location()
|
||||
for(var/id in 1 to 9)
|
||||
var/turf/T = get_turf_for_id(id)
|
||||
if(!T)
|
||||
return FALSE
|
||||
if(istype(T,/turf/closed/indestructible))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/effect/sliding_puzzle/proc/validate()
|
||||
if(finished)
|
||||
return
|
||||
|
||||
if(elements.len < 8) //Someone broke it
|
||||
qdel(src)
|
||||
|
||||
//Check if everything is in place
|
||||
for(var/id in 1 to 9)
|
||||
var/target_turf = get_turf_for_id(id)
|
||||
var/obj/structure/puzzle_element/E = locate() in target_turf
|
||||
if(id == empty_tile_id && !E) // This location should be empty.
|
||||
continue
|
||||
if(!E || E.id != id) //wrong tile or no tile at all
|
||||
return
|
||||
//Ding ding
|
||||
finish()
|
||||
|
||||
/obj/effect/sliding_puzzle/Destroy()
|
||||
if(LAZYLEN(elements))
|
||||
for(var/obj/structure/puzzle_element/E in elements)
|
||||
E.source = null
|
||||
elements.Cut()
|
||||
return ..()
|
||||
|
||||
#define COLLAPSE_DURATION 7
|
||||
|
||||
/obj/effect/sliding_puzzle/proc/finish()
|
||||
finished = TRUE
|
||||
for(var/mob/M in range(7,src))
|
||||
shake_camera(M, COLLAPSE_DURATION , 1)
|
||||
for(var/obj/structure/puzzle_element/E in elements)
|
||||
E.collapse()
|
||||
|
||||
dispense_reward()
|
||||
|
||||
/obj/effect/sliding_puzzle/proc/dispense_reward()
|
||||
new reward_type(get_turf(src))
|
||||
|
||||
/obj/effect/sliding_puzzle/proc/is_solvable()
|
||||
var/list/current_ordering = list()
|
||||
for(var/obj/structure/puzzle_element/E in elements_in_order())
|
||||
current_ordering += E.id
|
||||
|
||||
var/swap_tally = 0
|
||||
for(var/i in 1 to current_ordering.len)
|
||||
var/checked_value = current_ordering[i]
|
||||
for(var/j in i to current_ordering.len)
|
||||
if(current_ordering[j] < checked_value)
|
||||
swap_tally++
|
||||
|
||||
return swap_tally % 2 == 0
|
||||
|
||||
//swap two tiles in same row
|
||||
/obj/effect/sliding_puzzle/proc/make_solvable()
|
||||
var/first_tile_id = 1
|
||||
var/other_tile_id = 2
|
||||
if(empty_tile_id == 1 || empty_tile_id == 2) //Can't swap with empty one so just grab some in second row
|
||||
first_tile_id = 4
|
||||
other_tile_id = 5
|
||||
|
||||
var/turf/T1 = get_turf_for_id(first_tile_id)
|
||||
var/turf/T2 = get_turf_for_id(other_tile_id)
|
||||
|
||||
var/obj/structure/puzzle_element/E1 = locate() in T1
|
||||
var/obj/structure/puzzle_element/E2 = locate() in T2
|
||||
|
||||
E1.forceMove(T2)
|
||||
E2.forceMove(T1)
|
||||
|
||||
/proc/cmp_xy_desc(atom/movable/A,atom/movable/B)
|
||||
if(A.y > B.y)
|
||||
return -1
|
||||
if(A.y < B.y)
|
||||
return 1
|
||||
if(A.x > B.x)
|
||||
return 1
|
||||
if(A.x < B.x)
|
||||
return -1
|
||||
return 0
|
||||
|
||||
/obj/effect/sliding_puzzle/proc/elements_in_order()
|
||||
return sortTim(elements,cmp=/proc/cmp_xy_desc)
|
||||
|
||||
/obj/effect/sliding_puzzle/proc/get_base_icon()
|
||||
var/icon/I = new('icons/obj/puzzle.dmi')
|
||||
var/list/puzzles = icon_states(I)
|
||||
var/puzzle_state = pick(puzzles)
|
||||
var/icon/P = new('icons/obj/puzzle.dmi',puzzle_state)
|
||||
return P
|
||||
|
||||
/obj/effect/sliding_puzzle/proc/setup()
|
||||
//First we slice the 96x96 icon into 32x32 pieces
|
||||
var/list/puzzle_pieces = list() //id -> icon list
|
||||
|
||||
var/width = 3
|
||||
var/height = 3
|
||||
var/list/left_ids = list()
|
||||
var/tile_count = width * height
|
||||
|
||||
//Generate per tile icons
|
||||
for(var/id in 1 to tile_count)
|
||||
var/y = width - round((id - 1) / width)
|
||||
var/x = ((id - 1) % width) + 1
|
||||
|
||||
var/x_start = 1 + (x - 1) * world.icon_size
|
||||
var/x_end = x_start + world.icon_size - 1
|
||||
var/y_start = 1 + ((y - 1) * world.icon_size)
|
||||
var/y_end = y_start + world.icon_size - 1
|
||||
|
||||
var/icon/T = get_base_icon()
|
||||
T.Crop(x_start,y_start,x_end,y_end)
|
||||
puzzle_pieces["[id]"] = T
|
||||
left_ids += id
|
||||
|
||||
//Setup random empty tile
|
||||
empty_tile_id = pick_n_take(left_ids)
|
||||
var/turf/empty_tile_turf = get_turf_for_id(empty_tile_id)
|
||||
empty_tile_turf.PlaceOnTop(floor_type,null,CHANGETURF_INHERIT_AIR)
|
||||
var/mutable_appearance/MA = new(puzzle_pieces["[empty_tile_id]"])
|
||||
MA.layer = empty_tile_turf.layer + 0.1
|
||||
empty_tile_turf.add_overlay(MA)
|
||||
|
||||
elements = list()
|
||||
var/list/empty_spots = left_ids.Copy()
|
||||
for(var/spot_id in empty_spots)
|
||||
var/turf/T = get_turf_for_id(spot_id)
|
||||
T = T.PlaceOnTop(floor_type,null,CHANGETURF_INHERIT_AIR)
|
||||
var/obj/structure/puzzle_element/E = new element_type(T)
|
||||
elements += E
|
||||
var/chosen_id = pick_n_take(left_ids)
|
||||
E.puzzle_icon = puzzle_pieces["[chosen_id]"]
|
||||
E.source = src
|
||||
E.id = chosen_id
|
||||
E.set_puzzle_icon()
|
||||
|
||||
if(!is_solvable())
|
||||
make_solvable()
|
||||
|
||||
/obj/structure/puzzle_element
|
||||
name = "mysterious pillar"
|
||||
desc = "puzzling..."
|
||||
icon = 'icons/obj/lavaland/artefacts.dmi'
|
||||
icon_state = "puzzle_pillar"
|
||||
anchored = FALSE
|
||||
density = TRUE
|
||||
var/id = 0
|
||||
var/obj/effect/sliding_puzzle/source
|
||||
var/icon/puzzle_icon
|
||||
|
||||
/obj/structure/puzzle_element/Move(nloc, dir)
|
||||
if(!isturf(nloc) || moving_diagonally || get_dist(get_step(src,dir),get_turf(source)) > 1)
|
||||
return 0
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/puzzle_element/proc/set_puzzle_icon()
|
||||
cut_overlays()
|
||||
if(puzzle_icon)
|
||||
//Need to scale it down a bit to fit the static border
|
||||
var/icon/C = new(puzzle_icon)
|
||||
C.Scale(19,19)
|
||||
var/mutable_appearance/puzzle_small = new(C)
|
||||
puzzle_small.layer = layer + 0.1
|
||||
puzzle_small.pixel_x = 7
|
||||
puzzle_small.pixel_y = 7
|
||||
add_overlay(puzzle_small)
|
||||
|
||||
/obj/structure/puzzle_element/Destroy()
|
||||
if(source)
|
||||
source.elements -= src
|
||||
source.validate()
|
||||
return ..()
|
||||
|
||||
//Set the full image on the turf and delete yourself
|
||||
/obj/structure/puzzle_element/proc/collapse()
|
||||
var/turf/T = get_turf(src)
|
||||
var/mutable_appearance/MA = new(puzzle_icon)
|
||||
MA.layer = T.layer + 0.1
|
||||
T.add_overlay(MA)
|
||||
//Some basic shaking animation
|
||||
for(var/i in 1 to COLLAPSE_DURATION)
|
||||
animate(src, pixel_x=rand(-5,5), pixel_y=rand(-2,2), time=1)
|
||||
QDEL_IN(src,COLLAPSE_DURATION)
|
||||
|
||||
/obj/structure/puzzle_element/Moved()
|
||||
. = ..()
|
||||
source.validate()
|
||||
|
||||
//Admin abuse version so you can pick the icon before it sets up
|
||||
/obj/effect/sliding_puzzle/admin
|
||||
auto_setup = FALSE
|
||||
var/icon/puzzle_icon
|
||||
var/puzzle_state
|
||||
|
||||
/obj/effect/sliding_puzzle/admin/get_base_icon()
|
||||
var/icon/I = new(puzzle_icon,puzzle_state)
|
||||
return I
|
||||
|
||||
//Ruin version
|
||||
/obj/effect/sliding_puzzle/lavaland
|
||||
reward_type = /obj/structure/closet/crate/necropolis/puzzle
|
||||
|
||||
/obj/effect/sliding_puzzle/lavaland/dispense_reward()
|
||||
if(prob(25))
|
||||
//If it's not roaming somewhere else already.
|
||||
var/mob/living/simple_animal/hostile/megafauna/bubblegum/B = locate() in GLOB.mob_list
|
||||
if(!B)
|
||||
reward_type = /mob/living/simple_animal/hostile/megafauna/bubblegum
|
||||
return ..()
|
||||
|
||||
//Prison cube version
|
||||
/obj/effect/sliding_puzzle/prison
|
||||
auto_setup = FALSE //This will be done by cube proc
|
||||
var/mob/living/prisoner
|
||||
element_type = /obj/structure/puzzle_element/prison
|
||||
|
||||
/obj/effect/sliding_puzzle/prison/get_base_icon()
|
||||
if(!prisoner)
|
||||
CRASH("Prison cube without prisoner")
|
||||
prisoner.setDir(SOUTH)
|
||||
var/icon/I = getFlatIcon(prisoner)
|
||||
I.Scale(96,96)
|
||||
return I
|
||||
|
||||
/obj/effect/sliding_puzzle/prison/Destroy()
|
||||
if(prisoner)
|
||||
to_chat(prisoner,"<span class='userdanger'>With the cube broken by force, you can feel your body falling apart.</span>")
|
||||
prisoner.death()
|
||||
qdel(prisoner)
|
||||
. = ..()
|
||||
|
||||
/obj/effect/sliding_puzzle/prison/dispense_reward()
|
||||
prisoner.forceMove(get_turf(src))
|
||||
prisoner.notransform = FALSE
|
||||
prisoner = null
|
||||
|
||||
//Some armor so it's harder to kill someone by mistake.
|
||||
/obj/structure/puzzle_element/prison
|
||||
armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 50, "rad" = 50, "fire" = 50, "acid" = 50)
|
||||
|
||||
/obj/structure/puzzle_element/prison/relaymove(mob/user)
|
||||
return
|
||||
|
||||
/obj/item/prisoncube
|
||||
name = "Prison Cube"
|
||||
desc = "Dusty cube with humanoid imprint on it."
|
||||
icon = 'icons/obj/lavaland/artefacts.dmi'
|
||||
icon_state = "prison_cube"
|
||||
|
||||
/obj/item/prisoncube/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!proximity_flag || !isliving(target))
|
||||
return
|
||||
var/mob/living/victim = target
|
||||
var/mob/living/carbon/carbon_victim = victim
|
||||
//Handcuffed or unconcious
|
||||
if(istype(carbon_victim) && carbon_victim.handcuffed || victim.stat != CONSCIOUS)
|
||||
if(!puzzle_imprison(target))
|
||||
to_chat(user,"<span class='warning'>[src] does nothing.</span>")
|
||||
return
|
||||
to_chat(user,"<span class='warning'>You trap [victim] in the prison cube!</span>")
|
||||
qdel(src)
|
||||
else
|
||||
to_chat(user,"<span class='notice'>[src] only accepts restrained or unconcious prisoners.</span>")
|
||||
|
||||
/proc/puzzle_imprison(mob/living/prisoner)
|
||||
var/turf/T = get_turf(prisoner)
|
||||
var/obj/effect/sliding_puzzle/prison/cube = new(T)
|
||||
if(!cube.check_setup_location())
|
||||
qdel(cube)
|
||||
return FALSE
|
||||
|
||||
//First grab the prisoner and move them temporarily into the generator so they won't get thrown around.
|
||||
prisoner.notransform = TRUE
|
||||
prisoner.forceMove(cube)
|
||||
to_chat(prisoner,"<span class='userdanger'>You're trapped by the prison cube! You will remain trapped until someone solves it.</span>")
|
||||
|
||||
//Clear the area from objects (and cube user)
|
||||
var/list/things_to_throw = list()
|
||||
for(var/atom/movable/AM in range(1,T))
|
||||
if(!AM.anchored)
|
||||
things_to_throw += AM
|
||||
|
||||
for(var/atom/movable/AM in things_to_throw)
|
||||
var/throwtarget = get_edge_target_turf(T, get_dir(T, get_step_away(AM, T)))
|
||||
AM.throw_at(throwtarget, 2, 3)
|
||||
|
||||
//Create puzzle itself
|
||||
cube.prisoner = prisoner
|
||||
cube.setup()
|
||||
|
||||
//Move them into random block
|
||||
var/obj/structure/puzzle_element/E = pick(cube.elements)
|
||||
prisoner.forceMove(E)
|
||||
return TRUE
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
BIN
icons/obj/puzzle.dmi
Normal file
BIN
icons/obj/puzzle.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 956 B |
@@ -2468,6 +2468,7 @@
|
||||
#include "code\modules\research\xenobiology\crossbreeding\stabilized.dm"
|
||||
#include "code\modules\ruins\lavaland_ruin_code.dm"
|
||||
#include "code\modules\ruins\lavalandruin_code\biodome_clown_planet.dm"
|
||||
#include "code\modules\ruins\lavalandruin_code\puzzle.dm"
|
||||
#include "code\modules\ruins\lavalandruin_code\sloth.dm"
|
||||
#include "code\modules\ruins\lavalandruin_code\surface.dm"
|
||||
#include "code\modules\ruins\objects_and_mobs\ash_walker_den.dm"
|
||||
|
||||
Reference in New Issue
Block a user