This commit is contained in:
LetterJay
2017-06-26 23:19:54 -05:00
60 changed files with 5414 additions and 774 deletions
+1
View File
@@ -14,6 +14,7 @@
#define CLOSED_TURF_LAYER 2.05
#define ABOVE_NORMAL_TURF_LAYER 2.08
#define LATTICE_LAYER 2.2
#define OVER_LATTICE_LAYER 2.25
#define DISPOSAL_PIPE_LAYER 2.3
#define GAS_PIPE_HIDDEN_LAYER 2.35
#define WIRE_LAYER 2.4
+6 -1
View File
@@ -45,4 +45,9 @@
#define HYPERSPACE_LAUNCH 2
#define HYPERSPACE_END 3
#define CALL_SHUTTLE_REASON_LENGTH 12
#define CALL_SHUTTLE_REASON_LENGTH 12
//Engine related
#define ENGINE_COEFF_MIN 0.5
#define ENGINE_COEFF_MAX 2
#define ENGINE_DEFAULT_MAXSPEED_ENGINES 5
+6 -1
View File
@@ -47,7 +47,7 @@ SUBSYSTEM_DEF(shuttle)
var/lockdown = FALSE //disallow transit after nuke goes off
var/auto_call = 99000 //time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3.
var/auto_call = 99000 //time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3.
/datum/controller/subsystem/shuttle/Initialize(timeofday)
if(!arrivals)
@@ -544,3 +544,8 @@ SUBSYSTEM_DEF(shuttle)
for(var/obj/docking_port/mobile/M in mobile)
if(M.is_in_shuttle_bounds(A))
return TRUE
/datum/controller/subsystem/shuttle/proc/get_containing_shuttle(atom/A)
for(var/obj/docking_port/mobile/M in mobile)
if(M.is_in_shuttle_bounds(A))
return M
@@ -0,0 +1,9 @@
diff a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm (rejected hunks)
@@ -536,3 +536,7 @@ SUBSYSTEM_DEF(shuttle)
if(M.is_in_shuttle_bounds(A))
return TRUE
+/datum/controller/subsystem/shuttle/proc/get_containing_shuttle(atom/A)
+ for(var/obj/docking_port/mobile/M in mobile)
+ if(M.is_in_shuttle_bounds(A))
+ return M
+1 -1
View File
@@ -29,7 +29,7 @@
qdel(H.gloves)
var/obj/item/clothing/suit/space/space_ninja/theSuit = new(H)
var/obj/item/weapon/katana/energy/EK = new(H)
var/obj/item/weapon/dash/energy_katana/EK = new(H)
theSuit.energyKatana = EK
H.equip_to_slot_or_del(new /obj/item/device/radio/headset(H), slot_ears)
+10
View File
@@ -21,6 +21,10 @@
mappath = "[prefix][shuttle_id].dmm"
. = ..()
//Whatever special stuff you want
/datum/map_template/shuttle/proc/on_bought()
return
/datum/map_template/shuttle/emergency
port_id = "emergency"
name = "Base Shuttle Template (Emergency)"
@@ -49,6 +53,12 @@
// first 10 minutes only
return world.time - SSticker.round_start_time < 6000
/datum/map_template/shuttle/emergency/airless/on_bought()
//enable buying engines from cargo
var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shuttle_engine]
P.special_enabled = TRUE
/datum/map_template/shuttle/emergency/asteroid
suffix = "asteroid"
name = "Asteroid Station Emergency Shuttle"
@@ -262,7 +262,7 @@
/obj/item/weapon/nullrod/clown
icon = 'icons/obj/wizard.dmi'
icon_state = "honkrender"
icon_state = "clownrender"
item_state = "render"
name = "clown dagger"
desc = "Used for absolutely hilarious sacrifices."
@@ -62,6 +62,15 @@
new /obj/item/device/multitool(src)
new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange"))
/obj/item/weapon/storage/belt/utility/full/engi/PopulateContents()
new /obj/item/weapon/screwdriver(src)
new /obj/item/weapon/wrench(src)
new /obj/item/weapon/weldingtool/largetank(src)
new /obj/item/weapon/crowbar(src)
new /obj/item/weapon/wirecutters(src)
new /obj/item/device/multitool(src)
new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange"))
/obj/item/weapon/storage/belt/utility/atmostech/PopulateContents()
new /obj/item/weapon/screwdriver(src)
@@ -58,7 +58,7 @@
for(var/i in 1 to 3)
new /obj/item/clothing/head/welding(src)
for(var/i in 1 to 3)
new /obj/item/weapon/weldingtool/largetank(src)
new /obj/item/weapon/weldingtool(src)
/obj/structure/closet/secure_closet/engineering_personal
name = "engineer's locker"
+7 -2
View File
@@ -91,6 +91,11 @@
icon_state = "[icon_state][rand(1, 6)]"
..()
/obj/structure/flora/tree/jungle/small
pixel_y = 0
pixel_x = -32
icon = 'icons/obj/flora/jungletreesmall.dmi'
//grass
/obj/structure/flora/grass
name = "grass"
@@ -381,5 +386,5 @@
pixel_y = -16
/obj/structure/flora/rock/pile/largejungle/Initialize()
..()
icon_state = "[initial(icon_state)][rand(1,3)]"
. = ..()
icon_state = "[initial(icon_state)][rand(1,3)]"
+81 -3
View File
@@ -1,21 +1,95 @@
#define ENGINE_UNWRENCHED 0
#define ENGINE_WRENCHED 1
#define ENGINE_WELDED 2
#define ENGINE_WELDTIME 200
/obj/structure/shuttle
name = "shuttle"
icon = 'icons/turf/shuttle.dmi'
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
obj_integrity = 500
max_integrity = 500
armor = list(melee = 100, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) //default + ignores melee
/obj/structure/shuttle/engine
name = "engine"
density = 1
anchored = 1
density = TRUE
anchored = TRUE
var/engine_power = 1
var/state = ENGINE_WELDED //welding shmelding
//Ugh this is a lot of copypasta from emitters, welding need some boilerplate reduction
/obj/structure/shuttle/engine/can_be_unfasten_wrench(mob/user, silent)
if(state == ENGINE_WELDED)
if(!silent)
to_chat(user, "<span class='warning'>[src] is welded to the floor!</span>")
return FAILED_UNFASTEN
return ..()
/obj/structure/shuttle/engine/default_unfasten_wrench(mob/user, obj/item/weapon/wrench/W, time = 20)
. = ..()
if(. == SUCCESSFUL_UNFASTEN)
if(anchored)
state = ENGINE_WRENCHED
else
state = ENGINE_UNWRENCHED
/obj/structure/shuttle/engine/attackby(obj/item/I, mob/user, params)
add_fingerprint(user)
if(default_unfasten_wrench(user, I))
return
else if(istype(I, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = I
switch(state)
if(ENGINE_UNWRENCHED)
to_chat(user, "<span class='warning'>The [src.name] needs to be wrenched to the floor!</span>")
if(EM_SECURED)
if(WT.remove_fuel(0,user))
playsound(loc, WT.usesound, 50, 1)
user.visible_message("[user.name] starts to weld the [name] to the floor.", \
"<span class='notice'>You start to weld \the [src] to the floor...</span>", \
"<span class='italics'>You hear welding.</span>")
if(do_after(user,ENGINE_WELDTIME*WT.toolspeed, target = src) && WT.isOn())
state = ENGINE_WELDED
to_chat(user, "<span class='notice'>You weld \the [src] to the floor.</span>")
alter_engine_power(engine_power)
if(EM_WELDED)
if(WT.remove_fuel(0,user))
playsound(loc, WT.usesound, 50, 1)
user.visible_message("[user.name] starts to cut the [name] free from the floor.", \
"<span class='notice'>You start to cut \the [src] free from the floor...</span>", \
"<span class='italics'>You hear welding.</span>")
if(do_after(user,ENGINE_WELDTIME*WT.toolspeed, target = src) && WT.isOn())
state = ENGINE_WRENCHED
to_chat(user, "<span class='notice'>You cut \the [src] free from the floor.</span>")
alter_engine_power(-engine_power)
return
else
return ..()
/obj/structure/shuttle/engine/Destroy()
if(state == ENGINE_WELDED)
alter_engine_power(-engine_power)
. = ..()
//Propagates the change to the shuttle.
/obj/structure/shuttle/engine/proc/alter_engine_power(mod)
if(mod == 0)
return
if(SSshuttle.is_in_shuttle_bounds(src))
var/obj/docking_port/mobile/M = SSshuttle.get_containing_shuttle(src)
if(M)
M.alter_engines(mod)
/obj/structure/shuttle/engine/heater
name = "heater"
icon_state = "heater"
engine_power = 0 // todo make these into 2x1 parts
/obj/structure/shuttle/engine/platform
name = "platform"
icon_state = "platform"
engine_power = 0
/obj/structure/shuttle/engine/propulsion
name = "propulsion"
@@ -25,6 +99,10 @@
/obj/structure/shuttle/engine/propulsion/burst
name = "burst"
/obj/structure/shuttle/engine/propulsion/burst/cargo
state = ENGINE_UNWRENCHED
anchored = FALSE
/obj/structure/shuttle/engine/propulsion/burst/left
name = "left"
icon_state = "burst_l"
+10
View File
@@ -0,0 +1,10 @@
diff a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm (rejected hunks)
@@ -46,7 +117,7 @@
bound_height = 64
appearance_flags = 0
-obj/structure/shuttle/engine/huge
+/obj/structure/shuttle/engine/huge
name = "engine"
opacity = 1
icon = 'icons/obj/3x3.dmi'
+17
View File
@@ -116,6 +116,23 @@
underlay_appearance.icon_state = "necro1"
return TRUE
/turf/closed/indestructible/riveted/boss
name = "stone wall"
desc = "A thick, seemingly indestructible stone wall."
icon = 'icons/turf/walls/boss_wall.dmi'
icon_state = "wall"
explosion_block = 50
baseturf = /turf/closed/indestructible/riveted/boss
/turf/closed/indestructible/riveted/boss/hot
icon = 'icons/turf/walls/boss_wall_hot.dmi'
baseturf = /turf/closed/indestructible/riveted/boss/hot
/turf/closed/indestructible/riveted/boss/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
underlay_appearance.icon = 'icons/turf/floors.dmi'
underlay_appearance.icon_state = "basalt"
return TRUE
/turf/closed/indestructible/riveted/hierophant
name = "wall"
desc = "A wall made out of a strange metal. The squares on it pulse in a predictable pattern."
+8
View File
@@ -54,8 +54,16 @@
else
to_chat(user, "<span class='warning'>The plating is going to need some support! Place metal rods first.</span>")
/turf/open/chasm/proc/is_safe()
//if anything matching this typecache is found in the chasm, we don't drop things
var/static/list/chasm_safeties_typecache = typecacheof(list(/obj/structure/lattice/catwalk, /obj/structure/stone_tile))
var/list/found_safeties = typecache_filter_list(contents, chasm_safeties_typecache)
return LAZYLEN(found_safeties)
/turf/open/chasm/proc/drop_stuff(AM)
. = 0
if(is_safe())
return FALSE
var/thing_to_check = src
if(AM)
thing_to_check = list(AM)
@@ -54,7 +54,7 @@
/turf/open/floor/plating/lava/proc/is_safe()
//if anything matching this typecache is found in the lava, we don't burn things
var/static/list/lava_safeties_typecache = typecacheof(list(/obj/structure/lattice/catwalk))
var/static/list/lava_safeties_typecache = typecacheof(list(/obj/structure/lattice/catwalk, /obj/structure/stone_tile))
var/list/found_safeties = typecache_filter_list(contents, lava_safeties_typecache)
return LAZYLEN(found_safeties)
+9
View File
@@ -710,6 +710,15 @@
/obj/item/weapon/am_containment)
crate_name = "antimatter jar crate"
/datum/supply_pack/engineering/shuttle_engine
name = "Shuttle Engine Crate"
cost = 5000
access = GLOB.access_ce
contains = list(/obj/structure/shuttle/engine/propulsion/burst/cargo)
crate_name = "shuttle engine crate"
crate_type = /obj/structure/closet/crate/secure/engineering
special = TRUE
//////////////////////////////////////////////////////////////////////////////
//////////////////////////// Medical /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -83,7 +83,7 @@ Station Engineer
name = "Station Engineer"
jobtype = /datum/job/engineer
belt = /obj/item/weapon/storage/belt/utility/full
belt = /obj/item/weapon/storage/belt/utility/full/engi
l_pocket = /obj/item/device/pda/engineering
ears = /obj/item/device/radio/headset/headset_eng
uniform = /obj/item/clothing/under/rank/engineer
@@ -309,7 +309,7 @@
/obj/item/weapon/melee/supermatter_sword,
/obj/item/weapon/shield/changeling,
/obj/item/weapon/lava_staff,
/obj/item/weapon/katana/energy,
/obj/item/weapon/dash/energy_katana,
/obj/item/weapon/hierophant_club,
/obj/item/weapon/his_grace,
/obj/item/weapon/gun/ballistic/minigun,
+5 -2
View File
@@ -4,12 +4,14 @@
var/blacklisted_turfs
var/whitelisted_turfs
var/banned_areas
var/banned_objects
/datum/map_template/shelter/New()
. = ..()
blacklisted_turfs = typecacheof(/turf/closed)
whitelisted_turfs = list()
banned_areas = typecacheof(/area/shuttle)
banned_objects = list()
/datum/map_template/shelter/proc/check_deploy(turf/deploy_location)
var/affected = get_affected_turfs(deploy_location, centered=TRUE)
@@ -24,7 +26,7 @@
return SHELTER_DEPLOY_BAD_TURFS
for(var/obj/O in T)
if(O.density && O.anchored)
if((O.density && O.anchored) || is_type_in_typecache(O, banned_objects))
return SHELTER_DEPLOY_ANCHORED_OBJECTS
return SHELTER_DEPLOY_ALLOWED
@@ -40,7 +42,7 @@
/datum/map_template/shelter/alpha/New()
. = ..()
whitelisted_turfs = typecacheof(/turf/closed/mineral)
banned_objects = typecacheof(/obj/structure/stone_tile)
/datum/map_template/shelter/beta
name = "Shelter Beta"
@@ -55,3 +57,4 @@
/datum/map_template/shelter/beta/New()
. = ..()
whitelisted_turfs = typecacheof(/turf/closed/mineral)
banned_objects = typecacheof(/obj/structure/stone_tile)
+3
View File
@@ -164,6 +164,9 @@
. = ..()
/mob/living/silicon/ai/IgniteMob()
fire_stacks = 0
. = ..()
/mob/living/silicon/ai/verb/pick_icon()
set category = "AI Commands"
+2 -3
View File
@@ -4,12 +4,11 @@
stat = DEAD
canmove = 0
card.removePersonality()
if(holoform)
card.forceMove(loc)
card.forceMove(loc)
update_sight()
clear_fullscreens()
//New pAI's get a brand new mind to prevent meta stuff from their previous life. This new mind causes problems down the line if it's not deleted here.
GLOB.living_mob_list -= src
ghostize()
qdel(src)
qdel(src)
@@ -316,7 +316,7 @@ Difficulty: Medium
for(var/mob/living/L in T.contents)
if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon))
continue
if(!islist(flame_hit) || !flame_hit[L])
if(islist(flame_hit) && !flame_hit[L])
L.adjustFireLoss(40)
to_chat(L, "<span class='userdanger'>You're hit by the drake's fire breath!</span>")
flame_hit[L] = TRUE
@@ -350,7 +350,7 @@ Difficulty: Hard
INVOKE_ASYNC(src, .proc/arena_squares, T, d)
for(var/t in RANGE_TURFS(11, T))
if(t && get_dist(t, T) == 11)
new /obj/effect/temp_visual/hierophant/wall(t)
new /obj/effect/temp_visual/hierophant/wall(t, src)
new /obj/effect/temp_visual/hierophant/blast(t, src, FALSE)
if(get_dist(src, T) >= 11) //hey you're out of range I need to get closer to you!
INVOKE_ASYNC(src, .proc/blink, T)
@@ -469,6 +469,8 @@ Difficulty: Hard
return ..()
/obj/effect/temp_visual/hierophant/wall/CanPass(atom/movable/mover, turf/target, height = 0)
if(QDELETED(caster))
return FALSE
if(mover == caster.pulledby)
return TRUE
if(istype(mover, /obj/item/projectile))
+81 -19
View File
@@ -1,30 +1,93 @@
/obj/item/weapon/katana/energy
/obj/item/weapon/dash
name = "abstract dash weapon"
var/max_charges = 3
var/current_charges = 3
var/charge_rate = 30 //In deciseconds
var/dash_toggled = TRUE
var/bypass_density = FALSE //Can we beam past windows/airlocks/etc
var/start_effect_type = /obj/effect/temp_visual/dir_setting/ninja/phase/out
var/end_effect_type = /obj/effect/temp_visual/dir_setting/ninja/phase
var/beam_icon_state = "blur"
var/dash_beam_type = /obj/effect/ebeam
/obj/item/weapon/dash/proc/charge()
current_charges = Clamp(current_charges + 1, 0, max_charges)
if(istype(loc, /mob/living))
to_chat(loc, "<span class='notice'>[src] now has [current_charges]/[max_charges] charges.</span>")
/obj/item/weapon/dash/attack_self(mob/user)
dash_toggled = !dash_toggled
to_chat(user, "<span class='notice'>You [dash_toggled ? "enable" : "disable"] the dash function on [src].</span>")
/obj/item/weapon/dash/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
if(dash_toggled)
dash(user, target)
return
/obj/item/weapon/dash/proc/dash(mob/user, atom/target)
if(!current_charges)
return
if(Adjacent(target))
return
if(target.density)
return
var/turf/T = get_turf(target)
if(!bypass_density)
for(var/turf/turf in getline(get_turf(user),T))
for(var/atom/A in turf)
if(A.density)
return
if(target in view(user.client.view, get_turf(user)))
var/obj/spot1 = new start_effect_type(T, user.dir)
user.forceMove(T)
playsound(T, 'sound/magic/blink.ogg', 25, 1)
playsound(T, "sparks", 50, 1)
var/obj/spot2 = new end_effect_type(get_turf(user), user.dir)
spot1.Beam(spot2, beam_icon_state,time = 2, maxdistance = 20, beam_type = dash_beam_type)
current_charges--
addtimer(CALLBACK(src, .proc/charge), charge_rate)
/obj/item/weapon/dash/energy_katana
name = "energy katana"
desc = "A katana infused with strong energy."
icon_state = "energy_katana"
item_state = "energy_katana"
force = 40
throwforce = 20
block_chance = 50
armour_penetration = 50
w_class = WEIGHT_CLASS_NORMAL
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
block_chance = 50
sharpness = IS_SHARP
obj_integrity = 200
max_integrity = 200
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
bypass_density = TRUE
var/datum/effect_system/spark_spread/spark_system
/obj/item/weapon/katana/energy/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
if(!user || !target)
return
if(proximity_flag)
if(isobj(target) || issilicon(target))
spark_system.start()
playsound(user, "sparks", 50, 1)
playsound(user, 'sound/weapons/blade1.ogg', 50, 1)
target.emag_act(user)
/obj/item/weapon/dash/energy_katana/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
if(dash_toggled)
return ..()
if(proximity_flag && (isobj(target) || issilicon(target)))
spark_system.start()
playsound(user, "sparks", 50, 1)
playsound(user, 'sound/weapons/blade1.ogg', 50, 1)
target.emag_act(user)
//If we hit the Ninja who owns this Katana, they catch it.
//Works for if the Ninja throws it or it throws itself or someone tries
//To throw it at the ninja
/obj/item/weapon/katana/energy/throw_impact(atom/hit_atom)
/obj/item/weapon/dash/energy_katana/throw_impact(atom/hit_atom)
if(ishuman(hit_atom))
var/mob/living/carbon/human/H = hit_atom
if(istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja))
@@ -35,7 +98,7 @@
..()
/obj/item/weapon/katana/energy/proc/returnToOwner(mob/living/carbon/human/user, doSpark = 1, caught = 0)
/obj/item/weapon/dash/energy_katana/proc/returnToOwner(mob/living/carbon/human/user, doSpark = 1, caught = 0)
if(!istype(user))
return
forceMove(get_turf(user))
@@ -62,13 +125,12 @@
if(msg)
to_chat(user, "<span class='notice'>[msg]</span>")
/obj/item/weapon/katana/energy/New()
..()
/obj/item/weapon/dash/energy_katana/Initialize()
. = ..()
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
spark_system.attach(src)
/obj/item/weapon/katana/energy/Destroy()
qdel(spark_system)
spark_system = null
return ..()
/obj/item/weapon/dash/energy_katana/Destroy()
QDEL_NULL(spark_system)
return ..()
+2 -5
View File
@@ -22,7 +22,7 @@ Contents:
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30, fire = 100, acid = 100)
strip_delay = 12
actions_types = list(/datum/action/item_action/initialize_ninja_suit, /datum/action/item_action/ninjajaunt, /datum/action/item_action/ninjasmoke, /datum/action/item_action/ninjaboost, /datum/action/item_action/ninjapulse, /datum/action/item_action/ninjastar, /datum/action/item_action/ninjanet, /datum/action/item_action/ninja_sword_recall, /datum/action/item_action/ninja_stealth, /datum/action/item_action/toggle_glove)
actions_types = list(/datum/action/item_action/initialize_ninja_suit, /datum/action/item_action/ninjasmoke, /datum/action/item_action/ninjaboost, /datum/action/item_action/ninjapulse, /datum/action/item_action/ninjastar, /datum/action/item_action/ninjanet, /datum/action/item_action/ninja_sword_recall, /datum/action/item_action/ninja_stealth, /datum/action/item_action/toggle_glove)
//Important parts of the suit.
var/mob/living/carbon/human/affecting = null
@@ -31,7 +31,7 @@ Contents:
var/list/reagent_list = list("omnizine","salbutamol","spaceacillin","charcoal","nutriment","radium","potass_iodide")//The reagents ids which are added to the suit at New().
var/list/stored_research = list()//For stealing station research.
var/obj/item/weapon/disk/tech_disk/t_disk//To copy design onto disk.
var/obj/item/weapon/katana/energy/energyKatana //For teleporting the katana back to the ninja (It's an ability)
var/obj/item/weapon/dash/energy_katana/energyKatana //For teleporting the katana back to the ninja (It's an ability)
//Other articles of ninja gear worn together, used to easily reference them after initializing.
var/obj/item/clothing/head/helmet/space/space_ninja/n_hood
@@ -181,9 +181,6 @@ Contents:
if(!s_initialized)
to_chat(user, "<span class='warning'><b>ERROR</b>: suit offline. Please activate suit.</span>")
return FALSE
if(istype(action, /datum/action/item_action/ninjajaunt))
ninjajaunt()
return TRUE
if(istype(action, /datum/action/item_action/ninjasmoke))
ninjasmoke()
return TRUE
@@ -71,7 +71,6 @@
/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_three(delay, mob/living/carbon/human/U)
to_chat(U, "<span class='notice'>Logging off, [U:real_name]. Shutting down <B>SpiderOS</B>.</span>")
remove_ninja_verbs()
addtimer(CALLBACK(src, .proc/deinitialize_four, delay, U), delay)
/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_four(delay, mob/living/carbon/human/U)
@@ -22,11 +22,5 @@ Contents:
/obj/item/clothing/suit/space/space_ninja/proc/grant_ninja_verbs()
verbs += /obj/item/clothing/suit/space/space_ninja/proc/ninjashift
s_initialized=1
slowdown=0
/obj/item/clothing/suit/space/space_ninja/proc/remove_ninja_verbs()
verbs -= /obj/item/clothing/suit/space/space_ninja/proc/ninjashift
slowdown=0
+2
View File
@@ -87,6 +87,8 @@
new impact_effect_type(target_loca, target, src)
return 0
var/mob/living/L = target
if(L.buckled && ismob(L.buckled))
L = L.buckled
if(blocked != 100) // not completely blocked
if(damage && L.blood_volume && damage_type == BRUTE)
var/splatter_dir = dir
@@ -60,3 +60,168 @@
. = ..()
else
return QDEL_HINT_LETMELIVE
//stone tiles for boss arenas
/obj/structure/stone_tile
name = "stone tile"
icon = 'icons/turf/boss_floors.dmi'
icon_state = "pristine_tile1"
layer = OVER_LATTICE_LAYER
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/tile_key = "pristine_tile"
var/tile_random_sprite_max = 24
/obj/structure/stone_tile/Initialize(mapload)
. = ..()
icon_state = "[tile_key][rand(1, tile_random_sprite_max)]"
/obj/structure/stone_tile/block
name = "stone block"
icon_state = "pristine_block1"
tile_key = "pristine_block"
tile_random_sprite_max = 4
/obj/structure/stone_tile/slab
name = "stone slab"
icon_state = "pristine_slab1"
tile_key = "pristine_slab"
tile_random_sprite_max = 4
/obj/structure/stone_tile/center
name = "stone center tile"
icon_state = "pristine_center1"
tile_key = "pristine_center"
tile_random_sprite_max = 4
/obj/structure/stone_tile/surrounding
name = "stone surrounding slab"
icon_state = "pristine_surrounding1"
tile_key = "pristine_surrounding"
tile_random_sprite_max = 2
/obj/structure/stone_tile/surrounding_tile
name = "stone surrounding tile"
icon_state = "pristine_surrounding_tile1"
tile_key = "pristine_surrounding_tile"
tile_random_sprite_max = 2
//cracked stone tiles
/obj/structure/stone_tile/cracked
name = "cracked stone tile"
icon_state = "cracked_tile1"
tile_key = "cracked_tile"
/obj/structure/stone_tile/block/cracked
name = "cracked stone block"
icon_state = "cracked_block1"
tile_key = "cracked_block"
/obj/structure/stone_tile/slab/cracked
name = "cracked stone slab"
icon_state = "cracked_slab1"
tile_key = "cracked_slab"
tile_random_sprite_max = 1
/obj/structure/stone_tile/center/cracked
name = "cracked stone center tile"
icon_state = "cracked_center1"
tile_key = "cracked_center"
/obj/structure/stone_tile/surrounding/cracked
name = "cracked stone surrounding slab"
icon_state = "cracked_surrounding1"
tile_key = "cracked_surrounding"
tile_random_sprite_max = 1
/obj/structure/stone_tile/surrounding_tile/cracked
name = "cracked stone surrounding tile"
icon_state = "cracked_surrounding_tile1"
tile_key = "cracked_surrounding_tile"
//burnt stone tiles
/obj/structure/stone_tile/burnt
name = "burnt stone tile"
icon_state = "burnt_tile1"
tile_key = "burnt_tile"
/obj/structure/stone_tile/block/burnt
name = "burnt stone block"
icon_state = "burnt_block1"
tile_key = "burnt_block"
/obj/structure/stone_tile/slab/burnt
name = "burnt stone slab"
icon_state = "burnt_slab1"
tile_key = "burnt_slab"
/obj/structure/stone_tile/center/burnt
name = "burnt stone center tile"
icon_state = "burnt_center1"
tile_key = "burnt_center"
/obj/structure/stone_tile/surrounding/burnt
name = "burnt stone surrounding slab"
icon_state = "burnt_surrounding1"
tile_key = "burnt_surrounding"
/obj/structure/stone_tile/surrounding_tile/burnt
name = "burnt stone surrounding tile"
icon_state = "burnt_surrounding_tile1"
tile_key = "burnt_surrounding_tile"
//hot stone tiles, cosmetic only
/obj/structure/stone_tile/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/block/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/slab/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/center/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/surrounding/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/surrounding_tile/hot
icon = 'icons/turf/boss_floors_hot.dmi'
//hot cracked stone tiles, cosmetic only
/obj/structure/stone_tile/cracked/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/block/cracked/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/slab/cracked/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/center/cracked/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/surrounding/cracked/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/surrounding_tile/cracked/hot
icon = 'icons/turf/boss_floors_hot.dmi'
//hot burnt stone tiles, cosmetic only
/obj/structure/stone_tile/burnt/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/block/burnt/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/slab/burnt/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/center/burnt/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/surrounding/burnt/hot
icon = 'icons/turf/boss_floors_hot.dmi'
/obj/structure/stone_tile/surrounding_tile/burnt/hot
icon = 'icons/turf/boss_floors_hot.dmi'
+2 -2
View File
@@ -213,7 +213,7 @@
set_coefficient = 1
else
set_coefficient = 0.5
var/call_time = SSshuttle.emergencyCallTime * set_coefficient
var/call_time = SSshuttle.emergencyCallTime * set_coefficient * engine_coeff
switch(mode)
// The shuttle can not normally be called while "recalling", so
// if this proc is called, it's via admin fiat
@@ -359,7 +359,7 @@
enterTransit()
mode = SHUTTLE_ESCAPE
launch_status = ENDGAME_LAUNCHED
setTimer(SSshuttle.emergencyEscapeTime)
setTimer(SSshuttle.emergencyEscapeTime * engine_coeff)
priority_announce("The Emergency Shuttle has left the station. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.", null, null, "Priority")
if(SHUTTLE_STRANDED)
+3
View File
@@ -285,6 +285,9 @@
message_admins(msg)
WARNING(msg)
return
//Everything fine
S.on_bought()
/obj/machinery/shuttle_manipulator/proc/unload_preview()
if(preview_shuttle)
+73 -13
View File
@@ -12,7 +12,7 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
anchored = 1
//
var/id
// this should point -away- from the dockingport door, ie towards the ship
dir = NORTH
@@ -224,11 +224,9 @@
var/timid = FALSE
var/list/ripples = list()
/obj/docking_port/mobile/Initialize()
. = ..()
if(!timid)
register()
var/engine_coeff = 1 //current engine coeff
var/current_engines = 0 //current engine power
var/initial_engines = 0 //initial engine power
/obj/docking_port/mobile/proc/register()
SSshuttle.mobile += src
@@ -244,6 +242,8 @@
/obj/docking_port/mobile/Initialize(mapload)
. = ..()
if(!timid)
register()
var/area/A = get_area(src)
if(istype(A, /area/shuttle))
@@ -259,6 +259,9 @@
areaInstance.name = name
areaInstance.contents += return_ordered_turfs()
initial_engines = count_engines()
current_engines = initial_engines
#ifdef DOCKING_PORT_HIGHLIGHT
highlight("#0f0")
#endif
@@ -325,17 +328,17 @@
switch(mode)
if(SHUTTLE_CALL)
if(S == destination)
if(timeLeft(1) < callTime)
setTimer(callTime)
if(timeLeft(1) < callTime * engine_coeff)
setTimer(callTime * engine_coeff)
else
destination = S
setTimer(callTime)
setTimer(callTime * engine_coeff)
if(SHUTTLE_RECALL)
if(S == destination)
setTimer(callTime - timeLeft(1))
setTimer(callTime * engine_coeff - timeLeft(1))
else
destination = S
setTimer(callTime)
setTimer(callTime * engine_coeff)
mode = SHUTTLE_CALL
if(SHUTTLE_IDLE, SHUTTLE_IGNITING)
destination = S
@@ -621,7 +624,7 @@
return
else
mode = SHUTTLE_CALL
setTimer(callTime)
setTimer(callTime * engine_coeff)
enterTransit()
return
@@ -685,7 +688,7 @@
var/ds_remaining
if(!timer)
ds_remaining = callTime
ds_remaining = callTime * engine_coeff
else
ds_remaining = max(0, timer - world.time)
@@ -765,5 +768,62 @@
return TRUE
return FALSE
// Losing all initial engines should get you 2
// Adding another set of engines at 0.5 time
/obj/docking_port/mobile/proc/alter_engines(mod)
if(mod == 0)
return
var/old_coeff = engine_coeff
engine_coeff = get_engine_coeff(current_engines,mod)
current_engines = max(0,current_engines + mod)
if(in_flight())
var/delta_coeff = engine_coeff / old_coeff
modTimer(delta_coeff)
/obj/docking_port/mobile/proc/count_engines()
. = 0
for(var/obj/structure/shuttle/engine/E in areaInstance.contents)
if(!QDELETED(E))
. += E.engine_power
// Double initial engines to get to 0.5 minimum
// Lose all initial engines to get to 2
//For 0 engine shuttles like BYOS 5 engines to get to doublespeed
/obj/docking_port/mobile/proc/get_engine_coeff(current,engine_mod)
var/new_value = max(0,current + engine_mod)
if(new_value == initial_engines)
return 1
if(new_value > initial_engines)
var/delta = new_value - initial_engines
var/change_per_engine = (1 - ENGINE_COEFF_MIN) / ENGINE_DEFAULT_MAXSPEED_ENGINES // 5 by default
if(initial_engines > 0)
change_per_engine = (1 - ENGINE_COEFF_MIN) / initial_engines // or however many it had
return Clamp(1 - delta * change_per_engine,ENGINE_COEFF_MIN,ENGINE_COEFF_MAX)
if(new_value < initial_engines)
var/delta = initial_engines - new_value
var/change_per_engine = 1 //doesn't really matter should not be happening for 0 engine shuttles
if(initial_engines > 0)
change_per_engine = (ENGINE_COEFF_MAX - 1) / initial_engines //just linear drop to max delay
return Clamp(1 + delta * change_per_engine,ENGINE_COEFF_MIN,ENGINE_COEFF_MAX)
/obj/docking_port/mobile/proc/in_flight()
switch(mode)
if(SHUTTLE_CALL,SHUTTLE_RECALL)
return TRUE
if(SHUTTLE_IDLE,SHUTTLE_IGNITING)
return FALSE
else
return FALSE // hmm
/obj/docking_port/mobile/emergency/in_flight()
switch(mode)
if(SHUTTLE_ESCAPE)
return TRUE
if(SHUTTLE_STRANDED,SHUTTLE_ENDGAME)
return FALSE
else
return ..()
#undef DOCKING_PORT_HIGHLIGHT
@@ -0,0 +1,4 @@
author: "BeeSting12"
delete-after: True
changes:
- bugfix: "The clowndagger now has the correct skin."
@@ -0,0 +1,5 @@
author: "Steelpoint"
delete-after: True
changes:
- rscadd: "Station Engineers spawn with a Industrial Welder in their toolbelt."
- tweak: "Engineering Welder Locker now only holds three standard Welders."
@@ -0,0 +1,5 @@
author: "Kor"
delete-after: True
changes:
- rscadd: "Added dash weapons, which let you do a short teleport within line of sight."
- rscadd: "The ninjas energy katana now lets him dash. He can no longer teleport with right click."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

-1
View File
@@ -1875,7 +1875,6 @@
#include "code\modules\ninja\suit\n_suit_verbs\ninja_stars.dm"
#include "code\modules\ninja\suit\n_suit_verbs\ninja_stealth.dm"
#include "code\modules\ninja\suit\n_suit_verbs\ninja_sword_recall.dm"
#include "code\modules\ninja\suit\n_suit_verbs\ninja_teleporting.dm"
#include "code\modules\orbit\orbit.dm"
#include "code\modules\paperwork\clipboard.dm"
#include "code\modules\paperwork\contract.dm"
-30
View File
@@ -1,30 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C# Express 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "midi2piano", "midi2piano\midi2piano.csproj", "{68C84B61-F710-491C-BEE8-5E362C167897}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{68C84B61-F710-491C-BEE8-5E362C167897}.Debug|Any CPU.ActiveCfg = Debug|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Debug|Mixed Platforms.Build.0 = Debug|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Debug|x86.ActiveCfg = Debug|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Debug|x86.Build.0 = Debug|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Release|Any CPU.ActiveCfg = Release|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Release|Mixed Platforms.ActiveCfg = Release|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Release|Mixed Platforms.Build.0 = Release|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Release|x86.ActiveCfg = Release|x86
{68C84B61-F710-491C-BEE8-5E362C167897}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
-135
View File
@@ -1,135 +0,0 @@
namespace midi2piano
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.OutputTxt = new System.Windows.Forms.TextBox();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.importMIDIToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.importDlg = new System.Windows.Forms.OpenFileDialog();
this.halpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.menuStrip1.SuspendLayout();
this.SuspendLayout();
//
// OutputTxt
//
this.OutputTxt.Dock = System.Windows.Forms.DockStyle.Fill;
this.OutputTxt.Location = new System.Drawing.Point(0, 24);
this.OutputTxt.Multiline = true;
this.OutputTxt.Name = "OutputTxt";
this.OutputTxt.ReadOnly = true;
this.OutputTxt.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.OutputTxt.Size = new System.Drawing.Size(284, 240);
this.OutputTxt.TabIndex = 0;
//
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem,
this.copyToolStripMenuItem,
this.halpToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(284, 24);
this.menuStrip1.TabIndex = 1;
this.menuStrip1.Text = "menuStrip1";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.importMIDIToolStripMenuItem,
this.exitToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
this.fileToolStripMenuItem.Text = "&File";
//
// importMIDIToolStripMenuItem
//
this.importMIDIToolStripMenuItem.Name = "importMIDIToolStripMenuItem";
this.importMIDIToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.I)));
this.importMIDIToolStripMenuItem.Size = new System.Drawing.Size(184, 22);
this.importMIDIToolStripMenuItem.Text = "&Import MIDI...";
this.importMIDIToolStripMenuItem.Click += new System.EventHandler(this.importMIDIToolStripMenuItem_Click);
//
// exitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.Size = new System.Drawing.Size(184, 22);
this.exitToolStripMenuItem.Text = "E&xit";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
//
// copyToolStripMenuItem
//
this.copyToolStripMenuItem.Name = "copyToolStripMenuItem";
this.copyToolStripMenuItem.Size = new System.Drawing.Size(47, 20);
this.copyToolStripMenuItem.Text = "&Copy";
this.copyToolStripMenuItem.Click += new System.EventHandler(this.copyToolStripMenuItem_Click);
//
// importDlg
//
this.importDlg.Filter = "MIDI File|*.midi;*.mid";
//
// halpToolStripMenuItem
//
this.halpToolStripMenuItem.Name = "halpToolStripMenuItem";
this.halpToolStripMenuItem.Size = new System.Drawing.Size(44, 20);
this.halpToolStripMenuItem.Text = "&Halp";
this.halpToolStripMenuItem.Click += new System.EventHandler(this.halpToolStripMenuItem_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 264);
this.Controls.Add(this.OutputTxt);
this.Controls.Add(this.menuStrip1);
this.MainMenuStrip = this.menuStrip1;
this.Name = "Form1";
this.Text = "MIDI2Piano";
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox OutputTxt;
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem importMIDIToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.OpenFileDialog importDlg;
private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem halpToolStripMenuItem;
}
}
-298
View File
@@ -1,298 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Sanford.Multimedia;
using Sanford.Multimedia.Midi;
namespace midi2piano
{
public partial class Form1 : Form
{
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
public Form1()
{
InitializeComponent();
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
struct PNote
{
public float Length;
public string Note;
public PNote(float length, string note)
{
Length = length;
Note = note;
}
public static readonly PNote Default = new PNote(0, "");
}
private void importMIDIToolStripMenuItem_Click(object sender, EventArgs e)
{
if (importDlg.ShowDialog(this)
== System.Windows.Forms.DialogResult.Cancel)
return;
List<PNote> notes = new List<PNote>();
PNote curNote = PNote.Default;
float tempo = 1;
float timeSig = 4f;
// first, we pull midi data
Sequence s = new Sequence(importDlg.FileName);
// quickly see if there's a piano track first
// and get the tempo as well
int piano = -1;
for (int it = 0; it < s.Count; it++)
{
Track t = s[it];
foreach (MidiEvent me in t.Iterator())
{
switch (me.MidiMessage.MessageType)
{
case MessageType.Channel:
{
ChannelMessage m = (ChannelMessage)me.MidiMessage;
if (m.Command == ChannelCommand.ProgramChange)
if ((GeneralMidiInstrument)m.Data1 == GeneralMidiInstrument.AcousticGrandPiano)
{
piano = it;
}
}
break;
case MessageType.Meta:
{
MetaMessage m = (MetaMessage)me.MidiMessage;
if (m.MetaType == MetaType.Tempo)
tempo = (new TempoChangeBuilder(m)).Tempo;
else if (m.MetaType == MetaType.TimeSignature)
timeSig = new TimeSignatureBuilder(m).Denominator;
}
break;
}
if (piano >= 0)
break;
}
if (piano >= 0)
break;
}
// didn't find one, so just try 0th track anyway
if (piano == -1)
piano = 0;
// now, pull all notes (and tempo)
// and make sure it's a channel that has content
for (int it = piano; it < s.Count; it++)
{
Track t = s[it];
int delta = 0;
foreach (MidiEvent me in t.Iterator())
{
delta += me.DeltaTicks;
switch (me.MidiMessage.MessageType)
{
case MessageType.Channel:
{
ChannelMessage m = (ChannelMessage)me.MidiMessage;
switch (m.Command)
{
case ChannelCommand.NoteOn:
if (curNote.Note != "")
{
curNote.Length = delta / 1000F;
delta = 0;
notes.Add(curNote);
}
curNote.Note = note2Piano(m.Data1);
break;
}
}
break;
case MessageType.Meta:
{
MetaMessage m = (MetaMessage)me.MidiMessage;
if (m.MetaType == MetaType.Tempo)
tempo = (new TempoChangeBuilder(m)).Tempo;
}
break;
}
}
// make sure we get last note
if (curNote.Note != "")
{
curNote.Length = delta / 1000F;
notes.Add(curNote);
}
// we found a track with content!
if (notes.Count > 0)
break;
}
// compress redundant accidentals/octaves
char[] notemods = new char[7];
int[] noteocts = new int[7];
for (int i = 0; i < 7; i++)
{
notemods[i] = 'n';
noteocts[i] = 3;
}
for (int i = 0; i < notes.Count; i++)
{
string noteStr = notes[i].Note;
int cur_note = noteStr[0] - 0x41;
char mod = noteStr[1];
int oct = int.Parse(noteStr.Substring(2));
noteStr = noteStr.Substring(0, 1);
if (mod != notemods[cur_note])
{
noteStr += new string(mod, 1);
notemods[cur_note] = mod;
}
if (oct != noteocts[cur_note])
{
noteStr += oct.ToString();
noteocts[cur_note] = oct;
}
notes[i] = new PNote(notes[i].Length, noteStr);
}
// now, we find what the "beat" length should be,
// by counting numbers of times for each length, and finding statistical mode
Dictionary<float, int> scores = new Dictionary<float, int>();
foreach (PNote n in notes)
{
if (n.Length != 0)
if (scores.Keys.Contains(n.Length))
scores[n.Length]++;
else
scores.Add(n.Length, 1);
}
float winner = 1;
int score = 0;
foreach (KeyValuePair<float, int> kv in scores)
{
if (kv.Value > score)
{
winner = kv.Key;
score = kv.Value;
}
}
// realign all of them to match beat length
for (int i = 0; i < notes.Count; i++)
{
notes[i] = new PNote(notes[i].Length / winner, notes[i].Note);
}
// compress chords down
for (int i = 0; i < notes.Count; i++)
{
if (notes[i].Length == 0 && i < notes.Count - 1)
{
notes[i + 1] = new PNote(notes[i + 1].Length, notes[i].Note + "-" + notes[i + 1].Note);
notes.RemoveAt(i);
i--;
}
}
// add in time
for (int i = 0; i < notes.Count; i++)
{
float len = notes[i].Length;
notes[i] = new PNote(len, notes[i].Note + (len != 1 ? "/" + (1 / len).ToString("0.##") : ""));
}
// what is the bpm, anyway?
int rpm = (int)(28800000 / tempo / winner); // 60 * 1,000,000 * .48 the .48 is because note lengths for some reason midi makes the beat note be .48 long
// now, output!
string line = "";
string output = "";
int lineCount = 1;
foreach (PNote n in notes)
{
if (line.Length + n.Note.Length + 1 > 51)
{
output += line.Substring(0, line.Length - 1) + "\r\n";
line = "";
if (lineCount == 50)
break;
lineCount++;
}
line += n.Note + ",";
}
if (line.Length > 0)
output += line.Substring(0, line.Length - 1);
OutputTxt.Text = "BPM: " + rpm.ToString() + "\r\n" + output;
OutputTxt.SelectAll();
}
public enum NoteNames
{
C = 0,
D = 2,
E = 4,
F = 5,
G = 7,
A = 9,
B = 11
}
string note2Piano(int n)
{
string name, arg, octave;
name = Enum.GetName(typeof(NoteNames), (NoteNames)(n % 12));
if (name == null)
{
name = Enum.GetName(typeof(NoteNames), (NoteNames)((n + 1) % 12));
arg = "b";
}
else
{
arg = "n";
}
octave = (n / 12 - 1).ToString();
return name + arg + octave;
}
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
{
OutputTxt.SelectAll();
OutputTxt.Copy();
}
private void halpToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show(this,
"This program prefers MIDIs that have a single track, otherwise it picks the first piano track it finds, else the first track. Songs with odd tempos may have their BPM's calculated wrong.",
"Halp", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
-126
View File
@@ -1,126 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="importDlg.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>132, 17</value>
</metadata>
</root>
@@ -1,34 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("midi2piano")]
[assembly: AssemblyProduct("midi2piano")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCopyright("Copyright © 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. Only Windows
// assemblies support COM.
[assembly: ComVisible(false)]
// On Windows, the following GUID is for the ID of the typelib if this
// project is exposed to COM. On other platforms, it unique identifies the
// title storage container when deploying this assembly to the device.
[assembly: Guid("9752c562-edc1-40da-8fa1-619df747e0f3")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>midi2piano</RootNamespace>
<AssemblyName>midi2piano</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x86\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x86\Release</OutputPath>
<DefineConstants>TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib">
<Private>False</Private>
</Reference>
<Reference Include="Sanford.Multimedia.Midi, Version=5.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\Sanford.Multimedia.Midi.dll</HintPath>
</Reference>
<Reference Include="System">
<Private>False</Private>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml">
<Private>False</Private>
</Reference>
<Reference Include="System.Core">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
<Private>False</Private>
</Reference>
<Reference Include="System.Net">
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
+31
View File
@@ -0,0 +1,31 @@
This is a remake of 2013 midi2piano tool. Previous version should be considered obsolete.
Requirements:
Python 3
Simply run midi2piano.py and choose midi file you want to convert.
The "sheet music" will be copied to the clipboard.
There are some constants defined at the top of midi2piano.py.
Change their value if needed.
LINE_LENGTH_LIM - max length of line allowed in the sheet music
LINES_LIMIT - max amount of lines allowed in the sheet music. Extra lines will be cropped.
OVERALL_IMPORT_LIM - max amount of characters allowed in the sheet music.
You can also transpose music if you need to
OCTAVE_TRANSPOSE - amount of octaves you melody will be shifted by
FLOAT_PRECISION - read comment
Additional notes:
1. Unlike previous midi2piano, this tool optimizes sheet music to fit more in less lines.
2. If two notes are less than 50 ms apart, they are chorded. BYOND works in 1/10th of a second so 50 ms is time quanta.
4. MIDI event set_tempo is NOT supported. If your MIDI file uses set_tempo to change BPM significantly, consider using some other midi file.
This tool is considered final.
Made by EditorRUS/Delta Epsilon from Animus Station, ss13.ru
Contact me in Discord if you find any major issues: DeltaEpsilon#7787
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
from midi.midi import *
File diff suppressed because it is too large Load Diff
+307
View File
@@ -0,0 +1,307 @@
"""
This module allows user to convert MIDI melodies to SS13 sheet music ready
for copy-and-paste
"""
from functools import reduce
import midi as mi
import easygui as egui
import pyperclip as pclip
LINE_LENGTH_LIM = 50
LINES_LIMIT = 200
OVERALL_IMPORT_LIM = 12000
END_OF_LINE_CHAR = """
""" # BYOND can't parse \n and I am forced to define my own NEWLINE char
OCTAVE_TRANSPOSE = 0 # Change here to transpose melodies by octaves
FLOAT_PRECISION = 2 # Change here to allow more or less numbers after dot in floats
OCTAVE_KEYS = 12
HIGHEST_OCTAVE = 8
"""
class Meta():
version = 1.0
integer = 1
anti_integer = -1
maximum = 1000
epsilon = 0.51
delta_epsilon = -0.1
integral = []
tensor = [[],[],[]]
o_complexity = epsilon**2
random_variance = 0.01
"""
# UTILITY FUNCTIONS
def condition(event):
"""
This function check if given MIDI event is meaningful
"""
if event[0] == 'track_name' and event[2] == 'Drums': # Percussion
return False
if event[0] == 'note': # Only thing that matters
return True
return False
def notenum2string(num, accidentals, octaves):
"""
This function converts given notenum to SS13 note according to previous
runs expressed using _accidentals_ and _octaves_
"""
names = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
convert_table = {1:0, 3:1, 6:2, 8:3, 10:4}
inclusion_table = {0:0, 2:1, 5:2, 7:3, 9:4}
num += OCTAVE_KEYS * OCTAVE_TRANSPOSE
octave = int(num / OCTAVE_KEYS)
if octave < 1 or octave > HIGHEST_OCTAVE:
return ["", accidentals, octaves]
accidentals = accidentals.copy()
octaves = octaves.copy()
output_octaves = list(octaves)
name_indx = num % OCTAVE_KEYS
accidental = (len(names[name_indx]) == 2)
output_octaves[name_indx] = octave
add_n = False
if accidental:
accidentals[convert_table[name_indx]] = True
else:
if name_indx in inclusion_table:
add_n = accidentals[inclusion_table[name_indx]]
accidentals[inclusion_table[name_indx]] = False
return [
(
names[name_indx]+
("n" if add_n else "")+
str((octave if octave != octaves[name_indx] else ""))
),
accidentals,
output_octaves
]
def dur2mod(dur, bpm_mod=1.0):
"""
This functions returns float representation of duration ready to be
added to the note after /
"""
mod = bpm_mod / dur
mod = round(mod, FLOAT_PRECISION)
return str(mod).rstrip('0').rstrip('.')
# END OF UTILITY FUNCTIONS
# CONVERSION FUNCTIONS
def obtain_midi_file():
"""
Asks user to select MIDI and returns this file opened in binary mode for reading
"""
file = egui.fileopenbox(msg='Choose MIDI file to convert',
title='MIDI file selection',
filetypes=[['*.mid', 'MID files']])
if not file:
return None
file = open(file, mode='rb').read()
return file
def midi2score_without_ticks(midi_file):
"""
Transforms aforementioned file into a score, truncates it and returns it
"""
opus = mi.midi2opus(midi_file)
opus = mi.to_millisecs(opus)
score = mi.opus2score(opus)
return score[1:] # Ticks don't matter anymore, it is always 1000
def filter_events_from_score(score):
"""
Filters out irrevelant events and returns new score
"""
return list(map( # For each score track
lambda score_track: list(filter( # Filter irrevelant events
condition,
score_track
)),
score
))
def filter_empty_tracks(score):
"""
Filters out empty tracks and returns new score
"""
return list(filter(
lambda score_track: score_track,
score))
def filter_start_time_and_note_num(score):
"""
Recreates score with only note numbers and start time of each note and returns new score
"""
return list(map(
lambda score_track: list(map(
lambda event: [event[1], event[4]],
score_track)),
score))
def merge_events(score):
"""Merges all tracks together and returns new score"""
return list(reduce(
lambda lst1, lst2: lst1+lst2,
score))
def sort_score_by_event_times(score):
"""Sorts events by start time and returns new score"""
return list(map(
lambda index: score[index],
sorted(
list(range(len(score))),
key=lambda indx: score[indx][0])
))
def convert_into_delta_times(score):
"""
Transform start_time into delta_time and returns new score
"""
return list(map(
lambda super_event: (
[
super_event[1][0]-super_event[0][0],
super_event[0][1]
]), # [ [1, 2], [3, 4] ] -> [ [2, 2] ]
zip(score[:-1], score[1:]) # Shifted association. [1, 2, 3] -> [ (1, 2), (2, 3) ]
))+[[1000, score[-1][1]]] # Add 1 second note to the end
def perform_roundation(score):
"""
Rounds delta times to the nearest multiple of 100 ms as BYOND can't
process duration less than that and returns new score
"""
return list(map(
lambda event: [100*round(event[0]/100), event[1]],
score))
def obtain_common_duration(score):
"""
Returns the most frequent duration throughout the whole melody
"""
# Parse durations and filter out 0s
durs = list(filter(lambda x: x, list(map(lambda event: event[0], score))))
unique_durs = []
for dur in durs:
if dur not in unique_durs:
unique_durs.append(dur)
# How many such durations occur throughout the melody?
counter = [durs.count(dur) for dur in unique_durs]
highest_counter = max(counter) # Highest counter
dur_n_count = list(zip(durs, counter))
dur_n_count = list(filter(lambda e: e[1] == highest_counter, dur_n_count))
return dur_n_count[0][0] # Will be there
def reduce_score_to_chords(score):
"""
Reforms score into a chord-duration list:
[[chord_notes], duration_of_chord]
and returns it
"""
new_score = []
new_chord = [[], 0]
# [ [chord notes], duration of chord ]
for event in score:
new_chord[0].append(event[1]) # Append new note to the chord
if event[0] == 0:
continue # Add new notes to the chord until non-zero duration is hit
new_chord[1] = event[0] # This is the duration of chord
new_score.append(new_chord) # Append chord to the list
new_chord = [[], 0] # Reset the chord
return new_score
def obtain_sheet_music(score, most_frequent_dur):
"""
Returns unformated sheet music from score
"""
result = ""
octaves = [3 for i in range(12)]
accidentals = [False for i in range(7)]
for event in score:
for note_indx in range(len(event[0])):
data = notenum2string(event[0][note_indx], accidentals, octaves)
result += data[0]
accidentals = data[1]
octaves = data[2]
if note_indx != len(event[0])-1:
result += '-'
if event[1] != most_frequent_dur: # Quarters are default
result += '/'
result += dur2mod(event[1], most_frequent_dur)
result += ','
return result
def explode_sheet_music(sheet_music):
"""
Splits unformatted sheet music into formated lines of LINE_LEN_LIM
and such and returns a list of such lines
"""
split_music = sheet_music.split(',')
split_music = list(map(lambda note: note+',', split_music))
split_list = []
counter = 0
line_counter = 1
for note in split_music:
if line_counter > LINES_LIMIT-1:
break
if counter+len(note) > LINE_LENGTH_LIM-2:
last_note_num = len(split_list)-1
split_list[last_note_num] = split_list[last_note_num].rstrip(',')
split_list[last_note_num] += END_OF_LINE_CHAR
counter = 0
line_counter += 1
split_list.append(note)
counter += len(note)
return split_list
def finalize_sheet_music(split_music, most_frequent_dur):
"""
Recreates sheet music from exploded sheet music, truncates it and returns it
"""
sheet_music = ""
for note in split_music:
sheet_music += note
sheet_music = sheet_music.rstrip(',') # Trim the last ,
sheet_music = "BPM: " + str(int(60000 / most_frequent_dur)) + END_OF_LINE_CHAR + sheet_music
return sheet_music[:min(len(sheet_music), OVERALL_IMPORT_LIM)]
# END OF CONVERSION FUNCTIONS
def main_cycle():
"""
Activate the script
"""
while True:
midi_file = obtain_midi_file()
if not midi_file:
return # Cancel
score = midi2score_without_ticks(midi_file)
score = filter_events_from_score(score)
score = filter_start_time_and_note_num(score)
score = filter_empty_tracks(score)
score = merge_events(score)
score = sort_score_by_event_times(score)
score = convert_into_delta_times(score)
score = perform_roundation(score)
most_frequent_dur = obtain_common_duration(score)
score = reduce_score_to_chords(score)
sheet_music = obtain_sheet_music(score, most_frequent_dur)
split_music = explode_sheet_music(sheet_music)
sheet_music = finalize_sheet_music(split_music, most_frequent_dur)
pclip.copy(sheet_music)
main_cycle()
+103
View File
@@ -0,0 +1,103 @@
"""
Pyperclip
A cross-platform clipboard module for Python. (only handles plain text for now)
By Al Sweigart al@inventwithpython.com
BSD License
Usage:
import pyperclip
pyperclip.copy('The text to be copied to the clipboard.')
spam = pyperclip.paste()
if not pyperclip.copy:
print("Copy functionality unavailable!")
On Windows, no additional modules are needed.
On Mac, the module uses pbcopy and pbpaste, which should come with the os.
On Linux, install xclip or xsel via package manager. For example, in Debian:
sudo apt-get install xclip
Otherwise on Linux, you will need the gtk or PyQt4 modules installed.
gtk and PyQt4 modules are not available for Python 3,
and this module does not work with PyGObject yet.
"""
__version__ = '1.5.27'
import platform
import os
import subprocess
from .clipboards import (init_osx_clipboard,
init_gtk_clipboard, init_qt_clipboard,
init_xclip_clipboard, init_xsel_clipboard,
init_klipper_clipboard, init_no_clipboard)
from .windows import init_windows_clipboard
# `import PyQt4` sys.exit()s if DISPLAY is not in the environment.
# Thus, we need to detect the presence of $DISPLAY manually
# and not load PyQt4 if it is absent.
HAS_DISPLAY = os.getenv("DISPLAY", False)
CHECK_CMD = "where" if platform.system() == "Windows" else "which"
def _executable_exists(name):
return subprocess.call([CHECK_CMD, name],
stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
def determine_clipboard():
# Determine the OS/platform and set
# the copy() and paste() functions accordingly.
if 'cygwin' in platform.system().lower():
# FIXME: pyperclip currently does not support Cygwin,
# see https://github.com/asweigart/pyperclip/issues/55
pass
elif os.name == 'nt' or platform.system() == 'Windows':
return init_windows_clipboard()
if os.name == 'mac' or platform.system() == 'Darwin':
return init_osx_clipboard()
if HAS_DISPLAY:
# Determine which command/module is installed, if any.
try:
import gtk # check if gtk is installed
except ImportError:
pass
else:
return init_gtk_clipboard()
try:
import PyQt4 # check if PyQt4 is installed
except ImportError:
pass
else:
return init_qt_clipboard()
if _executable_exists("xclip"):
return init_xclip_clipboard()
if _executable_exists("xsel"):
return init_xsel_clipboard()
if _executable_exists("klipper") and _executable_exists("qdbus"):
return init_klipper_clipboard()
return init_no_clipboard()
def set_clipboard(clipboard):
global copy, paste
clipboard_types = {'osx': init_osx_clipboard,
'gtk': init_gtk_clipboard,
'qt': init_qt_clipboard,
'xclip': init_xclip_clipboard,
'xsel': init_xsel_clipboard,
'klipper': init_klipper_clipboard,
'windows': init_windows_clipboard,
'no': init_no_clipboard}
copy, paste = clipboard_types[clipboard]()
copy, paste = determine_clipboard()
__all__ = ["copy", "paste"]
@@ -0,0 +1,134 @@
import sys
import subprocess
from .exceptions import PyperclipException
EXCEPT_MSG = """
Pyperclip could not find a copy/paste mechanism for your system.
For more information, please visit https://pyperclip.readthedocs.org """
PY2 = sys.version_info[0] == 2
text_type = unicode if PY2 else str
def init_osx_clipboard():
def copy_osx(text):
p = subprocess.Popen(['pbcopy', 'w'],
stdin=subprocess.PIPE, close_fds=True)
p.communicate(input=text.encode('utf-8'))
def paste_osx():
p = subprocess.Popen(['pbpaste', 'r'],
stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
return stdout.decode('utf-8')
return copy_osx, paste_osx
def init_gtk_clipboard():
import gtk
def copy_gtk(text):
global cb
cb = gtk.Clipboard()
cb.set_text(text)
cb.store()
def paste_gtk():
clipboardContents = gtk.Clipboard().wait_for_text()
# for python 2, returns None if the clipboard is blank.
if clipboardContents is None:
return ''
else:
return clipboardContents
return copy_gtk, paste_gtk
def init_qt_clipboard():
# $DISPLAY should exist
from PyQt4.QtGui import QApplication
app = QApplication([])
def copy_qt(text):
cb = app.clipboard()
cb.setText(text)
def paste_qt():
cb = app.clipboard()
return text_type(cb.text())
return copy_qt, paste_qt
def init_xclip_clipboard():
def copy_xclip(text):
p = subprocess.Popen(['xclip', '-selection', 'c'],
stdin=subprocess.PIPE, close_fds=True)
p.communicate(input=text.encode('utf-8'))
def paste_xclip():
p = subprocess.Popen(['xclip', '-selection', 'c', '-o'],
stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
return stdout.decode('utf-8')
return copy_xclip, paste_xclip
def init_xsel_clipboard():
def copy_xsel(text):
p = subprocess.Popen(['xsel', '-b', '-i'],
stdin=subprocess.PIPE, close_fds=True)
p.communicate(input=text.encode('utf-8'))
def paste_xsel():
p = subprocess.Popen(['xsel', '-b', '-o'],
stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
return stdout.decode('utf-8')
return copy_xsel, paste_xsel
def init_klipper_clipboard():
def copy_klipper(text):
p = subprocess.Popen(
['qdbus', 'org.kde.klipper', '/klipper', 'setClipboardContents',
text.encode('utf-8')],
stdin=subprocess.PIPE, close_fds=True)
p.communicate(input=None)
def paste_klipper():
p = subprocess.Popen(
['qdbus', 'org.kde.klipper', '/klipper', 'getClipboardContents'],
stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
# Workaround for https://bugs.kde.org/show_bug.cgi?id=342874
# TODO: https://github.com/asweigart/pyperclip/issues/43
clipboardContents = stdout.decode('utf-8')
# even if blank, Klipper will append a newline at the end
assert len(clipboardContents) > 0
# make sure that newline is there
assert clipboardContents.endswith('\n')
if clipboardContents.endswith('\n'):
clipboardContents = clipboardContents[:-1]
return clipboardContents
return copy_klipper, paste_klipper
def init_no_clipboard():
class ClipboardUnavailable(object):
def __call__(self, *args, **kwargs):
raise PyperclipException(EXCEPT_MSG)
if PY2:
def __nonzero__(self):
return False
else:
def __bool__(self):
return False
return ClipboardUnavailable(), ClipboardUnavailable()
@@ -0,0 +1,11 @@
import ctypes
class PyperclipException(RuntimeError):
pass
class PyperclipWindowsException(PyperclipException):
def __init__(self, message):
message += " (%s)" % ctypes.WinError()
super(PyperclipWindowsException, self).__init__(message)
+151
View File
@@ -0,0 +1,151 @@
"""
This module implements clipboard handling on Windows using ctypes.
"""
import time
import contextlib
import ctypes
from ctypes import c_size_t, sizeof, c_wchar_p, get_errno, c_wchar
from .exceptions import PyperclipWindowsException
class CheckedCall(object):
def __init__(self, f):
super(CheckedCall, self).__setattr__("f", f)
def __call__(self, *args):
ret = self.f(*args)
if not ret and get_errno():
raise PyperclipWindowsException("Error calling " + self.f.__name__)
return ret
def __setattr__(self, key, value):
setattr(self.f, key, value)
def init_windows_clipboard():
from ctypes.wintypes import (HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND,
HINSTANCE, HMENU, BOOL, UINT, HANDLE)
windll = ctypes.windll
safeCreateWindowExA = CheckedCall(windll.user32.CreateWindowExA)
safeCreateWindowExA.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD, INT, INT,
INT, INT, HWND, HMENU, HINSTANCE, LPVOID]
safeCreateWindowExA.restype = HWND
safeDestroyWindow = CheckedCall(windll.user32.DestroyWindow)
safeDestroyWindow.argtypes = [HWND]
safeDestroyWindow.restype = BOOL
OpenClipboard = windll.user32.OpenClipboard
OpenClipboard.argtypes = [HWND]
OpenClipboard.restype = BOOL
safeCloseClipboard = CheckedCall(windll.user32.CloseClipboard)
safeCloseClipboard.argtypes = []
safeCloseClipboard.restype = BOOL
safeEmptyClipboard = CheckedCall(windll.user32.EmptyClipboard)
safeEmptyClipboard.argtypes = []
safeEmptyClipboard.restype = BOOL
safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData)
safeGetClipboardData.argtypes = [UINT]
safeGetClipboardData.restype = HANDLE
safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData)
safeSetClipboardData.argtypes = [UINT, HANDLE]
safeSetClipboardData.restype = HANDLE
safeGlobalAlloc = CheckedCall(windll.kernel32.GlobalAlloc)
safeGlobalAlloc.argtypes = [UINT, c_size_t]
safeGlobalAlloc.restype = HGLOBAL
safeGlobalLock = CheckedCall(windll.kernel32.GlobalLock)
safeGlobalLock.argtypes = [HGLOBAL]
safeGlobalLock.restype = LPVOID
safeGlobalUnlock = CheckedCall(windll.kernel32.GlobalUnlock)
safeGlobalUnlock.argtypes = [HGLOBAL]
safeGlobalUnlock.restype = BOOL
GMEM_MOVEABLE = 0x0002
CF_UNICODETEXT = 13
@contextlib.contextmanager
def window():
"""
Context that provides a valid Windows hwnd.
"""
# we really just need the hwnd, so setting "STATIC"
# as predefined lpClass is just fine.
hwnd = safeCreateWindowExA(0, b"STATIC", None, 0, 0, 0, 0, 0,
None, None, None, None)
try:
yield hwnd
finally:
safeDestroyWindow(hwnd)
@contextlib.contextmanager
def clipboard(hwnd):
"""
Context manager that opens the clipboard and prevents
other applications from modifying the clipboard content.
"""
# We may not get the clipboard handle immediately because
# some other application is accessing it (?)
# We try for at least 500ms to get the clipboard.
t = time.time() + 0.5
success = False
while time.time() < t:
success = OpenClipboard(hwnd)
if success:
break
time.sleep(0.01)
if not success:
raise PyperclipWindowsException("Error calling OpenClipboard")
try:
yield
finally:
safeCloseClipboard()
def copy_windows(text):
# This function is heavily based on
# http://msdn.com/ms649016#_win32_Copying_Information_to_the_Clipboard
with window() as hwnd:
# http://msdn.com/ms649048
# If an application calls OpenClipboard with hwnd set to NULL,
# EmptyClipboard sets the clipboard owner to NULL;
# this causes SetClipboardData to fail.
# => We need a valid hwnd to copy something.
with clipboard(hwnd):
safeEmptyClipboard()
if text:
# http://msdn.com/ms649051
# If the hMem parameter identifies a memory object,
# the object must have been allocated using the
# function with the GMEM_MOVEABLE flag.
count = len(text) + 1
handle = safeGlobalAlloc(GMEM_MOVEABLE,
count * sizeof(c_wchar))
locked_handle = safeGlobalLock(handle)
ctypes.memmove(c_wchar_p(locked_handle), c_wchar_p(text), count * sizeof(c_wchar))
safeGlobalUnlock(handle)
safeSetClipboardData(CF_UNICODETEXT, handle)
def paste_windows():
with clipboard(None):
handle = safeGetClipboardData(CF_UNICODETEXT)
if not handle:
# GetClipboardData may return NULL with errno == NO_ERROR
# if the clipboard is empty.
# (Also, it may return a handle to an empty buffer,
# but technically that's not empty)
return ""
return c_wchar_p(handle).value
return copy_windows, paste_windows