Sparring Sect! (#61316)

Adds the sparring god. Your deity wants you to go out and spar people for self improvement.

Sparring
You can create contracts from paper as the chaplain. The contracts come with a set of conditions for the battle.

You can set which weapons are allowed.

You can set what you earn from winning.

You can set where you fight.

BOTH participants in the sparring match must agree to the terms for the fight to begin. Changing the terms will remove any other signatures on the contract, requiring them to review and willingly sign again.

Setting the stakes to be holy will give you favor for winning the fight. You can only do this once per person, so you can't farm points. You can use points on declaring new areas as arenas, or if you save up enough, buff your heart to keep fighting in dangerous conditions.
This commit is contained in:
tralezab
2021-09-17 21:04:47 +01:00
committed by GitHub
parent a7a5ec9302
commit b69d87a76e
28 changed files with 1128 additions and 35 deletions
+4
View File
@@ -501,6 +501,8 @@
#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size"
///Called when a movable is hit by a plunger in layer mode, from /obj/item/plunger/attack_atom()
#define COMSIG_MOVABLE_CHANGE_DUCT_LAYER "movable_change_duct_layer"
///Called when a movable is teleported from `do_teleport()`: (destination, channel)
#define COMSIG_MOVABLE_TELEPORTED "movable_teleported"
// /mob signals
@@ -1015,6 +1017,8 @@
///called in /obj/item/gun/process_fire (user, target, params, zone_override)
#define COMSIG_GRENADE_DETONATE "grenade_prime"
//called from many places in grenade code (armed_by, nade, det_time, delayoverride)
#define COMSIG_MOB_GRENADE_ARMED "grenade_mob_armed"
///called in /obj/item/gun/process_fire (user, target, params, zone_override)
#define COMSIG_GRENADE_ARMED "grenade_armed"
-12
View File
@@ -485,18 +485,6 @@ GLOBAL_LIST_INIT(pda_styles, sortList(list(MONO, VT, ORBITRON, SHARE)))
#define FALL_NO_MESSAGE (1<<1) //Used to suppress the "[A] falls through [old_turf]" messages where it'd make little sense at all, like going downstairs.
#define FALL_STOP_INTERCEPTING (1<<2) //Used in situations where halting the whole "intercept" loop would be better, like supermatter dusting (and thus deleting) the atom.
//Religion
///role below priests, for losing most powers of priests but still being holy.
#define HOLY_ROLE_DEACON 1
///default priestly role
#define HOLY_ROLE_PRIEST 2
///the one who designates the religion
#define HOLY_ROLE_HIGHPRIEST 3
#define ALIGNMENT_GOOD "good"
#define ALIGNMENT_NEUT "neutral"
#define ALIGNMENT_EVIL "evil"
// Play time / EXP
#define PLAYTIME_HARDCORE_RANDOM 120
+50
View File
@@ -0,0 +1,50 @@
///role below priests, for losing most powers of priests but still being holy.
#define HOLY_ROLE_DEACON 1
///default priestly role
#define HOLY_ROLE_PRIEST 2
///the one who designates the religion
#define HOLY_ROLE_HIGHPRIEST 3
#define ALIGNMENT_GOOD "good"
#define ALIGNMENT_NEUT "neutral"
#define ALIGNMENT_EVIL "evil"
//## which weapons should we use?
// unused but for clarity
#define CONDITION_FIST_FIGHT 1
///can only use the ritual weapons the sparring chaplain makes.
#define CONDITION_CEREMONIAL_ONLY 2
///melee weapon condition, default sparring condition.
#define CONDITION_MELEE_ONLY 3
///any weapon is cool... probably a terrible idea against security
#define CONDITION_ANY_WEAPON 4
//
///must use weapons the chaplain makes from their sect. no fist fighting, even! it ensures a fair fight.
// #define RITUAL_WEAPONS 2
//## where should we fight?
// default value - /area/service/chapel
//## what are the stakes? people you've beaten before can only fight in no stakes battles, to prevent farming
///just for fun
#define STAKES_NONE 1
///standard stakes, winning gets you a point. losing counts towards standard excommunication.
#define STAKES_HOLY_MATCH 2
///no stakes god wise, but whomever wins gets all the money of the other
#define STAKES_MONEY_MATCH 3
///the winner gets the other's soul. you said this was a neutral sect, right?
#define STAKES_YOUR_SOUL 4
///the left signing part of the contract
#define CONTRACT_LEFT_FIELD "left"
///curses the sinner
#define PUNISHMENT_OMEN "omen"
///smites the sinner
#define PUNISHMENT_LIGHTNING "lightningbolt"
///brands the sinner
#define PUNISHMENT_BRAND "brand"
+8
View File
@@ -325,6 +325,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BYPASS_MEASURES "bypass_lagswitch_measures"
/// Someone can safely be attacked with honorbound with ONLY a combat mode check, the trait is assuring holding a weapon and hitting won't hurt them..
#define TRAIT_ALLOWED_HONORBOUND_ATTACK "allowed_honorbound_attack"
/// The user is sparring
#define TRAIT_SPARRING "sparring"
#define TRAIT_NOBLEED "nobleed" //This carbon doesn't bleed
/// This atom can ignore the "is on a turf" check for simple AI datum attacks, allowing them to attack from bags or lockers as long as any other conditions are met
@@ -526,6 +528,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// If present on a [/mob/living/carbon], will make them appear to have a medium level disease on health HUDs.
#define TRAIT_DISEASELIKE_SEVERITY_MEDIUM "diseaselike_severity_medium"
/// trait denoting someone will crawl faster in soft crit
#define TRAIT_TENACIOUS "tenacious"
/// trait denoting someone will sometimes recover out of crit
#define TRAIT_UNBREAKABLE "unbreakable"
//Medical Categories for quirks
#define CAT_QUIRK_ALL 0
#define CAT_QUIRK_NOTES 1
+31
View File
@@ -0,0 +1,31 @@
/datum/component/unbreakable
COOLDOWN_DECLARE(surge_cooldown)
/datum/component/unbreakable/Initialize()
if(!ishuman(parent))
return COMPONENT_INCOMPATIBLE
ADD_TRAIT(parent, TRAIT_UNBREAKABLE, INNATE_TRAIT)
/datum/component/unbreakable/Destroy(force, silent)
. = ..()
REMOVE_TRAIT(parent, TRAIT_UNBREAKABLE, INNATE_TRAIT)
/datum/component/unbreakable/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_STATCHANGE, .proc/surge)
/datum/component/unbreakable/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_MOB_STATCHANGE)
/datum/component/unbreakable/proc/surge(mob/living/carbon/human/surged, new_stat)
SIGNAL_HANDLER
if(new_stat < SOFT_CRIT || new_stat >= DEAD)
return
if(!COOLDOWN_FINISHED(src, surge_cooldown))
return
COOLDOWN_START(src, surge_cooldown, 1 MINUTES)
surged.balloon_alert(surged, "you refuse to give up!")//breaks balloon alert conventions by using a "!" for a fail message but that's okay because it's a pretty awesome moment
surged.heal_overall_damage(15, 15, 0, BODYPART_ORGANIC)
if(surged.reagents.get_reagent_amount(/datum/reagent/medicine/ephedrine) < 20)
surged.reagents.add_reagent(/datum/reagent/medicine/ephedrine, 10)
if(surged.reagents.get_reagent_amount(/datum/reagent/medicine/epinephrine) < 20)
surged.reagents.add_reagent(/datum/reagent/medicine/epinephrine, 10)
+33
View File
@@ -0,0 +1,33 @@
/**
* tenacious element; which makes the parent move faster while crawling
*
* Used by sparring sect!
*/
/datum/element/tenacious
element_flags = ELEMENT_DETACH
/datum/element/tenacious/Attach(datum/target)
. = ..()
if(!ishuman(target))
return COMPONENT_INCOMPATIBLE
var/mob/living/carbon/human/valid_target = target
on_stat_change(valid_target, new_stat = valid_target.stat) //immediately try adding movement bonus if they're in soft crit
RegisterSignal(target, COMSIG_MOB_STATCHANGE, .proc/on_stat_change)
ADD_TRAIT(target, TRAIT_TENACIOUS, INNATE_TRAIT)
/datum/element/tenacious/Detach(datum/target)
. = ..()
UnregisterSignal(target, COMSIG_MOB_STATCHANGE)
REMOVE_TRAIT(target, TRAIT_TENACIOUS, INNATE_TRAIT)
///signal called by the stat of the target changing
/datum/element/tenacious/proc/on_stat_change(mob/living/carbon/human/target, new_stat)
SIGNAL_HANDLER
if(new_stat == SOFT_CRIT)
target.balloon_alert(target, "your tenacity kicks in")
target.add_movespeed_modifier(/datum/movespeed_modifier/tenacious)
else
target.balloon_alert(target, "your tenacity wears off")
target.remove_movespeed_modifier(/datum/movespeed_modifier/tenacious)
@@ -283,6 +283,21 @@
icon_file = 'icons/obj/items/cleric_mace.dmi'
json_config = 'code/datums/greyscale/json_configs/items/cleric_mace_worn_gold.json'
/datum/greyscale_config/ceremonial_blade
name = "Base Ceremonial Blade"
icon_file = 'icons/obj/items/ritual_weapon.dmi'
json_config = 'code/datums/greyscale/json_configs/items/ceremonial_blade.json'
/datum/greyscale_config/ceremonial_blade_lefthand
name = "Base Held Ceremonial Blade, Left"
icon_file = 'icons/obj/items/ritual_weapon.dmi'
json_config = 'code/datums/greyscale/json_configs/items/ceremonial_blade_lefthand.json'
/datum/greyscale_config/ceremonial_blade_righthand
name = "Base Held Ceremonial Blade, Right"
icon_file = 'icons/obj/items/ritual_weapon.dmi'
json_config = 'code/datums/greyscale/json_configs/items/ceremonial_blade_righthand.json'
/datum/greyscale_config/beret
name = "Beret"
icon_file = 'icons/obj/clothing/head/beret.dmi'
@@ -0,0 +1,15 @@
{
"default": [
{
"type": "icon_state",
"icon_state": "base",
"blend_mode": "overlay",
"color_ids": [ 1 ]
},
{
"type": "icon_state",
"icon_state": "handle",
"blend_mode": "overlay"
}
]
}
@@ -0,0 +1,10 @@
{
"default": [
{
"type": "icon_state",
"icon_state": "inhand_left",
"blend_mode": "overlay",
"color_ids": [ 1 ]
}
]
}
@@ -0,0 +1,10 @@
{
"default": [
{
"type": "icon_state",
"icon_state": "inhand_right",
"blend_mode": "overlay",
"color_ids": [ 1 ]
}
]
}
+2
View File
@@ -26,6 +26,8 @@
if (isnull(precision))
precision = 0
SEND_SIGNAL(teleatom, COMSIG_MOVABLE_TELEPORTED, destination, channel)
switch(channel)
if(TELEPORT_CHANNEL_BLUESPACE)
if(istype(teleatom, /obj/item/storage/backpack/holding))
@@ -199,15 +199,6 @@
lightningbolt(user)
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "honorbound", /datum/mood_event/holy_smite)//permanently lose your moodlet after this
/datum/mutation/human/honorbound/proc/lightningbolt(mob/living/user)
var/turf/lightning_source = get_step(get_step(user, NORTH), NORTH)
lightning_source.Beam(user, icon_state="lightning[rand(1,12)]", time = 5)
user.adjustFireLoss(LIGHTNING_BOLT_DAMAGE)
playsound(get_turf(user), 'sound/magic/lightningbolt.ogg', 50, TRUE)
if(ishuman(user))
var/mob/living/carbon/human/human_target = user
human_target.electrocution_animation(LIGHTNING_BOLT_ELECTROCUTION_ANIMATION_LENGTH)
/obj/effect/proc_holder/spell/pointed/declare_evil
name = "Declare Evil"
desc = "If someone is so obviously an evil of this world you can spend a huge amount of favor to declare them guilty."
+7
View File
@@ -299,3 +299,10 @@
infestation_rate = 0.075 // appx 4.33 minutes to reach sepsis without any treatment
flesh_damage = 20
scar_keyword = "burncritical"
///special severe wound caused by sparring interference or other god related punishments.
/datum/wound/burn/severe/brand
name = "Holy Brand"
desc = "Patient is suffering extreme burns from a strange brand marking, creating serious risk of infection and greatly reduced limb integrity."
examine_desc = "appears to have holy symbols painfully branded into their flesh, leaving severe burns."
occur_text = "chars rapidly into a strange pattern of holy symbols, burned into the flesh."
@@ -19,6 +19,8 @@
icon_state = initial(icon_state) + "_active"
playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', volume, TRUE)
SEND_SIGNAL(src, COMSIG_GRENADE_ARMED, det_time, delayoverride)
if(user)
SEND_SIGNAL(src, COMSIG_MOB_GRENADE_ARMED, user, src, det_time, delayoverride)
addtimer(CALLBACK(src, .proc/detonate), isnull(delayoverride)? det_time : delayoverride)
/obj/item/grenade/gas_crystal/healium_crystal
+11 -7
View File
@@ -4,11 +4,15 @@
/datum/smite/lightning/effect(client/user, mob/living/target)
. = ..()
var/turf/lightning_source = get_step(get_step(target, NORTH), NORTH)
lightning_source.Beam(target, icon_state="lightning[rand(1,12)]", time = 5)
target.adjustFireLoss(LIGHTNING_BOLT_DAMAGE)
playsound(get_turf(user), 'sound/magic/lightningbolt.ogg', 50, TRUE)
if(ishuman(target))
var/mob/living/carbon/human/human_target = target
human_target.electrocution_animation(LIGHTNING_BOLT_ELECTROCUTION_ANIMATION_LENGTH)
lightningbolt(target)
to_chat(target, span_userdanger("The gods have punished you for your sins!"), confidential = TRUE)
///this is the actual bolt effect and damage, made into its own proc because it is used elsewhere
/proc/lightningbolt(mob/living/user)
var/turf/lightning_source = get_step(get_step(user, NORTH), NORTH)
lightning_source.Beam(user, icon_state="lightning[rand(1,12)]", time = 5)
user.adjustFireLoss(LIGHTNING_BOLT_DAMAGE)
playsound(get_turf(user), 'sound/magic/lightningbolt.ogg', 50, TRUE)
if(ishuman(user))
var/mob/living/carbon/human/human_target = user
human_target.electrocution_animation(LIGHTNING_BOLT_ELECTROCUTION_ANIMATION_LENGTH)
+2 -2
View File
@@ -123,8 +123,8 @@
/datum/antagonist/monkey/leader/on_gain()
. = ..()
var/obj/item/organ/heart/freedom/F = new
F.Insert(owner.current, drop_if_replaced = FALSE)
var/obj/item/organ/heart/freedom/super_heart = new
super_heart.Insert(owner.current, drop_if_replaced = FALSE)
owner.special_role = "Monkey Leader"
/datum/antagonist/monkey/leader/on_removal()
@@ -88,6 +88,15 @@
one_use = TRUE
grab_sleeping = FALSE
/obj/item/soulstone/anybody/chaplain/sparring
icon_state = "purified_soulstone"
theme = THEME_HOLY
/obj/item/soulstone/anybody/sparring/Initialize(mapload)
. = ..()
name = "[GLOB.deity]'s punishment"
desc = "A prison for those who lost [GLOB.deity]'s game."
/obj/item/soulstone/anybody/mining
grab_sleeping = FALSE
@@ -260,7 +269,8 @@
return TRUE
else
to_chat(user, "[span_userdanger("Capture failed!")]: The soul has already fled its mortal frame. You attempt to bring it back...")
return getCultGhost(victim,user)
INVOKE_ASYNC(src, .proc/getCultGhost, victim, user)
return TRUE //it'll probably get someone ;)
///captures a shade that was previously released from a soulstone.
/obj/item/soulstone/proc/capture_shade(mob/living/simple_animal/shade/shade, mob/user)
@@ -7,6 +7,10 @@
multiplicative_slowdown = -7
movetypes = GROUND
/datum/movespeed_modifier/tenacious
multiplicative_slowdown = -0.7
movetypes = GROUND
/datum/movespeed_modifier/sanity
id = MOVESPEED_ID_SANITY
movetypes = (~FLYING)
+29
View File
@@ -377,3 +377,32 @@
return TRUE
#undef MINIMUM_YUCK_REQUIRED
/datum/religion_sect/spar
name = "Sparring God"
quote = "Your next swing must be faster, neophyte. Steel your heart."
desc = "Spar other crewmembers to gain favor or other rewards. Exchange favor to steel yourself against real battles."
tgui_icon = "fist-raised"
altar_icon_state = "convertaltar-orange"
alignment = ALIGNMENT_NEUT
rites_list = list(
/datum/religion_rites/sparring_contract,
/datum/religion_rites/ceremonial_weapon,
/datum/religion_rites/declare_arena,
/datum/religion_rites/tenacious,
/datum/religion_rites/unbreakable,
)
///the one allowed contract. making a new contract dusts the old one
var/obj/item/sparring_contract/existing_contract
///places you can spar in. rites can be used to expand this list with new arenas!
var/list/arenas = list(
"Recreation Area" = /area/commons/fitness/recreation,
"Chapel" = /area/service/chapel
)
///how many matches you've lost with holy stakes. 3 = excommunication
var/matches_lost = 0
///past opponents who you've beaten in holy battles. You can't fight them again to prevent favor farming
var/list/past_opponents = list()
/datum/religion_sect/spar/tool_examine(mob/living/holy_creature)
return "You have [round(favor)] sparring matches won in [GLOB.deity]'s name to redeem. You have lost [matches_lost] holy matches. You will be excommunicated after losing three matches."
+138 -1
View File
@@ -117,7 +117,7 @@
ritual_invocations =list( "Let your will power our forges.",
"...Help us in our great conquest!")
invoke_msg = "The end of flesh is near!"
favor_cost = 2000
favor_cost = 2000
/datum/religion_rites/machine_blessing/invoke_effect(mob/living/user, atom/movable/religious_tool)
..()
@@ -603,4 +603,141 @@
new /obj/item/ritual_totem(altar_turf)
return TRUE
///sparring god rites
/datum/religion_rites/sparring_contract
name = "Summon Sparring Contract"
desc = "Turns some paper into a sparring contract."
invoke_msg = "I will train in the name of my god."
///paper to turn into a sparring contract
var/obj/item/paper/contract_target
/datum/religion_rites/sparring_contract/perform_rite(mob/living/user, atom/religious_tool)
for(var/obj/item/paper/could_contract in get_turf(religious_tool))
if(could_contract.info) //blank paper pls
continue
contract_target = could_contract
return ..()
to_chat(user, span_warning("You need to place blank paper on [religious_tool] to do this!"))
return FALSE
/datum/religion_rites/sparring_contract/invoke_effect(mob/living/user, atom/movable/religious_tool)
..()
var/obj/item/paper/blank_paper = contract_target
var/turf/tool_turf = get_turf(religious_tool)
contract_target = null
if(QDELETED(blank_paper) || !(tool_turf == blank_paper.loc)) //check if the same paper is still there
to_chat(user, span_warning("Your target left the altar!"))
return FALSE
blank_paper.visible_message(span_notice("words magically form on [blank_paper]!"))
playsound(tool_turf, 'sound/effects/pray.ogg', 50, TRUE)
var/datum/religion_sect/spar/sect = GLOB.religious_sect
if(sect.existing_contract)
sect.existing_contract.visible_message(span_warning("[src] fizzles into nothing!"))
qdel(sect.existing_contract)
sect.existing_contract = new /obj/item/sparring_contract(tool_turf)
qdel(blank_paper)
return TRUE
/datum/religion_rites/declare_arena
name = "Declare Arena"
desc = "Declare a new area as fit for sparring. You'll be able to select it in contracts."
ritual_length = 6 SECONDS
ritual_invocations = list("I seek new horizons ...")
invoke_msg = "... may my climb be steep."
favor_cost = 1 //only costs one holy battle for a new area
var/area/area_instance
/datum/religion_rites/declare_arena/perform_rite(mob/living/user, atom/religious_tool)
var/list/filtered = list()
for(var/area/unfiltered_area as anything in GLOB.sortedAreas)
if(istype(unfiltered_area, /area/centcom)) //youuu dont need thaaat
continue
if(!(unfiltered_area.area_flags & HIDDEN_AREA))
filtered += unfiltered_area
area_instance = tgui_input_list(user, "Choose an area to mark as an arena!", "Arena Declaration", filtered)
if(!area_instance)
return FALSE
. = ..()
/datum/religion_rites/declare_arena/invoke_effect(mob/living/user, atom/movable/religious_tool)
. = ..()
var/datum/religion_sect/spar/sect = GLOB.religious_sect
sect.arenas[area_instance.name] = area_instance.type
to_chat(user, span_warning("[area_instance] is a now an option to select on sparring contracts."))
/datum/religion_rites/ceremonial_weapon
name = "Forge Ceremonial Gear"
desc = "Turn some material into ceremonial gear. Ceremonial blades are weak outside of sparring, and are quite heavy to lug around."
ritual_length = 10 SECONDS
invoke_msg = "Weapons in your name! Battles with your blood!"
favor_cost = 0
///the material that will be attempted to be forged into a weapon
var/obj/item/stack/sheet/converted
/datum/religion_rites/ceremonial_weapon/perform_rite(mob/living/user, atom/religious_tool)
for(var/obj/item/stack/sheet/could_blade in get_turf(religious_tool))
if(!(GET_MATERIAL_REF(could_blade.material_type) in SSmaterials.materials_by_category[MAT_CATEGORY_ITEM_MATERIAL]))
continue
if(could_blade.amount < 5)
continue
converted = could_blade
return ..()
to_chat(user, span_warning("You need at least 5 sheets of a material that can be made into items!"))
return FALSE
/datum/religion_rites/ceremonial_weapon/invoke_effect(mob/living/user, atom/movable/religious_tool)
..()
var/altar_turf = get_turf(religious_tool)
var/obj/item/stack/sheet/used_for_blade = converted
converted = null
if(QDELETED(used_for_blade) || !(get_turf(religious_tool) == used_for_blade.loc) || used_for_blade.amount < 5) //check if the same food is still there
to_chat(user, span_warning("Your target left the altar!"))
return FALSE
var/material_used = used_for_blade.material_type
to_chat(user, span_warning("[used_for_blade] reshapes into a ceremonial blade!"))
if(!used_for_blade.use(5))//use 5 of the material
return
var/obj/item/ceremonial_blade/blade = new(altar_turf)
blade.set_custom_materials(list(GET_MATERIAL_REF(material_used) = MINERAL_MATERIAL_AMOUNT * 5))
return TRUE
/datum/religion_rites/unbreakable
name = "Become Unbreakable"
desc = "Your training has made you unbreakable. In times of crisis, you will attempt to keep fighting on."
ritual_length = 10 SECONDS
invoke_msg = "My will must be unbreakable. Grant me this boon!"
favor_cost = 4 //4 duels won
/datum/religion_rites/unbreakable/perform_rite(mob/living/carbon/human/user, atom/religious_tool)
if(!ishuman(user))
return FALSE
if(HAS_TRAIT_FROM(user, TRAIT_UNBREAKABLE, INNATE_TRAIT))
to_chat(user, span_warning("Your spirit is already unbreakable!"))
return FALSE
return ..()
/datum/religion_rites/unbreakable/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
..()
to_chat(user, span_nicegreen("You feel [GLOB.deity]'s will to keep fighting pouring into you!"))
user.AddComponent(/datum/component/unbreakable)
/datum/religion_rites/tenacious
name = "Become Tenacious"
desc = "Your training has made you tenacious. In times of crisis, you will be able to crawl faster."
ritual_length = 10 SECONDS
invoke_msg = "Grant me your tenacity! I have proven myself!"
favor_cost = 3 //3 duels won
/datum/religion_rites/tenacious/perform_rite(mob/living/carbon/human/user, atom/religious_tool)
if(!ishuman(user))
return FALSE
if(HAS_TRAIT_FROM(user, TRAIT_TENACIOUS, INNATE_TRAIT))
to_chat(user, span_warning("Your spirit is already tenacious!"))
return FALSE
return ..()
/datum/religion_rites/tenacious/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
..()
to_chat(user, span_nicegreen("You feel [GLOB.deity]'s tenacity pouring into you!"))
user.AddElement(/datum/element/tenacious)
@@ -0,0 +1,56 @@
///ritual weapons. they're really bad, but they become normal weapons when sparring.
/obj/item/ceremonial_blade
name = "ceremonial blade"
desc = "A blade created to spar with. It seems weak, but if you spar with it...?"
icon_state = "default"
inhand_icon_state = "default"
icon = 'icons/obj/items/ritual_weapon.dmi'
//does the exact thing we want so heck why not
greyscale_config = /datum/greyscale_config/ceremonial_blade
greyscale_config_inhand_left = /datum/greyscale_config/ceremonial_blade_lefthand
greyscale_config_inhand_right = /datum/greyscale_config/ceremonial_blade_righthand
greyscale_colors = "#FFFFFF"
hitsound = 'sound/weapons/bladeslice.ogg'
custom_materials = list(/datum/material/iron = 12000) //Defaults to an Iron blade.
force = 2 //20
throwforce = 1 //10
w_class = WEIGHT_CLASS_NORMAL
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
block_chance = 3 //30
sharpness = SHARP_EDGED
max_integrity = 200
material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_GREYSCALE //doesn't affect stats of the weapon as to avoid gamering your opponent with a dope weapon
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 50)
resistance_flags = FIRE_PROOF
/obj/item/ceremonial_blade/Initialize()
. = ..()
AddComponent(/datum/component/butchering, 40, 105)
RegisterSignal(src, COMSIG_ITEM_SHARPEN_ACT, .proc/block_sharpening)
/obj/item/ceremonial_blade/melee_attack_chain(mob/user, atom/target, params)
if(!HAS_TRAIT(target, TRAIT_SPARRING))
return ..()
var/old_force = force
var/old_throwforce = throwforce
force *= 10
throwforce *= 10
. = ..()
force = old_force
throwforce = old_throwforce
/obj/item/ceremonial_blade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(attack_type != MELEE_ATTACK || !ishuman(hitby.loc))
return ..()
if(HAS_TRAIT(hitby.loc, TRAIT_SPARRING))
//becomes 30 block
final_block_chance *= 10
. = ..()
/obj/item/ceremonial_blade/proc/block_sharpening(datum/source, increment, max)
SIGNAL_HANDLER
//this breaks it
return COMPONENT_BLOCK_SHARPEN_BLOCKED
@@ -0,0 +1,145 @@
/obj/item/sparring_contract
desc = "A contract for setting up sparring matches. Both sparring partners must agree with the terms to begin."
icon = 'icons/obj/wizard.dmi'
icon_state = "scroll"
drop_sound = 'sound/items/handling/paper_drop.ogg'
pickup_sound = 'sound/items/handling/paper_pickup.ogg'
throw_range = 1
throw_speed = 1
w_class = WEIGHT_CLASS_TINY
///what weapons will be allowed during the sparring match
var/weapons_condition = CONDITION_MELEE_ONLY
///what arena the fight will take place in
var/arena_condition = /area/service/chapel
///what stakes the fight will have
var/stakes_condition = STAKES_NONE
///who has signed this contract. fills itself with WEAKREFS, to prevent hanging references
var/list/datum/weakref/signed_by = list(null, null)
/obj/item/sparring_contract/Initialize()
. = ..()
name = "[GLOB.deity]'s sparring contract"
/obj/item/sparring_contract/Destroy()
QDEL_NULL(signed_by)
var/datum/religion_sect/spar/sect = GLOB.religious_sect
sect?.existing_contract = null
. = ..()
/obj/item/sparring_contract/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SparringContract", name)
ui.open()
/obj/item/sparring_contract/ui_static_data(mob/user)
var/list/data = list()
var/area/arena = GLOB.areas_by_type[arena_condition]
data["set_weapon"] = weapons_condition
data["set_area"] = arena?.name
data["set_stakes"] = stakes_condition
data["possible_areas"] = get_possible_areas()
return data
/obj/item/sparring_contract/ui_data(mob/user)
var/list/data = list()
var/area/arena = GLOB.areas_by_type[arena_condition]
var/mob/living/carbon/human/left_partner
if(signed_by[1])
left_partner = signed_by[1].resolve()
var/mob/living/carbon/human/right_partner
if(signed_by[2])
right_partner = signed_by[2].resolve()
data["in_area"] = ((left_partner && right_partner && arena) && (left_partner in arena.contents) && (right_partner in arena.contents))
data["no_chaplains"] = (!left_partner?.mind?.holy_role && !right_partner?.mind?.holy_role)
data["left_sign"] = left_partner ? left_partner.real_name : "none"
data["right_sign"] = right_partner ? right_partner.real_name : "none"
return data
/obj/item/sparring_contract/proc/get_possible_areas()
var/list/area_names = list()
var/datum/religion_sect/spar/sect = GLOB.religious_sect
for(var/key in sect.arenas)
area_names += key
return area_names
/obj/item/sparring_contract/ui_act(action, list/params)
. = ..()
if(.)
return
var/mob/user = usr
if(!ishuman(user))
to_chat(user, span_warning("This contract refuses to be signed by a lesser creature such as yourself."))
return
var/datum/religion_sect/spar/sect = GLOB.religious_sect
var/list/resolved_opponents = list()
for(var/datum/weakref/resolve_me as anything in sect.past_opponents)
var/resolved = resolve_me.resolve()
if(!isnull(resolved))
resolved_opponents += resolved
if(user in resolved_opponents && params["stakes"] == STAKES_HOLY_MATCH)
to_chat(user, span_warning("This contract refuses to be signed up for a holy match by a previous holy match loser. Pick a different stake!"))
//any updating of the terms should update the UI to display new terms
. = TRUE
var/mob/living/carbon/human/left_partner
if(signed_by[1])
left_partner = signed_by[1].resolve()
var/mob/living/carbon/human/right_partner
if(signed_by[2])
right_partner = signed_by[2].resolve()
switch(action)
if("clear")
signed_by = list(null, null)//remove weakrefs
if("fight")
if(!left_partner || !right_partner || !left_partner.mind || !right_partner.mind)
return
if(HAS_TRAIT(left_partner, TRAIT_SPARRING) || HAS_TRAIT(right_partner, TRAIT_SPARRING))
to_chat(user, span_warning("One participant is already sparring!"))
return
var/chaplain = left_partner.mind.holy_role ? left_partner : right_partner
var/opponent = left_partner.mind.holy_role ? right_partner : left_partner
new /datum/sparring_match(weapons_condition, GLOB.areas_by_type[arena_condition], stakes_condition, chaplain, opponent)
qdel(src)
if("sign")
if(user == left_partner || user == right_partner)
to_chat(user, span_warning("You've already signed one side of the contract."))
return
var/area/arena_condition_name = GLOB.areas_by_type[arena_condition]
arena_condition_name = format_text(arena_condition_name.name)
//setting/checking for terms changed
var/terms_changed = FALSE
if(params["weapon"] != weapons_condition)
if(!params["weapon"])
return //they hit f5 to clear data then submitted
terms_changed = TRUE
weapons_condition = params["weapon"]
if(params["area"] != arena_condition_name)
if(!params["area"])
return //they hit f5 to clear data then submitted
terms_changed = TRUE
var/new_area_condition = sect.arenas[params["area"]]
arena_condition = new_area_condition
if(params["stakes"] != stakes_condition)
if(!params["stakes"])
return //they hit f5 to clear data then submitted
terms_changed = TRUE
stakes_condition = params["stakes"]
//if you change the terms you have to get the other person to sign again.
if(terms_changed && (left_partner || right_partner))
signed_by = list(null, null)//remove weakrefs
to_chat(user, span_warning("You will need to get your sparring partner to sign again under these new terms you've set."))
//fluff and signing
var/datum/weakref/user_ref = WEAKREF(user)
if(params["sign_position"] == CONTRACT_LEFT_FIELD)
signed_by[1] = user_ref
else
signed_by[2] = user_ref
@@ -0,0 +1,304 @@
/datum/sparring_match
///the chaplain. it isn't actually a chaplain all the time, but in the cases where the chaplain is needed this will always be them.
var/mob/living/carbon/human/chaplain
///the other fighter
var/mob/living/carbon/human/opponent
///what weapons will be allowed during the sparring match
var/weapons_condition
///area instance the participants must stay in
var/area/arena_condition
///what stakes the fight will have
var/stakes_condition
///cheats from the chaplain
var/chaplain_violations_allowed = 2
///cheats from the non-chaplain
var/opponent_violations_allowed = 2
///outside interventions that ruin the match
var/flubs = 2
/datum/sparring_match/New(weapons_condition, arena_condition, stakes_condition, mob/living/carbon/human/chaplain, mob/living/carbon/human/opponent)
. = ..()
src.weapons_condition = weapons_condition
src.arena_condition = arena_condition
src.stakes_condition = stakes_condition
src.chaplain = chaplain
src.opponent = opponent
ADD_TRAIT(chaplain, TRAIT_SPARRING, TRAIT_GENERIC)
ADD_TRAIT(opponent, TRAIT_SPARRING, TRAIT_GENERIC)
hook_signals(chaplain)
hook_signals(opponent)
chaplain.add_filter("sparring_outline", 9, list("type" = "outline", "color" = "#e02200"))
opponent.add_filter("sparring_outline", 9, list("type" = "outline", "color" = "#004ee0"))
/datum/sparring_match/proc/hook_signals(mob/living/carbon/human/sparring)
//weapon conditions
if(weapons_condition < CONDITION_ANY_WEAPON)
RegisterSignal(sparring, COMSIG_MOB_FIRED_GUN, .proc/gun_violation)
RegisterSignal(sparring, COMSIG_MOB_GRENADE_ARMED, .proc/grenade_violation)
if(weapons_condition <= CONDITION_CEREMONIAL_ONLY)
RegisterSignal(sparring, COMSIG_PARENT_ATTACKBY, .proc/melee_violation)
//arena conditions
RegisterSignal(sparring, COMSIG_MOVABLE_MOVED, .proc/arena_violation)
//severe violations (insta violation win for other party) conditions
RegisterSignal(sparring, COMSIG_MOVABLE_TELEPORTED, .proc/teleport_violation)
//win conditions
RegisterSignal(sparring, COMSIG_MOB_STATCHANGE, .proc/check_for_victory)
//flub conditions
RegisterSignal(sparring, COMSIG_PARENT_ATTACKBY, .proc/outsider_interference)
RegisterSignal(sparring, COMSIG_ATOM_HULK_ATTACK, .proc/hulk_interference)
RegisterSignal(sparring, COMSIG_ATOM_ATTACK_HAND, .proc/hand_interference)
RegisterSignal(sparring, COMSIG_ATOM_ATTACK_PAW, .proc/paw_interference)
RegisterSignal(sparring, COMSIG_ATOM_HITBY, .proc/thrown_interference)
RegisterSignal(sparring, COMSIG_ATOM_BULLET_ACT, .proc/projectile_interference)
//severe flubs (insta match ender, no winners) conditions
RegisterSignal(sparring, COMSIG_LIVING_DEATH, .proc/death_flub)
RegisterSignal(sparring, COMSIG_PARENT_QDELETING, .proc/deletion_flub)
/datum/sparring_match/proc/unhook_signals(mob/living/carbon/human/sparring)
if(!sparring)
return
UnregisterSignal(sparring, list(
COMSIG_MOB_FIRED_GUN,
COMSIG_MOB_GRENADE_ARMED,
COMSIG_MOB_ITEM_ATTACK,
COMSIG_MOVABLE_MOVED,
COMSIG_MOVABLE_TELEPORTED,
COMSIG_MOB_STATCHANGE,
COMSIG_PARENT_ATTACKBY,
COMSIG_ATOM_HULK_ATTACK,
COMSIG_ATOM_ATTACK_HAND,
COMSIG_ATOM_ATTACK_PAW,
COMSIG_ATOM_HITBY,
COMSIG_ATOM_BULLET_ACT,
COMSIG_LIVING_DEATH,
COMSIG_PARENT_QDELETING,
))
///someone is changing health state, end the fight in crit
/datum/sparring_match/proc/check_for_victory(datum/participant, new_stat)
SIGNAL_HANDLER
//death needs to be a flub, conscious means they haven't won
if(new_stat == CONSCIOUS || new_stat == DEAD)
return
if(participant == chaplain)
end_match(opponent, chaplain)
else
end_match(chaplain, opponent)
// SIGNALS THAT ARE FOR BEING ATTACKED FIRST (GUILTY)
/datum/sparring_match/proc/outsider_interference(datum/source, obj/item/I, mob/attacker)
SIGNAL_HANDLER
if(attacker == chaplain || attacker == opponent)
return
INVOKE_ASYNC(src, .proc/flub, attacker)
/datum/sparring_match/proc/hulk_interference(datum/source, mob/attacker)
SIGNAL_HANDLER
if((attacker == chaplain || attacker == opponent))
// fist fighting a hulk is so dumb. i can't fathom why you would do this.
return
INVOKE_ASYNC(src, .proc/flub, attacker)
/datum/sparring_match/proc/hand_interference(datum/source, mob/living/attacker)
SIGNAL_HANDLER
if(attacker == chaplain || attacker == opponent)
//you can pretty much always use fists as a participant
return
INVOKE_ASYNC(src, .proc/flub, attacker)
/datum/sparring_match/proc/paw_interference(datum/source, mob/living/attacker)
SIGNAL_HANDLER
if(attacker == chaplain || attacker == opponent)
//you can pretty much always use paws as a participant
return
INVOKE_ASYNC(src, .proc/flub, attacker)
/datum/sparring_match/proc/thrown_interference(datum/source, atom/movable/thrown_movable, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
if(istype(thrown_movable, /obj/item))
var/mob/living/honorbound = source
var/obj/item/thrown_item = thrown_movable
var/mob/thrown_by = thrown_item.thrownby?.resolve()
if(thrown_item.throwforce < honorbound.health && ishuman(thrown_by))
INVOKE_ASYNC(src, .proc/flub, thrown_by)
/datum/sparring_match/proc/projectile_interference(datum/participant, obj/projectile/proj)
SIGNAL_HANDLER
if(proj.firer == chaplain || proj.firer == opponent)
//oh, well that's allowed. or maybe it isn't. doesn't matter because firing the gun will trigger a violation, so no additional violation needed
return
var/mob/living/interfering
if(isliving(proj.firer))
interfering = proj.firer
INVOKE_ASYNC(src, .proc/flub, interfering)
///someone randomly fucking died
/datum/sparring_match/proc/death_flub(datum/deceased)
SIGNAL_HANDLER
flubbed_match()
///someone randomly fucking deleted
/datum/sparring_match/proc/deletion_flub(datum/qdeleting)
SIGNAL_HANDLER
flubbed_match()
///someone used a gun
/datum/sparring_match/proc/gun_violation(datum/offender)
SIGNAL_HANDLER
violation(offender, "using guns")
///someone used a grenade
/datum/sparring_match/proc/grenade_violation(datum/offender)
SIGNAL_HANDLER
violation(offender, "using grenades")
///someone used melee weapons
/datum/sparring_match/proc/melee_violation(datum/offender, obj/item/thing, mob/user, params)
SIGNAL_HANDLER
if(weapons_condition != CONDITION_CEREMONIAL_ONLY)
violation(offender, "using melee weapons")
if(istype(thing, /obj/item/ceremonial_blade))
return
violation(offender, "using non ceremonial weapons")
/datum/sparring_match/proc/teleport_violation(datum/offender)
SIGNAL_HANDLER
if(offender == chaplain)
end_match(opponent, chaplain, violation_victory = TRUE)
else
end_match(chaplain, opponent, violation_victory = TRUE)
///someone tried to leave
/datum/sparring_match/proc/arena_violation(atom/movable/mover, atom/oldloc, direction)
SIGNAL_HANDLER
var/area/inhabited_area = get_area(mover)
if(inhabited_area == arena_condition)
return //still in the ring!! :)
violation(mover, "leaving the arena")
var/atom/throw_target = get_edge_target_turf(mover, REVERSE_DIR(direction))
mover.throw_at(throw_target, 6, 4)
/datum/sparring_match/proc/violation(mob/living/carbon/human/offender, reason)
SIGNAL_HANDLER
to_chat(offender, span_userdanger("Violation! No [reason]!"))
if(offender == chaplain)
chaplain_violations_allowed--
if(!chaplain_violations_allowed)
end_match(opponent, chaplain, violation_victory = TRUE)
else
opponent_violations_allowed--
if(!opponent_violations_allowed)
end_match(chaplain, opponent, violation_victory = TRUE)
/datum/sparring_match/proc/flub(mob/living/interfering)
if(interfering)
var/list/possible_punishments = list(PUNISHMENT_OMEN, PUNISHMENT_LIGHTNING)
if(ishuman(interfering))
possible_punishments += PUNISHMENT_BRAND
switch(pick(possible_punishments))
if(PUNISHMENT_OMEN)
to_chat(interfering, span_warning("You get a bad feeling... for interfering with [chaplain]'s sparring match..."))
interfering.AddComponent(/datum/component/omen, TRUE, null, FALSE)
if(PUNISHMENT_LIGHTNING)
to_chat(interfering, span_warning("[GLOB.deity] has punished you for interfering with [chaplain]'s sparring match!"))
lightningbolt(interfering)
if(PUNISHMENT_BRAND)
var/mob/living/carbon/human/branded = interfering
to_chat(interfering, span_warning("[GLOB.deity] brands your flesh for interfering with [chaplain]'s sparring match!!"))
var/obj/item/bodypart/branded_limb = pick(branded.bodyparts)
branded_limb.force_wound_upwards(/datum/wound/burn/severe/brand)
branded.emote("scream")
flubs--
if(!flubs) //too many interferences
flubbed_match()
///this match was interfered on, nobody wins or loses anything, just end
/datum/sparring_match/proc/flubbed_match()
cleanup_sparring_match()
if(chaplain) //flubing means we don't know who is still standing
to_chat(chaplain, span_boldannounce("The match was flub'd! No winners, no losers. You may restart the match with another contract."))
if(opponent)
to_chat(opponent, span_boldannounce("The match was flub'd! No winners, no losers."))
qdel(src)
///helper to remove all the effects after a match ends
/datum/sparring_match/proc/cleanup_sparring_match()
REMOVE_TRAIT(chaplain, TRAIT_SPARRING, TRAIT_GENERIC)
REMOVE_TRAIT(opponent, TRAIT_SPARRING, TRAIT_GENERIC)
unhook_signals(chaplain)
unhook_signals(opponent)
chaplain.remove_filter("sparring_outline")
opponent.remove_filter("sparring_outline")
/datum/sparring_match/proc/end_match(mob/living/carbon/human/winner, mob/living/carbon/human/loser, violation_victory = FALSE)
cleanup_sparring_match()
to_chat(chaplain, span_boldannounce("[violation_victory ? "[loser] DISQUALIFIED!" : ""] [winner] HAS WON!"))
to_chat(opponent, span_boldannounce("[violation_victory ? "[loser] DISQUALIFIED!" : ""] [winner] HAS WON!"))
win(winner, loser, violation_victory)
lose(loser, winner)
if(stakes_condition != STAKES_YOUR_SOUL)
var/healing_message = "You may want to heal up the loser now."
if(winner == chaplain)
healing_message += " Your bible will heal the loser for awhile."
to_chat(winner, span_notice(healing_message))
qdel(src)
///most of the effects are handled on `lose()` instead.
/datum/sparring_match/proc/win(mob/living/carbon/human/winner, mob/living/carbon/human/loser, violation_victory)
switch(stakes_condition)
if(STAKES_HOLY_MATCH)
if(winner == chaplain)
if(violation_victory)
to_chat(winner, span_warning("[GLOB.deity] is not entertained from a matched decided by violations. No favor awarded..."))
else
to_chat(winner, span_nicegreen("You've won favor with [GLOB.deity]!"))
var/datum/religion_sect/spar/sect = GLOB.religious_sect
sect.adjust_favor(1, winner)
sect.past_opponents += WEAKREF(loser)
if(STAKES_MONEY_MATCH)
to_chat(winner, span_nicegreen("You've won all of [loser]'s money!"))
if(STAKES_YOUR_SOUL)
to_chat(winner, span_nicegreen("You've won [loser]'s SOUL!"))
/datum/sparring_match/proc/lose(mob/living/carbon/human/loser, mob/living/carbon/human/winner)
if(!loser) //shit happened?
return
switch(stakes_condition)
if(STAKES_HOLY_MATCH)
if(loser == chaplain)
var/datum/religion_sect/spar/sect = GLOB.religious_sect
sect.matches_lost++
if(sect.matches_lost < 3)
to_chat(loser, span_userdanger("[GLOB.deity] is angry you lost in their name!"))
return
to_chat(loser, span_userdanger("[GLOB.deity] is enraged by your lackluster sparring record!"))
lightningbolt(loser)
SEND_SIGNAL(loser, COMSIG_ADD_MOOD_EVENT, "sparring", /datum/mood_event/banished)
loser.mind.holy_role = NONE
to_chat(loser, span_userdanger("You have been excommunicated! You are no longer holy!"))
if(STAKES_MONEY_MATCH)
to_chat(loser, span_userdanger("You've lost all your money to [winner]!"))
var/datum/bank_account/loser_account = loser.get_bank_account()
var/datum/bank_account/winner_account = winner.get_bank_account()
if(!loser_account || !winner_account)//the winner is pretty owned in this case but whatever shoulda read the fine print of the contract
return
winner_account.transfer_money(loser_account, loser_account.account_balance)
if(STAKES_YOUR_SOUL)
var/turf/shard_turf = get_turf(loser)
if(!shard_turf)
return
to_chat(loser, span_userdanger("You've lost ownership over your soul to [winner]!"))
var/obj/item/soulstone/anybody/chaplain/sparring/shard = new(shard_turf)
shard.capture_soul(loser, winner, forced = TRUE)
-3
View File
@@ -253,9 +253,6 @@
if(owner.reagents.get_reagent_amount(/datum/reagent/medicine/ephedrine) < 20)
owner.reagents.add_reagent(/datum/reagent/medicine/ephedrine, 10)
/obj/item/organ/heart/ethereal
name = "crystal core"
icon_state = "ethereal_heart" //Welp. At least it's more unique in functionaliy.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

+6
View File
@@ -122,6 +122,7 @@
#include "code\__DEFINES\reactions.dm"
#include "code\__DEFINES\reagents.dm"
#include "code\__DEFINES\reagents_specific_heat.dm"
#include "code\__DEFINES\religion.dm"
#include "code\__DEFINES\research.dm"
#include "code\__DEFINES\robots.dm"
#include "code\__DEFINES\role_preferences.dm"
@@ -629,6 +630,7 @@
#include "code\datums\components\trapdoor.dm"
#include "code\datums\components\twohanded.dm"
#include "code\datums\components\udder.dm"
#include "code\datums\components\unbreakable.dm"
#include "code\datums\components\uplink.dm"
#include "code\datums\components\usb_port.dm"
#include "code\datums\components\vacuum.dm"
@@ -790,6 +792,7 @@
#include "code\datums\elements\strippable.dm"
#include "code\datums\elements\surgery_initiator.dm"
#include "code\datums\elements\swabbable.dm"
#include "code\datums\elements\tenacious.dm"
#include "code\datums\elements\tool_flash.dm"
#include "code\datums\elements\turf_transparency.dm"
#include "code\datums\elements\undertile.dm"
@@ -3441,6 +3444,9 @@
#include "code\modules\religion\religion_sects.dm"
#include "code\modules\religion\religion_structures.dm"
#include "code\modules\religion\rites.dm"
#include "code\modules\religion\sparring\ceremonial_gear.dm"
#include "code\modules\religion\sparring\sparring_contract.dm"
#include "code\modules\religion\sparring\sparring_datum.dm"
#include "code\modules\requests\request.dm"
#include "code\modules\requests\request_manager.dm"
#include "code\modules\research\bepis.dm"
@@ -0,0 +1,235 @@
import { BooleanLike } from 'common/react';
import { multiline } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { BlockQuote, Button, Dropdown, Section, Stack } from '../components';
import { Window } from '../layouts';
// defined this so the code is more readable
const STAKES_HOLY_MATCH = 1;
const weaponlist = [
"Fist Fight",
"Ceremonial Weapons",
"Melee Only",
"Any Weapons",
];
const stakelist = [
"No Stakes",
"Holy Match",
"Money Match",
"Your Soul",
];
const weaponblurb = [
"You will fight with your fists only. Any weapons will be considered a violation.",
"You can only fight with ceremonial weapons. You will be at a severe disadvantage without one!",
"You can fight with weapons, or fists if you have none. Ranged weapons are a violation.",
"You can fight with any and all weapons as you please. Try not to kill them, okay?",
];
const stakesblurb = [
"No stakes, just for fun. Who doesn't love some recreational sparring?",
"A match for the chaplain's deity. The Chaplain suffers large consequences for failure, but advances their sect by winning.",
"A match with money on the line. Whomever wins takes all the money of whomever loses.",
"A lethal match with the loser's soul becoming under ownership of the winner.",
];
type Info = {
set_weapon: number;
set_area: string;
set_stakes: number;
left_sign: string;
right_sign: string;
in_area: BooleanLike;
no_chaplains: BooleanLike;
possible_areas: Array<string>;
};
export const SparringContract = (props, context) => {
const { data, act } = useBackend<Info>(context);
const {
set_weapon,
set_area,
set_stakes,
possible_areas,
left_sign,
right_sign,
in_area,
no_chaplains,
} = data;
const [weapon, setWeapon] = useLocalState(context, "weapon", set_weapon);
const [area, setArea] = useLocalState(context, "area", set_area);
const [stakes, setStakes] = useLocalState(context, "stakes", set_stakes);
return (
<Window
width={420}
height={380}>
<Window.Content>
<Section fill>
<Stack vertical fill>
<Stack.Item>
<Stack vertical>
<Stack.Item fontSize="16px">
Weapons:
</Stack.Item>
<Stack.Item>
<Dropdown
width="100%"
selected={weaponlist[weapon-1]}
options={weaponlist}
onSelected={value =>
setWeapon(weaponlist.findIndex(title => (
title === value
))+1)} />
</Stack.Item>
<Stack.Item>
<BlockQuote>
{weaponblurb[weapon-1]}
</BlockQuote>
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item>
<Stack vertical>
<Stack.Item fontSize="16px">
Arena:
</Stack.Item>
<Stack.Item>
<Dropdown
width="100%"
selected={area}
options={possible_areas}
onSelected={value => setArea(value)} />
</Stack.Item>
<Stack.Item>
<BlockQuote>
This fight will take place in the {area}.
Leaving the arena mid-fight is a violation.
</BlockQuote>
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item>
<Stack vertical>
<Stack.Item fontSize="16px">
Stakes:
</Stack.Item>
<Stack.Item>
<Dropdown
width="100%"
selected={stakelist[stakes-1]}
options={stakelist}
onSelected={value =>
setStakes(stakelist.findIndex(title => (
title === value
))+1)} />
</Stack.Item>
<Stack.Item>
<BlockQuote>
{stakesblurb[stakes-1]}
</BlockQuote>
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item grow>
<Stack grow textAlign="center">
<Stack.Item
fontSize={left_sign !== "none" && "14px"}
grow>
{left_sign === 'none' && (
<Button
icon="pen"
onClick={() => act('sign', {
"weapon": weapon,
"area": area,
"stakes": stakes,
"sign_position": "left",
})}>
Sign Here
</Button>
) || (
left_sign
)}
</Stack.Item>
<Stack.Item fontSize="16px">
VS
</Stack.Item>
<Stack.Item
fontSize={right_sign !== "none" && "14px"}
grow>
{right_sign === "none" && (
<Button
icon="pen"
onClick={() => act('sign', {
"weapon": weapon,
"area": area,
"stakes": stakes,
})}>
Sign Here
</Button>
) || (
right_sign
)}
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item mb={-0.5}>
<Stack fill>
<Stack.Item grow>
<Button
disabled={
!in_area
|| (no_chaplains && set_stakes === STAKES_HOLY_MATCH)
}
icon="fist-raised"
onClick={() => act('fight')} >
FIGHT!
</Button>
<Button
tooltip={multiline`
If you've already signed but you want to renegotiate
the terms, you can clear out the signatures with
this button.
`}
icon="door-open"
onClick={() => act('clear')} >
Clear
</Button>
</Stack.Item>
<Stack.Item>
<Button
tooltip={in_area
&& "Both participants are present in the "+ area + "."
|| "Both participants need to be in the arena!"}
color={in_area && "green" || "red"}
icon="ring" >
Arena
</Button>
<Button
tooltip={(left_sign !== "none" && right_sign !== "none")
&& "Both signatures present, terms agreed upon."
|| "You need signatures from both fighters on the terms!"}
color={(left_sign !== "none" && right_sign !== "none")
&& "green"
|| "red"}
icon="file-signature" >
Signatures
</Button>
<Button
tooltip={!no_chaplains
&& "At least one chaplain is present. Holy matches allowed."
|| "No chaplain present for this fight. No Holy Matches!"}
color={!no_chaplains && "green" || "yellow"}
icon="cross" >
Chaplain
</Button>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Section>
</Window.Content>
</Window>
);
};