commit 174de160613bacbc6c493ef7e36a60b75df8425d Author: Lars Mueller Date: Fri May 3 19:09:08 2019 +0200 Updates via shellscript diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..43ba14c --- /dev/null +++ b/Readme.md @@ -0,0 +1,7 @@ +# Kill History(`deathlist`) +A mod displaying a colored list of recent kills/deaths and the causes(killer, used item). + +## About +Depends on [`modlib`](https://github.com/appgurueu/modlib) and - depending on your configuration - also other mods. If you use the default configuration, it depends on `default` and `fire`. +**If there are other mods registering `on_punchplayer` or `on_hp_change` handlers, add them to the dependency list to make sure they execute before `deathlist`.** +Licensed under MIT. \ No newline at end of file diff --git a/config_help.md b/config_help.md new file mode 100644 index 0000000..0669a9e --- /dev/null +++ b/config_help.md @@ -0,0 +1,75 @@ +# Kill History - Configuration + + +JSON Configuration : `/config/deathlist.json` + +Text Logs : `/logs/deathlist/.txt` + +Explaining document(this, Markdown) : `/deathlist/config_help.md` + +Readme : `/deathlist/Readme.md` + +Located under `/deathlist/default_config.json` +```json +{ + "max_messages":5, + "mode":"list", + "autoremove_interval":30, + "hud_pos":{"x":0.75,"y":1}, + "hud_base_offset": {"x":0,"y":-122}, + "enable_environmental": true, + "enable_unknown": false, + "environmental_reasons": { + "falling":{ + "name":"Falling", + "color":{"r": 255, "g": 255, "b": 255}, + "method":"deathlist_tombstone.png" + }, + "unknown":{ + "name":"Something", + "color":{"r": 255, "g": 255, "b": 255}, + "method":"deathlist_tombstone.png" + }, + "drowning":{ + "color":{"r": 105, "g": 201, "b": 231}, + "method":"bubble.png", + "nodes": { + "default:water_flowing" : { "name" : "Water" } + } + }, + "node_damage":{ + "color":{"r": 255, "g": 255, "b": 255}, + "method":"generate", + "nodes": { + "default:lava_flowing" : { "name" : "Lava", "color" : {"r": 244, "g": 114, "b": 9}, "method" : "fire_basic_flame.png" }, + "default:lava_source" : { "color" : {"r": 244, "g": 114, "b": 9}, "method" : "fire_basic_flame.png" } + } + } + } +} +``` + +#### `max_messages` +Maximum amount of messages to be displayed. Number > 0. +#### `mode` +String, either `"stack"` or `"list"`. Specifies message orientation. A stack means that the most recent message is always on the top, while list means exactly the opposite. +#### `autoremove_interval` +Can be set to `false` or a positive number in seconds. Specifies how often messages are removed despite having enough space left for the purpose of not cluttering the HUD. +#### `hud_pos` +X- and Y-Coordinate, numbers, position on HUD. +#### `hud_base_offset` +X- and Y-Offset, numbers in pixels(?). +#### `enable_environmental` +Boolean, whether to enable environmental reasons inside kill history. +#### `enable_unknown` +Boolean, whether to enable unknown (none of `falling`, `drowning` or `node_damage`) reasons inside kill history. +#### `environmental_reasons` +Table/Dictionairy of possible deaths due to environment. +##### Basic structure of a kill message +* `name` of killing thing, colorized using a `color`(table/dictionairy, r/g/b) +* `method` used by the killing thing, a texture. If `"generate"` is specified and a node is responsible, the node texture will be used. +##### `falling` / `unknown` +Player died through falling or an unknown reason; no node responsible. One definition of `name`, `color` and `method`. +##### `drowning` / `node_damage` +Player died by drowning node or by taking node damage. A node *must* be responsible. +Using the `nodes` table/dictionairy, you can specify overrides for each specific node concerning `name`, `color`, and `method`. If not specified, default values are taken. \ No newline at end of file diff --git a/default_config.json b/default_config.json new file mode 100644 index 0000000..5d5812c --- /dev/null +++ b/default_config.json @@ -0,0 +1,36 @@ +{ + "max_messages":5, + "mode":"list", + "autoremove_interval":30, + "hud_pos":{"x":0.75,"y":1}, + "hud_base_offset": {"x":0,"y":-122}, + "enable_environmental": true, + "enable_unknown": false, + "environmental_reasons": { + "falling":{ + "name":"Falling", + "color":{"r": 255, "g": 255, "b": 255}, + "method":"deathlist_tombstone.png" + }, + "unknown":{ + "name":"Something", + "color":{"r": 255, "g": 255, "b": 255}, + "method":"deathlist_tombstone.png" + }, + "drowning":{ + "color":{"r": 105, "g": 201, "b": 231}, + "method":"bubble.png", + "nodes": { + "default:water_flowing" : { "name" : "Water" } + } + }, + "node_damage":{ + "color":{"r": 255, "g": 255, "b": 255}, + "method":"generate", + "nodes": { + "default:lava_flowing" : { "name" : "Lava", "color" : {"r": 244, "g": 114, "b": 9}, "method" : "fire_basic_flame.png" }, + "default:lava_source" : { "color" : {"r": 244, "g": 114, "b": 9}, "method" : "fire_basic_flame.png" } + } + } + } +} \ No newline at end of file diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..83293c0 --- /dev/null +++ b/init.lua @@ -0,0 +1 @@ +include_mod("deathlist") \ No newline at end of file diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..8a79a6e --- /dev/null +++ b/main.lua @@ -0,0 +1,252 @@ +--- +--- Generated by EmmyLua(https://github.com/EmmyLua) +--- Created by lars. +--- DateTime: 11.03.19 18:28 +--- + +log.create_channel("deathlist") -- Create log channel + +local coordinate={type="table", + children={ + x={type="number"}, + y={type="number"} + }} +local color={type="table", + children={ + r={type="number", interval={0,255}}, + g={type="number", interval={0,255}}, + b={type="number", interval={0,255}} + }} +local node_caused={children={ + color=color, + method={type="string"}, + nodes={type="table", keys={type="string"}, values={type="table", keys={possible_values={name=true, color=true, method=true}}}}, +}} +local config=conf.import("deathlist",{ + type="table", + children={ + max_messages={type="number", interval={1}}, + mode={type="string", possible_values={list=true,stack=true}}, + autoremove_interval={func=function(interval) + if type(interval) ~= "number" and interval ~= false then + return "Wrong type : Expected number or false, found "..type(interval) + end + if interval <= 0 then + return "Too small : Interval has to be > 0" + end + end}, + hud_pos=coordinate, + hud_base_offset=coordinate, + enable_environmental={type="boolean"}, + enable_unknown={type="boolean"}, + + environmental_reasons={ + children={ + falling={ + children={ + name={type="string"}, + color=color, + method={type="string"} + } + }, + unknown={ + children={ + name={type="string"}, + color=color, + method={type="string"} + } + }, + drowning=node_caused, + node_damage=node_caused + } + } + } +}) + +table_ext.add_all(getfenv(1), config) + +table_ext.map(environmental_reasons, function(v) + v.color=mt_ext.get_color_int(v.color) + return v +end) + +table_ext.map(environmental_reasons.drowning.nodes, function(v) + if v.color then v.color=mt_ext.get_color_int(v.color) end + return v +end) + +table_ext.map(environmental_reasons.node_damage.nodes, function(v) + if v.color then v.color=mt_ext.get_color_int(v.color) end + return v +end) + +hud_lists={killers={}, items={}, victims={}} -- in order to reduce overhead + +minetest.register_on_joinplayer(function(player) + hud_lists.killers[player:get_player_name()]={} + hud_lists.items[player:get_player_name()]={} + hud_lists.victims[player:get_player_name()]={} +end) + +minetest.register_on_leaveplayer(function(player) + hud_lists.killers[player:get_player_name()]=nil + hud_lists.items[player:get_player_name()]=nil + hud_lists.victims[player:get_player_name()]=nil +end) + +function remove_last_kill_msg_from_hud(listname, x_offset) + local list=hud_lists[listname] + for _,player in pairs(minetest.get_connected_players()) do + local name=player:get_player_name() + local hud_ids=list[name] + local i=#list[name] + if mode=="list" then + player:hud_remove(hud_ids[i]) + hud_ids[i]=nil + else + player:hud_remove(hud_ids[1]) -- Will be replaced + for j=2,i do + local new={x=hud_base_offset.x+x_offset,y=hud_base_offset.y-((j-2)*20)} + player:hud_change(hud_ids[j],"offset",new) + hud_ids[j-1]=hud_ids[j] --Perform index shift + end + hud_ids[i]=nil + end + list[name]=hud_ids + end +end + +function remove_last_kill_message() + if not threading_ext.request("deathlist.hud_lists",remove_last_kill_message) then return end + remove_last_kill_msg_from_hud("killers",-20) + remove_last_kill_msg_from_hud("victims",20) + remove_last_kill_msg_from_hud("items",0) + threading_ext.free("deathlist.hud_lists") +end + +local last_message=0 + +if autoremove_interval then + minetest.register_globalstep(function(dtime) + last_message=last_message+dtime + if last_message > autoremove_interval then + remove_last_kill_message() + last_message=0 + end + end) +end + +function add_kill_msg_to_hud(msg, listname, hud_def, x_offset) -- MAY NOT BE CALLED SIMULTANEOUSLY + local _, value=next(hud_lists[listname]) + if table_ext.is_empty(value) then + last_message=0 + end + local list=hud_lists[listname] + hud_def.text=msg + hud_def.offset={x=x_offset+hud_base_offset.x} + for _,player in pairs(minetest.get_connected_players()) do + local name=player:get_player_name() + local hud_ids=list[name] --Hud elem IDs + hud_ids=list[name] or {} + local i=#hud_ids + if (i == max_messages) then --Have to remove + if mode=="list" then + player:hud_remove(hud_ids[i]) + else + player:hud_remove(hud_ids[1]) -- Will be replaced + for j=2,i do + local new={x=hud_def.offset.x,y=hud_base_offset.y-((j-2)*20)} + player:hud_change(hud_ids[j],"offset",new) + hud_ids[j-1]=hud_ids[j] --Perform index shift + end + end + i=i-1 + end + if mode=="list" then + for j=i,1,-1 do + local new={x=hud_def.offset.x,y=hud_base_offset.y-(j*20)} + player:hud_change(hud_ids[j],"offset",new) + hud_ids[j+1]=hud_ids[j] --Perform index shift + end + hud_def.offset.y=hud_base_offset.y + hud_ids[1]=player:hud_add(hud_def) + else + hud_def.offset.y=hud_base_offset.y-(i*20) + hud_ids[i+1]=player:hud_add(hud_def) + end + list[name]=hud_ids --Update IDs + end +end + +function add_kill_message(killer, tool_image, victim) + if not threading_ext.request("deathlist.hud_lists",add_kill_message, killer, tool_image, victim) then return end + add_kill_msg_to_hud(killer.name,"killers",{hud_elem_type="text",position=hud_pos,scale={x=100,y=100}, number=killer.color or 0xFFFFFF, alignment = {x=-1,y=0}},-20) + add_kill_msg_to_hud(victim.name,"victims",{hud_elem_type="text",position=hud_pos,number=victim.color or 0xFFFFFF,alignment = {x=1,y=0}},20) + add_kill_msg_to_hud((tool_image or "deathlist_tombstone.png").."^[resize:16x16", "items",{hud_elem_type="image",position=hud_pos,scale={x=1,y=1}, alignment = {x=0,y=0}},0) + threading_ext.free("deathlist.hud_lists") +end + +function add_environmental_kill_message(cause, victim) --Falling & Unknown + add_kill_message({name=environmental_reasons[cause].name, color=environmental_reasons[cause].color}, + environmental_reasons[cause].method,victim) +end + +function add_node_kill_message(killing_node, cause, victim) --Drowning & Node Damage + local override=environmental_reasons[cause].nodes[killing_node.name] or {} + local method=override.method or environmental_reasons[cause].method + if method=="generate" then + method=mt_ext.get_node_inventory_image(killing_node.name) + end + add_kill_message({name=override.name or killing_node.description, + color=override.color or environmental_reasons[cause].color}, + method, + victim) +end + +if enable_environmental then + minetest.register_on_player_hpchange( + function(player, hp_change, reason) + if player:get_hp() > 0 and player:get_hp()+hp_change <= 0 then + local victim={name=player:get_player_name(), color=player_ext.get_color_int(player)} + if reason.type=="fall" then + add_environmental_kill_message("falling", victim) + log.write("deathlist","Player "..victim.name.." died due to falling") + elseif reason.type=="drown" then + local eye_pos=vector.add(player:get_pos(), {x=0, z=0, y=player:get_properties().eye_height}) + local drowning_node=minetest.registered_nodes[minetest.get_node(eye_pos).name] + add_node_kill_message(drowning_node, "drowning", victim) + log.write("deathlist","Player "..victim.name.." died due to drowning in "..drowning_node.name) + elseif reason.type=="node_damage" then + local killing_node_feet=minetest.registered_nodes[minetest.get_node(player:get_pos()).name] + local eye_pos=vector.add(player:get_pos(), {x=0, z=0, y=player:get_properties().eye_height}) + local killing_node_head=minetest.registered_nodes[minetest.get_node(eye_pos).name] + local killing_node=killing_node_feet + if (killing_node_head.node_damage or 0) > (killing_node_feet.node_damage or 0) then + killing_node=killing_node_head + end + add_node_kill_message(killing_node, "node_damage", victim) + log.write("deathlist","Player "..victim.name.." died due to node damage of "..killing_node.name) + elseif reason.type~="punch" and enable_unknown then + add_environmental_kill_message("unknown", victim) + log.write("deathlist","Player "..victim.name.." died for unknown reasons.") + end + end + end + ) +end + +minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage) + if player:get_hp() > 0 and player:get_hp()-damage <= 0 and hitter then + local wielded_item_name=hitter:get_wielded_item():get_name() + local tool + if minetest.registered_nodes[wielded_item_name] then + tool=mt_ext.get_node_inventory_image(wielded_item_name) + else + tool=(minetest.registered_items[wielded_item_name] or {inventory_image="deathlist_gravestone.png"}).inventory_image + end + local killer={name=hitter:get_player_name(), color=player_ext.get_color_int(hitter)} + local victim={name=player:get_player_name(), color=player_ext.get_color_int(player)} + add_kill_message(killer,tool,victim) + log.write("deathlist","Player "..killer.name.." killed "..victim.name.." using "..wielded_item_name) + end +end ) diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..040e24d --- /dev/null +++ b/mod.conf @@ -0,0 +1,3 @@ +name=deathlist +description=Adds a kill history. +depends=modlib, default \ No newline at end of file diff --git a/textures/deathlist_tombstone.png b/textures/deathlist_tombstone.png new file mode 100644 index 0000000..9c3bdd9 Binary files /dev/null and b/textures/deathlist_tombstone.png differ