Compare commits
No commits in common. "master" and "v0.1.0" have entirely different histories.
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,38 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
1
.gitignore
vendored
@ -1 +0,0 @@
|
||||
*.code-workspace
|
@ -1,76 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at t.affeldt@tu-braunschweig.de. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
108
README.md
@ -1,8 +1,5 @@
|
||||
# Climate API
|
||||
A powerful engine for weather presets and visual effects.
|
||||
Requires a weather pack like [Regional Weather](https://github.com/t-affeldt/regional_weather).
|
||||
|
||||
![](https://raw.githubusercontent.com/t-affeldt/climate_api/master/screenshot.png)
|
||||
A powerful engine for weather presets and visual effects
|
||||
|
||||
Use the regional climate to set up different effects for different regions.
|
||||
Control where your effects are activated based on temperature, humidity, wind,
|
||||
@ -11,107 +8,8 @@ Climate API provides temperature and humidity values on a block-per-block basis
|
||||
that follow the seasons, day / night cycle and random changes.
|
||||
Make it rain, change the sky or poison the player - it's up to you.
|
||||
|
||||
## Troubleshooting
|
||||
Generally speaking, most mods should be compatible.
|
||||
|
||||
If you notice __odd movement speeds__ or jump heights of players, you should check for mods that also modify player physics. Use a compatibility mod like [player_monoids](https://github.com/minetest-mods/player_monoids) or [playerphysics](https://forum.minetest.net/viewtopic.php?t=22172) to get rid of this problem. This requires the conflicting mod to also support the chosen compatibility layer.
|
||||
|
||||
Mods that __modify the sky__ (including skybox, moon, sun, stars and clouds) are sadly not fully compatible because they conflict with Climate API's sky system. You should deactivate the sky features in either mod. You can do this here using the ``Override the skybox`` setting. If you're a mod maker then you can also optionally depend on climate_api and use ``climate_api.skybox.add_layer(playername, layer_name, options)`` to register your skybox change in a compatible way. Note that you need __at least Minetest v5.2.0__ for skybox changes to have any effect.
|
||||
|
||||
__Important__: Conflicting skybox changes include the ``weather`` mod included in vanilla __Minetest Game__. You will want to disable that mod in order to use the more advanced cloud system introduced by Climate API. Head to ``Settings → All Settings → Games → Minetest Game`` and set ``Enable weather`` to ``Disabled``. This setting will only exist if you are using Minetest Game v5.2.0 or higher.
|
||||
|
||||
The following mods have been created specifically with Climate API in mind:
|
||||
- [Regional Weather](https://github.com/t-affeldt/regional_weather): My own weather pack for climate based weather effects
|
||||
- [Moon Phases](https://github.com/t-affeldt/minetest_moon_phase): Complements weather effects with dynamic sky changes and a full moon cycle
|
||||
- [Sailing Kit](https://github.com/t-affeldt/sailing_kit) (Fork): Uses Climate API's new wind system to sail across the sea.
|
||||
|
||||
The following mods complement Climate API particularly well:
|
||||
- [Lightning](https://github.com/minetest-mods/lightning): Adds to heavy rain by enabling additional lightning effects
|
||||
- [Ambience](https://notabug.org/TenPlus1/ambience): Plays some nice ambient sound effects based on where you are.
|
||||
|
||||
## Chat Commands
|
||||
- ``/weather``: Display information on current weather effects. This command will show you current temperature and humidity, active weather presets and currently playing effects
|
||||
- ``/weather_settings``: Display current mod configuration in the chat
|
||||
- ``/weather_influences``: Display all different factors and how they affect you in this moment.
|
||||
- ``/weather_status``: Display a list of all installed weather presets and whether they have been forced on, turned off, or are running normally (auto). If no weather presets are listed here then you need to install a weather mod like Regional Weather.
|
||||
- ``/grant <playername> weather``: Enable a specified player to modify the current weather.
|
||||
- ``/set_heat <value>``: Override global heat levels with given value.
|
||||
- ``/set_base_heat <value>``: Override the base heat value used to calculate local climate. Positive numbers will increase temperature by X degrees Fahrenheit, whereas negative values will lower it.
|
||||
- ``/set_humidity <value>``: Override global humidity levels with given value.
|
||||
- ``/set_base_humidity <value>``: Override the base humidity value used to calculate local climate. Positive numbers will increase humidity by X percent, whereas negative values will lower it.
|
||||
- ``/set_wind <x> <z>``: Override wind speed and direction. Higher absolute values result in stronger wind. The sign indicates direction.
|
||||
- ``/set_weather <weather> <on|off|auto>``: Set a weather preset to always be applied (on), disable it completely (off), or reset it to be applied automatically (auto). Turning presets on manually might result in partially missing effects (like no sound if you enable sandstorms but no storms). Use ``/weather_status`` for a full list of installed weather presets. The prefix is important.
|
||||
|
||||
## Configuration Options
|
||||
You can find all mod configuration options in your Minetest launcher.
|
||||
Go to ``Settings → All Settings → Mods → climate_api`` to change them.
|
||||
Individual weather packs may provide additional configuration options in their respective mod configuration section.
|
||||
|
||||
### Performance
|
||||
- ``Update speed of weather effects`` (default 1.0):
|
||||
This value regulates how often weather presets are recalculated.
|
||||
Higher values will result in smoother transitions between effects as well as faster response times to traveling players.
|
||||
Lower values will significantly increase overall performance at the cost of rougher looking effects.
|
||||
- ``Multiplicator for used particles`` (default 1.0):
|
||||
This value regulates how many particles will be spawned.
|
||||
A value of 1 will use the recommended amount of particles.
|
||||
Lower values can possible increase performance.
|
||||
- ``Dynamically modify nodes`` (default true):
|
||||
If set to true, weather packs are allowed to register node update handlers.
|
||||
These can be used to dynamically place snow layers, melt ice, or hydrate soil.
|
||||
- ``Include wind speed in damage checks`` (default true):
|
||||
If set to true, Climate API will factor in wind speed and obstacles to determine damage sources.
|
||||
If set to false, a simple check will be used whether the player is outside.
|
||||
|
||||
### Weather Effects
|
||||
- ``Cause player damage`` (default true):
|
||||
If set to true, dangerous weather presets will damage affected players over time.
|
||||
- ``Show particle effects`` (default true):
|
||||
If set to true, weather effects (like rain) are allowed to render particles.
|
||||
Deactivating this feature will prevent some presets from being visible.
|
||||
For performance considerations it is recommended to decrease the amount of particles instead.
|
||||
- ``Override the skybox`` (default true):
|
||||
If set to true, weather effects are allowed to modify a player's sky.
|
||||
This includes skybox, sun, moon, and clouds (also used for fog effects).
|
||||
Running this mod on Minetest 5.1.2 or earlier versions will automatically disable this feature.
|
||||
- ``Display HUD overlays`` (default true):
|
||||
If set to true, weather effects are allowed to render an image on top of the gameplay.
|
||||
This is usually an optional effect used to increase immersion (like a frozen-over camera in a snow storm).
|
||||
|
||||
### Environment
|
||||
- ``Global base temperature`` (default 0):
|
||||
This value will be added to all biome's base temperatures before applying random modifiers.
|
||||
Every unit here will increase the global base heat by one degree Fahrenheit.
|
||||
Negative values will cool down global base heat respectively.
|
||||
- ``Global base humidity`` (default 0):
|
||||
This value will be added to all biome's base humidity before applying random modifiers.
|
||||
Every unit here will increase the global base humidity by one percent.
|
||||
Negative values will dry up global base humidity respectively.
|
||||
- ``Time rate of weather changes`` (default 1.0):
|
||||
This value regulates how quickly environment factors like heat, humidity and wind are changing.
|
||||
A value of 2 will double the speed at which weather presets change.
|
||||
A value of 0.5 will half the speed respectively.
|
||||
|
||||
### Preferences
|
||||
- ``Show degrees in Fahrenheit instead of Celsius`` (default false):
|
||||
If set to true, temperature information in */weather* command will be displayed in Fahrenheit.
|
||||
- ``Play ambient sound loops`` (default true):
|
||||
If set to true, weather effects are allowed to play sound loops.
|
||||
Note that you can also adjust sound levels instead of deactivating this feature completely.
|
||||
- ``Volume of sound effects`` (default 1.0):
|
||||
This value regulates overall sound volume.
|
||||
A value of 2 will double the volume whereas a value of 0.5 will reduce the volume by half.
|
||||
|
||||
## Modding Information
|
||||
Check the [api_doc.md](https://github.com/t-affeldt/climate_api/blob/master/api_doc.md) for a (currently incomplete) documentation on how to register new weather presets and visual effects. Use my weather [presets](https://github.com/t-affeldt/regional_weather/tree/master/ca_weathers) and [effects](https://github.com/t-affeldt/regional_weather/tree/master/ca_effects) as an example. Ask in the [forum](https://forum.minetest.net/viewtopic.php?t=24569) or open an [issue](https://github.com/t-affeldt/climate_api/issues) if you run into problems. Also check the source code of predefined weather effects because they include usage documentation at the top of each file.
|
||||
|
||||
## License
|
||||
- Source Code: *GNU LGPL v3* by me
|
||||
- Sun and moon textures: *CC BY-SA (3.0)* by Cap
|
||||
|
||||
## Assets in screenshots
|
||||
- Screenshots and editing by me: *CC BY-SA (3.0)*
|
||||
- Logos and artwork: *CC BY-SA (3.0)* by Cap
|
||||
- All screenshots and editing by me: *CC BY-SA (4.0)*
|
||||
- Lato Font (for the Logo): *OFL* by Łukasz Dziedzic from http://www.latofonts.com/lato-free-fonts/
|
||||
- Source Sans Pro (for the subtitles): *OFL*, see https://fonts.google.com/specimen/Source+Sans+Pro
|
||||
- Liberation Fonts (for the text): *OFL*, see https://github.com/liberationfonts/liberation-fonts
|
||||
- Used texture pack: Polygonia (128px edition) *CC BY-SA (4.0)* by Lokrates. See https://forum.minetest.net/viewtopic.php?f=4&t=19043
|
26
ROADMAP.md
@ -1,11 +1,30 @@
|
||||
# TODO
|
||||
|
||||
## Needs improvement
|
||||
- Write documentation on how to add weathers and effects
|
||||
## Required for MVP
|
||||
- Find good values for weather conditions
|
||||
- Make sure all weather presets are working
|
||||
|
||||
## Required for Beta
|
||||
- Ability to register environment conditions dynamically (like the heat)
|
||||
|
||||
## Planned for first release
|
||||
- Improve test_condition function
|
||||
- Write helpful README
|
||||
- Set effects on player join
|
||||
- Improve value structures of particle and skybox effects
|
||||
- Make sounds adjust to changes by weather presets
|
||||
- Ability to force set a wind speed via chat commands
|
||||
|
||||
## Nice to have
|
||||
- Write documentation on how to add weathers and effects
|
||||
- Assign meta data (like "downfall", "wind", etc.) to weather presets
|
||||
- Fog effects
|
||||
- Optimize performance by replacing some particles with animated texture planes
|
||||
- Make switches between effects more smooth
|
||||
- Adjust size of particle boxes based on player speed
|
||||
- Create conditions for time of day, annual progression, biome filters
|
||||
- Fork lightning so that it uses skylayer
|
||||
- Support for sail boats mod
|
||||
|
||||
## Future Plans & Ideas
|
||||
- Complete season system
|
||||
@ -32,5 +51,4 @@
|
||||
- swimming will cool down
|
||||
- standing near fire will warm up
|
||||
- craftable warm clothes
|
||||
- metal armor will worsen heat issues
|
||||
- A flag indicating wind direction
|
||||
- metal armor will worsen heat issues
|
189
api_doc.md
@ -1,189 +0,0 @@
|
||||
# API Documentation
|
||||
|
||||
## How to read this document
|
||||
If a function states multiple parameters of the same name then either of them has to be passed on function call. Look at the function signature to determine the order of required parameters. So far, all parameters are mandatory.
|
||||
|
||||
## Custom Weather Registration
|
||||
|
||||
### Register Weather Preset
|
||||
``climate_api.register_weather(name, conditions, effects)``
|
||||
|
||||
Invoke this function in order to create and register a new weather preset. Presets control which effects are to be applied to a player under specific circumstances. As an example, a preset for rain could require a high level of humidity and apply particle and sound effects.
|
||||
|
||||
__Parameters__:
|
||||
- ``name <string>``: A unique identifier resembling the new weather preset.
|
||||
Should be prefixed with the mod's name in a way that could look like ``mymod:awesome_weather``. This name should only be used once.
|
||||
- ``conditions <table>``: An associative array that checks weather influences for specified values. Keys should be the name of an influence, values should be of matching type for repective influence. Keys can be prefixed with ``min_`` to accept any value equal or higher than influence value. A prefix of ``max_`` will accept any value lesser than influence value. A prefix of ``has_`` can be used in conjunction with a numeric array as a value. It will accept any influence value that is present in the specified table. Omitting a prefix will accept the specified value only. All table entries have to be matched positively for the weather preset to be applied.
|
||||
- ``conditions <function>``: For more control, a function can be specified instead of a conditions table. The function will receive a table as its first parameter, consisting of key-value pairs indicating the current value for each weather influence. The function is expected to return true if the weather preset is to be applied or false otherwise.
|
||||
- ``effects <table>``: An associative array indicating which weather effects are to be applied whenever the weather preset is active. The key should be a registered weather effect name. The value will be passed as a parameter to the effect. Look at the documentation of individual effects to determine valid values.
|
||||
- ``effects <function>``: A generator function that returns a set of weather effects and its parameters. This function will receive a table as its first parameter, consisting of key-value pairs indicating the current value for each weather influence. It is expected to return a table in the same fashion as ``effects <table>`` would have looked like.
|
||||
|
||||
__Returns__: ``nil``
|
||||
|
||||
|
||||
### Register Weather Effect
|
||||
``climate_api.register_effect(name, handler, htype)``
|
||||
|
||||
__Parameters__:
|
||||
- ``name <string>``: A unique identifier resembling the new weather effect.
|
||||
Should be prefixed with the mod's name in a way that could look like ``mymod:special_effect``. Call this function multiple times with the same name in order to apply multiple handler methods to the same effect.
|
||||
- ``handler <function>``: This function will be called whenever players are affected by the registered effect. It receives a single parameter containing an accociative array. The keys represent player names and the values represent the set of applied preset parameters. This set is an accociative array as well with the keys representing the names of applied weather presets and the values representing the supplied data from those presets. This parameter could look like this:
|
||||
``{ singleplayer = {"mymod:awesome_weather" = "a", "mymod:amazing_weather" = "b"} }``. If ``htype`` is ``tick`` then the list of players represents all currently affected players. If it is ``start`` or ``end`` then the list contains all players going though that change.
|
||||
- ``htype <"start" | "tick" | "end">``: Determines when the handler will be called. ``start`` results in a callback whenever an effect is applied to at least one player, meaning that the very first weather preset applies it. ``tick`` results in a callback every update cycle as long as at least one player has the effect from at least one preset. ``end`` results in a callback whenever at least one player loses the effect completely.
|
||||
|
||||
__Returns__: ``nil``
|
||||
|
||||
|
||||
### Set Update Cycle for Effect
|
||||
``climate_api.set_effect_cycle(name, cycle)``
|
||||
|
||||
__Parameters__:
|
||||
- ``name <string>``: The identifier of a registered weather effect
|
||||
- ``cycle <number>``: The minimal time between update calls to registered effect handlers in seconds. This value defaults to ``climate_api.DEFAULT_CYCLE`` (2.0s). Other values are ``climate_api.SHORT_CYCLE`` (0s) for frequent update calls (like for particle effects) and ``climate_api.LONG_CYCLE`` (5.0s) for ressource intensive tasks and timed effects (like lightning strikes). You can also use any other custom number representing the amount of time in seconds.
|
||||
|
||||
__Returns__: ``nil``
|
||||
|
||||
### Register Global Environment Influence
|
||||
``climate_api.register_global_influence(name, func)``
|
||||
|
||||
__Parameters__:
|
||||
- ``name <string>`` A unique name identifying the registered influence
|
||||
- ``func <function>``: A generator function that returns some value which is in turn supplied to weather presets and can be used as a condition.
|
||||
|
||||
__Returns__: ``nil``
|
||||
|
||||
### Register Local Environment Influence
|
||||
``climate_api.register_influence(name, func)``
|
||||
|
||||
__Parameters__:
|
||||
- ``name <string>`` A unique name identifying the registered influence
|
||||
- ``func <function>``: A generator function that returns some value which is in turn supplied to weather presets and can be used as a condition. This function will receive a vector as its single parameter indicating the current position.
|
||||
|
||||
__Returns__: ``nil``
|
||||
|
||||
### Register Active Block Modifier
|
||||
``climate_api.register_abm(config)``
|
||||
|
||||
|
||||
## Environment Access
|
||||
|
||||
### Get Temperature At Position
|
||||
``climate_api.environment.get_heat(pos)``
|
||||
|
||||
__Parameter__: ``pos <vector>``: Coordinates of requested location
|
||||
|
||||
__Returns__: ``<number>`` indicating current temperature in °F
|
||||
|
||||
### Get Humidity At Position
|
||||
``climate_api.environment.get_humidity(pos)``
|
||||
|
||||
__Parameter__: ``pos <vector>``: Coordinates of requested location
|
||||
|
||||
__Returns__: ``<number>`` indicating current humidity
|
||||
|
||||
### Get Current Windspeed
|
||||
``climate_api.environment.get_wind(pos)``
|
||||
|
||||
__Parameter__: ``pos <vector>``: Coordinates of requested location. Right now, only y-coordinate is used.
|
||||
|
||||
__Returns__: ``<vector>`` indicating speed and direction
|
||||
|
||||
### Get Active Weather Presets
|
||||
``climate_api.environment.get_weather_presets(player)``
|
||||
|
||||
### Get Active Weather Effects
|
||||
``climate_api.environment.get_effects(player)``
|
||||
|
||||
|
||||
## Skybox Modification
|
||||
|
||||
### Add Sky Configuration Layer
|
||||
``climate_api.skybox.add(playername, name, sky)``
|
||||
|
||||
### Remove Sky Configuration Layer
|
||||
``climate_api.skybox.remove(playername, name)``
|
||||
|
||||
### Update Player Sky
|
||||
``climate_api.skybox.update(playername)``
|
||||
|
||||
|
||||
## Player Physics Modifications
|
||||
Climate API provides an easy way of modfying player physics in a compatible way.
|
||||
The API is similar to those of ``player_monoids`` or ``playerphysics`` because it also uses multiplication to account for multiple modifiers.
|
||||
In fact, these functions use ``player_monoids`` under the hud if that mod is available. If not, they will fall back to ``playerphysics``, ``pova``, or native overrides in that order.
|
||||
|
||||
### Add Physics Modifier
|
||||
``climate_api.player_physics.add(id, player, effect, value)``
|
||||
|
||||
Register a new modifier that will be multiplied with the current value to set the new physics factor. Call this function again with the same id in order to change an existing modifier.
|
||||
|
||||
__Parameters__:
|
||||
- ``id <string>``: A unique name used to identify the modifier. Should be prefixed with the mod's name.
|
||||
- ``player <ObjectRef>``: The player affected by the physics change
|
||||
- ``effect <"speed" | "jump" | "gravity">``: The type of physics to be changed
|
||||
- ``value <number>``: The multiplicator. Use values between 0 and 1 to reduce physics attribute. Use values above 1 to increase it.
|
||||
|
||||
__Returns__: ``nil``
|
||||
|
||||
### Remove Physics Modifier
|
||||
``climate_api.player_physics.remove(id, player, effect)``
|
||||
|
||||
Use this function to completely remove a physics modifer from the attribute calculation.
|
||||
|
||||
__Parameters__:
|
||||
- ``id <string>``: The name used in ``player_physics.add`` that identifies a registered modifier
|
||||
- ``player <ObjectRef>``: The player affected by the physics change
|
||||
- ``effect <"speed" | "jump" | "gravity">``: The type of physics to be changed
|
||||
|
||||
__Returns__: ``nil``
|
||||
|
||||
|
||||
## Utility Functions
|
||||
|
||||
### Merge Tables
|
||||
``climate_api.utility.merge_tables(a, b)``
|
||||
|
||||
This function will merge two given accociative tables and return the result.
|
||||
If in conflict, attributes of table B will override those of table A.
|
||||
This is especially useful when assigning default values to a specified configuration with possibly missing entries.
|
||||
Note that this function will also modify table A. If you want to prevent that, you should copy the table first: ``climate_api.utility.merge_tables(table.copy(a), b)``.
|
||||
|
||||
__Parameters__:
|
||||
- ``a <table>``: The base table consisting of default values or other data
|
||||
- ``b <table>``: The prioritized table to merge with, and possibly override A
|
||||
|
||||
__Returns__: ``<table>`` consisting of all attributes from A and B.
|
||||
|
||||
### Limit Numeric Boundaries
|
||||
``climate_api.utility.rangelim(value, min, max)``
|
||||
|
||||
This function will return the specified value if it falls in range between minimum and maximum. Otherwise, it will return the closest boundary.
|
||||
|
||||
__Parameter__:
|
||||
- ``value <number>``: The number to check and return
|
||||
- ``min <number>``: The lower boundary
|
||||
- ``max <number>``: The upper boundary
|
||||
|
||||
__Returns__: ``<number>`` being either the value or the closest boundary
|
||||
|
||||
### Statistical Sigmoid Function
|
||||
``climate_api.utility.sigmoid(value, max, growth, midpoint)``
|
||||
|
||||
This method provides a logistic function that will result in growing return values for greater input values. The resulting function graph will look like an S and fall in range between ``0`` and ``max``. You can adjust the curve by specifying growth rate and midpoint.
|
||||
|
||||
__Parameters__:
|
||||
- ``value <number>``: x value supplied to the function
|
||||
- ``max <number>``: Maximum return value
|
||||
- ``growth <number>``: Logistic growth rate
|
||||
- ``midpoint <number>`` Return value for ``value = 0`` and function midpoint
|
||||
|
||||
__Returns__: ``<number>`` indicating y coordinate of logistic function
|
||||
|
||||
### Normalized Sinus Cycle
|
||||
``climate_api.utility.normalized_cycle(value)``
|
||||
|
||||
This function provides an easy way to generate a simple curve with a maximal turning point of ``y = 1`` located at ``value = 0`` and the lowest points of ``y = 0`` at ``value = +/- 1``.
|
||||
|
||||
__Parameter__: ``value <number>``: The supplied x coordinate
|
||||
|
||||
__Returns__: ``<number>`` indicating resulting y coordinate between ``0`` and ``1``.
|
@ -1,83 +0,0 @@
|
||||
--[[
|
||||
# Player Damage Effect
|
||||
Use this effect to damage a player during dangerous weather events.
|
||||
Expects a table as the parameter containing the following values:
|
||||
- value <int> [1]: The amount of damage to be applied per successful roll.
|
||||
- rarity <int> [1]: Defines a 1/x chance per cycle for the player to get damaged. Higher values result in less frequent damage.
|
||||
- check <table> [nil]: Use an additional outdoors check before applying damage. Consists of the following values:
|
||||
- type <"light"|"raycast"> ["light"] (Whether the light level should be used a raycast should be performed)
|
||||
- height <number> [0] (Height offset of weather origin from the player. Only used for raycasts)
|
||||
- velocity <number> [1] (Velocity of damaging particles. Only used for raycasts)
|
||||
- use_wind <bool> [true] (Whether the wind should be factored in. Only used for raycasts)
|
||||
]]
|
||||
|
||||
if not minetest.is_yes(minetest.settings:get_bool("enable_damage"))
|
||||
or not climate_mod.settings.damage then return end
|
||||
|
||||
local EFFECT_NAME = "climate_api:damage"
|
||||
|
||||
local rng = PcgRandom(7819792)
|
||||
|
||||
local function check_hit(player, ray)
|
||||
local ppos = vector.add(player:get_pos(), {x=0, y=1, z=0})
|
||||
if ray.type ~= nil and ray.type ~= "light" and ray.type ~= "raycast" then
|
||||
minetest.log("warning", "[Climate API] Invalid damage check configuration")
|
||||
return false
|
||||
end
|
||||
|
||||
-- use light level if specified or in performance mode
|
||||
if ray.type == nil
|
||||
or ray.type == "light"
|
||||
or not climate_mod.settings.raycast then
|
||||
return minetest.get_node_light(ppos, 0.5) == 15
|
||||
end
|
||||
|
||||
-- use raycating to factor in wind speed
|
||||
local origin = vector.add(ppos, {x = 0, y = ray.height or 0, z = 0 })
|
||||
if ray.use_wind ~= false then
|
||||
local wind = climate_api.environment.get_wind(origin)
|
||||
local velocity = ray.velocity or 1
|
||||
local windpos = vector.multiply(
|
||||
vector.normalize(vector.add({ x = 0, y = -velocity, z = 0 }, wind)),
|
||||
-vector.length(wind)
|
||||
)
|
||||
origin = vector.add(origin, windpos)
|
||||
end
|
||||
ray = minetest.raycast(origin, ppos)
|
||||
local obj = ray:next()
|
||||
-- found nothing
|
||||
if obj == nil then return false end
|
||||
-- found node
|
||||
if obj.type ~= "object" then return false end
|
||||
-- found different entity
|
||||
if not obj.ref:is_player() then return false end
|
||||
-- found another player
|
||||
if obj.ref:get_player_name() ~= player:get_player_name() then return false end
|
||||
return true
|
||||
end
|
||||
|
||||
local function calc_damage(player, dmg)
|
||||
if dmg.value == nil then dmg.value = 1 end
|
||||
if dmg.rarity == nil then dmg.rarity = 1 end
|
||||
-- check if damage should be applied
|
||||
if rng:next(1, dmg.rarity) ~= 1 then return 0 end
|
||||
if dmg.check ~= nil then
|
||||
-- check for obstacles in the way
|
||||
if not check_hit(player, dmg.check) then return 0 end
|
||||
end
|
||||
return dmg.value
|
||||
end
|
||||
|
||||
local function handle_effect(player_data)
|
||||
for playername, data in pairs(player_data) do
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
local hp = player:get_hp()
|
||||
for weather, dmg in pairs(data) do
|
||||
hp = hp - calc_damage(player, dmg)
|
||||
end
|
||||
-- deal damage to player
|
||||
player:set_hp(hp, "weather damage")
|
||||
end
|
||||
end
|
||||
|
||||
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
|
@ -1,41 +1,12 @@
|
||||
--[[
|
||||
# HUD Overlay Effect
|
||||
Use this effect to display a fullscreen image as part of a player's HUD.
|
||||
Expects a table as the parameter containing the following values:
|
||||
- ``file <string>``: The name (including file ending) if the image to be displayed
|
||||
- ``z_index <number>`` (optional): The z_index to forward to player.hud_add. Defaults to 1
|
||||
- ``color_correction <bool>`` (optional): Whether the image should automatically darken based on current light. Defaults to false.
|
||||
]]
|
||||
|
||||
if not climate_mod.settings.hud_overlay then return end
|
||||
|
||||
local EFFECT_NAME = "climate_api:hud_overlay"
|
||||
|
||||
local handles = {}
|
||||
local function apply_hud(pname, weather, hud)
|
||||
local player = minetest.get_player_by_name(pname)
|
||||
if player == nil then return end
|
||||
|
||||
if handles[pname] == nil then handles[pname] = {} end
|
||||
if handles[pname][weather] ~= nil then
|
||||
player:hud_remove(handles[pname][weather])
|
||||
end
|
||||
|
||||
local file
|
||||
if hud.color_correction then
|
||||
local pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0})
|
||||
local node_light = minetest.env:get_node_light(pos)
|
||||
if not node_light then node_light = 0 end
|
||||
local light = math.floor(math.max(node_light / 15, 0.2) * 256)
|
||||
local shadow = 256 - light
|
||||
|
||||
local dark_file = hud.file .. "^[multiply:#000000ff^[opacity:" .. shadow
|
||||
local light_file = hud.file .. "^[opacity:" .. light
|
||||
file = "(" .. light_file .. ")^(" .. dark_file .. ")"
|
||||
else
|
||||
file = hud.file
|
||||
end
|
||||
|
||||
if handles[pname][weather] ~= nil then return end
|
||||
local player = minetest.get_player_by_name(pname)
|
||||
local handle = player:hud_add({
|
||||
name = weather,
|
||||
hud_elem_type = "image",
|
||||
@ -43,18 +14,16 @@ local function apply_hud(pname, weather, hud)
|
||||
alignment = {x = 1, y = 1},
|
||||
scale = { x = -100, y = -100},
|
||||
z_index = hud.z_index,
|
||||
text = file,
|
||||
text = hud.file,
|
||||
offset = {x = 0, y = 0}
|
||||
})
|
||||
handles[pname][weather] = handle
|
||||
end
|
||||
|
||||
local function remove_hud(pname, weather, hud)
|
||||
local player = minetest.get_player_by_name(pname)
|
||||
if player == nil then return end
|
||||
if handles[pname] == nil or handles[pname][weather] == nil then return end
|
||||
|
||||
local handle = handles[pname][weather]
|
||||
local player = minetest.get_player_by_name(pname)
|
||||
player:hud_remove(handle)
|
||||
handles[pname][weather] = nil
|
||||
end
|
||||
@ -70,12 +39,7 @@ end
|
||||
local function handle_effect(player_data, prev_data)
|
||||
for playername, data in pairs(player_data) do
|
||||
for weather, value in pairs(data) do
|
||||
if prev_data[playername][weather] == nil
|
||||
or value.color_correction == true
|
||||
or prev_data[playername][weather].color_correction == true
|
||||
or value.file ~= prev_data[playername][weather].file
|
||||
or value.z_index ~= prev_data[playername][weather].z_index
|
||||
then
|
||||
if prev_data[playername][weather] == nil then
|
||||
apply_hud(playername, weather, value)
|
||||
end
|
||||
end
|
||||
@ -101,3 +65,4 @@ end
|
||||
climate_api.register_effect(EFFECT_NAME, start_effect, "start")
|
||||
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
|
||||
climate_api.register_effect(EFFECT_NAME, stop_effect, "stop")
|
||||
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.MEDIUM_CYCLE)
|
@ -1,215 +1,67 @@
|
||||
--[[
|
||||
# Particle Effect
|
||||
Use this effect to render downfall or similar visuals using particles.
|
||||
Expects a table as the parameter containing information for the spawner.
|
||||
All values for ParticleSpawner definitions are valid.
|
||||
See https://minetest.gitlab.io/minetest/definition-tables/#particlespawner-definition
|
||||
|
||||
Furthermore, the following default values have been changed:
|
||||
- time <int> [0.5] (reduced time results in smoother position updates, but more lag)
|
||||
- collisiondetection <bool> [true]
|
||||
- collision_removal <bool> [true]
|
||||
- playername <string> [current player] (Set to empty string to show for everyone)
|
||||
- vertical <bool> [nil] (Unless explicitly set, particle facing rotation will be automatically set based on direction of velocity)
|
||||
|
||||
The following optional values have been introduced for convenience:
|
||||
- boxsize <vector> [nil] (Overrides minpos and maxpos based on specified sizes per direction with the player in the center)
|
||||
- boxsize <number> [nil] (If set to a number, the resulting vector will have the specified size in all directions)
|
||||
- v_offset <int> [0] (Use in conjunctin with boxsize. Adds specified height to minpos and maxpos y-coordinates)
|
||||
- attach_to_player <bool> [false] (Overrides attached object with current player)
|
||||
|
||||
The following optional values have been expanded with additional value types for convenience:
|
||||
- size <int> [nil] (Overrides both minsize and maxsize if set)
|
||||
- minvel <int> [nil] (Overrides minvel with a downward facing vector of specified length)
|
||||
- maxvel <int> [nil] (Overrides maxvel with a downward facing vector of specified length)
|
||||
- velocity <vector | int> [nil] (Overrides both minvel and maxvel if set)
|
||||
- minacc <int> [nil] (Overrides minacc with a downward facing vector of specified length)
|
||||
- maxacc <int> [nil] (Overrides maxacc with a downward facing vector of specified length)
|
||||
- acceleration <vector | int> [nil] (Overrides both minacc and maxacc if set)
|
||||
|
||||
The following new behaviours have been introduced:
|
||||
- use_wind <bool> [true] (Adjusts velocity and position for current windspeed)
|
||||
- detach <bool> [false] (Unless enabled, considers positions as relative to current player as if spawner's position would be attached)
|
||||
- adjust_for_velocity <bool> [true] (Corrects position of particle spawner by player's movement speed. Only applicable if detach = false and not manually attached)
|
||||
]]
|
||||
|
||||
if not climate_mod.settings.particles then return end
|
||||
|
||||
local EFFECT_NAME = "climate_api:particles"
|
||||
local CYCLE_LENGTH = climate_api.SHORT_CYCLE
|
||||
|
||||
-- parse config by injecting default values and adding additional parameters
|
||||
local function parse_config(player, particles)
|
||||
-- override default values with more useful ones
|
||||
local defaults = {
|
||||
local function get_particle_texture(particles)
|
||||
if type(particles.textures) == "nil" or next(particles.textures) == nil then
|
||||
return particles.texture
|
||||
end
|
||||
return particles.textures[math.random(#particles.textures)]
|
||||
end
|
||||
|
||||
local function spawn_particles(player, particles)
|
||||
local ppos = player:getpos()
|
||||
local wind_x = climate_mod.state:get_float("wind_x")
|
||||
local wind_z = climate_mod.state:get_float("wind_z")
|
||||
local wind = vector.new(wind_x, 0, wind_z)
|
||||
local wind_pos = vector.multiply(wind, -1)
|
||||
local wind_speed = vector.length(wind)
|
||||
|
||||
local amount = particles.amount * climate_mod.settings.particle_count
|
||||
local texture = get_particle_texture(particles)
|
||||
|
||||
local minp = vector.add(vector.add(ppos, particles.min_pos), wind_pos)
|
||||
local maxp = vector.add(vector.add(ppos, particles.max_pos), wind_pos)
|
||||
|
||||
local vel = vector.new({
|
||||
x = wind.x,
|
||||
y = -particles.falling_speed,
|
||||
z = wind.z
|
||||
})
|
||||
local acc = vector.new({x=0, y=0, z=0})
|
||||
|
||||
local exp = particles.exptime
|
||||
local vertical = math.abs(vector.normalize(vel).y) >= 0.6
|
||||
|
||||
minetest.add_particlespawner({
|
||||
amount = amount,
|
||||
time = 0.5,
|
||||
minpos = minp,
|
||||
maxpos = maxp,
|
||||
minvel = vel,
|
||||
maxvel = vel,
|
||||
minacc = acc,
|
||||
maxacc = acc,
|
||||
minexptime = exp,
|
||||
maxexptime = exp,
|
||||
minsize = particles.size,
|
||||
maxsize = particles.size,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
playername = player:get_player_name(),
|
||||
use_wind = true,
|
||||
attach_to_player = false,
|
||||
detach = false,
|
||||
adjust_for_velocity = true
|
||||
}
|
||||
|
||||
-- inject missing default values into specified config
|
||||
local config = climate_api.utility.merge_tables(defaults, particles)
|
||||
|
||||
-- scale particle amount based on mod config
|
||||
if particles.amount ~= nil then
|
||||
config.amount = particles.amount * climate_mod.settings.particle_count
|
||||
end
|
||||
|
||||
-- restore default visibility if specified
|
||||
if particles.playername == "" then
|
||||
config.playername = nil
|
||||
end
|
||||
|
||||
-- provide easier param for exptime
|
||||
if particles.expirationtime ~= nil then
|
||||
config.minexptime = particles.expirationtime
|
||||
config.maxexptime = particles.expirationtime
|
||||
config.expirationtime = nil
|
||||
end
|
||||
|
||||
-- provide easier param for size
|
||||
if particles.size ~= nil then
|
||||
config.minsize = particles.size
|
||||
config.maxsize = particles.size
|
||||
config.size = nil
|
||||
end
|
||||
|
||||
-- randomly select a texture when given a table
|
||||
if type(particles.texture) == "table" then
|
||||
config.texture = particles.texture[math.random(#particles.texture)]
|
||||
end
|
||||
|
||||
if particles.pos ~= nil then
|
||||
config.minpos = particles.pos
|
||||
config.maxpos = particles.pos
|
||||
config.pos = nil
|
||||
end
|
||||
|
||||
-- provide easier size based param for position
|
||||
if type(particles.boxsize) == "number" then
|
||||
particles.boxsize = {
|
||||
x = particles.boxsize,
|
||||
y = particles.boxsize,
|
||||
z = particles.boxsize
|
||||
}
|
||||
end
|
||||
|
||||
if particles.boxsize ~= nil then
|
||||
local size_x = particles.boxsize.x or 0
|
||||
local size_y = particles.boxsize.y or 0
|
||||
local size_z = particles.boxsize.z or 0
|
||||
local v_offset = particles.v_offset or 0
|
||||
v_offset = v_offset + (size_y / 2)
|
||||
config.minpos = {
|
||||
x = -size_x / 2,
|
||||
y = v_offset - (size_y / 2),
|
||||
z = -size_z / 2
|
||||
}
|
||||
config.maxpos = {
|
||||
x = size_x / 2,
|
||||
y = v_offset + (size_y / 2),
|
||||
z = size_z / 2
|
||||
}
|
||||
config.size_x = nil
|
||||
config.size_y = nil
|
||||
config.size_z = nil
|
||||
config.v_offset = nil
|
||||
end
|
||||
|
||||
-- provide easy param to define unanimous falling speed
|
||||
if particles.velocity ~= nil then
|
||||
particles.minvel = particles.velocity
|
||||
particles.maxvel = particles.velocity
|
||||
config.velocity = nil
|
||||
end
|
||||
|
||||
if type(particles.minvel) == "number" then
|
||||
config.minvel = { x = 0, y = -particles.minvel, z = 0 }
|
||||
end
|
||||
|
||||
if type(particles.maxvel) ~= nil then
|
||||
config.maxvel = { x = 0, y = -particles.maxvel, z = 0 }
|
||||
end
|
||||
|
||||
-- provide easy param to define unanimous falling acceleration
|
||||
if particles.acceleration ~= nil then
|
||||
particles.minacc = particles.acceleration
|
||||
particles.maxacc = particles.acceleration
|
||||
config.acceleration = nil
|
||||
end
|
||||
|
||||
if type(particles.minacc) == "number" then
|
||||
config.minacc = { x = 0, y = -particles.minacc, z = 0 }
|
||||
end
|
||||
|
||||
if type(particles.maxacc) == "number" then
|
||||
config.maxacc = { x = 0, y = -particles.maxacc, z = 0 }
|
||||
end
|
||||
|
||||
-- attach particles to current player if specified
|
||||
if config.attach_to_player then
|
||||
config.attached = player
|
||||
end
|
||||
config.attach_to_player = nil
|
||||
|
||||
-- attach coordinates to player unless specified or already attached
|
||||
if (not config.detach) and config.attached == nil then
|
||||
local ppos = player:get_pos()
|
||||
config.minpos = vector.add(config.minpos, ppos)
|
||||
config.maxpos = vector.add(config.maxpos, ppos)
|
||||
|
||||
-- correct spawn coordinates to adjust for player movement
|
||||
if config.adjust_for_velocity then
|
||||
local velocity = player:get_velocity()
|
||||
config.minpos = vector.add(config.minpos, velocity)
|
||||
config.maxpos = vector.add(config.maxpos, velocity)
|
||||
end
|
||||
end
|
||||
config.detach = nil
|
||||
config.adjust_for_velocity = nil
|
||||
|
||||
-- move particles in wind direction
|
||||
if config.use_wind then
|
||||
local pos = vector.multiply(vector.add(config.minpos, config.maxpos), 0.5)
|
||||
local wind = climate_api.environment.get_wind(pos)
|
||||
-- adjust velocity to include wind
|
||||
config.minvel = vector.add(config.minvel, wind)
|
||||
config.maxvel = vector.add(config.maxvel, wind)
|
||||
|
||||
-- adjust spawn position for better visibility
|
||||
local vel = vector.multiply(vector.add(config.minvel, config.maxvel), 0.5)
|
||||
local windpos = vector.multiply(
|
||||
vector.normalize(vel),
|
||||
-vector.length(wind)
|
||||
)
|
||||
config.minpos = vector.add(config.minpos, windpos)
|
||||
config.maxpos = vector.add(config.maxpos, windpos)
|
||||
end
|
||||
config.use_wind = nil
|
||||
|
||||
-- if unspecified, use 2D or 3D rotation based on movement direction
|
||||
if particles.vertical == nil then
|
||||
local vel = vector.multiply(vector.add(config.minvel, config.maxvel), 0.5)
|
||||
config.vertical = math.abs(vector.normalize(vel).y) >= 0.6
|
||||
end
|
||||
|
||||
return config
|
||||
vertical = vertical,
|
||||
texture = texture,
|
||||
player = player:get_player_name()
|
||||
})
|
||||
end
|
||||
|
||||
local function handle_effect(player_data)
|
||||
for playername, data in pairs(player_data) do
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
for weather, value in pairs(data) do
|
||||
local config = parse_config(player, value)
|
||||
minetest.add_particlespawner(config)
|
||||
spawn_particles(player, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
|
||||
climate_api.set_effect_cycle(EFFECT_NAME, CYCLE_LENGTH)
|
||||
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.SHORT_CYCLE)
|
@ -1,44 +1,47 @@
|
||||
--[[
|
||||
# Skybox Effect
|
||||
Use this effect to modify a player's sky, clouds, sun, moon, or stars
|
||||
Expects a table as the parameter containing the following values:
|
||||
- ``sky_data <table>`` (optional): Sky paramaters to be applied using player.set_sky
|
||||
- ``cloud_data <table>`` (optional): Cloud paramaters to be applied using player.set_clouds
|
||||
- ``sun_data <table>`` (optional): Sun paramaters to be applied using player.set_sun
|
||||
- ``moon_data <table>`` (optional): Sky paramaters to be applied using player.set_moon
|
||||
- ``star_data <table>`` (optional): Sky paramaters to be applied using player.set_stars
|
||||
- ``priority <number>`` (optional): A skybox with higher priority will override lower rated ones (defaults to 1)
|
||||
]]
|
||||
|
||||
if not climate_mod.settings.skybox then return end
|
||||
|
||||
local EFFECT_NAME = "climate_api:skybox"
|
||||
|
||||
local function handle_effect(player_data, prev_data)
|
||||
for playername, data in pairs(prev_data) do
|
||||
for weather, _ in pairs(data) do
|
||||
if player_data[playername] == nil or player_data[playername][weather] == nil then
|
||||
climate_api.skybox.remove(playername, weather)
|
||||
end
|
||||
end
|
||||
local function set_skybox(player, sky)
|
||||
if sky.sky_data ~= nil then
|
||||
player:set_sky(sky.sky_data)
|
||||
end
|
||||
if sky.cloud_data ~= nil then
|
||||
player:set_clouds(sky.cloud_data)
|
||||
end
|
||||
if sky.moon_data ~= nil then
|
||||
player:set_moon(sky.moon_data)
|
||||
end
|
||||
if sky.sun_data ~= nil then
|
||||
player:set_sun(sky.sun_data)
|
||||
end
|
||||
if sky.stars_data ~= nil then
|
||||
player:set_sun(sky.stars_data)
|
||||
end
|
||||
end
|
||||
|
||||
local function remove_skybox(player)
|
||||
player:set_sky({ type = "regular", clouds = true})
|
||||
end
|
||||
|
||||
local function handle_effect(player_data)
|
||||
for playername, data in pairs(player_data) do
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
local sky = {}
|
||||
for weather, value in pairs(data) do
|
||||
climate_api.skybox.add(playername, weather, value)
|
||||
climate_api.utility.merge_tables(sky, value)
|
||||
end
|
||||
climate_api.skybox.update(playername)
|
||||
set_skybox(player, sky)
|
||||
end
|
||||
end
|
||||
|
||||
local function remove_effect(player_data)
|
||||
for playername, data in pairs(player_data) do
|
||||
for weather, _ in pairs(data) do
|
||||
climate_api.skybox.remove(playername, weather)
|
||||
end
|
||||
climate_api.skybox.update(playername)
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
remove_skybox(player)
|
||||
end
|
||||
end
|
||||
|
||||
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
|
||||
climate_api.register_effect(EFFECT_NAME, remove_effect, "stop")
|
||||
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.LONG_CYCLE)
|
||||
--climate_api.set_effect_cycle("climate_api:skybox", climate_api.LONG_CYCLE)
|
@ -1,32 +1,29 @@
|
||||
--[[
|
||||
# Sound Loop Effect
|
||||
Use this effect to loop an ambient sound effect
|
||||
Expects a table as the parameter containing the following values:
|
||||
- ``name <string>``: Name of the played sound effect (without .ogg file ending)
|
||||
- ``gain <number>`` (optional): Volume of the sound (defaults to 1.0)
|
||||
- ``pitch <number>`` (optional): Pitch of the sound (defaults to 1.0)
|
||||
]]
|
||||
|
||||
if not climate_mod.settings.sound then return end
|
||||
|
||||
local EFFECT_NAME = "climate_api:sound"
|
||||
local FADE_DURATION = climate_api.LONG_CYCLE
|
||||
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
local soundloop = dofile(modpath .. "/lib/soundloop.lua")
|
||||
|
||||
local function start_sound(pname, sound)
|
||||
return soundloop.play(pname, sound, FADE_DURATION)
|
||||
local handles = {}
|
||||
local function start_sound(pname, weather, sound)
|
||||
if handles[pname] == nil then handles[pname] = {} end
|
||||
if handles[pname][weather] ~= nil then return end
|
||||
local handle = minetest.sound_play(sound, {
|
||||
to_player = pname,
|
||||
loop = true
|
||||
})
|
||||
handles[pname][weather] = handle
|
||||
end
|
||||
|
||||
local function stop_sound(pname, sound)
|
||||
return soundloop.stop(pname, sound, FADE_DURATION)
|
||||
local function stop_sound(pname, weather, sound)
|
||||
if handles[pname] == nil or handles[pname][weather] == nil then return end
|
||||
local handle = handles[pname][weather]
|
||||
minetest.sound_stop(handle)
|
||||
handles[pname][weather] = nil
|
||||
end
|
||||
|
||||
local function start_effect(player_data)
|
||||
for playername, data in pairs(player_data) do
|
||||
for weather, value in pairs(data) do
|
||||
start_sound(playername, value)
|
||||
start_sound(playername, weather, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -35,7 +32,7 @@ local function handle_effect(player_data, prev_data)
|
||||
for playername, data in pairs(player_data) do
|
||||
for weather, value in pairs(data) do
|
||||
if prev_data[playername][weather] == nil then
|
||||
start_sound(playername, value)
|
||||
start_sound(playername, weather, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -43,16 +40,17 @@ local function handle_effect(player_data, prev_data)
|
||||
for playername, data in pairs(prev_data) do
|
||||
for weather, value in pairs(data) do
|
||||
if player_data[playername][weather] == nil then
|
||||
stop_sound(playername, value)
|
||||
stop_sound(playername, weather, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function stop_effect(prev_data)
|
||||
minetest.log(dump2(prev_data, "stop_effect"))
|
||||
for playername, data in pairs(prev_data) do
|
||||
for weather, value in pairs(data) do
|
||||
stop_sound(playername, value)
|
||||
stop_sound(playername, weather, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -60,3 +58,4 @@ end
|
||||
climate_api.register_effect(EFFECT_NAME, start_effect, "start")
|
||||
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
|
||||
climate_api.register_effect(EFFECT_NAME, stop_effect, "stop")
|
||||
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.LONG_CYCLE)
|
67
init.lua
@ -1,22 +1,17 @@
|
||||
-- warn about outdated Minetest versions
|
||||
assert(minetest.add_particlespawner, "[Climate API] This mod requires a more current version of Minetest")
|
||||
|
||||
-- initialize global API interfaces
|
||||
climate_api = {}
|
||||
climate_mod = {}
|
||||
|
||||
-- set mod path for file imports
|
||||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
|
||||
-- retrieve boolean value from mod config
|
||||
local function get_setting_bool(name, default)
|
||||
local value = minetest.settings:get_bool("climate_api_" .. name)
|
||||
if type(value) == "nil" then value = default end
|
||||
return minetest.is_yes(value)
|
||||
end
|
||||
|
||||
-- retrive numeric value from mod config
|
||||
local function get_setting_number(name, default)
|
||||
local value = minetest.settings:get("climate_api_" .. name)
|
||||
if type(value) == "nil" then value = default end
|
||||
@ -25,74 +20,40 @@ end
|
||||
|
||||
-- load settings from config file
|
||||
climate_mod.settings = {
|
||||
damage = get_setting_bool("damage", true),
|
||||
raycast = get_setting_bool("raycast", true),
|
||||
particles = get_setting_bool("particles", true),
|
||||
skybox = get_setting_bool("skybox", true),
|
||||
sound = get_setting_bool("sound", true),
|
||||
hud_overlay = get_setting_bool("hud_overlay", true),
|
||||
wind = get_setting_bool("wind", true),
|
||||
seasons = get_setting_bool("seasons", true),
|
||||
fahrenheit = get_setting_bool("fahrenheit", false),
|
||||
block_updates = get_setting_bool("block_updates", true),
|
||||
heat = get_setting_number("heat_base", 0),
|
||||
humidity = get_setting_number("humidity_base", 0),
|
||||
time_spread = get_setting_number("time_spread", 1),
|
||||
particle_count = get_setting_number("particle_count", 1),
|
||||
tick_speed = get_setting_number("tick_speed", 1),
|
||||
volume = get_setting_number("volume", 1),
|
||||
ceiling_checks = get_setting_number("ceiling_checks", 10)
|
||||
particles = get_setting_bool("particles", true),
|
||||
skybox = get_setting_bool("skybox", true),
|
||||
sound = get_setting_bool("sound", true),
|
||||
hud_overlay = get_setting_bool("hud_overlay", true),
|
||||
wind = get_setting_bool("wind", true),
|
||||
seasons = get_setting_bool("seasons", true),
|
||||
fahrenheit = get_setting_bool("fahrenheit", false),
|
||||
heat = get_setting_number("heat_base", 0),
|
||||
humidity = get_setting_number("humidity_base", 0),
|
||||
time_spread = get_setting_number("time_spread", 1),
|
||||
particle_count = get_setting_number("particle_count", 1)
|
||||
}
|
||||
|
||||
climate_mod.i18n = minetest.get_translator("climate_api")
|
||||
|
||||
-- attempt to disable MTG weather mod
|
||||
if climate_mod.settings.skybox and minetest.get_modpath("weather") then
|
||||
if weather ~= nil and weather.get ~= nil then
|
||||
weather.get = function(player)
|
||||
return {}
|
||||
end
|
||||
elseif minetest.settings:get_bool("enable_weather") then
|
||||
-- old version with no API support
|
||||
-- warn about clouds being overriden by MTG weather
|
||||
minetest.log("warning", "[Regional Weather] " ..
|
||||
climate_mod.i18n("Disable MTG weather for the best experience. Check the forum for more information."))
|
||||
end
|
||||
end
|
||||
|
||||
-- initialize empty registers
|
||||
-- initiate empty registers
|
||||
climate_mod.weathers = {}
|
||||
climate_mod.effects = {}
|
||||
climate_mod.cycles = {}
|
||||
climate_mod.global_environment = {}
|
||||
climate_mod.global_influences = {}
|
||||
climate_mod.influences = {}
|
||||
climate_mod.current_weather = {}
|
||||
climate_mod.current_effects = {}
|
||||
climate_mod.forced_weather = {}
|
||||
climate_mod.forced_enviroment = {}
|
||||
|
||||
-- handle persistent mod storage
|
||||
climate_mod.state = dofile(modpath .. "/lib/datastorage.lua")
|
||||
|
||||
-- import core API
|
||||
climate_mod.state = dofile(modpath .. "/lib/datastorage.lua")
|
||||
climate_api = dofile(modpath .. "/lib/api.lua")
|
||||
climate_api.utility = dofile(modpath .. "/lib/api_utility.lua")
|
||||
climate_api.skybox = dofile(modpath .. "/lib/skybox_merger.lua")
|
||||
climate_api.player_physics = dofile(modpath .. "/lib/player_physics.lua")
|
||||
climate_api.environment = dofile(modpath .. "/lib/environment.lua")
|
||||
--climate_api = dofile(modpath .. "/lib/influences.lua")
|
||||
climate_mod.world = dofile(modpath .. "/lib/world.lua")
|
||||
climate_mod.trigger = dofile(modpath .. "/lib/trigger.lua")
|
||||
|
||||
-- start event loop and register chat commands
|
||||
dofile(modpath.."/lib/main.lua")
|
||||
dofile(modpath.."/lib/commands.lua")
|
||||
|
||||
-- register environment influences
|
||||
dofile(modpath .. "/lib/influences.lua")
|
||||
|
||||
-- import predefined environment effects
|
||||
dofile(modpath .. "/ca_effects/damage.lua")
|
||||
dofile(modpath .. "/ca_effects/hud_overlay.lua")
|
||||
dofile(modpath .. "/ca_effects/particles.lua")
|
||||
dofile(modpath .. "/ca_effects/skybox.lua")
|
||||
|
67
lib/api.lua
@ -1,16 +1,12 @@
|
||||
-- initialize API interface
|
||||
local api = {}
|
||||
|
||||
-- define various standard effect cycle lengths
|
||||
api.SHORT_CYCLE = 0 -- for particles and fast animations (use GSCYCLE)
|
||||
api.DEFAULT_CYCLE = 2.0 -- for most effect types
|
||||
api.LONG_CYCLE = 5.0 -- for ressource intensive tasks or timed effects
|
||||
api.SHORT_CYCLE = 0.03 -- for particles and fast animations
|
||||
api.DEFAULT_CYCLE = 0.1 -- for most effect types
|
||||
api.MEDIUM_CYCLE = 2.0 -- for ressource intensive tasks
|
||||
api.LONG_CYCLE = 5.0 -- for write operations and skybox changes
|
||||
|
||||
-- register new weather presets (like rain)
|
||||
-- @param name <string> Unique preset name, ideally prefixed
|
||||
-- @param conditions <table> A collection of required influences
|
||||
-- @param effects <table> A <table> containing all applied effects as keys and parameters as values
|
||||
function api.register_weather(name, conditions, effects)
|
||||
-- TODO: check and sanitize
|
||||
climate_mod.weathers[name] = {
|
||||
conditions = conditions,
|
||||
effects = effects,
|
||||
@ -18,14 +14,10 @@ function api.register_weather(name, conditions, effects)
|
||||
}
|
||||
end
|
||||
|
||||
-- register new weather effects (like particles)
|
||||
-- @param name <string> Unique effect name, ideally prefixed
|
||||
-- @param handler <function> A function to be called when the effect is active
|
||||
-- @param htype <string: start|tick|stop> Determines when the function is called
|
||||
function api.register_effect(name, handler, htype)
|
||||
-- check for valid handler types
|
||||
if htype ~= "start" and htype ~= "tick" and htype ~= "stop" then
|
||||
minetest.log("warning", "[Climate API] Effect " .. dump(name) .. " uses invalid callback type: " .. dump(htype))
|
||||
minetest.log("warning", "[Climate API] Invalid effect handler type: " .. htype)
|
||||
return
|
||||
end
|
||||
-- create effect handler registry if not existent yet
|
||||
@ -37,66 +29,49 @@ function api.register_effect(name, handler, htype)
|
||||
table.insert(climate_mod.effects[name][htype], handler)
|
||||
end
|
||||
|
||||
-- set cycle length of given effect
|
||||
-- @param name <string> Name of the affected effect
|
||||
-- @param cycle <number> Duration between function calls
|
||||
function api.set_effect_cycle(name, cycle)
|
||||
climate_mod.cycles[name].timespan = cycle
|
||||
end
|
||||
|
||||
-- register new environment influence that is independent of position
|
||||
-- @param name <string> Unique influence name
|
||||
-- @param func <function> Returns current influence value for entire world
|
||||
function api.register_global_influence(name, func)
|
||||
climate_mod.global_influences[name] = func
|
||||
end
|
||||
|
||||
-- register new environment influence based on position
|
||||
-- @param name <string> Unique influence name
|
||||
-- @param func <function> Returns current influence value for given position
|
||||
function api.register_influence(name, func)
|
||||
--[[function api.register_influence(name, func)
|
||||
climate_mod.influences[name] = func
|
||||
end
|
||||
end]]
|
||||
|
||||
-- register new Active Block Modifier dependent on weather status
|
||||
-- Uses same config as Minetest.register_abm() but also adds
|
||||
-- conditions similiar to weather presets and provides local environment
|
||||
-- to action event handler as third parameter.
|
||||
-- @param config <table> ABM configuration with additional information
|
||||
function api.register_abm(config)
|
||||
if not climate_mod.settings.block_updates then return end
|
||||
|
||||
local conditions = config.conditions
|
||||
local action = config.action
|
||||
local pos_override = config.pos_override
|
||||
|
||||
-- override action handler to inject weather status
|
||||
local override = function(pos, node)
|
||||
local env = climate_mod.trigger.get_position_environment(pos)
|
||||
if type(pos_override) == "function" then
|
||||
pos = pos_override(pos)
|
||||
node = minetest.get_node(pos)
|
||||
end
|
||||
|
||||
-- get environment influences for current position
|
||||
local env = climate_mod.trigger.get_position_environment(pos)
|
||||
|
||||
if conditions == nil then
|
||||
return action(pos, node, env)
|
||||
end
|
||||
|
||||
-- check if all conditions are met
|
||||
minetest.log(dump2(env, "env"))
|
||||
minetest.log(dump2(conditions, "conditions"))
|
||||
|
||||
for condition, goal in pairs(conditions) do
|
||||
local is_applicable = climate_mod.trigger.test_condition(condition, env, goal)
|
||||
if not is_applicable then return end
|
||||
local value = env[condition:sub(5)]
|
||||
if condition:sub(1, 4) == "min_" then
|
||||
if type(value) == "nil" or goal > value then return end
|
||||
elseif condition:sub(1, 4) == "max_" then
|
||||
if type(value) == "nil" or goal <= value then return end
|
||||
else
|
||||
value = env[condition]
|
||||
if type(value) == "nil" or goal ~= value then return end
|
||||
end
|
||||
end
|
||||
return action(pos, node, env)
|
||||
end
|
||||
|
||||
-- register overridden abm setup
|
||||
config.conditions = nil
|
||||
config.action = override
|
||||
minetest.register_abm(config)
|
||||
end
|
||||
|
||||
-- return supplied API endpoint
|
||||
return api
|
||||
|
@ -1,3 +1,6 @@
|
||||
local mod_player_monoids = minetest.get_modpath("player_monoids") ~= nil
|
||||
local mod_playerphysics = minetest.get_modpath("playerphysics") ~= nil
|
||||
|
||||
local utility = {}
|
||||
|
||||
function utility.rangelim(value, min, max)
|
||||
@ -30,5 +33,31 @@ function utility.normalized_cycle(value)
|
||||
return math.cos((2 * value + 1) * math.pi) / 2 + 0.5
|
||||
end
|
||||
|
||||
-- override player physics
|
||||
-- use utility mod if possible to avoid conflict
|
||||
function utility.add_physics(id, player, effect, value)
|
||||
if mod_player_monoids then
|
||||
player_monoids[effect]:add_change(player, value, id)
|
||||
elseif mod_playerphysics then
|
||||
playerphysics.add_physics_factor(player, effect, id, value)
|
||||
else
|
||||
local override = {}
|
||||
override[effect] = value
|
||||
player:set_physics_override(override)
|
||||
end
|
||||
end
|
||||
|
||||
-- reset player phsysics to normal
|
||||
function utility.remove_physics(id, player, effect)
|
||||
if mod_player_monoids then
|
||||
player_monoids[effect]:del_change(player, id)
|
||||
elseif mod_playerphysics then
|
||||
playerphysics.remove_physics_factor(player, effect, id)
|
||||
else
|
||||
local override = {}
|
||||
override[effect] = 1
|
||||
player:set_physics_override(override)
|
||||
end
|
||||
end
|
||||
|
||||
return utility
|
||||
|
174
lib/commands.lua
@ -1,7 +1,3 @@
|
||||
local S = climate_mod.i18n
|
||||
|
||||
-- parse heat values into readable format
|
||||
-- also convert to Celsius if configured
|
||||
local function parse_heat(heat)
|
||||
local indicator = "°F"
|
||||
if not climate_mod.settings.fahrenheit then
|
||||
@ -12,16 +8,13 @@ local function parse_heat(heat)
|
||||
return heat .. indicator
|
||||
end
|
||||
|
||||
|
||||
-- register weather privilege in order to modify the weather status
|
||||
minetest.register_privilege("weather", {
|
||||
description = S("Make changes to the current weather"),
|
||||
description = "Make changes to the current weather",
|
||||
give_to_singleplayer = false
|
||||
})
|
||||
|
||||
-- display general information on current weather
|
||||
minetest.register_chatcommand("weather", {
|
||||
description = S("Display weather information"),
|
||||
description ="Display weather information",
|
||||
func = function(playername)
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
local ppos = player:get_pos()
|
||||
@ -31,161 +24,82 @@ minetest.register_chatcommand("weather", {
|
||||
local humidity = math.floor(climate_api.environment.get_humidity(ppos) * 100) / 100
|
||||
local msg = ""
|
||||
if #weathers > 0 then
|
||||
msg = msg .. S("The following weather presets are active for you:") .. " "
|
||||
msg = msg .. "The following weather presets are active for you: "
|
||||
for _, weather in ipairs(weathers) do
|
||||
msg = msg .. weather .. ", "
|
||||
end
|
||||
msg = msg:sub(1, #msg-2) .. "\n"
|
||||
else
|
||||
msg = msg .. S("Your sky is clear. No weather presets are currently active.") .. "\n"
|
||||
msg = msg .. "Your sky is clear. No weather presets are currently active.\n"
|
||||
end
|
||||
if #effects > 0 then
|
||||
msg = msg .. S("As a result, the following environment effects are applied:") .. " "
|
||||
msg = msg .. "As a result, the following environment effects are applied: "
|
||||
for _, effect in ipairs(effects) do
|
||||
msg = msg .. effect .. ", "
|
||||
end
|
||||
msg = msg:sub(1, #msg-2) .. "\n"
|
||||
end
|
||||
local heat_desc
|
||||
if heat > 80 then heat_desc = S("scorching")
|
||||
elseif heat > 50 then heat_desc = S("pleasant")
|
||||
else heat_desc = S("chilly") end
|
||||
msg = msg .. S("It is a @1 @2 right now and humidity is at @3%.", heat_desc, parse_heat(heat), humidity) .. "\n"
|
||||
if heat > 80 then heat_desc = "scorching"
|
||||
elseif heat > 50 then heat_desc = "pleasant"
|
||||
else heat_desc = "chilly" end
|
||||
msg = msg .. "It is a " .. heat_desc .. " " .. parse_heat(heat) .. " right now and "
|
||||
msg = msg .. "humidity is at " .. humidity .. "%.\n"
|
||||
minetest.chat_send_player(playername, msg)
|
||||
end
|
||||
})
|
||||
|
||||
-- set base heat to increase or decrease global climate temperatures
|
||||
minetest.register_chatcommand("set_base_heat", {
|
||||
params = S("<heat>"),
|
||||
description = S("Override the weather algorithm's base heat"),
|
||||
privs = { weather = true },
|
||||
func = function(playername, param)
|
||||
if param == nil or param == "" then
|
||||
minetest.chat_send_player(playername, S("Provide a number to modify the base heat"))
|
||||
return
|
||||
end
|
||||
if param == "auto" then param = 0 end
|
||||
climate_mod.settings.heat = tonumber(param) or 0
|
||||
minetest.chat_send_player(playername, S("Base heat changed"))
|
||||
end
|
||||
})
|
||||
|
||||
-- override global heat levels with given value
|
||||
minetest.register_chatcommand("set_heat", {
|
||||
params = S("<heat>"),
|
||||
description = S("Override the weather algorithm's heat"),
|
||||
params = "<heat>",
|
||||
description = "Override the weather algorithm's base heat",
|
||||
privs = { weather = true },
|
||||
func = function(playername, param)
|
||||
if param == nil or param == "" then
|
||||
minetest.chat_send_player(playername, S("Provide a number to modify the heat"))
|
||||
minetest.chat_send_player(playername, "Provide a number to modify the base heat")
|
||||
return
|
||||
end
|
||||
if param == "auto" then
|
||||
climate_mod.forced_enviroment.heat = nil
|
||||
minetest.chat_send_player(playername, S("Heat value reset"))
|
||||
else
|
||||
climate_mod.forced_enviroment.heat = tonumber(param) or 0
|
||||
minetest.chat_send_player(playername, S("Heat value changed"))
|
||||
end
|
||||
climate_mod.settings.heat = tonumber(param)
|
||||
minetest.chat_send_player(playername, "Heat changed")
|
||||
end
|
||||
})
|
||||
|
||||
-- set base heat to increase or decrease global climate humidity
|
||||
minetest.register_chatcommand("set_base_humidity", {
|
||||
params = S("<humidity>"),
|
||||
description = S("Override the weather algorithm's base humidity"),
|
||||
privs = { weather = true },
|
||||
func = function(playername, param)
|
||||
if param == "auto" then param = 0 end
|
||||
if param == nil or param == "" then
|
||||
minetest.chat_send_player(playername, S("Provide a number to modify the base humidity"))
|
||||
return
|
||||
end
|
||||
climate_mod.settings.humidity = tonumber(param) or 0
|
||||
minetest.chat_send_player(playername, S("Base humidity changed"))
|
||||
end
|
||||
})
|
||||
|
||||
-- override global humidity with given value
|
||||
minetest.register_chatcommand("set_humidity", {
|
||||
params = S("<humidity>"),
|
||||
description = S("Override the weather algorithm's humidity"),
|
||||
params = "<humidity>",
|
||||
description = "Override the weather algorithm's base humidity",
|
||||
privs = { weather = true },
|
||||
func = function(playername, param)
|
||||
if param == nil or param == "" then
|
||||
minetest.chat_send_player(playername, S("Provide a number to modify the humidity"))
|
||||
minetest.chat_send_player(playername, "Provide a number to modify the base humidity")
|
||||
return
|
||||
end
|
||||
if param == "auto" then
|
||||
climate_mod.forced_enviroment.humidity = nil
|
||||
minetest.chat_send_player(playername, S("Humidity value reset"))
|
||||
else
|
||||
climate_mod.forced_enviroment.humidity = tonumber(param) or 0
|
||||
minetest.chat_send_player(playername, S("Humidity value changed"))
|
||||
end
|
||||
climate_mod.settings.humidity = tonumber(param)
|
||||
minetest.chat_send_player(playername, "Humidity changed")
|
||||
end
|
||||
})
|
||||
|
||||
-- override wind direction and speed with given values
|
||||
minetest.register_chatcommand("set_wind", {
|
||||
params = S("<wind>"),
|
||||
description = S("Override the weather algorithm's windspeed"),
|
||||
privs = { weather = true },
|
||||
func = function(playername, param)
|
||||
if param == nil or param == "" then
|
||||
minetest.chat_send_player(playername, S("Provide a vector of two numbers to modify the wind"))
|
||||
return
|
||||
end
|
||||
local arguments = {}
|
||||
for w in param:gmatch("%S+") do table.insert(arguments, w) end
|
||||
local arg1 = arguments[1]
|
||||
local wind_x = tonumber(arguments[1])
|
||||
local wind_z = tonumber(arguments[2])
|
||||
if arg1 == "auto" then
|
||||
climate_mod.forced_enviroment.wind = nil
|
||||
minetest.chat_send_player(playername, S("Wind reset"))
|
||||
elseif wind_x == nil or wind_z == nil then
|
||||
minetest.chat_send_player(playername, S("Invalid wind configuration"))
|
||||
else
|
||||
climate_mod.forced_enviroment.wind = vector.new({
|
||||
x = wind_x,
|
||||
y = 0,
|
||||
z = wind_z
|
||||
})
|
||||
minetest.chat_send_player(playername, S("Wind changed"))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- display current mod config
|
||||
minetest.register_chatcommand("weather_settings", {
|
||||
description = S("Print the active Climate API configuration"),
|
||||
description = "Print the active Climate API configuration",
|
||||
func = function(playername)
|
||||
minetest.chat_send_player(playername, S("Current Settings") .. "\n================")
|
||||
minetest.chat_send_player(playername, "Current Settings\n================")
|
||||
for setting, value in pairs(climate_mod.settings) do
|
||||
minetest.chat_send_player(playername, dump2(value, setting))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- force a weather preset or disable it
|
||||
minetest.register_chatcommand("set_weather", {
|
||||
params = S("<weather> <status>"),
|
||||
description = S("Turn the specified weather preset on or off for all players or reset it to automatic"),
|
||||
params ="<weather> <status>",
|
||||
description ="Turn the specified weather preset on or off for all players or reset it to automatic",
|
||||
privs = { weather = true },
|
||||
func = function(playername, param)
|
||||
local arguments = {}
|
||||
for w in param:gmatch("%S+") do table.insert(arguments, w) end
|
||||
local weather = arguments[1]
|
||||
if weather == nil or climate_mod.weathers[weather] == nil then
|
||||
minetest.chat_send_player(playername, S("Unknown weather preset"))
|
||||
if climate_mod.weathers[weather] == nil then
|
||||
minetest.chat_send_player(playername, "Unknown weather preset")
|
||||
return
|
||||
end
|
||||
local status
|
||||
if arguments[2] == nil or arguments[2] == "" then
|
||||
arguments[2] = "on"
|
||||
end
|
||||
if arguments[2] == "on" then
|
||||
status = true
|
||||
elseif arguments[2] == "off" then
|
||||
@ -193,19 +107,18 @@ minetest.register_chatcommand("set_weather", {
|
||||
elseif arguments[2] == "auto" then
|
||||
status = nil
|
||||
else
|
||||
minetest.chat_send_player(playername, S("Invalid weather status. Set the preset to either on, off or auto."))
|
||||
minetest.chat_send_player(playername, "Invalid weather status. Set the preset to either on, off or auto.")
|
||||
return
|
||||
end
|
||||
climate_mod.forced_weather[weather] = status
|
||||
minetest.chat_send_player(playername, S("Weather @1 successfully set to @2", weather, arguments[2]))
|
||||
minetest.chat_send_player(playername, "Weather " .. weather .. " successfully set to " .. arguments[2])
|
||||
end
|
||||
})
|
||||
|
||||
-- list all weather presets and whether they have been forced or disabled
|
||||
minetest.register_chatcommand("weather_status", {
|
||||
description = S("Prints which weather presets are enforced or disabled"),
|
||||
description = "Prints which weather presets are enforced or disabled",
|
||||
func = function(playername)
|
||||
minetest.chat_send_player(playername, S("Current activation rules:") .. "\n================")
|
||||
minetest.chat_send_player(playername, "Current activation rules:\n================")
|
||||
for weather, _ in pairs(climate_mod.weathers) do
|
||||
local status = "auto"
|
||||
if climate_mod.forced_weather[weather] == true then
|
||||
@ -217,30 +130,3 @@ minetest.register_chatcommand("weather_status", {
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- show all environment influences and their values for the executing player
|
||||
minetest.register_chatcommand("weather_influences", {
|
||||
description = S("Prints which weather influences cause your current weather"),
|
||||
func = function(playername)
|
||||
minetest.chat_send_player(playername, S("Current influences rules:") .. "\n================")
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
local influences = climate_mod.trigger.get_player_environment(player)
|
||||
for influence, value in pairs(influences) do
|
||||
minetest.chat_send_player(playername, dump2(value, influence))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- used to debug downfall
|
||||
minetest.register_chatcommand("explain_humidity", {
|
||||
description = S("Explains how the humidity value got calculated"),
|
||||
func = function(playername)
|
||||
local base = climate_mod.settings.humidity
|
||||
local biome = minetest.get_humidity((minetest.get_player_by_name(playername)):get_pos())
|
||||
local random = climate_mod.state:get_float("humidity_random");
|
||||
local random_base = climate_mod.state:get_float("humidity_base");
|
||||
minetest.chat_send_player(playername, dump2(base, "base"))
|
||||
minetest.chat_send_player(playername, dump2(biome, "biome"))
|
||||
minetest.chat_send_player(playername, dump2(random, "random"))
|
||||
end
|
||||
})
|
||||
|
@ -1,12 +1,13 @@
|
||||
local state = minetest.get_mod_storage()
|
||||
|
||||
if not state:contains("heat_random") then
|
||||
if not state:contains("noise_timer") then
|
||||
state:from_table({
|
||||
heat_random = 1,
|
||||
humidity_random = 1,
|
||||
humidity_base = 50,
|
||||
wind_x = 0.5,
|
||||
wind_z = 0.5
|
||||
wind_z = 0.5,
|
||||
noise_timer = math.random(0, 300000)
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -1,36 +1,44 @@
|
||||
local environment = {}
|
||||
|
||||
local function get_heat_time()
|
||||
local time = minetest.get_timeofday()
|
||||
return climate_api.utility.normalized_cycle(time) * 0.6 + 0.7
|
||||
end
|
||||
|
||||
local function get_heat_calendar()
|
||||
-- European heat center in August instead of June
|
||||
local day = minetest.get_day_count()
|
||||
local progression = ((day + 61) % 365) / 365
|
||||
return climate_api.utility.normalized_cycle(progression) * 0.6 + 0.7
|
||||
end
|
||||
|
||||
local function get_heat_height(y)
|
||||
return climate_api.utility.rangelim((-y + 10) / 15, -10, 10)
|
||||
end
|
||||
|
||||
function environment.get_heat(pos)
|
||||
if climate_mod.forced_enviroment.heat ~= nil then
|
||||
return climate_mod.forced_enviroment.heat
|
||||
end
|
||||
local base = climate_mod.settings.heat
|
||||
local biome = minetest.get_heat(pos)
|
||||
local height = climate_api.utility.rangelim((-pos.y + 10) / 15, -10, 10)
|
||||
local time = climate_api.utility.normalized_cycle(minetest.get_timeofday()) * 0.6 + 0.7
|
||||
local height = get_heat_height(pos.y)
|
||||
local time = get_heat_time()
|
||||
local date = get_heat_calendar()
|
||||
local random = climate_mod.state:get_float("heat_random");
|
||||
return base + ((biome + height) * time * random)
|
||||
return (base + biome + height) * time * date * random
|
||||
end
|
||||
|
||||
function environment.get_humidity(pos)
|
||||
if climate_mod.forced_enviroment.humidity ~= nil then
|
||||
return climate_mod.forced_enviroment.humidity
|
||||
end
|
||||
local base = climate_mod.settings.humidity
|
||||
local biome = minetest.get_humidity(pos)
|
||||
local random = climate_mod.state:get_float("humidity_random");
|
||||
return base + ((biome * 0.7 + 40 * 0.3) * random)
|
||||
end
|
||||
|
||||
function environment.get_wind(pos)
|
||||
if climate_mod.forced_enviroment.wind ~= nil then
|
||||
return climate_mod.forced_enviroment.wind
|
||||
end
|
||||
local wind_x = climate_mod.state:get_float("wind_x")
|
||||
local wind_z = climate_mod.state:get_float("wind_z")
|
||||
local base_wind = vector.new({ x = wind_x, y = 0, z = wind_z })
|
||||
local height_modifier = climate_api.utility.sigmoid(pos.y, 2, 0.02, 1)
|
||||
return vector.multiply(base_wind, height_modifier)
|
||||
local random_base = climate_mod.state:get_float("humidity_base");
|
||||
--[[for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local pname = player:get_player_name()
|
||||
minetest.chat_send_player(pname, dump2(biome, "biome"))
|
||||
minetest.chat_send_player(pname, dump2(random_base, "random_base"))
|
||||
minetest.chat_send_player(pname, dump2(random, "random"))
|
||||
minetest.chat_send_player(pname, dump2((base + biome * 0.7 + random_base * 0.3) * random, "total"))
|
||||
end]]
|
||||
return (base + biome * 0.7 + random_base * 0.3) * random
|
||||
end
|
||||
|
||||
function environment.get_weather_presets(player)
|
||||
|
@ -1,80 +1,30 @@
|
||||
climate_api.register_influence("heat",
|
||||
climate_api.environment.get_heat
|
||||
)
|
||||
|
||||
climate_api.register_influence("base_heat",
|
||||
minetest.get_heat
|
||||
)
|
||||
|
||||
climate_api.register_influence("humidity",
|
||||
climate_api.environment.get_humidity
|
||||
)
|
||||
|
||||
climate_api.register_influence("biome_humidity",
|
||||
minetest.get_humidity
|
||||
)
|
||||
|
||||
-- see https://en.wikipedia.org/wiki/Dew_point#Simple_approximation
|
||||
climate_api.register_influence("dewpoint", function(pos)
|
||||
local heat = climate_api.environment.get_heat(pos)
|
||||
local humidity = climate_api.environment.get_humidity(pos)
|
||||
return heat - (9/25 * (100 - humidity))
|
||||
climate_api.register_influence("heat", function(player)
|
||||
return climate_mod.get_heat(player:get_pos())
|
||||
end)
|
||||
|
||||
climate_api.register_influence("base_dewpoint", function(pos)
|
||||
local heat = minetest.get_heat(pos)
|
||||
local humidity = minetest.get_humidity(pos)
|
||||
return heat - (9/25 * (100 - humidity))
|
||||
climate_api.register_influence("humidity", function(player)
|
||||
return climate_mod.get_humidity(player:get_pos())
|
||||
end)
|
||||
|
||||
climate_api.register_influence("biome", function(pos)
|
||||
local data = minetest.get_biome_data(pos)
|
||||
local biome = minetest.get_biome_name(data.biome)
|
||||
return biome
|
||||
climate_api.register_influence("windspeed", function(player)
|
||||
local wind_x = climate_mod.state:get_float("wind_x")
|
||||
local wind_z = climate_mod.state:get_float("wind_z")
|
||||
return vector.length({x = wind_x, y = 0, z = wind_z})
|
||||
end)
|
||||
|
||||
climate_api.register_influence("windspeed", function(pos)
|
||||
local wind = climate_api.environment.get_wind(pos)
|
||||
return vector.length(wind)
|
||||
climate_api.register_influence("wind_x", function(player)
|
||||
return climate_mod.state:get_float("wind_x")
|
||||
end)
|
||||
|
||||
climate_api.register_global_influence("wind_yaw", function()
|
||||
local wind = climate_api.environment.get_wind({x = 0, y = 0, z = 0})
|
||||
if vector.length(wind) == 0 then return 0 end
|
||||
return minetest.dir_to_yaw(wind)
|
||||
climate_api.register_influence("wind_z", function(player)
|
||||
return climate_mod.state:get_float("wind_z")
|
||||
end)
|
||||
|
||||
climate_api.register_influence("height", function(pos)
|
||||
return pos.y
|
||||
climate_api.register_influence("height", function(player)
|
||||
local ppos = player:get_pos()
|
||||
return ppos.y
|
||||
end)
|
||||
|
||||
climate_api.register_influence("light", function(pos)
|
||||
pos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||
return minetest.get_node_light(pos) or 0
|
||||
end)
|
||||
|
||||
climate_api.register_influence("daylight", function(pos)
|
||||
pos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||
return minetest.get_natural_light(pos, 0.5) or 0
|
||||
end)
|
||||
|
||||
climate_api.register_influence("indoors", function(pos)
|
||||
pos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||
local daylight = minetest.get_natural_light(pos, 0.5) or 0
|
||||
-- max light is 15 but allow adjacent nodes to still be outdoors
|
||||
-- to reduce effect switching on and off when walking underneath single nodes
|
||||
if daylight < 14 then return true end
|
||||
|
||||
for i = 1, climate_mod.settings.ceiling_checks do
|
||||
local lpos = vector.add(pos, {x = 0, y = i, z = 0})
|
||||
local node = minetest.get_node_or_nil(lpos)
|
||||
if node ~= nil and node.name ~= "air" and node.name ~= "ignore" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
climate_api.register_global_influence("time",
|
||||
minetest.get_timeofday
|
||||
)
|
||||
climate_api.register_influence("light", function(player)
|
||||
return minetest.env:get_node_light(player:get_pos(), 0.5)
|
||||
end)
|
29
lib/main.lua
@ -1,39 +1,26 @@
|
||||
local GSCYCLE = 0.06 * climate_mod.settings.tick_speed -- only process event loop after this amount of time
|
||||
local WORLD_CYCLE = 30.00 * climate_mod.settings.tick_speed -- only update global environment influences after this amount of time
|
||||
local GSCYCLE = 0.03
|
||||
local WORLD_CYCLE = 2
|
||||
|
||||
local gs_timer = 0
|
||||
local world_timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
local player_list = minetest.get_connected_players()
|
||||
if #player_list == 0 then return end
|
||||
|
||||
gs_timer = gs_timer + dtime
|
||||
world_timer = world_timer + dtime
|
||||
|
||||
if gs_timer + dtime < GSCYCLE then return else gs_timer = 0 end
|
||||
|
||||
if world_timer >= WORLD_CYCLE then
|
||||
local noise_timer = climate_mod.state:get_float("noise_timer") + world_timer
|
||||
world_timer = 0
|
||||
climate_mod.world.update_status(minetest.get_gametime())
|
||||
climate_mod.global_environment = climate_mod.trigger.get_global_environment()
|
||||
climate_mod.state:set_float("noise_timer", noise_timer)
|
||||
climate_mod.world.update_status(noise_timer)
|
||||
end
|
||||
|
||||
|
||||
local previous_effects = table.copy(climate_mod.current_effects)
|
||||
-- skip weather changes for offline players
|
||||
for effect, data in pairs(previous_effects) do
|
||||
for playername, _ in pairs(data) do
|
||||
if not minetest.get_player_by_name(playername) then
|
||||
previous_effects[effect][playername] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local current_effects = climate_mod.trigger.get_active_effects(player_list)
|
||||
local current_effects = climate_mod.trigger.get_active_effects()
|
||||
|
||||
for name, effect in pairs(climate_mod.effects) do
|
||||
local cycle = climate_mod.cycles[name].timespan * climate_mod.settings.tick_speed
|
||||
if cycle < climate_mod.cycles[name].timer + dtime then
|
||||
if climate_mod.cycles[name].timespan < climate_mod.cycles[name].timer + dtime then
|
||||
climate_mod.cycles[name].timer = 0
|
||||
climate_mod.current_effects[name] = current_effects[name]
|
||||
climate_mod.trigger.call_handlers(name, current_effects[name], previous_effects[name])
|
||||
@ -41,4 +28,4 @@ minetest.register_globalstep(function(dtime)
|
||||
climate_mod.cycles[name].timer = climate_mod.cycles[name].timer + dtime
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
@ -1,87 +0,0 @@
|
||||
local mod_player_monoids = minetest.get_modpath("player_monoids") ~= nil
|
||||
local mod_playerphysics = minetest.get_modpath("playerphysics") ~= nil
|
||||
local mod_pova = minetest.get_modpath("pova") ~= nil
|
||||
|
||||
local physics = {}
|
||||
|
||||
-- use player monoids if available
|
||||
if mod_player_monoids then
|
||||
function physics.add(id, player, effect, value)
|
||||
player_monoids[effect]:add_change(player, value, id)
|
||||
end
|
||||
|
||||
function physics.remove(id, player, effect)
|
||||
player_monoids[effect]:del_change(player, id)
|
||||
end
|
||||
|
||||
-- fallback to playerphysics if available
|
||||
elseif mod_playerphysics then
|
||||
function physics.add(id, player, effect, value)
|
||||
playerphysics.add_physics_factor(player, effect, id, value)
|
||||
end
|
||||
|
||||
function physics.remove(id, player, effect)
|
||||
playerphysics.remove_physics_factor(player, effect, id)
|
||||
end
|
||||
|
||||
-- fallback to pova if available
|
||||
-- pova uses additive effect modifiers
|
||||
-- this tries to simulate multiplication
|
||||
-- by including the default value in modifier calculation
|
||||
elseif mod_pova then
|
||||
function physics.add(id, player, effect, value)
|
||||
local playername = player:get_player_name()
|
||||
local defaults = pova.get_override(playername, "default")
|
||||
local default
|
||||
if defaults == nil or defaults[effect] == nil then default = 1
|
||||
else default = defaults[effect] end
|
||||
local override = {}
|
||||
override[effect] = (value * default) - default
|
||||
pova.add_override(playername, id, override)
|
||||
pova.do_override(player)
|
||||
end
|
||||
|
||||
function physics.remove(id, player, effect)
|
||||
local playername = player:get_player_name()
|
||||
pova.del_override(playername, id)
|
||||
pova.do_override(player)
|
||||
end
|
||||
|
||||
-- fallback to vanilla override as last resort
|
||||
else
|
||||
local function apply_physics(player)
|
||||
local playername = player:get_player_name()
|
||||
local override = { speed = 1, jump = 1, gravity = 1 }
|
||||
for effect, modifiers in pairs(physics[playername]) do
|
||||
override[effect] = 1
|
||||
for _, modifier in pairs(modifiers) do
|
||||
override[effect] = override[effect] * modifier
|
||||
end
|
||||
end
|
||||
player:set_physics_override(override)
|
||||
end
|
||||
|
||||
function physics.add(id, player, effect, value)
|
||||
local playername = player:get_player_name()
|
||||
if physics[playername] == nil then physics[playername] = {} end
|
||||
if physics[playername][effect] == nil then physics[playername][effect] = {} end
|
||||
physics[playername][effect][id] = value
|
||||
apply_physics(player)
|
||||
end
|
||||
|
||||
function physics.remove(id, player, effect)
|
||||
local playername = player:get_player_name()
|
||||
if physics[playername] == nil then return end
|
||||
if physics[playername][effect] == nil then return end
|
||||
if physics[playername][effect][id] == nil then return end
|
||||
physics[playername][effect][id] = nil
|
||||
apply_physics(player)
|
||||
end
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local playername = player:get_player_name()
|
||||
physics[playername] = nil
|
||||
end)
|
||||
end
|
||||
|
||||
return physics
|
@ -1,120 +0,0 @@
|
||||
local mod_lighting_monoid = minetest.get_modpath("lighting_monoid") ~= nil
|
||||
|
||||
local default_sky = {
|
||||
sky_data = {
|
||||
base_color = nil,
|
||||
type = "regular",
|
||||
textures = nil,
|
||||
clouds = true,
|
||||
sky_color = {
|
||||
day_sky = "#8cbafa",
|
||||
day_horizon = "#9bc1f0",
|
||||
dawn_sky = "#b4bafa",
|
||||
dawn_horizon = "#bac1f0",
|
||||
night_sky = "#006aff",
|
||||
night_horizon = "#4090ff",
|
||||
indoors = "#646464",
|
||||
fog_tint_type = "default"
|
||||
}
|
||||
},
|
||||
cloud_data = {
|
||||
density = 0.4,
|
||||
color = "#fff0f0e5",
|
||||
ambient = "#000000",
|
||||
height = 120,
|
||||
thickness = 16,
|
||||
speed = {x=0, z=-2}
|
||||
},
|
||||
sun_data = {
|
||||
visible = true,
|
||||
texture = "sun.png",
|
||||
tonemap = "sun_tonemap.png",
|
||||
sunrise = "sunrisebg.png",
|
||||
sunrise_visible = true,
|
||||
scale = 1
|
||||
},
|
||||
moon_data = {
|
||||
visible = true,
|
||||
texture = "moon.png",
|
||||
tonemap = "moon_tonemap.png",
|
||||
scale = 1
|
||||
},
|
||||
star_data = {
|
||||
visible = true,
|
||||
count = 1000,
|
||||
star_color = "#ebebff69",
|
||||
scale = 1
|
||||
},
|
||||
light_data = {
|
||||
shadow_intensity = 0.33,
|
||||
saturation = 1
|
||||
}
|
||||
}
|
||||
|
||||
local skybox = {}
|
||||
local layers = {}
|
||||
|
||||
-- from https://stackoverflow.com/a/29133654
|
||||
-- merges two tables together
|
||||
-- if in conflict, b will override values of a
|
||||
local function merge_tables(a, b)
|
||||
if type(a) == "table" and type(b) == "table" then
|
||||
for k,v in pairs(b) do
|
||||
if type(v)=="table" and type(a[k] or false)=="table" then
|
||||
merge_tables(a[k],v)
|
||||
else a[k]=v end
|
||||
end
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
local function set_skybox(playername, sky)
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
if player == nil or not player.get_stars then return end
|
||||
player:set_sky(sky.sky_data)
|
||||
player:set_clouds(sky.cloud_data)
|
||||
player:set_moon(sky.moon_data)
|
||||
player:set_sun(sky.sun_data)
|
||||
player:set_stars(sky.star_data)
|
||||
local lighting = {
|
||||
shadows = { intensity = sky.light_data.shadow_intensity },
|
||||
saturation = sky.light_data.saturation
|
||||
}
|
||||
lighting_monoid:add_change(player, lighting, "climate_api:merged_lighting")
|
||||
end
|
||||
|
||||
function skybox.update(playername)
|
||||
local p_layers = layers[playername]
|
||||
local sky = table.copy(default_sky)
|
||||
if p_layers == nil then p_layers = {} end
|
||||
local numbered_layers = {}
|
||||
for layer, values in pairs(p_layers) do
|
||||
table.insert(numbered_layers, values)
|
||||
end
|
||||
table.sort(numbered_layers, function(left, right)
|
||||
if left.priority == nil then left.priority = 1 end
|
||||
if right.priority == nil then right.priority = 1 end
|
||||
return left.priority < right.priority
|
||||
end)
|
||||
for i = 1, #numbered_layers do
|
||||
sky = merge_tables(sky, numbered_layers[i])
|
||||
end
|
||||
set_skybox(playername, sky)
|
||||
end
|
||||
|
||||
function skybox.add(playername, name, sky)
|
||||
if layers[playername] == nil then layers[playername] = {} end
|
||||
layers[playername][name] = sky
|
||||
end
|
||||
|
||||
function skybox.remove(playername, name)
|
||||
if layers[playername] == nil or layers[playername][name] == nil then return end
|
||||
layers[playername][name] = nil
|
||||
end
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local playername = player:get_player_name()
|
||||
layers[playername] = nil
|
||||
end)
|
||||
|
||||
return skybox
|
@ -1,56 +0,0 @@
|
||||
local soundloop = {}
|
||||
local sounds = {}
|
||||
|
||||
local function parse_sound(sound)
|
||||
if type(sound) == "string" then
|
||||
return { name = sound, gain = 1, pitch = 1 }
|
||||
end
|
||||
if sound.gain == nil then sound.gain = 1 end
|
||||
if sound.pitch == nil then sound.pitch = 1 end
|
||||
return sound
|
||||
end
|
||||
|
||||
soundloop.play = function(player, sound, fade)
|
||||
sound = parse_sound(sound)
|
||||
if fade == nil then fade = 1 end
|
||||
local step
|
||||
local handle
|
||||
local start_gain
|
||||
if sounds[player] == nil then sounds[player] = {} end
|
||||
if sounds[player][sound.name] == nil then
|
||||
step = sound.gain / fade
|
||||
elseif sounds[player][sound.name] ~= sound.gain then
|
||||
minetest.sound_stop(sounds[player][sound.name].handle)
|
||||
start_gain = sounds[player][sound.name].gain
|
||||
local change = sound.gain - start_gain
|
||||
step = change / fade
|
||||
else
|
||||
return
|
||||
end
|
||||
handle = minetest.sound_play(sound.name, {
|
||||
to_player = player,
|
||||
loop = true,
|
||||
gain = 0
|
||||
})
|
||||
sounds[player][sound.name] = {
|
||||
gain = sound.gain,
|
||||
handle = handle
|
||||
}
|
||||
minetest.sound_fade(handle, step, sound.gain)
|
||||
return handle
|
||||
end
|
||||
|
||||
soundloop.stop = function(player, sound, fade)
|
||||
sound = parse_sound(sound)
|
||||
if sounds[player] == nil or sounds[player][sound.name] == nil then
|
||||
return
|
||||
end
|
||||
if fade == nil then fade = 1 end
|
||||
local handle = sounds[player][sound.name].handle
|
||||
local step = -sounds[player][sound.name].gain / fade
|
||||
minetest.sound_fade(handle, step, 0)
|
||||
sounds[player][sound.name].gain = 0
|
||||
minetest.after(fade, minetest.sound_stop, handle)
|
||||
end
|
||||
|
||||
return soundloop
|
105
lib/trigger.lua
@ -1,53 +1,38 @@
|
||||
local trigger = {}
|
||||
|
||||
function trigger.get_global_environment()
|
||||
local env = {}
|
||||
for influence, func in pairs(climate_mod.global_influences) do
|
||||
env[influence] = func()
|
||||
end
|
||||
return env
|
||||
end
|
||||
|
||||
function trigger.get_position_environment(pos)
|
||||
local env = trigger.get_global_environment()
|
||||
for influence, func in pairs(climate_mod.influences) do
|
||||
env[influence] = func(pos)
|
||||
end
|
||||
local wind_x = climate_mod.state:get_float("wind_x")
|
||||
local wind_z = climate_mod.state:get_float("wind_z")
|
||||
|
||||
local env = {}
|
||||
env.pos = pos
|
||||
env.height = pos.y
|
||||
env.wind = vector.new(wind_x, 0, wind_z)
|
||||
env.windspeed = vector.length(env.wind)
|
||||
env.heat = climate_api.environment.get_heat(pos)
|
||||
env.humidity = climate_api.environment.get_humidity(pos)
|
||||
env.time = minetest.get_timeofday()
|
||||
env.date = minetest.get_day_count()
|
||||
env.light = minetest.get_node_light(vector.add(pos, vector.new({x=0,y=1,z=0})), 0.5)
|
||||
return env
|
||||
end
|
||||
|
||||
function trigger.get_player_environment(player)
|
||||
local ppos = player:get_pos()
|
||||
if ppos == nil then return end
|
||||
local env = trigger.get_position_environment(ppos)
|
||||
env.player = player
|
||||
return env
|
||||
end
|
||||
|
||||
function trigger.test_condition(condition, env, goal)
|
||||
local function test_condition(condition, env, goal)
|
||||
local value = env[condition:sub(5)]
|
||||
if condition:sub(1, 4) == "min_" then
|
||||
return value ~= nil and goal <= value
|
||||
return type(value) ~= "nil" and goal <= value
|
||||
elseif condition:sub(1, 4) == "max_" then
|
||||
return value ~= nil and goal > value
|
||||
elseif condition:sub(1, 4) == "has_" then
|
||||
if value == nil then return false end
|
||||
for _, g in ipairs(goal) do
|
||||
if value == g then return true end
|
||||
end
|
||||
return false
|
||||
elseif condition:sub(1, 4) == "not_" then
|
||||
if value == nil then return true end
|
||||
if type(goal) ~= "table" then
|
||||
return value ~= goal
|
||||
end
|
||||
for _, g in ipairs(goal) do
|
||||
if value == g then return false end
|
||||
end
|
||||
return true
|
||||
return type(value) ~= "nil" and goal > value
|
||||
else
|
||||
value = env[condition]
|
||||
return type(value) == "nil" or goal == value
|
||||
Minetest.log("warning", "[Climate API] Invalid effect condition")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
@ -60,7 +45,7 @@ local function is_weather_active(player, weather, env)
|
||||
return config.conditions(env)
|
||||
end
|
||||
for condition, goal in pairs(config.conditions) do
|
||||
if not trigger.test_condition(condition, env, goal) then
|
||||
if not test_condition(condition, env, goal) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -68,7 +53,7 @@ local function is_weather_active(player, weather, env)
|
||||
end
|
||||
|
||||
local function get_weather_effects(player, weather_config, env)
|
||||
local config
|
||||
local config = {}
|
||||
local effects = {}
|
||||
if type(weather_config.effects) == "function" then
|
||||
config = weather_config.effects(env)
|
||||
@ -76,41 +61,39 @@ local function get_weather_effects(player, weather_config, env)
|
||||
config = weather_config.effects
|
||||
end
|
||||
for effect, value in pairs(config) do
|
||||
if climate_mod.effects[effect] ~= nil then
|
||||
if type(climate_mod.effects[effect]) ~= "nil" then
|
||||
effects[effect] = value
|
||||
end
|
||||
end
|
||||
return effects
|
||||
end
|
||||
|
||||
function trigger.get_active_effects(players)
|
||||
function trigger.get_active_effects()
|
||||
local environments = {}
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
environments[player:get_player_name()] = trigger.get_player_environment(player)
|
||||
end
|
||||
|
||||
local effects = {}
|
||||
climate_mod.current_weather = {}
|
||||
|
||||
for _, player in ipairs(players) do
|
||||
local pname = player:get_player_name()
|
||||
local hp = player:get_hp()
|
||||
-- skip weather presets for dead players
|
||||
if hp ~= nil and hp > 0 then
|
||||
local env = trigger.get_player_environment(player)
|
||||
environments[pname] = env
|
||||
for wname, wconfig in pairs(climate_mod.weathers) do
|
||||
if is_weather_active(player, wname, env) then
|
||||
if climate_mod.current_weather[pname] == nil then
|
||||
climate_mod.current_weather[pname] = {}
|
||||
for wname, wconfig in pairs(climate_mod.weathers) do
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local pname = player:get_player_name()
|
||||
local env = environments[pname]
|
||||
if is_weather_active(player, wname, env) then
|
||||
if type(climate_mod.current_weather[pname]) == "nil" then
|
||||
climate_mod.current_weather[pname] = {}
|
||||
end
|
||||
table.insert(climate_mod.current_weather[pname], wname)
|
||||
local player_effects = get_weather_effects(player, wconfig, env)
|
||||
for effect, value in pairs(player_effects) do
|
||||
if type(effects[effect]) == "nil" then
|
||||
effects[effect] = {}
|
||||
end
|
||||
table.insert(climate_mod.current_weather[pname], wname)
|
||||
local player_effects = get_weather_effects(player, wconfig, env)
|
||||
for effect, value in pairs(player_effects) do
|
||||
if type(effects[effect]) == "nil" then
|
||||
effects[effect] = {}
|
||||
end
|
||||
if type(effects[effect][pname]) == "nil" then
|
||||
effects[effect][pname] = {}
|
||||
end
|
||||
effects[effect][pname][wname] = value
|
||||
if type(effects[effect][pname]) == "nil" then
|
||||
effects[effect][pname] = {}
|
||||
end
|
||||
effects[effect][pname][wname] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -134,6 +117,7 @@ function trigger.call_handlers(name, effect, prev_effect)
|
||||
has_ticks = true
|
||||
ticks.current[player] = sources
|
||||
ticks.prev[player] = prev_effect[player]
|
||||
--prev_effect[player] = nil -- remove any found entries
|
||||
else
|
||||
has_starts = true
|
||||
starts[player] = sources
|
||||
@ -161,10 +145,11 @@ function trigger.call_handlers(name, effect, prev_effect)
|
||||
|
||||
-- remaining table lists ending effects
|
||||
if has_stops then
|
||||
minetest.log(dump2(name, "AAAAAAAAAAA"))
|
||||
for _, handler in ipairs(climate_mod.effects[name]["stop"]) do
|
||||
handler(stops)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return trigger
|
||||
return trigger
|
@ -1,13 +1,13 @@
|
||||
local world = {}
|
||||
local BASE_TIME = 0.2 * climate_mod.settings.time_spread
|
||||
|
||||
local WIND_SPREAD = 600
|
||||
local WIND_SCALE = 2
|
||||
local HEAT_SPREAD = 400
|
||||
local HEAT_SPREAD = 200
|
||||
local HEAT_SCALE = 0.3
|
||||
local HUMIDITY_SPREAD = 150
|
||||
local HUMIDITY_SCALE = 1
|
||||
local HUMIDITY_TIMESCALE = 1
|
||||
local HUMIDITY_SPREAD = 60
|
||||
local HUMIDITY_SCALE = 0.5
|
||||
local HUMIDITY_BASE_SPREAD = 600
|
||||
local HUMIDITY_BASE_SCALE = 40
|
||||
|
||||
local nobj_wind_x
|
||||
local nobj_wind_z
|
||||
@ -17,7 +17,7 @@ local nobj_humidity_base
|
||||
|
||||
local pn_wind_speed_x = {
|
||||
offset = 0,
|
||||
scale = WIND_SCALE,
|
||||
scale = WIND_SCALE * climate_mod.settings.time_spread,
|
||||
spread = {x = WIND_SPREAD, y = WIND_SPREAD, z = WIND_SPREAD},
|
||||
seed = 31441,
|
||||
octaves = 2,
|
||||
@ -27,7 +27,7 @@ local pn_wind_speed_x = {
|
||||
|
||||
local pn_wind_speed_z = {
|
||||
offset = 0,
|
||||
scale = WIND_SCALE,
|
||||
scale = WIND_SCALE * climate_mod.settings.time_spread,
|
||||
spread = {x = WIND_SPREAD, y = WIND_SPREAD, z = WIND_SPREAD},
|
||||
seed = 938402,
|
||||
octaves = 2,
|
||||
@ -37,9 +37,9 @@ local pn_wind_speed_z = {
|
||||
|
||||
local pn_heat = {
|
||||
offset = 1,
|
||||
scale = HEAT_SCALE,
|
||||
scale = HEAT_SCALE * climate_mod.settings.time_spread,
|
||||
spread = {x = HEAT_SPREAD, y = HEAT_SPREAD, z = HEAT_SPREAD},
|
||||
seed = 235896,
|
||||
seed = 24,
|
||||
octaves = 2,
|
||||
persist = 0.5,
|
||||
lacunarity = 2
|
||||
@ -47,13 +47,22 @@ local pn_heat = {
|
||||
|
||||
local pn_humidity = {
|
||||
offset = 1,
|
||||
scale = HUMIDITY_SCALE,
|
||||
scale = HUMIDITY_SCALE * climate_mod.settings.time_spread,
|
||||
spread = {x = HUMIDITY_SPREAD, y = HUMIDITY_SPREAD, z = HUMIDITY_SPREAD},
|
||||
seed = 8374061,
|
||||
octaves = 3,
|
||||
persist = 0.5,
|
||||
lacunarity = 2
|
||||
}
|
||||
|
||||
local pn_humidity_base = {
|
||||
offset = 50,
|
||||
scale = HUMIDITY_BASE_SCALE * climate_mod.settings.time_spread,
|
||||
spread = {x = HUMIDITY_BASE_SPREAD, y = HUMIDITY_BASE_SPREAD, z = HUMIDITY_BASE_SPREAD},
|
||||
seed = 3803465,
|
||||
octaves = 2,
|
||||
persist = 0.5,
|
||||
lacunarity = 2,
|
||||
flags = "noeased"
|
||||
lacunarity = 2
|
||||
}
|
||||
|
||||
local function update_wind(timer)
|
||||
@ -73,12 +82,14 @@ end
|
||||
|
||||
local function update_humidity(timer)
|
||||
nobj_humidity = nobj_humidity or minetest.get_perlin(pn_humidity)
|
||||
local n_humidity = nobj_humidity:get_2d({x = timer * HUMIDITY_TIMESCALE, y = 0})
|
||||
local n_humidity = nobj_humidity:get_2d({x = timer, y = 0})
|
||||
climate_mod.state:set_float("humidity_random", n_humidity)
|
||||
nobj_humidity_base = nobj_humidity_base or minetest.get_perlin(pn_humidity_base)
|
||||
local n_humidity_base = nobj_humidity_base:get_2d({x = timer, y = 0})
|
||||
climate_mod.state:set_float("humidity_base", n_humidity_base)
|
||||
end
|
||||
|
||||
function world.update_status(timer)
|
||||
timer = math.floor(timer * BASE_TIME)
|
||||
update_wind(timer)
|
||||
update_heat(timer)
|
||||
update_humidity(timer)
|
||||
|
@ -1,44 +0,0 @@
|
||||
# textdomain: climate_api
|
||||
Make changes to the current weather=Ändere das aktuelle Wetter
|
||||
Display weather information=Betrachte Informationen zum Wetter
|
||||
The following weather presets are active for you:=Die folgenden Wetterklassen sind gerade aktiv
|
||||
Your sky is clear. No weather presets are currently active.=Der Himmel ist klar. Es sind keine Wetterklassen aktiv.
|
||||
As a result, the following environment effects are applied:=Deshalb werden die folgenden Umwelt-Effekte dargestellt:
|
||||
scorching=stechend heiße
|
||||
pleasant=angenehme
|
||||
chilly=kühle
|
||||
It is a @1 @2 right now and humidity is at @3%.=Es sind gerade @1 @2 und die Luftfeuchtigkeit liegt bei @3%.
|
||||
<heat>=
|
||||
Override the weather algorithm's base heat=Überschreibe die Standard-Temperatur
|
||||
Provide a number to modify the base heat=Gebe eine Zahl an, um die Standard-Temperatur anzupassen
|
||||
Base heat changed=Die Standard-Temperatur wurde geändert
|
||||
Override the weather algorithm's heat=Überschreibe die tatsächliche Temperatur
|
||||
Provide a number to modify the heat=Gebe eine Zahl an, um die Temperatur anzupassen
|
||||
Heat value reset=Die Temperatur wurde zurückgesetzt
|
||||
Heat value changed=Die Temperatur wurde geändert
|
||||
<humidity>=
|
||||
Override the weather algorithm's base humidity=Überschreibe die Standard-Luftfeuchtigkeit
|
||||
Provide a number to modify the base humidity=Gebe eine Zahl an, um die Standard-Temperatur anzupassen
|
||||
Base humidity changed=Die Standard-Luftfeuchtigkeit wurde geändert
|
||||
Override the weather algorithm's humidity=Überschreibe die tatsächliche Luftfeuchtigkeit
|
||||
Provide a number to modify the humidity=Gebe eine Zahl an, um die Luftfeuchtigkeit anzupassen
|
||||
Humidity value reset=Die Luftfeuchtigkeit wurde zurückgesetzt
|
||||
Humidity value changed=Die Luftfeuchtigkeit wurde geändert
|
||||
<wind>=
|
||||
Override the weather algorithm's windspeed=Überschreibe die Windgeschwindigkeit
|
||||
Provide a vector of two numbers to modify the wind=Gebe einen Vektor aus zwei Zahlen an, um die Windgeschwindigkeit anzupassen
|
||||
Wind reset=Der Wind wurde zurückgesetzt
|
||||
Invalid wind configuration=Fehlerhafte Windkonfiguration
|
||||
Wind changed=Der Wind wurde geändert
|
||||
Print the active Climate API configuration=Betrachte die Einstellungen für Climate API
|
||||
Current Settings=Aktuelle Einstellungen
|
||||
<weather> <status>=
|
||||
Turn the specified weather preset on or off for all players or reset it to automatic=Schalte für alle Spieler die angegebene Wetterklasse an, aus oder schalte sie auf automatisch
|
||||
Unknown weather preset=Unbekannte Wetterklasse
|
||||
Invalid weather status. Set the preset to either on, off or auto.=Unbekannter Status. Setze die Wetterklasse entweder auf on, off oder auto
|
||||
Weather @1 successfully set to @2=Wetter @1 wurde erfolgreich auf @2 gesetzt
|
||||
Prints which weather presets are enforced or disabled=Betrachte, welche Wetterklassen erzwungen oder deaktiviert sind
|
||||
Current activation rules:=Aktuelle Aktivierungsregeln
|
||||
Prints which weather influences cause your current weather=Betrachte, welche Umwelteinflüsse das aktuelle Wetter hervorrufen
|
||||
Current influences rules:=Aktuelle Einflussfaktoren
|
||||
Explains how the humidity value got calculated=
|
@ -1,44 +0,0 @@
|
||||
# textdomain: climate_api
|
||||
Make changes to the current weather=Ŝanĝi la nunan veteron
|
||||
Display weather information=Montri veterajn informojn
|
||||
The following weather presets are active for you:=La jenaj veteragordoj estas ŝaltitaj por vi:
|
||||
Your sky is clear. No weather presets are currently active.=La suno brilas. Neniu veteragordo estas ŝaltita.
|
||||
As a result, the following environment effects are applied:=Pro tio, la jenaj efikoj estas aktivaj:
|
||||
scorching=Varmas
|
||||
pleasant=Komfortas
|
||||
chilly=Malvarmas
|
||||
It is a @1 @2 right now and humidity is at @3%.=@1 je @2 ĝuste nun, kun aermalsekeco je @3 gradoj.
|
||||
<heat>=<varm>
|
||||
Override the weather algorithm's base heat=Anstataŭigi la bazan varmecon de la vetera algoritmo
|
||||
Provide a number to modify the base heat=Doni nombron anstataŭiguntan la bazan varmecon
|
||||
Base heat changed=Baza varmeco ŝanĝita
|
||||
Override the weather algorithm's heat=Anstataŭigi la varmecon de la vetera algoritmo
|
||||
Provide a number to modify the heat=Doni nombron anstataŭiguntan la varmecon
|
||||
Heat value reset=Varmeco malagordita
|
||||
Heat value changed=Varmeco ŝanĝita
|
||||
<humidity>=<aermalsek>
|
||||
Override the weather algorithm's base humidity=Anstataŭigi la bazan aermalsekecon de la vetera algoritmo
|
||||
Provide a number to modify the base humidity=Doni nombron anstataŭiguntan la bazan aermalsekecon
|
||||
Base humidity changed=Baza aermalsekeco ŝanĝita
|
||||
Override the weather algorithm's humidity=Anstataŭigi la aermalsekecon de la vetera algoritmo
|
||||
Provide a number to modify the humidity=Doni nombron anstataŭiguntan la aermalsekecon
|
||||
Humidity value reset=Aermalsekeco malagordita
|
||||
Humidity value changed=Aermalsekeca ŝanĝita
|
||||
<wind>=<vent>
|
||||
Override the weather algorithm's windspeed=Anstataŭigi la ventforton de la vetera algoritmo
|
||||
Provide a vector of two numbers to modify the wind=Doni nombroparon anstataŭiguntan la ventoforton
|
||||
Wind reset=Ventoforto malagordita
|
||||
Invalid wind configuration=Nevalida ventagordo
|
||||
Wind changed=Vento ŝanĝita
|
||||
Print the active Climate API configuration=Presi la nunan agordon por Climate API
|
||||
Current Settings=Nunaj Agordoj
|
||||
<weather> <status>=<veter> <ŝaltec>
|
||||
Turn the specified weather preset on or off for all players or reset it to automatic=Agordi veteragordon por ĉiuj ludantoj: «on» (ŝalti), «off» (malŝalti), «reset» (malfari)
|
||||
Unknown weather preset=Nekonata veteragordo
|
||||
Invalid weather status. Set the preset to either on, off or auto.=Nevalida veterostato. Agordu la veteragordon al «on» (ŝalti), «off» (malŝalti), aŭ «auto» (memagi).
|
||||
Weather @1 successfully set to @2=Vetero @1 sukcese agordita al @2
|
||||
Prints which weather presets are enforced or disabled=Listigas veterstatojn laŭ ŝaltiteco
|
||||
Current activation rules:=Nunaj aktivigaj reguloj:
|
||||
Prints which weather influences cause your current weather=Presas la influojn kiuj venigis la nunan veteron
|
||||
Current influences rules:=Nunaj influaj reguloj:
|
||||
Explains how the humidity value got calculated=Klarigas kiel la aermalseka valoro kalkuliĝis
|
@ -1,44 +0,0 @@
|
||||
# textdomain: climate_api
|
||||
Make changes to the current weather=
|
||||
Display weather information=
|
||||
The following weather presets are active for you:=
|
||||
Your sky is clear. No weather presets are currently active.=
|
||||
As a result, the following environment effects are applied:=
|
||||
scorching=
|
||||
pleasant=
|
||||
chilly=
|
||||
It is a @1 @2 right now and humidity is at @3%.=
|
||||
<heat>=
|
||||
Override the weather algorithm's base heat=
|
||||
Provide a number to modify the base heat=
|
||||
Base heat changed=
|
||||
Override the weather algorithm's heat=
|
||||
Provide a number to modify the heat=
|
||||
Heat value reset=
|
||||
Heat value changed=
|
||||
<humidity>=
|
||||
Override the weather algorithm's base humidity=
|
||||
Provide a number to modify the base humidity=
|
||||
Base humidity changed=
|
||||
Override the weather algorithm's humidity=
|
||||
Provide a number to modify the humidity=
|
||||
Humidity value reset=
|
||||
Humidity value changed=
|
||||
<wind>=
|
||||
Override the weather algorithm's windspeed=
|
||||
Provide a vector of two numbers to modify the wind=
|
||||
Wind reset=
|
||||
Invalid wind configuration=
|
||||
Wind changed=
|
||||
Print the active Climate API configuration=
|
||||
Current Settings=
|
||||
<weather> <status>=
|
||||
Turn the specified weather preset on or off for all players or reset it to automatic=
|
||||
Unknown weather preset=
|
||||
Invalid weather status. Set the preset to either on, off or auto.=
|
||||
Weather @1 successfully set to @2=
|
||||
Prints which weather presets are enforced or disabled=
|
||||
Current activation rules:=
|
||||
Prints which weather influences cause your current weather=
|
||||
Current influences rules:=
|
||||
Explains how the humidity value got calculated=
|
4
mod.conf
@ -1,8 +1,8 @@
|
||||
name = climate_api
|
||||
title = Climate API
|
||||
author = TestificateMods
|
||||
depends = lighting_monoid
|
||||
optional_depends = player_monoids, playerphysics, pova, weather
|
||||
release = 1
|
||||
optional_depends = skylayer, player_monoids, playerphysics
|
||||
description = """
|
||||
A powerful engine for weather presets and visual effects.
|
||||
Use the regional climate to set up different effects for different regions.
|
||||
|
BIN
screenshot.2.png
Normal file
After Width: | Height: | Size: 2.0 MiB |
BIN
screenshot.3.png
Normal file
After Width: | Height: | Size: 2.0 MiB |
BIN
screenshot.png
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.3 MiB |
@ -1,77 +1,11 @@
|
||||
[Performance]
|
||||
|
||||
# This value regulates how often weather presets are recalculated.
|
||||
# Higher values will result in smoother transitions between effects as well as faster response times to traveling players.
|
||||
# Lower values will significantly increase overall performance at the cost of rougher looking effects.
|
||||
climate_api_tick_speed (Update speed of weather effects) float 1 0.1 10
|
||||
|
||||
# This value regulates how many particles will be spawned.
|
||||
# A value of 1 will use the recommended amount of particles.
|
||||
# Lower values can possible increase performance.
|
||||
climate_api_particle_count (Multiplicator for used particles) float 1 0.1 2
|
||||
|
||||
# If set to true, weather packs are allowed to register node update handlers.
|
||||
# These can be used to dynamically place snow layers, melt ice, or hydrate soil.
|
||||
climate_api_block_updates (Dynamically modify nodes) bool true
|
||||
|
||||
# If set to true, Climate API will factor in wind speed and obstacles to determine damage sources.
|
||||
# If set to false, a simple check will be used whether the player is outside.
|
||||
# Only applied if climate_api_damage is also set to true.
|
||||
climate_api_raycast (Include wind speed in damage checks) bool true
|
||||
|
||||
# Increase this number if you notice snow or rain inside large glass domes.
|
||||
# Decrease this number if you notice lag spikes or overall reduced game performance.
|
||||
ceiling_checks (Check x nodes on outdoor test) int 10 0 50
|
||||
|
||||
|
||||
[Weather Effects]
|
||||
|
||||
# If set to true, dangerous weather presets will damage affected players over time.
|
||||
climate_api_damage (Cause player damage) bool true
|
||||
|
||||
# If set to true, weather effects (like rain) are allowed to render particles.
|
||||
# Deactivating this feature will prevent some presets from being visible.
|
||||
# For performance considerations it is recommended to decrease the amount of particles instead.
|
||||
climate_api_particles (Show particle effects) bool true
|
||||
|
||||
# If set to true, weather effects are allowed to modify a player's sky.
|
||||
# This includes skybox, sun, moon, and clouds (also used for fog effects).
|
||||
# Running this mod on Minetest 5.1.2 or earlier versions will automatically disable this feature.
|
||||
climate_api_skybox (Override the skybox) bool true
|
||||
|
||||
# If set to true, weather effects are allowed to render an image on top of the gameplay.
|
||||
# This is usually an optional effect used to increase immersion (like a frozen-over camera in a snow storm).
|
||||
climate_api_hud_overlay (Display HUD overlays) bool true
|
||||
|
||||
|
||||
[Environment]
|
||||
|
||||
# This value will be added to all biome's base temperatures before applying random modifiers.
|
||||
# Every unit here will increase the global base heat by one degree Fahrenheit.
|
||||
# Negative values will cool down global base heat respectively.
|
||||
climate_api_skybox (Allow weather effects to modify the skybox) bool true
|
||||
climate_api_sound (Allow weather presets to play ambient sounds) bool true
|
||||
climate_api_hud_overlay (Allow weather presets to display a HUD overlay) bool true
|
||||
climate_api_wind (Allow wind to angle rainfall) bool true
|
||||
climate_api_seasons (Change global temperature based on an annual cycle) bool true
|
||||
climate_api_heat_base (Global base temperature) float 0
|
||||
|
||||
# This value will be added to all biome's base humidity before applying random modifiers.
|
||||
# Every unit here will increase the global base humidity by one percent.
|
||||
# Negative values will dry up global base humidity respectively.
|
||||
climate_api_humidity_base (Global base humidity) float 0
|
||||
|
||||
# This value regulates how quickly environment factors like heat, humidity and wind are changing.
|
||||
# A value of 2 will double the speed at which weather presets change.
|
||||
# A value of 0.5 will half the speed respectively.
|
||||
climate_api_time_spread (Time rate of weather changes) float 1 0.1 10
|
||||
|
||||
|
||||
[Preferences]
|
||||
|
||||
# If set to true, temperature information in /weather command will be displayed in Fahrenheit.
|
||||
climate_api_fahrenheit (Show degrees in Fahrenheit instead of Celsius) bool false
|
||||
|
||||
# If set to true, weather effects are allowed to play sound loops.
|
||||
# You can also adjust sound levels instead of deactivating this feature completely.
|
||||
# Setting this value to false will be slightly more performant than setting the volume to zero.
|
||||
climate_api_sound (Play ambient sound loops) bool true
|
||||
|
||||
# This value regulates overall sound volume.
|
||||
# A value of 2 will double the volume whereas a value of 0.5 will reduce the volume by half.
|
||||
climate_api_volume (Volume of sound effects) float 1 0 10
|
||||
climate_api_time_spread (Regulates how quickly the weather changes) float 1 0.1 10
|
||||
climate_api_particle_count (Multiplicator for used particles) float 1 0.1 2
|
||||
climate_api_fahrenheit (Show degrees in Fahrenheit instead of Celsius) bool false
|
BIN
textures/climate_hud.png
Normal file
After Width: | Height: | Size: 2.2 MiB |
Before Width: | Height: | Size: 5.8 KiB |
BIN
textures/sun.png
Before Width: | Height: | Size: 1.2 KiB |