# +-------------------------- # | # | Temperature # | # | Calculates player temperatures and doles out the consequences # | # +-------------------------- # # @author TheHusseler # @date 2022/04/08 # @denizen-build REL-1765 # @script-version 1.0 ##Configuration TemperatureData: type: data #Turn this on if you want higher altitudes to have lower temperatures. Altitude only affects normal worlds altitudeAffectsTemp: true #This will apply adjustments based on the world environment. Leave these as 0 to keep it as is. Negative makes it colder, positive makes it warmer. environmentTemps: nether: 0 normal: 0 the_end: 0 #Same as above, if you want to make a certain world warmer or colder than another. Put the name of the world here and the amount you want to change it by. worldTemps: world: 0 world_end: 0 world_nether: 0 jerrys_desert_world: 0.5 #This affects the temperature at which cold affects begin to take place #0.15 should reduce this to only snowy biomes, 0.3 includes all biomes that the game considers "cold" coldTemperature: 0.3 #When a player is increasing in cold, it drives up their temperature_cold "score". frozenScore is the number at which #the script begins applying freeze affects and damage frozenScore: 50 #Cold Ocean, Deep Cold Ocean, and Deep Frozen Oceans are given a "Temperate" biome with a 0.5 temperature (likely to avoid ice) #If you want to treat Cold ocean biomes as cold biomes, set this to true. coldOceans: true #Rivers are also set to 0.5 temperature. If you set this to true, we will ignore the River biome temperature in favor #of the player's last checked biome coldRivers: true #This determines whether night should be treated as colder and by how much. This also makes the day warmer by the same amount, dawn and dusk being the unmodified ones #Day/night only affects normal worlds coldNightTemp: 0.1 #How much being in water brings down temperature while in cold biomes coldWaterModifier: 0.2 #How much equipment affects your temperature. Leather and netherite will be double this value equipmentModifier: 0.02 #How much of a multiplier to apply per level of an enchantment on equipment enchantmentMultiplierPerLevel: 0.4 #This shows the benefit of having a torch or lantern in one of your hands. Soul torch or lantern doubles this heldTorchModifier: 0.1 #This is the temperature bonus applied to players who are on fire burningTemp: 1 #This determines what the value of "maximum" shelter will reduce cold by shelterModifier: 0.5 #The distance from the player to check for heat sources heatSourceRadius: 5 #A list of relevant heat source blocks and their associated cold modifier heatSourceBlocks: lava: 1 lava_cauldron: 0.9 magma_block: 0.8 soul_fire: 0.7 soul_campfire: 0.6 fire: 0.5 campfire: 0.4 #Checks furnace_burn_duration to verify this can be applied first furnace: 0.4 soul_lantern: 0.2 soul_torch: 0.2 soul_wall_torch: 0.2 smoker: 0.2 torch: 0.1 wall_torch: 0.1 lantern: 0.1 nether_portal: 0.1 ##Events TemperatureEvents: type: world debug: false events: on player enters biome: - if : - if == river: - flag temperature_lastBiome: - else if : - flag temperature_lastBiome:! - ratelimit 2s - run EvaluateTemperature def.player: def.multiplier:1 on delta time secondly every:20: #Every 20 seconds we do a broad check for onset of cold - foreach as:player: - if !<[player].has_flag[temperature_cold]>: #Sets multiplier to 0 here because we don't want to add cold in this loop, merely add the flag - run EvaluateTemperature def.player:<[player]> def.multiplier:0 #Wait to avoid heavy calculations on all players at once - wait 10t on delta time secondly every:5: #Every 5 seconds we check players flagged cold for updates #This second delta time is to ensure we get more accurate and frequent updates while a player is in a cold environment - foreach as:player: #We ignore players that are frozen, as they are covered by the below check - if !<[player].has_flag[temperature_frozen]>: #Sets multiplier to 5 here because we are only executing this once every 5 seconds - run EvaluateTemperature def.player:<[player]> def.multiplier:5 #Wait to avoid heavy calculations on all players at once - wait 5t on delta time secondly: #Every second we check frozen players, to give more fidelity while at risk of death - foreach as:player: - if <[player].has_flag[temperature_frozen]>: #Sets multiplier to 1 since this fires more regularly - run EvaluateTemperature def.player:<[player]> def.multiplier:1 ##Command TemperatureCommands: type: command debug: false name: temperature description: Gets player temperature data usage: /temperature aliases: - temp script: - define arg - choose <[arg]>: - case default: - define lines - foreach <[lines]>: - narrate <[value]> - case celsius c: - define lines - foreach <[lines]>: - narrate <[value]> ##Evaluating Temperature EvaluateTemperature: type: task debug: false definitions: player | multiplier script: #Since this is infrequent, do all the checks when running this one. - inject GetLocationTemperature #Flagging this temperature for information purposes - flag <[player]> locationTemp:<[temperature].round_to_precision[0.0001]> - define locationTemp <[temperature]> - inject GetPersonalTemperature - inject CustomTemperatureLogic - if <[player].flag[temperature_cold]||0> == 0: - if <[temperature]> > && <[locationTemp]> > : #At this point we check if the player has the appropriate flag and if the location and personal temperatures are cold. If they aren't #Then we can exit here early, avoiding the heavier calculations below - flag <[player]> shelterScore:! - flag <[player]> temperature_cold:! - flag <[player]> temperature_frozen:! - flag <[player]> finalTemp:<[temperature].round_to_precision[0.0001]> - stop - inject GetShelterScore - define temperature <[temperature].add[<[shelterScore]>]> - inject GetHeatSource - define temperature <[temperature].add[<[warmestBlock]>]> # Flagging this temperature for information purposes - flag <[player]> finalTemp:<[temperature].round_to_precision[0.0001]> - define coldness ]> - if !<[player].has_flag[temperature_cold]>: #If the player doesn't have the flag yet then we know the location or personal temps are cold enough to be relevant. Flag them. - flag <[player]> temperature_cold:0 - actionbar targets:<[player]> "You begin to feel cold" - stop - if <[coldness]> < 0: #If coldness is negative (getting warmer) we double it to increase recovery rate - define coldness <[coldness].mul[2]> - if <[player].flag[temperature_cold]> < : - if <[player].flag[temperature_cold].add[<[coldness].mul[<[multiplier]>]>]> > : #Here we are informing them if their cold is passing the threshold of 2/3 towards frozen. - actionbar targets:<[player]> "You're dangerously cold!" # We adjust the player's cold flag based on the new coldness. Multiplier here is based on the rate of the loop causing the evaluation - flag <[player]> temperature_cold:+:<[coldness].mul[<[multiplier]>]> - if <[player].flag[temperature_cold]> <= 0: #If the player's cold stat is now 0 or below, we need to decide whether to clear it - if <[locationTemp]> < : #If it's below 0 but the location is still cold, we simply set it to 0 so as to keep checking while in this environment - flag <[player]> temperature_cold:0 - else: # If the location isn't cold then we clear the flag - actionbar targets:<[player]> "It feels warmer here" - flag <[player]> temperature_cold:! - else if <[player].flag[temperature_cold]> > : #The player is above the threshold for freezing - if !<[player].has_flag[temperature_frozen]>: #The player is newly frozen so we inform and warn them - actionbar targets:<[player]> "You're freezing! Find shelter and heat!" - flag <[player]> temperature_frozen #FrozenIntensity is how above the frozen threshold you're at. This determines the duration of the freeze which determines intensity of freezing negatives - define frozenIntensity <[player].flag[temperature_cold].sub[]> - adjust <[player]> freeze_duration:s]> - else if <[player].has_flag[temperature_frozen]> && <[player].flag[temperature_cold]> <= : #Player is frozen but is no longer above threshold so we remove the flag - flag <[player]> temperature_frozen:! ##Formatting GetTemperatureData: type: procedure debug: false definitions: formatProc script: #Color changes based on state: gold for normal, aqua for cold, purple for frozen - define color ].if_false[<&b>]>].if_false[<&6>]> - define line1 "<[color]><&l>Temperature Info" - define line2 "<[color]>Location: <&3>].context[]> <[color]>Shelter: <&3> " - define line3 "<[color]>Final: <&3>].context[]>" - define line4 "<[color]>Coldness: <&3>" - define line5 "<[color]>].if_false[You feel fine]>" - determine |<[line2]>|<[line3]>|<[line4]>|<[line5]>]> GetFormattedAsCelsius: type: procedure debug: false definitions: temperature script: - if <[temperature]> == null: - determine - - define farenheit ]> - determine <[farenheit].sub[32].mul[0.5555].round_to_precision[0.1]> GetFormattedAsFarenheit: type: procedure debug: false definitions: temperature script: - if <[temperature]> == null: - determine - #Below 0.4 we keep a constant measurement. So the minimum is applied to only execute the result for the 0.4 and below values #This scale is being applied for everything below 46 degrees - define result <[temperature].min[0.4].mul[40].add[30]> - if <[temperature]> > 0.4: # For 0.4 to 1.0 we want to increase the rate. This way we get more temperate values for these temperatures #This scale is applied for everything from 46 degrees to 82 degrees - define temperateScaled <[temperature].min[1].sub[0.4].mul[60]> - define result <[result].add[<[temperateScaled]>]> - if <[temperature]> > 1.0: #From 1.0 to 2.0 we want to have the rate slow, so we don't get crazy high temperatures. #This scale is applied for everything above 82 degrees - define hotScaled <[temperature].sub[1].mul[20]> - define result <[result].add[<[hotScaled]>]> - determine <[result].round_to_precision[0.1]> ##Custom logic and exception logic #This task is for applying custom logic. Checking for flags to provide exceptions (for example, a potion that protects against the cold) CustomTemperatureLogic: type: task debug: false definitions: player | temperature script: - stop if:<[player].gamemode.is[!=].to[survival]> - define clearCold false #A generic flag to allow spells/potions/foods, ect. that counterract cold - if <[player].has_flag[temperature_warmed]>: - define clearCold true ## NOTE #These lines have been removed to prevent spam if there isn't a _warmArea on the server. #If you want to use this feature, uncomment this after adding a noted area ending in _warmArea # - if <[player].location.is_in[*_warmArea]>: # - define clearCold true ## NOTE - if <[clearCold]>: #Since we don't want to just stop in many cases, we update the flags when exception conditions are hit - flag <[player]> shelterScore:! - flag <[player]> temperature_cold:! - flag <[player]> temperature_frozen:! - flag <[player]> finalTemp:<[temperature].round_to_precision[0.0001]> - stop #This is a generic flag that can be set to adjust the player's temperature - if <[player].has_flag[temperature_adjusted]>: - define temperature:+:<[player].flag[temperature_adjusted]> ##Enchantments for chill on weapons and warmth on armor temperature_chillenchantment: type: enchantment id: chill slots: - mainhand rarity: rare category: weapon full_name: Chill min_level: 1 max_level: 5 min_cost: max_cost: is_discoverable: true can_enchant: after hurt: #We adjust the victim's temperature. Hits within 5 seconds will reset the expiration #Divide the power by 10 - flag temperature_adjusted: expire:5s - if > 0: #Apply a short freeze duration, maximum of a full second at full power - adjust freeze_duration:s]> #Add cold score. A small amount so that it only contributes to their cold if they already are getting cold - if : - flag temperature_cold:+: - else: - flag temperature_cold: - actionbar targets: "You begin to feel cold" temperature_warmthenchantment: type: enchantment id: warmth slots: - feet - legs - chest - head rarity: common category: armor full_name: Warmth min_level: 1 max_level: 5 min_cost: max_cost: is_tradable: true is_discoverable: true can_enchant: ##Evalutation Helper Tasks GetHeatSource: type: task debug: false definitions: player script: - define blocks <[player].location.find_blocks[].within[]> #Get the list parsed as materials - define blockMaterials <[blocks].parse[material.name]> - define warmestBlock 0 #We looop through the list keys. Highest heat blocks are earlier in the list so we stop when we find a match - foreach : - if <[blockMaterials].contains[<[value]>]>: #We've found a block that matches a heatSource - if <[value]> == furnace || <[value]> == smoker: #If the block is a furnace or smoker, verify that it's burning first - foreach <[blocks].filter_tag[<[filter_value].material.name.is[==].to[<[value]>]>]> as:furnace: - if <[furnace].furnace_burn_duration.in_seconds> > 0: #If it's burning, stop the foreach and set warmest block - define warmestBlock ]> - foreach stop - if <[warmestBlock]> > 0: #If warmest block has been set then a furnace was burning, so stop checking heatSourceBlocks - foreach stop - else: #If the warmest block that was found isn't a furnace, then set warmestBlock and stop the foreach - define warmestBlock ]> - foreach stop GetShelterScore: type: task debug: false definitions: player script: #Check each direction. These add up to 100 for what amounts to a Shelter percentage. Use eye_location to avoid minor hills/terrain counting as easily # We add 1 to each 15/40 distance so that we can get closer to 100 for for a 1x1x2 shelter - define left |16|0|270]> - define right |16|0|90]> - define forward |16|0|0]> - define backward |16|0|180]> #Upwards is given a higher value, as it's more important for the sake of shelter. - define up |41|-89.9|0]> - if <[player].location.world.has_storm> && <[up]> == 0 && <[player].location.material.name> == water: #If the player is unroofed and it's raining, we give apply the wet factor. Checking if submerged to avoid duplicating the wet affect - define temperature <[temperature].sub[]> #Add the values together for a shelter score between 0 and 100. Subtract this from a 100 to make sure it's oriented properly - define shelterScore <[up].add[<[backward]>].add[<[forward]>].add[<[left]>].add[<[right]>]> #Flag the player with a shelterScore to give them info - flag <[player]> shelterScore:<[shelterScore]> #Divide score by 100 and multiply by modifier to determine how it will adjust temperature - define shelterScore <[shelterScore].div[100].mul[]> GetDirectionScore: type: procedure debug: false definitions: location | distance | pitch | yaw script: - define location <[location].with_pose[<[pitch]>,<[yaw]>]> - define target <[location].precise_cursor_on_block[<[distance]>]> #We subtract from the distance, so that the score is higher the closer to the target - determine <[distance].sub[<[location].distance[<[target]>]||<[distance]>>].round> GetPersonalTemperature: type: task debug: false definitions: player | temperature script: - define equipModifier 0 - if <[player].equipment.size> > 0: - foreach <[player].equipment> as:clothing: #Leather and Netherite are the most effective clothing for warmth, doubling the itemModifier - define itemMultiplier 1 - if <[clothing].material.name.contains[leather]> || <[clothing].material.name.contains[netherite]>: - define itemMultiplier 2 - else if <[clothing].material.name> == air: - define itemMultiplier 0 - if <[clothing].enchantment_map.contains[warmth]||false>: #If you have the warmth enchantment on your items then you get an additional 0.4 (per level) on the multiplier - define itemMultiplier <[itemMultiplier].add[<[clothing].enchantment_map.get[warmth].mul[]>]> #Add the equipmentModifier value from the config, multiplied by your itemMultiplier - define equipModifier <[equipModifier].add[]>]> - define temperature <[temperature].add[<[equipModifier]>]> #Check for torches or lanterns in either hand. No benefit given to double-fisting - if <[player].item_in_hand.material.name> == soul_torch || <[player].item_in_hand.material.name> == soul_lantern: - define temperature <[temperature].add[]> - else if <[player].item_in_offhand.material.name> == soul_torch || <[player].item_in_offhand.material.name> == soul_lantern: - define temperature <[temperature].add[]> - else if <[player].item_in_hand.material.name> == torch || <[player].item_in_hand.material.name> == lantern: - define temperature <[temperature].add[]> - else if <[player].item_in_offhand.material.name> == torch || <[player].item_in_offhand.material.name> == lantern: - define temperature <[temperature].add[]> - if <[player].location.material.name> == water: #If you're submerged in water, apply the coldWaterModifier - define temperature <[temperature].sub[]> - if <[player].on_fire>: - define temperatue <[temperature].add[]> GetLocationTemperature: type: task debug: false definitions: player | temperature script: - define biome <[player].location.biome> - if <[player].has_flag[temperature_lastBiome]>: #If they have this flag then they are in a river biome, get their last biome - define biome <[player].flag[temperature_lastBiome]> - define temperature <[biome].temperature> - if : #Override cold biome temperatures - choose <[biome].name>: - case cold_ocean deep_cold_ocean deep_frozen_ocean: #These are all given a 0.2 value of "Cold" - define temperature 0.2 #Apply modifier based on world environment: - define temperature <[temperature].add[]||0>]> #Apply modifier based on world name - if ]>: - define temperature <[temperature].add[]||0>]> - if <[player].location.world.environment> == normal: #Generate and apply the modifier for the time of day, only if it's a normal world - define timeTemp 0 - choose <[player].world.time.period>: - case night: - define timeTemp - - case day: #Multiply daytime temp by 2 (Reward day travel and adjust temperatures to feel more accurate) - define timeTemp - define temperature <[temperature].add[<[timeTemp]>]> - if && <[player].location.y> > 64: #If Altitude is turned on, we reduce the location accordingly. #Temperatures drop at a rate of 0.001666 per meter above sea level. ref: https://minecraft.fandom.com/wiki/Biome - define altitudeModifier <[player].location.y.sub[64].mul[0.001666]> - define temperature <[temperature].sub[<[altitudeModifier]>]>