add weather affect area support

master
Arturas Norkus 2017-04-06 16:17:45 +03:00
parent a234e89801
commit a82cda5849
5 changed files with 307 additions and 163 deletions

View File

@ -1,34 +1,44 @@
# Happy Weather API
a minetest mod-api for creating own weather
Happy Weather API
===================
Minetest mod-api to help organize weathers.
```
-- Main weather object template
local weather_obj = {
-- Weather code used to identify weather
code = "weather code",
Overview
------------
All weathers parameters and functions should be defined under single object. For reference lets call it `weather_obj`.
-- Managed by API. Returns true if weather active, false otherwise
active = "false",
Weather object fields
-------
Weather code is mandatory property.
-- Will be called to check if wheater should be activated
about_to_start = "function(dtime)",
property name | description
------------------ | ---------------------------------
code | used to identify weather
active | weather state flag managed by API
affected_players | table of players affected by weather managed by API
-- Will be called to check if wheater should be deactivated
about_to_end = "function(dtime)",
Weather object callback methods
-------
Callback methods which will be invoked by API. It's recommended to implement all bellow callback methods.
-- Will be called on weather start
setup = "function(player)",
function name | arguments | return type | description
------------- | --------------- | ----------- | -----------
is_starting | dtime, position | boolean | should return true if weather is ready to start, false otherwise.
is_ending | dtime | boolean | should return true if weather is ready to end, false otherwise.
add_player | player | void | should apply effect for player (change sky, initialize sounds and etc).
remove_player | player | void | should remove weather from player
in_area | position | boolean | should return true if position is in weather area, false otherwise.
render | dtime, player | void | should apply visual, sound or anything else needed to represent weather.
-- Will be called on game step
update = "function(dtime, player)",
These methods are not part of weather API lifecycle and expected to be invoked manually (e.g. weather commands, other weather related mods).
function name | arguments | return type | description
------------- | --------- | ----------- | -----------
start | position | void | should start weather at given position
stop | < none > | void | should end weather
-- Will be called on weather end (after condition_check returns false)
clear_up = "function(player)",
Weather API methods
-------
function name | arguments | return type | description
----------------- | ------------- | ----------- | -----------
register_weather | weather_obj | void | will register weather
is_weather_active | weather_code | boolean | will return true if weather is active, false otherwise
-- Will be called with intention to start weather
manual_trigger_start = "function()",
-- Will be called with intention to end weather
manual_trigger_end = "function()"
}
```

261
api.lua Normal file
View File

@ -0,0 +1,261 @@
---------------------------
-- Happy Weather API
-- License: MIT
-- Credits: xeranas
---------------------------
-- Main object which will be used in Weather API lifecycle
happy_weather = {}
-- Local variables which helps organize active and deactive weahers
local registered_weathers = {}
local active_weathers = {}
---------------------------
-- Weather API functions --
---------------------------
-- Adds weather to register_weathers table
happy_weather.register_weather = function(weather_obj)
table.insert(registered_weathers, weather_obj)
end
-- Returns true if weather is active right now, false otherwise
happy_weather.is_weather_active = function(weather_code)
if #active_weathers == 0 then
return false
end
for k, weather_ in ipairs(active_weathers) do
if weather_.code == weather_code then
return true
end
end
return false
end
-- Requests weaher to start
happy_weather.request_to_start = function(weather_code, position)
if #registered_weathers == 0 then
return
end
for k, weather_ in ipairs(registered_weathers) do
if weather_.code == weather_code and weather_.start ~= nil then
weather_.start(position)
return
end
end
end
-- Requests weaher to end
happy_weather.request_to_end = function(weather_code)
if #active_weathers == 0 then
return
end
for k, weather_ in ipairs(active_weathers) do
if weather_.code == weather_code and weather_.stop ~= nil then
weather_.stop()
return
end
end
end
------------------------------------
-- Local helper / utility methods --
------------------------------------
-- Adds weather to active_weathers table
local add_active_weather = function(weather_obj)
table.insert(active_weathers, weather_obj)
end
-- Remove weather from active_weathers table
local remove_active_weather = function(weather_code)
if #active_weathers == 0 then
return
end
for k, weather_ in ipairs(active_weathers) do
if weather_.code == weather_code then
table.remove(active_weathers, k)
return
end
end
end
-- adds player to affected_players table
local add_player = function(affected_players, player)
table.insert(affected_players, player)
end
-- remove player from affected_players table
local remove_player = function(affected_players, player_name)
if #affected_players == 0 then
return
end
for k, player_ in ipairs(affected_players) do
if player_:get_player_name() == player_name then
table.remove(affected_players, k)
return
end
end
end
local is_player_affected = function(affected_players, player_name)
if #affected_players == 0 then
return false
end
for k, player_ in ipairs(affected_players) do
if player_:get_player_name() == player_name then
return true
end
end
end
-----------------------------------------------------------------------------
-- Weather object callback wrappers to avoid issues from undefined methods --
-----------------------------------------------------------------------------
-- Weather is_starting method nil-safe wrapper
local weather_is_starting = function(weather_obj, dtime, position)
if weather_obj.is_starting == nil then
return false
end
return weather_obj.is_starting(dtime, position)
end
-- Weather is_starting method nil-safe wrapper
local weather_is_ending = function(weather_obj, dtime)
if weather_obj.is_ending == nil then
return false
end
return weather_obj.is_ending(dtime)
end
-- Weather add_player method nil-safe wrapper
local weather_add_player = function(weather_obj, player)
if weather_obj.add_player == nil then
return
end
weather_obj.add_player(player)
end
-- Weather remove_player method nil-safe wrapper
local weather_remove_player = function(weather_obj, player)
if weather_obj.remove_player == nil then
return
end
weather_obj.remove_player(player)
end
-- Weather remove_player method nil-safe wrapper
local weather_in_area = function(weather_obj, position)
if weather_obj.in_area == nil then
return true
end
return weather_obj.in_area(position)
end
-- Weather render method nil-safe wrapper
local weather_render = function(weather_obj, dtime, player)
if weather_obj.render == nil then
return
end
weather_obj.render(dtime, player)
end
-- Weather start method nil-safe wrapper
local weather_start = function(weather_obj, player)
if weather_obj.start == nil then
return
end
weather_obj.start(player)
end
-- Weather stop method nil-safe wrapper
local weather_stop = function(weather_obj, player)
if weather_obj.stop == nil then
return
end
weather_obj.stop(player)
end
-- Perform clean-up callbacks calls sets flags upon weaher end
local prepare_ending = function(weather_obj, player)
weather_obj.active = false
remove_active_weather(weather_obj.code)
weather_remove_player(weather_obj, player)
remove_player(weather_obj.affected_players, player:get_player_name())
end
-- Perform weather setup for certain player
local prepare_starting = function(weather_obj, player)
weather_obj.active = true
weather_obj.affected_players = {}
add_active_weather(weather_obj)
end
-- While still active weather can or can not affect players based on area they are
local render_if_in_area = function(weather_obj, dtime, player)
if is_player_affected(weather_obj.affected_players, player:get_player_name()) then
if weather_in_area(weather_obj, player:getpos()) then
weather_render(weather_obj, dtime, player)
else
weather_remove_player(weather_obj, player)
remove_player(weather_obj.affected_players, player:get_player_name())
end
else
if weather_in_area(weather_obj, player:getpos()) then
add_player(weather_obj.affected_players, player)
weather_add_player(weather_obj, player)
end
end
end
--------------------------
-- Global step function --
--------------------------
minetest.register_globalstep(function(dtime)
if #registered_weathers == 0 then
-- no registered weathers, do nothing.
return
end
if #minetest.get_connected_players() == 0 then
-- no actual players, do nothing.
return
end
-- Loop through registered weathers
for i, weather_ in ipairs(registered_weathers) do
-- Loop through connected players
for ii, player in ipairs(minetest.get_connected_players()) do
-- Weaher is active checking if it about to end
if weather_.active then
if weather_is_ending(weather_, dtime) then
prepare_ending(weather_, player)
-- Weather still active updating it
else
render_if_in_area(weather_, dtime, player)
end
-- Weaher is not active checking if it about to start
else
if weather_.is_starting(dtime, player:getpos()) then
prepare_starting(weather_, player)
end
end
end
end
end)

View File

@ -1,10 +1,10 @@
--
------------------------------------
-- Happy Weather API Chat Commands
-- License: MIT
-- Credits:
-- * xeranas
-- Credits: xeranas
------------------------------------
minetest.register_privilege("weather_manager", {
description = "Gives ability to control weather",
@ -18,7 +18,7 @@ minetest.register_chatcommand("start_weather", {
func = function(name, param)
if param ~= nil then
happy_weather.request_to_start(param)
minetest.log("action", name .. " requested to start weather '" .. param .. "' from chat command.")
minetest.log("action", name .. " requested weather '" .. param .. "' from chat command")
end
end
})
@ -30,7 +30,7 @@ minetest.register_chatcommand("stop_weather", {
func = function(name, param)
if param ~= nil then
happy_weather.request_to_end(param)
minetest.log("action", name .. " requested to stop weather '" .. param .. "' from chat command.")
minetest.log("action", name .. " requested weather '" .. param .. "' ending from chat command")
end
end
})

View File

@ -1,127 +0,0 @@
--
-- Happy Weather API
-- License: MIT
-- Credits:
-- * xeranas
happy_weather = {}
local registered_weathers = {}
local active_weathers = {}
happy_weather.register_weather = function(weather_obj)
table.insert(registered_weathers, weather_obj)
end
happy_weather.is_weather_active = function(weather_cd)
for k, weather_ in ipairs(active_weathers) do
if weather_.code == weather_cd then
return true
end
end
return false
end
local add_active_weather = function(weather_obj)
table.insert(active_weathers, weather_obj)
end
local remove_active_weather = function(weather_cd)
for k, weather_ in ipairs(active_weathers) do
if weather_.code == weather_cd then
table.remove(active_weathers, k)
return
end
end
end
-- Ask to start weather. Expected to be trigger from weather implelentation side.
happy_weather.request_to_start = function(weather_cd)
for k, weather_ in ipairs(registered_weathers) do
if weather_.code == weather_cd and weather_.manual_trigger_start ~= nil then
weather_.manual_trigger_start()
return
end
end
end
-- Ask to end weather. Expected to be trigger from weather implelentation side.
happy_weather.request_to_end = function(weather_cd)
for k, weather_ in ipairs(registered_weathers) do
if weather_.code == weather_cd and weather_.manual_trigger_end ~= nil then
weather_.manual_trigger_end()
return
end
end
end
-- Weather setup method wrapper (nil-safe)
local weather_setup = function(weather_obj, player)
if weather_obj.setup == nil then
return
end
weather_obj.setup(player)
end
-- Weather clear up method wrapper (nil-safe)
local weather_clear_up = function(weather_obj, player)
if weather_obj.clear_up == nil then
return
end
weather_obj.clear_up(player)
end
-- Weather update method wrapper (nil-safe)
local weather_update = function(weather_obj, dtime, player)
if weather_obj.update == nil then
return
end
weather_obj.update(dtime, player)
end
-- Global step function
-- loop through registered weathers and check which weather is about to start or end
minetest.register_globalstep(function(dtime)
if #registered_weathers == 0 then
-- no registered weathers, do nothing.
return
end
if #minetest.get_connected_players() == 0 then
-- no actual players, do nothing.
return
end
-- Loop through registered weathers
for i, weather_ in ipairs(registered_weathers) do
-- Loop through connected players (weathers are attached to players)
for ii, player in ipairs(minetest.get_connected_players()) do
-- Weaher is active checking if it about to end
if (weather_.active) then
if (weather_.about_to_end(dtime)) then
weather_clear_up(weather_, player)
weather_.active = false
remove_active_weather(weather_.code)
-- Weather still active updating it
else
weather_update(weather_, dtime, player)
end
-- Weaher is not active checking if it about to start
else
if (weather_.about_to_start(dtime)) then
weather_setup(weather_, player)
weather_.active = true
add_active_weather(weather_)
end
end
end
end
end)

View File

@ -1,11 +1,11 @@
--
-- Happy Weather API Initialization
--------------------------------------
-- Happy Weather API: initialization
-- License: MIT
-- Credits:
-- * xeranas
-- Credits: xeranas
--------------------------------------
local modpath = minetest.get_modpath("happy_weather_api");
dofile(modpath.."/happy_weather_api.lua")
dofile(modpath.."/api.lua")
dofile(modpath.."/commands.lua")