major work to sounds and reloading mechanics

This commit is contained in:
FatalErr42O 2024-01-26 19:16:18 -08:00
parent b972ee78b8
commit 8585180634
33 changed files with 1330 additions and 209 deletions

View File

@ -3,11 +3,18 @@
( ) add audio
( ) add config
(~) add audio
(x) sfx system
(x) did signifficant work on it
(x) firing sound effects
( ) reload sound effects
( ) firemode sound effects
(~) add config
(x) add a table for config storage, some settings
( ) integrate with minetest settings
(x) fix crash when switching from a gun into a gun with a sprite_scope
(x) add infinite ammo privelage and quick command
( ) privilege not directly tied to infinite ammo, fix without breaking performance?
(x) privilege not directly tied to infinite ammo, fix without breaking performance?
(x) fix animation rotation offset not displaying the correct frame
-was a problem with MTUL, changes push :D
( ) add entity scopes (for holo sights etc)
@ -17,7 +24,7 @@
( ) fractional
( ) flat (magless)
( ) fractional clip
( ) (for 5.9) make infinite ammo priv to rely on on_grant and on_revoke callback for runtime changes (broken as of 5.8)
( ) (for 5.9) make infinite ammo priv to rely on on_grant and on_revoke callback for runtime changes (cannot be done as broken in 5.8)
( ) (5.9) POTENTIALLY make models use new PR that allows bone offsets to be disabled
-I'd probably have to modify models at loadtime to have an eye and hipfire bone? Probably easier then current system though.
( ) Fix HORRIBLE namespace violation in misc_helpers.lua. Also migrate features to MTUL libraries
@ -42,5 +49,9 @@ documentation
( ) Bullet_ray
( ) play_sound.lua
( ) misc_helpers.lua
(x) play_sound.lua
(~) misc_helpers.lua
(x) weighted randoms
( ) unique ID
( ) table helpers
( ) openGL/irrlicht relative dir projection

View File

@ -26,7 +26,7 @@ end
function Guns4d.ammo.register_bullet(def)
assert(def.itemstring, "no itemstring")
assert(minetest.registered_items[def.itemstring], "no item '"..def.itemstring.."' found. Must be a registered item (check dependencies?)")
Guns4d.ammo.registered_bullets[def.itemstring] = table.fill(Default_bullet, def)
Guns4d.ammo.registered_bullets[def.itemstring] = Guns4d.table.fill(Default_bullet, def)
end
function Guns4d.ammo.initialize_mag_data(itemstack, meta)
meta = meta or itemstack:get_meta()
@ -45,9 +45,8 @@ function Guns4d.ammo.update_mag(def, itemstack, meta)
count = count + v
end
local new_wear = max_wear-(max_wear*count/def.capacity)
--itemstack:set_wear(math.clamp(new_wear, 1, max_wear-1))
--itemstack:set_wear(Guns4d.math.clamp(new_wear, 1, max_wear-1))
meta:set_int("guns4d_total_bullets", count)
meta:set_string("guns4d_next_bullet", current_bullet)
if count > 0 then
meta:set_string("count_meta", tostring(count).."/"..def.capacity)
else
@ -57,7 +56,7 @@ function Guns4d.ammo.update_mag(def, itemstack, meta)
end
function Guns4d.ammo.register_magazine(def)
def = table.fill(Default_mag, def)
def = Guns4d.table.fill(Default_mag, def)
assert(def.accepted_bullets, "missing property def.accepted_bullets. Need specified bullets to allow for loading")
assert(def.itemstring, "missing item name")
def.accepted_bullets_set = {} --this table is a "lookup" table, I didn't go to college so I have no idea

77
autogen_docs/index.html Normal file
View File

@ -0,0 +1,77 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>4dguns documentation</title>
<link rel="stylesheet" href="ldoc_new.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>4dguns</h1>
<h2>Scripts</h2>
<ul class="nowrap">
<li><a href="scripts/misc_helpers.html">misc_helpers</a></li>
<li><a href="scripts/play_sound.html">play_sound</a></li>
</ul>
<h2>Topics</h2>
<ul class="">
<li><a href="topics/readme.md.html">readme</a></li>
</ul>
</div>
<div id="content">
<h2>THEE ultimate 3d gun library.</h2>
<h2>Scripts</h2>
<table class="module_list">
<tr>
<td class="name" nowrap><a href="scripts/misc_helpers.html">misc_helpers</a></td>
<td class="summary">misc.</td>
</tr>
<tr>
<td class="name" nowrap><a href="scripts/play_sound.html">play_sound</a></td>
<td class="summary">implements tools for quickly playing audio.</td>
</tr>
</table>
<h2>Topics</h2>
<table class="module_list">
<tr>
<td class="name" nowrap><a href="topics/readme.md.html">readme.md</a></td>
<td class="summary"></td>
</tr>
</table>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/lunarmodules/ldoc">LDoc 1.5.0</a></i>
<i style="float:right;">Last updated 2024-01-19 14:08:01 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

290
autogen_docs/ldoc_new.css Normal file
View File

@ -0,0 +1,290 @@
body {
color: #47555c;
font-size: 16px;
font-family: "Open Sans", sans-serif;
margin: 0;
background: #eff4ff;
}
a:link { color: #008fee; }
a:visited { color: #008fee; }
a:hover { color: #22a7ff; }
h1 { font-size:26px; font-weight: normal; }
h2 { font-size:22px; font-weight: normal; }
h3 { font-size:18px; font-weight: normal; }
h4 { font-size:16px; font-weight: bold; }
hr {
height: 1px;
background: #c1cce4;
border: 0px;
margin: 15px 0;
}
code, tt {
font-family: monospace;
}
span.parameter {
font-family: monospace;
font-weight: bold;
color: rgb(99, 115, 131);
}
span.parameter:after {
content:":";
}
span.types:before {
content:"(";
}
span.types:after {
content:")";
}
.type {
font-weight: bold; font-style:italic
}
p.name {
font-family: "Andale Mono", monospace;
}
#navigation {
float: left;
background-color: white;
border-right: 1px solid #d3dbec;
border-bottom: 1px solid #d3dbec;
width: 14em;
vertical-align: top;
overflow: visible;
}
#navigation br {
display: none;
}
#navigation h1 {
background-color: white;
border-bottom: 1px solid #d3dbec;
padding: 15px;
margin-top: 0px;
margin-bottom: 0px;
}
#navigation h2 {
font-size: 18px;
background-color: white;
border-bottom: 1px solid #d3dbec;
padding-left: 15px;
padding-right: 15px;
padding-top: 10px;
padding-bottom: 10px;
margin-top: 30px;
margin-bottom: 0px;
}
#content h1 {
background-color: #2c3e67;
color: white;
padding: 15px;
margin: 0px;
}
#content h2 {
background-color: #6c7ea7;
color: white;
padding: 15px;
padding-top: 15px;
padding-bottom: 15px;
margin-top: 0px;
}
#content h2 a {
background-color: #6c7ea7;
color: white;
text-decoration: none;
}
#content h2 a:hover {
text-decoration: underline;
}
#content h3 {
font-style: italic;
padding-top: 15px;
padding-bottom: 4px;
margin-right: 15px;
margin-left: 15px;
margin-bottom: 5px;
border-bottom: solid 1px #bcd;
}
#content h4 {
margin-right: 15px;
margin-left: 15px;
border-bottom: solid 1px #bcd;
}
#content pre {
margin: 15px;
}
pre {
background-color: rgb(50, 55, 68);
color: white;
border-radius: 3px;
/* border: 1px solid #C0C0C0; /* silver */
padding: 15px;
overflow: auto;
font-family: "Andale Mono", monospace;
}
#content ul pre.example {
margin-left: 0px;
}
table.index {
/* border: 1px #00007f; */
}
table.index td { text-align: left; vertical-align: top; }
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 14em;
}
#content p {
padding-left: 15px;
padding-right: 15px;
}
#content table {
padding-left: 15px;
padding-right: 15px;
background-color: white;
}
#content p, #content table, #content ol, #content ul, #content dl {
max-width: 900px;
}
#about {
padding: 15px;
padding-left: 16em;
background-color: white;
border-top: 1px solid #d3dbec;
border-bottom: 1px solid #d3dbec;
}
table.module_list, table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
margin: 15px;
}
table.module_list td, table.function_list td {
border-width: 1px;
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
padding-bottom: 5px;
border: solid 1px rgb(193, 204, 228);
}
table.module_list td.name, table.function_list td.name {
background-color: white; min-width: 200px; border-right-width: 0px;
}
table.module_list td.summary, table.function_list td.summary {
background-color: white; width: 100%; border-left-width: 0px;
}
dl.function {
margin-right: 15px;
margin-left: 15px;
border-bottom: solid 1px rgb(193, 204, 228);
border-left: solid 1px rgb(193, 204, 228);
border-right: solid 1px rgb(193, 204, 228);
background-color: white;
}
dl.function dt {
color: rgb(99, 123, 188);
font-family: monospace;
border-top: solid 1px rgb(193, 204, 228);
padding: 15px;
}
dl.function dd {
margin-left: 15px;
margin-right: 15px;
margin-top: 5px;
margin-bottom: 15px;
}
#content dl.function dd h3 {
margin-top: 0px;
margin-left: 0px;
padding-left: 0px;
font-size: 16px;
color: rgb(128, 128, 128);
border-bottom: solid 1px #def;
}
#content dl.function dd ul, #content dl.function dd ol {
padding: 0px;
padding-left: 15px;
list-style-type: none;
}
ul.nowrap {
overflow:auto;
white-space:nowrap;
}
.section-description {
padding-left: 15px;
padding-right: 15px;
}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* make the target distinct; helps when we're navigating to a function */
a:target + * {
background-color: #FF9;
}
/* styles for prettification of source */
pre .comment { color: #bbccaa; }
pre .constant { color: #a8660d; }
pre .escape { color: #844631; }
pre .keyword { color: #ffc090; font-weight: bold; }
pre .library { color: #0e7c6b; }
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
pre .string { color: #8080ff; }
pre .number { color: #f8660d; }
pre .operator { color: #2239a8; font-weight: bold; }
pre .preprocessor, pre .prepro { color: #a33243; }
pre .global { color: #c040c0; }
pre .user-keyword { color: #800080; }
pre .prompt { color: #558817; }
pre .url { color: #272fc2; text-decoration: underline; }

View File

@ -0,0 +1,73 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>4dguns documentation</title>
<link rel="stylesheet" href="../ldoc_new.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>4dguns</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>Modules</h2>
<ul class="nowrap">
<li><strong>misc_helpers</strong></li>
</ul>
<h2>Scripts</h2>
<ul class="nowrap">
<li><a href="../scripts/play_sound.html">play_sound</a></li>
</ul>
<h2>Topics</h2>
<ul class="">
<li><a href="../topics/readme.md.html">readme</a></li>
</ul>
</div>
<div id="content">
<h1>Module <code>misc_helpers</code></h1>
<p>picks a random index, with odds based on it's value.</p>
<p> Returns the index of the selected.</p>
<br/>
<br/>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/lunarmodules/ldoc">LDoc 1.5.0</a></i>
<i style="float:right;">Last updated 2024-01-16 17:00:50 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -0,0 +1,112 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>4dguns documentation</title>
<link rel="stylesheet" href="../ldoc_new.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>4dguns</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#Functions">Functions</a></li>
</ul>
<h2>Scripts</h2>
<ul class="nowrap">
<li><strong>misc_helper</strong></li>
<li><a href="../scripts/play_sound.html">play_sound</a></li>
</ul>
<h2>Topics</h2>
<ul class="">
<li><a href="../topics/readme.md.html">readme</a></li>
</ul>
</div>
<div id="content">
<h1>Script <code>misc_helper</code></h1>
<p>misc.</p>
<p> common tools for 4dguns</p>
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#Guns4d.math.weighted_randoms">Guns4d.math.weighted_randoms (tbl)</a></td>
<td class="summary">picks a random index, with odds based on it's value.</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
<dl class="function">
<dt>
<a name = "Guns4d.math.weighted_randoms"></a>
<strong>Guns4d.math.weighted_randoms (tbl)</strong>
</dt>
<dd>
picks a random index, with odds based on it's value. Returns the index of the selected.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">tbl</span>
<p> a table containing weights, example</p>
<pre><code> {
["sound"] = 999, --999 in 1000 chance this plays
["rare_sound"] = 1 --1 in 1000 chance this plays
}
</code></pre>
</li>
</ul>
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/lunarmodules/ldoc">LDoc 1.5.0</a></i>
<i style="float:right;">Last updated 2024-01-16 17:04:01 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -0,0 +1,115 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>4dguns documentation</title>
<link rel="stylesheet" href="../ldoc_new.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>4dguns</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#math_helpers">math helpers </a></li>
</ul>
<h2>Scripts</h2>
<ul class="nowrap">
<li><strong>misc_helpers</strong></li>
<li><a href="../scripts/play_sound.html">play_sound</a></li>
</ul>
<h2>Topics</h2>
<ul class="">
<li><a href="../topics/readme.md.html">readme</a></li>
</ul>
</div>
<div id="content">
<h1>Script <code>misc_helpers</code></h1>
<p>misc.</p>
<p> common tools for 4dguns</p>
<h2><a href="#math_helpers">math helpers </a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#weighted_randoms">weighted_randoms (tbl)</a></td>
<td class="summary">picks a random index, with odds based on it's value.</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header has-description"><a name="math_helpers"></a>math helpers </h2>
<div class="section-description">
in guns4d.math
</div>
<dl class="function">
<dt>
<a name = "weighted_randoms"></a>
<strong>weighted_randoms (tbl)</strong>
</dt>
<dd>
picks a random index, with odds based on it's value. Returns the index of the selected.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">tbl</span>
<p> a table containing weights, example</p>
<pre><code> {
["sound"] = 999, --999 in 1000 chance this is chosen
["rare_sound"] = 1 --1 in 1000 chance this is chosen
}
</code></pre>
</li>
</ul>
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/lunarmodules/ldoc">LDoc 1.5.0</a></i>
<i style="float:right;">Last updated 2024-01-19 14:08:01 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -0,0 +1,162 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>4dguns documentation</title>
<link rel="stylesheet" href="../ldoc_new.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>4dguns</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#Functions">Functions</a></li>
<li><a href="#Tables">Tables</a></li>
</ul>
<h2>Scripts</h2>
<ul class="nowrap">
<li><a href="../scripts/misc_helpers.html">misc_helpers</a></li>
<li><strong>play_sound</strong></li>
</ul>
<h2>Topics</h2>
<ul class="">
<li><a href="../topics/readme.md.html">readme</a></li>
</ul>
</div>
<div id="content">
<h1>Script <code>play_sound</code></h1>
<p>implements tools for quickly playing audio.</p>
<p>
</p>
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#Guns4d.play_sound">Guns4d.play_sound (sound_specs)</a></td>
<td class="summary">allows you to play one or more sounds with more complex features, so sounds can be easily coded for guns without the need for functions.</td>
</tr>
</table>
<h2><a href="#Tables">Tables</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#guns4d_soundspec">guns4d_soundspec</a></td>
<td class="summary">defines a sound.</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
<dl class="function">
<dt>
<a name = "Guns4d.play_sound"></a>
<strong>Guns4d.play_sound (sound_specs)</strong>
</dt>
<dd>
allows you to play one or more sounds with more complex features, so sounds can be easily coded for guns without the need for functions.
WARNING: this function modifies the tables passed to it, use <code>Guns4d.table.shallow_copy()</code> for guns4d_soundspecs
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">sound_specs</span>
<p> a <a href="../scripts/play_sound.html#guns4d_soundspec">guns4d_soundspec</a> or a list of <a href="../scripts/play_sound.html#guns4d_soundspec">guns4d_soundspec</a>s indexed my number. Also allows for shared fields. Example:</p>
<pre><code> {
to_player = "singeplayer",
min_distance = 100, --soundspec_to_play1 &amp; soundspec_to_play2 share this parameter (as well as the to_player)
soundspec_to_play1,
soundspec_to_play2
}
</code></pre>
</li>
</ul>
<h3>Returns:</h3>
<ol>
out a list of Minetest sound handles [insert link] (in the order they came)
</ol>
</dd>
</dl>
<h2 class="section-header "><a name="Tables"></a>Tables</h2>
<dl class="function">
<dt>
<a name = "guns4d_soundspec"></a>
<strong>guns4d_soundspec</strong>
</dt>
<dd>
defines a sound.
This is passed to <code>minetest.sound_play</code> as a <a href="https://github.com/minetest/minetest/blob/master/doc/lua_api.md#sound-parameter-table"> sound parameter table</a>
however has the following changed or guns4d specific parameters.
<h3>Fields:</h3>
<ul>
<li><span class="parameter">min_hear_distance</span>
this is useful if you wish to play a sound which has a "far" sound, such as distant gunshots. incompatible <code>with to_player</code>
</li>
<li><span class="parameter">sounds</span>
a <a href="../scripts/misc_helpers.html#weighted_randoms"> weighted_randoms table</a> the output will overwrite the <code>sound</code> field.
</li>
<li><span class="parameter">to_player</span>
4dguns changes <code>to_player</code> so it only plays positionless audio (as it is only intended for first person audio)
</li>
</ul>
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/lunarmodules/ldoc">LDoc 1.5.0</a></i>
<i style="float:right;">Last updated 2024-01-19 14:08:01 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -0,0 +1,63 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>4dguns documentation</title>
<link rel="stylesheet" href="../ldoc_new.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>4dguns</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>Topics</h2>
<ul class="">
<li><strong>readme</strong></li>
</ul>
<h2>Scripts</h2>
<ul class="nowrap">
<li><a href="../scripts/misc_helpers.html">misc_helpers</a></li>
<li><a href="../scripts/play_sound.html">play_sound</a></li>
</ul>
</div>
<div id="content">
<h1>4dguns</h1>
<p>3dguns remastered. Currently a work in progress that is updating steadily (kind of).</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/lunarmodules/ldoc">LDoc 1.5.0</a></i>
<i style="float:right;">Last updated 2024-01-19 14:08:01 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -34,6 +34,7 @@ function Ammo_handler:update_meta(bullets)
meta:set_string("guns4d_loaded_bullets", bullets or minetest.serialize(self.ammo.loaded_bullets))
meta:set_int("guns4d_total_bullets", self.ammo.total_bullets)
meta:set_string("guns4d_next_bullet", self.ammo.next_bullet)
self.ammo.magazine_psuedo_empty = false
if self.gun.ammo_handler then --if it's a first occourance it cannot work.
self.gun:update_image_and_text_meta(meta)
end
@ -45,7 +46,7 @@ function Ammo_handler:spend_round()
local bullet_spent = self.ammo.next_bullet
local meta = self.gun.meta
--subtract the bullet
if self.ammo.total_bullets > 0 then
if (self.ammo.total_bullets > 0) and (bullet_spent ~= "empty") then
--only actually subtract the round if infinite_ammo is false.
if not self.handler.infinite_ammo then
self.ammo.loaded_bullets[bullet_spent] = self.ammo.loaded_bullets[bullet_spent]-1
@ -54,7 +55,7 @@ function Ammo_handler:spend_round()
end
--set the new current bullet
if next(self.ammo.loaded_bullets) then
self.ammo.next_bullet = math.weighted_randoms(self.ammo.loaded_bullets)
self.ammo.next_bullet = Guns4d.math.weighted_randoms(self.ammo.loaded_bullets)
meta:set_string("guns4d_next_bullet", self.ammo.next_bullet)
else
self.ammo.next_bullet = "empty"
@ -65,7 +66,10 @@ function Ammo_handler:spend_round()
return bullet_spent
end
end
function Ammo_handler:load_magazine()
function Ammo_handler:close_bolt()
self.ammo.next_bullet = Guns4d.math.weighted_randoms(self.ammo.loaded_bullets) or "empty"
end
function Ammo_handler:load_magazine(charge)
assert(self.instance, "attempt to call object method on a class")
local inv = self.inventory
local magstack_index
@ -108,7 +112,8 @@ function Ammo_handler:load_magazine()
self.ammo.loaded_mag = magstack:get_name()
self.ammo.loaded_bullets = minetest.deserialize(bullet_string)
self.ammo.total_bullets = magstack_meta:get_int("guns4d_total_bullets")
self.ammo.next_bullet = magstack_meta:get_string("guns4d_next_bullet")
self.ammo.next_bullet = ((not charge) and "empty") or Guns4d.math.weighted_randoms(self.ammo.loaded_bullets)
print(dump(self.ammo.next_bullet), dump(Guns4d.math.weighted_randoms(self.ammo.loaded_bullets)))
self:update_meta()
inv:set_stack("main", magstack_index, "")
@ -139,7 +144,12 @@ function Ammo_handler:can_load_magazine()
end
return false
end
--state will automatically set reset on update_meta()
function Ammo_handler:set_unloading(bool)
self.ammo.magazine_psuedo_empty = bool
self.gun:update_image_and_text_meta()
self.gun.player:set_wielded_item(self.gun.itemstack)
end
function Ammo_handler:unload_magazine(to_ground)
assert(self.instance, "attempt to call object method on a class")
if self.ammo.loaded_mag ~= "empty" then
@ -150,7 +160,6 @@ function Ammo_handler:unload_magazine(to_ground)
--set the mag's meta before updating ours and adding the item.
magmeta:set_string("guns4d_loaded_bullets", gunmeta:get_string("guns4d_loaded_bullets"))
magmeta:set_string("guns4d_total_bullets", gunmeta:get_string("guns4d_total_bullets"))
magmeta:set_string("guns4d_next_bullet", gunmeta:get_string("guns4d_next_bullet"))
magstack = Guns4d.ammo.update_mag(nil, magstack, magmeta)
--throw it on the ground if to_ground is true
local remaining

View File

@ -115,22 +115,20 @@ function ray:_iterate(initialized)
local distance = vector.distance(self.pos, end_pos)
if self.state == "free" then
self.energy = self.energy-(distance*self.energy_dropoff)
if distance ~= self.pos+(self.dir*self.range) then
if next_state == "transverse" then
print(vector.distance(self.pos, end_pos), vector.distance(self.pos, self.pos+(self.dir*self.range)))
self:bullet_hole(end_pos, end_normal)
end
else
if self.history[#self.history].state == "free" then
self:bullet_hole(self.pos, self.history[#self.history-1].normal)
end
--add exit holes
if next_state == "free" then
self:bullet_hole(end_pos, end_normal)
end
--calc penetration loss from traveling through the block
local penetration_loss = distance*Guns4d.node_properties[self.last_node_name].mmRHA
--calculate our energy loss based on the percentage of energy our penetration represents.
self.energy = self.energy-((self.init_energy*self.energy_sharp_ratio)*(penetration_loss/self.sharp_penetration))
end
if self.state ~= self.next_state then
end
--set values for next iteration.
self.range = self.range-distance
@ -187,11 +185,11 @@ function ray:hit_entity(object)
local resistance = object:get_armor_groups() -- support for different body parts is needed here, that's for... a later date, though.
local sharp_pen = self.sharp_penetration-(self.sharp_penetration*(self.energy/self.init_energy)*self.energy_sharp_ratio)
sharp_pen = math.clamp(sharp_pen - (resistance.guns4d_mmRHA or 0), 0, 65535)
sharp_pen = Guns4d.math.clamp(sharp_pen - (resistance.guns4d_mmRHA or 0), 0, 65535)
local converted_Pa = (resistance.guns4d_mmRHA or 0) * self.mmRHA_to_Pa_energy_ratio
local blunt_pen = converted_Pa+(self.blunt_penetration-(self.blunt_penetration*(self.energy/self.init_energy)*(1-self.energy_sharp_ratio)))
blunt_pen = math.clamp(blunt_pen - (resistance.guns4d_Pa or 0), 0, 65535)
blunt_pen = Guns4d.math.clamp(blunt_pen - (resistance.guns4d_Pa or 0), 0, 65535)
self:apply_damage(object, sharp_pen, blunt_pen)
--raw damage first

View File

@ -35,40 +35,44 @@ function controls:update(dt)
local pressed = self.player_pressed
local call_queue = {} --so I need to have a "call" queue so I can tell the functions the names of other active controls (busy_list)
local busy_list = self.busy_list or {} --list of controls that have their conditions met. Has to be reset at END of update, so on_use and on_secondary_use can be marked
for i, control in pairs(self.controls) do
if not (i=="on_use") and not (i=="on_secondary_use") then
local def = control
local data = control.data
local conditions_met = true
--check no conditions are false
for _, key in pairs(control.conditions) do
if not pressed[key] then conditions_met = false break end
end
if conditions_met then
busy_list[i] = true
data.timer = data.timer - dt
--when time is over, if it wasnt held (or loop is active) then reset and call the function.
--held indicates wether the function was called (as active) before last step.
if data.timer <= 0 and ((not data.held) or def.loop) then
data.held = true
table.insert(call_queue, {control=def, active=true, interrupt=false, data=data})
elseif def.call_before_timer and not data.held then --this is useful for functions that need to play animations for their progress.
table.insert(call_queue, {control=def, active=false, interrupt=false, data=data})
if not (self.gun.rechamber_time > 0 and self.gun.ammo_handler.ammo.next_bullet == "empty") then --check if the gun is being charged.
for i, control in pairs(self.controls) do
if not (i=="on_use") and not (i=="on_secondary_use") then
local def = control
local data = control.data
local conditions_met = true
--check no conditions are false
for _, key in pairs(control.conditions) do
if not pressed[key] then conditions_met = false break end
end
else
data.held = false
--detect interrupts, check if the timer was in progress
if data.timer ~= def.timer then
table.insert(call_queue, {control=def, active=false, interrupt=true, data=data})
data.timer = def.timer
if conditions_met then
busy_list[i] = true
data.timer = data.timer - dt
--when time is over, if it wasnt held (or loop is active) then reset and call the function.
--held indicates wether the function was called (as active) before last step.
if data.timer <= 0 and ((not data.held) or def.loop) then
data.held = true
table.insert(call_queue, {control=def, active=true, interrupt=false, data=data})
elseif def.call_before_timer and not data.held then --this is useful for functions that need to play animations for their progress.
table.insert(call_queue, {control=def, active=false, interrupt=false, data=data})
end
else
data.held = false
--detect interrupts, check if the timer was in progress
if data.timer ~= def.timer then
table.insert(call_queue, {control=def, active=false, interrupt=true, data=data})
data.timer = def.timer
end
end
end
end
for i, tbl in pairs(call_queue) do
tbl.control.func(tbl.active, tbl.interrupt, tbl.data, busy_list, self.handler.gun, self.handler)
end
self.busy_list = {}
elseif self.busy_list then
self.busy_list = nil
end
for i, tbl in pairs(call_queue) do
tbl.control.func(tbl.active, tbl.interrupt, tbl.data, busy_list, self.handler.gun, self.handler)
end
self.busy_list = {}
end
function controls:on_use(itemstack, pointed_thing)
assert(self.instance, "attempt to call object method on a class")
@ -87,7 +91,7 @@ function controls.construct(def)
if def.instance then
assert(def.controls, "no controls provided")
assert(def.player, "no player provided")
def.controls = table.deep_copy(def.controls)
def.controls = Guns4d.table.deep_copy(def.controls)
def.busy_list = {}
def.handler = Guns4d.players[def.player:get_player_name()]
for i, control in pairs(def.controls) do

View File

@ -30,7 +30,7 @@ end
local function render_length(rotation, fov)
local dir = vector.rotate({x=0,y=0,z=1}, {x=rotation.x*math.pi/180,y=0,z=0})
vector.rotate(dir,{x=0,y=rotation.y*math.pi/180,z=0})
local out = rltv_point_to_hud(dir, fov, 1)
local out = Guns4d.rltv_point_to_hud(dir, fov, 1)
return math.sqrt(out.x^2+out.y^2)
end
function Dynamic_crosshair:update(dt)
@ -85,13 +85,13 @@ function Dynamic_crosshair:update(dt)
--now figure out what frame will be our correct spread
local offset = rltv_point_to_hud(dir, fov, 1) --pretend it's a 1:1 ratio so we can do things correctly.
local offset = Guns4d.rltv_point_to_hud(dir, fov, 1) --pretend it's a 1:1 ratio so we can do things correctly.
local length = math.sqrt(offset.x^2+offset.y^2) --get the max length.
local img_perc = (self.scale*2*handler.wininfo.real_hud_scaling*self.width)/handler.wininfo.size.x --the percentage that the hud element takes up
local frame = length/img_perc --the percentage of the size the length takes up.
frame = math.floor(self.frames*frame)
frame = math.clamp(frame, 0, self.frames-1)
frame = Guns4d.math.clamp(frame, 0, self.frames-1)
--"^[vertical_frame:"..self.frames..":"..frame
self.player:hud_change(self.hud, "text", self.image.."^[verticalframe:"..self.frames..":"..frame)
else

View File

@ -84,17 +84,22 @@ local gun_default = {
},
breathing_scale = .5, --the max angluler offset caused by breathing.
controls = { --used by control_handler
__overfill=true, --if present, this table will not be filled in.
__overfill=true, --this table will not be filled in.
aim = Guns4d.default_controls.aim,
auto = Guns4d.default_controls.auto,
reload = Guns4d.default_controls.reload,
on_use = Guns4d.default_controls.on_use,
firemode = Guns4d.default_controls.firemode
},
charging = { --how the gun "cocks"
require_charge_on_swap = true,
bolt_charge_mode = "none", --"none"-chamber is always full, "catch"-when fired to dry bolt will not need to be charged after reload, "no_catch" bolt will always need to be charged after reload.
default_charge_time = 1,
},
reload = { --used by defualt controls. Still provides usefulness elsewhere.
__overfill=true, --if present, this table will not be filled in.
{type="unload", time=1, anim="unload", interupt="to_ground", hold = true},
{type="load", time=1, anim="load"}
__overfill=true,
--{type="unload_mag", time=1, anim="unload_mag", interupt="to_ground", hold = true, sound = {sound = "load magazine", pitch = {min=.9, max=1.1}}},
--{type="load", time=1, anim="load"}
},
ammo = { --used by ammo_handler
magazine_only = false,
@ -105,6 +110,7 @@ local gun_default = {
--mesh
backface_culling = true,
root = "gun",
magazine = "magazine",
arm_right = "right_aimpoint",
arm_left = "left_aimpoint",
animations = { --used by animations handler for idle, and default controls
@ -112,6 +118,35 @@ local gun_default = {
loaded = {x=1,y=1},
},
},
sounds = { --this does not contain reload sound effects.
fire = {
{
sound = "ar_firing",
max_hear_distance = 40, --far min_hear_distance is also this.
pitch = {
min = .95,
max = 1.05
},
gain = {
min = .9,
max = 1
}
},
{
sound = "ar_firing_far",
min_hear_distance = 40,
max_hear_distance = 600,
pitch = {
min = .95,
max = 1.05
},
gain = {
min = .9,
max = 1
}
}
},
},
--inventory_image
--inventory_image_empty
--used by ammo_handler
@ -168,6 +203,7 @@ local gun_default = {
KEYFRAME_SAMPLE_PRECISION = .1, --[[what frequency to take precalcualted keyframe samples. The lower this is the higher the memory allocation it will need- though minimal.
This will fuck shit up if you change it after gun construction/inheritence (interpolation between precalculated vectors will not work right)]]
WAG_CYCLE_SPEED = 1.6,
DEFAULT_MAX_HEAR_DISTANCE = 10,
DEFAULT_FPS = 60,
WAG_DECAY = 1, --divisions per second
HAS_RECOIL = true,
@ -189,6 +225,7 @@ local gun_default = {
}
]]
},
bolt_charged = false,
particle_spawners = {},
current_firemode = 1,
walking_tick = 0,
@ -202,7 +239,16 @@ local gun_default = {
function gun_default.multiplier_coefficient(multiplier, ratio)
return 1+((multiplier*ratio)-ratio)
end
--update the gun, da meat and da potatoes
function gun_default:charge()
assert(self.instance, "attempt to call object method on a class")
local props = self.properties
if props.visuals.animations.charge then
self:set_animation(props.visuals.animations.charge, props.charging.default_charge_time)
end
self.ammo_handler:close_bolt()
self.rechamber_time = props.charging.default_charge_time
end
--update gun, the main function.
function gun_default:update(dt)
assert(self.instance, "attempt to call object method on a class")
if not self:has_entity() then self:add_entity(); self:clear_animation() end
@ -251,6 +297,13 @@ function gun_default:update(dt)
self.crosshair:update()
end
--automatically cock if uncocked.
local ammo = self.ammo_handler.ammo
--[[if ammo.total_bullets and (ammo.total_bullets > 0 and ammo.next_bullet == "empty") then
self:charge()
end]]
print(dump(self.ammo_handler.ammo.next_bullet))
local offsets = self.offsets
--local player_axial = offsets.recoil.player_axial + offsets.walking.player_axial + offsets.sway.player_axial + offsets.breathing.player_axial
--local gun_axial = offsets.recoil.gun_axial + offsets.walking.gun_axial + offsets.sway.gun_axial
@ -298,9 +351,9 @@ function gun_default:update_image_and_text_meta(meta)
end
--pick the image
local image = self.properties.inventory_image
if ammo.total_bullets > 0 then
if (ammo.total_bullets > 0) and not ammo.magazine_psuedo_empty then
image = self.properties.inventory_image
elseif self.properties.inventory_image_magless and (ammo.loaded_mag == "empty" or ammo.loaded_mag == "") then
elseif self.properties.inventory_image_magless and ( (ammo.loaded_mag == "empty") or (ammo.loaded_mag == "") or ammo.magazine_psuedo_empty) then
image = self.properties.inventory_image_magless
elseif self.properties.inventory_image_empty then
image = self.properties.inventory_image_empty
@ -321,9 +374,10 @@ function gun_default:attempt_fire()
if spent_bullet and spent_bullet ~= "empty" then
local dir = self.dir
local pos = self.pos
local bullet_def = table.fill(Guns4d.ammo.registered_bullets[spent_bullet], {
local bullet_def = Guns4d.table.fill(Guns4d.ammo.registered_bullets[spent_bullet], {
player = self.player,
pos = pos,
--we don't want it to be doing fuckshit and letting players shoot through walls.
pos = pos-((self.handler.control_handler.ads and dir*self.properties.ads.offset.z) or dir*self.properties.hip.offset.z),
dir = dir,
gun = self
})
@ -333,19 +387,29 @@ function gun_default:attempt_fire()
end
self:recoil()
self:muzzle_flash()
local fire_sound = Guns4d.table.deep_copy(self.properties.sounds.fire) --important that we copy because play_sounds modifies it.
fire_sound.pos = self.pos
Guns4d.play_sounds(fire_sound)
self.rechamber_time = 60/self.properties.firerateRPM
return true
end
end
end
local function rand_sign(b)
b = b or .5
local int = 1
if math.random() > b then int=-1 end
return int
end
function gun_default:recoil()
assert(self.instance, "attempt to call object method on a class")
local rprops = self.properties.recoil
for axis, recoil in pairs(self.velocities.recoil) do
for _, i in pairs({"x","y"}) do
recoil[i] = recoil[i] + (rprops.angular_velocity[axis][i]
*math.rand_sign((rprops.bias[axis][i]/2)+.5))
*rand_sign((rprops.bias[axis][i]/2)+.5))
*self.multiplier_coefficient(rprops.hipfire_multiplier[axis], 1-self.handler.ads_location)
end
end
@ -539,7 +603,7 @@ function gun_default:update_recoil(dt)
for axis, _ in pairs(self.offsets.recoil) do
for _, i in pairs({"x","y"}) do
local recoil = self.offsets.recoil[axis][i]
local recoil_vel = math.clamp(self.velocities.recoil[axis][i],-self.properties.recoil.angular_velocity_max[axis],self.properties.recoil.angular_velocity_max[axis])
local recoil_vel = Guns4d.math.clamp(self.velocities.recoil[axis][i],-self.properties.recoil.angular_velocity_max[axis],self.properties.recoil.angular_velocity_max[axis])
local old_recoil_vel = recoil_vel
recoil = recoil + recoil_vel
if math.abs(recoil_vel) > 0.01 then
@ -560,7 +624,7 @@ function gun_default:update_recoil(dt)
if math.abs(recoil) > 0.001 then
local correction_multiplier = self.time_since_last_fire*self.properties.recoil.target_correction_factor[axis]
local correction_value = recoil*correction_multiplier
correction_value = math.clamp(math.abs(correction_value), 0, self.properties.recoil.target_correction_max_rate[axis])
correction_value = Guns4d.math.clamp(math.abs(correction_value), 0, self.properties.recoil.target_correction_max_rate[axis])
recoil=recoil-(correction_value*dt*(math.abs(recoil)/recoil))
--prevent overcorrection
if math.abs(recoil) > math.abs(old_recoil) then
@ -576,7 +640,7 @@ function gun_default:update_animation(dt)
local ent = self.entity
local data = self.animation_data
data.runtime = data.runtime + dt
data.current_frame = math.clamp(data.current_frame+(dt*data.fps), data.frames.x, data.frames.y)
data.current_frame = Guns4d.math.clamp(data.current_frame+(dt*data.fps), data.frames.x, data.frames.y)
if data.loop and (data.current_frame > data.frames.y) then
data.current_frame = data.frames.x
end
@ -743,7 +807,7 @@ gun_default.construct = function(def)
def.meta = meta
--create ID so we can track switches between weapons, also get some other data.
if meta:get_string("guns4d_id") == "" then
local id = tostring(Unique_id.generate())
local id = tostring(Guns4d.unique_id.generate())
meta:set_string("guns4d_id", id)
def.player:set_wielded_item(def.itemstack)
def.id = id
@ -756,10 +820,15 @@ gun_default.construct = function(def)
def.ammo_handler = def.properties.ammo_handler:new({ --initialize ammo handler from gun and gun metadata.
gun = def
})
local ammo = def.ammo_handler.ammo
if def.properties.require_charge_on_swap then
ammo.next_bullet = "empty"
end
minetest.after(0, function() if ammo.total_bullets > 0 then def:charge() end end)
def:update_image_and_text_meta() --has to be called manually in post as ammo_handler would not exist yet.
def.player:set_wielded_item(def.itemstack)
--unavoidable table instancing
def.properties = table.fill(def.base_class.properties, def.properties)
def.properties = Guns4d.table.fill(def.base_class.properties, def.properties)
def.particle_spawners = {} --Instantiatable_class only shallow copies. So tables will not change, and thus some need to be initialized.
def.property_modifiers = {}
def.total_offset_rotation = {
@ -768,7 +837,7 @@ gun_default.construct = function(def)
}
def.player_rotation = Vec.new()
--initialize all offsets
--def.offsets = table.deep_copy(def.base_class.offsets)
--def.offsets = Guns4d.table.deep_copy(def.base_class.offsets)
def.offsets = {}
for offset, tbl in pairs(def.base_class.offsets) do
def.offsets[offset] = {}
@ -781,7 +850,7 @@ gun_default.construct = function(def)
end
end
def.animation_rotation = vector.new()
--def.velocities = table.deep_copy(def.base_class.velocities)
--def.velocities = Guns4d.table.deep_copy(def.base_class.velocities)
def.velocities = {}
for i, tbl in pairs(def.base_class.velocities) do
def.velocities[i] = {}
@ -817,8 +886,8 @@ gun_default.construct = function(def)
end
--fill in the properties.
def.properties = table.fill(def.parent_class.properties, props or {})
def.consts = table.fill(def.parent_class.consts, def.consts or {})
def.properties = Guns4d.table.fill(def.parent_class.properties, props or {})
def.consts = Guns4d.table.fill(def.parent_class.consts, def.consts or {})
props = def.properties --have to reinitialize this as the reference is replaced.
--print(table.tostring(props))
@ -856,13 +925,13 @@ gun_default.construct = function(def)
table.insert(def.b3d_model.global_frames.rotation, newvec)
end
end
if main then
--[[if main then
local quat = mtul.math.quat.new(main.keys[1].rotation)
print(dump(main.keys[1]), vector.new(quat:to_euler_angles_unpack(quat)))
end
for i, v in pairs(def.b3d_model.global_frames.rotation) do
print(i, dump(vector.new(v:to_euler_angles_unpack())*180/math.pi))
end
end]]
--print()
-- if it's not a template, then create an item, override some props
if def.name ~= "__template" then

View File

@ -39,7 +39,7 @@ function player_handler:update(dt)
self.player_model_handler = nil
end
self.player_model_handler = Guns4d.player_model_handler.get_handler(self:get_properties().mesh):new({player=self.player})
self.control_handler = Guns4d.control_handler:new({player=player, controls=self.gun.properties.controls})
self.control_handler = Guns4d.control_handler:new({player=player, controls=self.gun.properties.controls, gun=self.gun})
--this needs to be stored for when the gun is unset!
self.horizontal_offset = self.gun.properties.ads.horizontal_offset
@ -50,7 +50,7 @@ function player_handler:update(dt)
--for the gun's scopes to work properly we need predictable offsets.
end
--update some properties.
self.look_rotation.x, self.look_rotation.y = math.clamp((player:get_look_vertical() or 0)*180/math.pi, -80, 80), -player:get_look_horizontal()*180/math.pi
self.look_rotation.x, self.look_rotation.y = Guns4d.math.clamp((player:get_look_vertical() or 0)*180/math.pi, -80, 80), -player:get_look_horizontal()*180/math.pi
if TICK % 10 == 0 then
self.wininfo = minetest.get_player_window_information(self.player:get_player_name())
end
@ -80,13 +80,13 @@ function player_handler:update(dt)
--eye offsets and ads_location
if (self.control_handler and self.control_handler.ads) and (self.ads_location<1) then
--if aiming, then increase ADS location
self.ads_location = math.clamp(self.ads_location + (dt/self.gun.properties.ads.aim_time), 0, 1)
self.ads_location = Guns4d.math.clamp(self.ads_location + (dt/self.gun.properties.ads.aim_time), 0, 1)
elseif ((not self.control_handler) or (not self.control_handler.ads)) and self.ads_location>0 then
local divisor = .2
if self.gun then
divisor = self.gun.properties.ads.aim_time/self.gun.consts.AIM_OUT_AIM_IN_SPEED_RATIO
end
self.ads_location = math.clamp(self.ads_location - (dt/divisor), 0, 1)
self.ads_location = Guns4d.math.clamp(self.ads_location - (dt/divisor), 0, 1)
end
self.look_offset.x = self.horizontal_offset*self.ads_location
@ -162,7 +162,7 @@ end
function player_handler:set_properties(properties)
assert(self.instance, "attempt to call object method on a class")
self.player:set_properties(properties)
self.properties = table.fill(self.properties, properties)
self.properties = Guns4d.table.fill(self.properties, properties)
end
function player_handler:is_holding_gun()
assert(self.instance, "attempt to call object method on a class")
@ -208,7 +208,7 @@ function player_handler.construct(def)
def[i] = v
end
end
def.look_rotation = table.deep_copy(player_handler.look_rotation)
def.look_rotation = Guns4d.table.deep_copy(player_handler.look_rotation)
def.infinite_ammo = minetest.check_player_privs(def.player, Guns4d.config.infinite_ammo_priv)
end
end

View File

@ -31,9 +31,9 @@ local Sprite_scope = Instantiatable_class:inherit({
def.player = def.gun.player
def.handler = def.gun.handler
def.elements = {}
local new_images = table.deep_copy(def.images)
local new_images = Guns4d.table.deep_copy(def.images)
if def.images then
def.images = table.fill(new_images, def.images)
def.images = Guns4d.table.fill(new_images, def.images)
end
for i, v in pairs(def.images) do
def.elements[i] = def.player:hud_add{
@ -63,12 +63,12 @@ function Sprite_scope:update()
dir = dir + (self.gun.properties.ads.offset+vector.new(self.gun.properties.ads.horizontal_offset,0,0))*0
end
local fov = self.player:get_fov()
local real_aim = rltv_point_to_hud(dir, fov, ratio)
local anim_aim = rltv_point_to_hud(vector.rotate({x=0,y=0,z=1}, self.gun.animation_rotation*math.pi/180), fov, ratio)
local real_aim = Guns4d.rltv_point_to_hud(dir, fov, ratio)
local anim_aim = Guns4d.rltv_point_to_hud(vector.rotate({x=0,y=0,z=1}, self.gun.animation_rotation*math.pi/180), fov, ratio)
real_aim.x = real_aim.x+anim_aim.x; real_aim.y = real_aim.y+anim_aim.y
--print(dump(self.gun.animation_rotation))
local paxial_aim = rltv_point_to_hud(self.gun.local_paxial_dir, fov, ratio)
local paxial_aim = Guns4d.rltv_point_to_hud(self.gun.local_paxial_dir, fov, ratio)
--so custom scopes can do their thing without doing more calcs
self.hud_projection_real = real_aim
self.hud_projection_paxial = paxial_aim

View File

@ -46,6 +46,7 @@ Guns4d.default_controls.reload = {
conditions = {"zoom"},
loop = false,
timer = 0, --1 so we have a call to initialize the timer.
--remember that the data table allows us to store arbitrary data
func = function(active, interrupted, data, busy_list, gun, handler)
local ammo_handler = gun.ammo_handler
local props = gun.properties
@ -55,28 +56,37 @@ Guns4d.default_controls.reload = {
end
local this_state = props.reload[data.state]
local next_state_index = data.state
local next_state = props.reload[next_state_index+1]
--this elseif chain has gotten egregiously long, so I'll have to create a system for registering these reload states eventually- both for the sake of organization aswell as a modular API.
if next_state_index == 0 then
--nothing to do, let animations get set down the line.
next_state_index = next_state_index + 1
elseif type(this_state.type) == "function" then
this_state.type(true, handler, gun)
elseif this_state.type == "unload" then
local pause = false
local next = props.reload[next_state_index+1]
if (next.type=="load_fractional" or next.type=="load") and (not ammo_handler:inventory_has_ammo()) then
pause=true
elseif this_state.type == "unload_mag" then
next_state_index = next_state_index + 1
if next_state and next_state.type == "store" then
ammo_handler:set_unloading(true) --if interrupted it will drop to ground, so just make it appear as if the gun is already unloaded.
else
ammo_handler:unload_magazine(true) --unload to ground
end
elseif this_state.type == "store" then
local pause = false
--needs to happen before so we don't detect the ammo we just unloaded
if next_state and (next_state.type=="load_fractional" or next_state.type=="load") and (not ammo_handler:inventory_has_ammo()) then
pause=true
end
if props.ammo.magazine_only and (ammo_handler.ammo.loaded_mag ~= "empty") then
ammo_handler:unload_magazine()
else
ammo_handler:unload_all()
end
--if there's no ammo make hold so you don't reload the same ammo you just unloaded.
if pause then
return
@ -91,17 +101,26 @@ Guns4d.default_controls.reload = {
else
ammo_handler:load_flat()
end
next_state_index = next_state_index +1
if not (next_state or (next_state.type ~= "charge")) then
--chamber the round automatically.
ammo_handler:close_bolt()
end
next_state_index = next_state_index + 1
elseif this_state.type == "charge" then
next_state_index = next_state_index + 1
ammo_handler:close_bolt()
--if not
elseif this_state.type == "unload_fractional" then
ammo_handler:unload_fractional()
if ammo_handler.ammo.total_bullets == 0 then
next_state_index = next_state_index + 1
end
elseif this_state.type == "load_fractional" then
ammo_handler:load_fractional()
if ammo_handler.ammo.total_bullets == props.ammo.capacity then
next_state_index = next_state_index + 1
@ -117,76 +136,107 @@ Guns4d.default_controls.reload = {
--check that the next states are actually valid, if not, skip them
local valid_state = false
while not valid_state do
local state_changed = false
next_state = props.reload[next_state_index]
if next_state then
local state_changed = false
if next_state.type == "unload" then
--determine wether the next_state is valid (can actually be completed)
local invalid_state = false
if next_state.type == "store" then
if props.ammo.magazine_only and (ammo_handler.ammo.loaded_mag == "empty") then
state_changed = true
invalid_state = true
end
--need to check for inventory room, because otherwise we just want to drop it to the ground.
--[[
if ... then
if props.ammo.magazine_only and (ammo_handler.ammo.loaded_mag ~= "empty") then
ammo_handler:unload_magazine(true)
else
ammo_handler:unload_all(true)
end
end
]]
elseif next_state.type == "unload_fractional" then
--[[elseif next_state.type == "unload_fractional" then --UNIMPLEMENTED
if not ammo_handler.ammo.total_bullets > 0 then
state_changed = true
invalid_state = true
end]]
elseif next_state.type == "unload_mag" then
if ammo_handler.ammo.loaded_mag == "empty" then
invalid_state = true
end
elseif next_state.type == "load" then
--check we have ammo
if props.ammo.magazine_only then
if not ammo_handler:can_load_magazine() then
state_changed = true
invalid_state = true
end
else
if not ammo_handler:can_load_flat() then
state_changed = true
invalid_state = true
end
end
end
if not state_changed then
if not invalid_state then
valid_state=true
else
next_state_index = next_state_index + 1
next_state = props.reload[next_state_index]
end
else
--if the next state doesn't exist, we've reached the end (the gun is reloaded) and we should restart. "held" so it doesn't continue unless the user lets go of the input button.
data.state = 0
data.timer = 0.5
data.held = true
return
end
end
--check if we're at cycle end
if next_state == nil then
--I don't think this is needed given the above.
--[[ if next_state == nil then
data.state = 0
data.timer = 0
data.held = true
return
else
data.state = next_state_index
data.timer = next_state.time
data.held = false
local anim = next_state.anim
if type(next_state.anim) == "string" then
anim = props.visuals.animations[next_state.anim]
end
if anim then
else]]
data.state = next_state_index
data.timer = next_state.time
data.held = false
local anim = next_state.anim
if type(next_state.anim) == "string" then
anim = props.visuals.animations[next_state.anim]
end
if anim then
if anim.x and anim.y then
gun:set_animation(anim, next_state.time)
else
minetest.log("error", "improperly set gun reload animation, reload state `"..next_state.type.."`, gun `"..gun.itemstring.."`")
end
end
if next_state.sounds then
local sounds = Guns4d.table.deep_copy(props.reload[next_state_index].sounds)
sounds.pos = gun.pos
sounds.max_hear_distance = sounds.max_hear_distance or gun.consts.DEFAULT_MAX_HEAR_DISTANCE
data.played_sounds = Guns4d.play_sounds(sounds)
end
print(dump(next_state_index))
--end
elseif interrupted then
local this_state = props.reload[data.state]
if this_state and (this_state.type == "unload") and (this_state.interupt == "to_ground") then
--true indicates to_ground (meaning they will be removed)
if this_state and (this_state.type == "store") then
--if the player was about to store the mag, eject it.
if props.ammo.magazine_only and (ammo_handler.ammo.loaded_mag ~= "empty") then
ammo_handler:unload_magazine(true)
ammo_handler:unload_magazine(true) --"true" is for to_ground
else
ammo_handler:unload_all(true)
end
end
if data.played_sounds then
Guns4d.stop_sounds(data.played_sounds)
data.played_sounds = nil
end
gun:clear_animation()
data.state = 0
end

11
ldoc/config.ld Normal file
View File

@ -0,0 +1,11 @@
project="4dguns"
title="4dguns documentation"
description="THEE ultimate 3d gun library."
format="markdown"
backtick_references=false
file = {
"../",
}
dir='../autogen_docs'
readme='../README.md'
style='!new'

View File

@ -0,0 +1,13 @@
#! /bin/sh
# on github, leafo/gh-actions-lua leafo/gh-actions-luarocks setup luarocks for us.
#~ sudo apt-get install lua5.3 liblua5.3-dev luarocks
# github ldoc is far ahead of the released version.
echo ldoc version:
git ls-remote https://github.com/lunarmodules/LDoc master
luarocks --local install https://raw.githubusercontent.com/lunarmodules/LDoc/master/ldoc-scm-3.rockspec
echo
cd ./doc
~/.luarocks/bin/ldoc .

View File

@ -0,0 +1,3 @@
# literally just so I dont have to open powershell every time.
@echo off
ldoc .

View File

@ -1,14 +1,15 @@
--can't be copyright claimed by myself, luckily... well actually knowing the legal system I probably could sue myself.
Unique_id = {
--- misc. common tools for 4dguns
-- @script misc_helpers
Guns4d.math = {}
Guns4d.table = {}
--store this so there arent duplicates
Guns4d.unique_id = {
generated = {},
}
function math.clamp(val, lower, upper)
if lower > upper then lower, upper = upper, lower end
return math.max(lower, math.min(upper, val))
end
--I need to store this so there arent duplicates lol
function Unique_id.generate()
local genned_ids = Unique_id.generated
function Guns4d.unique_id.generate()
local genned_ids = Guns4d.unique_id.generated
local id = string.sub(tostring(math.random()), 3)
while genned_ids[id] do
id = string.sub(tostring(math.random()), 3)
@ -16,8 +17,24 @@ function Unique_id.generate()
genned_ids[id] = true
return id
end
--i probably should stop violating the math namespace, but I'll worry about that *later*
function math.weighted_randoms(tbl)
---math helpers.
-- in guns4d.math
--@section math
--all of the following is disgusting and violates the namespace because I got used to love2d.
function Guns4d.math.clamp(val, lower, upper)
if lower > upper then lower, upper = upper, lower end
return math.max(lower, math.min(upper, val))
end
--- picks a random index, with odds based on it's value. Returns the index of the selected.
-- @param tbl a table containing weights, example
-- {
-- ["sound"] = 999, --999 in 1000 chance this is chosen
-- ["rare_sound"] = 1 --1 in 1000 chance this is chosen
-- }
-- @function weighted_randoms
function Guns4d.math.weighted_randoms(tbl)
local total_weight = 0
local new_tbl = {}
for i, v in pairs(tbl) do
@ -39,38 +56,13 @@ function math.weighted_randoms(tbl)
scaled_weight = scaled_weight + v[2]
end
end
--[[function math.get_rotation(dir)
local x = math.atan2(dir.y, dir.z)
local y =-math.atan2(dir.x, dir.z)
return vector.new(
x,
y,
0
)
end]]
--from luatic's old modlib, doesn't work to fix gimble lock, actually makes things worse (somehow)
function math.get_rotation(dir)
return vector.new(
math.atan2(dir.y, math.sqrt(dir.x^2 + dir.z^2)),
-math.atan2(dir.x, dir.z),
0
)
end
function math.rand_sign(b)
b = b or .5
local int = 1
if math.random() > b then int=-1 end
return int
end
--weighted randoms
--[[
--for table vectors that aren't vector objects
---@diagnostic disable-next-line: lowercase-global
function tolerance_check(a,b,tolerance)
local function tolerance_check(a,b,tolerance)
return math.abs(a-b) > tolerance
end
function vector.equals_tolerance(v, vb, tolerance)
function Guns4d.math.vectors_in_tolerance(v, vb, tolerance)
tolerance = tolerance or 0
return (
tolerance_check(v.x, vb.x, tolerance) and
@ -78,14 +70,20 @@ function vector.equals_tolerance(v, vb, tolerance)
tolerance_check(v.z, vb.z, tolerance)
)
end
]]
---table helpers.
-- in guns4d.table
--@section table
--copy everything
function table.deep_copy(tbl, copy_metatable, indexed_tables)
function Guns4d.table.deep_copy(tbl, copy_metatable, indexed_tables)
if not indexed_tables then indexed_tables = {} end
local new_table = {}
local metat = getmetatable(tbl)
if metat then
if copy_metatable then
setmetatable(new_table, table.deep_copy(metat, true))
setmetatable(new_table, Guns4d.table.deep_copy(metat, true))
else
setmetatable(new_table, metat)
end
@ -94,7 +92,7 @@ function table.deep_copy(tbl, copy_metatable, indexed_tables)
if type(v) == "table" then
if not indexed_tables[v] then
indexed_tables[v] = true
new_table[i] = table.deep_copy(v, copy_metatable)
new_table[i] = Guns4d.table.deep_copy(v, copy_metatable)
end
else
new_table[i] = v
@ -104,7 +102,7 @@ function table.deep_copy(tbl, copy_metatable, indexed_tables)
end
function table.contains(tbl, value)
function Guns4d.table.contains(tbl, value)
for i, v in pairs(tbl) do
if v == value then
return i
@ -120,7 +118,8 @@ local function parse_index(i)
end
end
--dump() sucks.
function table.tostring(tbl, shallow, list_length_lim, depth_limit, tables, depth)
local table_contains = Guns4d.table.contains
function Guns4d.table.tostring(tbl, shallow, list_length_lim, depth_limit, tables, depth)
--create a list of tables that have been tostringed in this chain
if not table then return "nil" end
if not tables then tables = {this_table = tbl} end
@ -141,11 +140,11 @@ function table.tostring(tbl, shallow, list_length_lim, depth_limit, tables, dept
if val_type == "string" then
str = str..initial_string..parse_index(i).." = \""..v.."\","
elseif val_type == "table" and (not shallow) then
local contains = table.contains(tables, v)
local contains = table_contains(tables, v)
--to avoid infinite loops, make sure that the table has not been tostringed yet
if not contains then
tables[i] = v
str = str..initial_string..parse_index(i).." = "..table.tostring(v, shallow, list_length_lim, depth_limit, tables, depth)..","
str = str..initial_string..parse_index(i).." = "..Guns4d.table.tostring(v, shallow, list_length_lim, depth_limit, tables, depth)..","
else
str = str..initial_string..parse_index(i).." = "..tostring(v).." (index: '"..tostring(contains).."'),"
end
@ -158,7 +157,7 @@ function table.tostring(tbl, shallow, list_length_lim, depth_limit, tables, dept
end
return str..string.sub(initial_string, 1, -5).."}"
end
function table.tostring_structure_only(tbl, shallow, tables, depth)
--[[function Guns4d.table.tostring_structure_only(tbl, shallow, tables, depth)
--create a list of tables that have been tostringed in this chain
if not table then return "nil" end
if not tables then tables = {this_table = tbl} end
@ -183,11 +182,11 @@ function table.tostring_structure_only(tbl, shallow, tables, depth)
iterations = iterations + 1
local val_type = type(v)
if val_type == "table" then
local contains = table.contains(tables, v)
local contains = table_contains(tables, v)
--to avoid infinite loops, make sure that the table has not been tostringed yet
if not contains then
tables[parse_index(i).." ["..tostring(v).."]"] = v
str = str..initial_string..parse_index(i).."("..tostring(v)..") = "..table.tostring_structure_only(v, shallow, tables, depth)..","
str = str..initial_string..parse_index(i).."("..tostring(v)..") = "..Guns4d.table.tostring_structure_only(v, shallow, tables, depth)..","
elseif type(v) == "table" then
str = str..initial_string..parse_index(i).." = "..tostring(v)
else
@ -201,14 +200,14 @@ function table.tostring_structure_only(tbl, shallow, tables, depth)
return "table too long"
end
return "{"..str..string.sub(initial_string, 1, -5).."}"
end
end]]
--replace fields (and fill sub-tables) in `tbl` with elements in `replacement`. Recursively iterates all sub-tables. use property __overfill=true for subtables that don't want to be overfilled.
function table.fill(tbl, replacement, preserve_reference, indexed_tables)
function Guns4d.table.fill(tbl, replacement, preserve_reference, indexed_tables)
if not indexed_tables then indexed_tables = {} end --store tables to prevent circular referencing
local new_table = tbl
if not preserve_reference then
new_table = table.deep_copy(tbl)
new_table = Guns4d.table.deep_copy(tbl)
end
for i, v in pairs(replacement) do
if new_table[i] then
@ -218,13 +217,13 @@ function table.fill(tbl, replacement, preserve_reference, indexed_tables)
if not indexed_tables[v] then
if not new_table[i].__overfill then
indexed_tables[v] = true
new_table[i] = table.fill(tbl[i], replacement[i], false, indexed_tables)
new_table[i] = Guns4d.table.fill(tbl[i], replacement[i], false, indexed_tables)
else --if overfill is present, we don't want to preserve the old table.
new_table[i] = table.deep_copy(replacement[i])
new_table[i] = Guns4d.table.deep_copy(replacement[i])
end
end
elseif not replacement[i].__no_copy then
new_table[i] = table.deep_copy(replacement[i])
new_table[i] = Guns4d.table.deep_copy(replacement[i])
else
new_table[i] = replacement[i]
end
@ -239,7 +238,7 @@ function table.fill(tbl, replacement, preserve_reference, indexed_tables)
return new_table
end
--for class based OOP, ensure values containing a table in btbl are tables in a_tbl- instantiate, but do not fill.
function table.instantiate_struct(tbl, btbl, indexed_tables)
--[[function table.instantiate_struct(tbl, btbl, indexed_tables)
if not indexed_tables then indexed_tables = {} end --store tables to prevent circular referencing
for i, v in pairs(btbl) do
if type(v) == "table" and not indexed_tables[v] then
@ -252,8 +251,8 @@ function table.instantiate_struct(tbl, btbl, indexed_tables)
end
end
return tbl
end
function table.shallow_copy(t)
end]]
function Guns4d.table.shallow_copy(t)
local new_table = {}
for i, v in pairs(t) do
new_table[i] = v
@ -261,14 +260,15 @@ function table.shallow_copy(t)
return new_table
end
---other helpers
--@section other
--for the following function only:
--for license see the link on the next line (direct permission was granted).
--https://github.com/3dreamengine/3DreamEngine
function rltv_point_to_hud(pos, fov, aspect)
function Guns4d.rltv_point_to_hud(pos, fov, aspect)
local n = .1 --near
local f = 1000 --far
--wherever you are
--I WILL FOLLOWWWW YOU
local scale = math.tan(fov * math.pi / 360)
local r = scale * n * aspect
local t = scale * n

View File

@ -2,4 +2,4 @@ name = guns4d
title = guns4d
description = Adds a library for 3d guns
author = FatalError42O
depends = mtul_b3d
depends = mtul_b3d, mtul_cpml, mtul_filesystem

View File

@ -1,3 +1,8 @@
--- implements tools for quickly playing audio.
-- @script play_sound
local sqrt = math.sqrt
--simple specification for playing a sound in relation to an action, acts as a layer of minetest.play_sound
--"gsp" guns4d-sound-spec
--first person for the gun holder, third person for everyone else. If first not present, third will be used.
@ -5,8 +10,6 @@
--example:
--[[
additional properties
first_person = playername,
second_person = playername
sounds = { --weighted randoms:
fire_fp = .5.
fire_fp_2 = .2.
@ -19,29 +22,66 @@
gain = 1, --format for pitch and gain is interchangable.
min_hear_distance = 20, --this is for distant gunshots, for example. Entirely optional. Cannot be used with to_player
exclude_player
to_player
--when present it automatically plays positionless audio, as this is for first person effects.
]]
local sqrt = math.sqrt
function Guns4d.play_sounds(...)
local args = {...}
--- defines a sound.
-- This is passed to `minetest.sound_play` as a [ sound parameter table](https://github.com/minetest/minetest/blob/master/doc/lua_api.md#sound-parameter-table)
-- however has the following changed or guns4d specific parameters.
-- @field min_hear_distance this is useful if you wish to play a sound which has a "far" sound, such as distant gunshots. incompatible `with to_player`
-- @field sounds a @{misc_helpers.weighted_randoms| weighted_randoms table} for randomly selecting sounds. The output will overwrite the `sound` field.
-- @field to_player 4dguns changes `to_player` so it only plays positionless audio (as it is only intended for first person audio)
-- @table guns4d_soundspec
local function handle_min_max(tbl)
return tbl.min+(math.random()*(tbl.max-tbl.min))
end
--- allows you to play one or more sounds with more complex features, so sounds can be easily coded for guns without the need for functions.
-- WARNING: this function modifies the tables passed to it, use `Guns4d.table.shallow_copy()` for guns4d_soundspecs
-- @param sound_specs a @{guns4d_soundspec} or a list of @{guns4d_soundspec}s indexed my number. Also allows for shared fields. Example:
-- {
-- to_player = "singeplayer",
-- min_distance = 100, --soundspec_to_play1 & soundspec_to_play2 share this parameter (as well as the to_player)
-- soundspec_to_play1,
-- soundspec_to_play2
-- }
-- @return out a list of Minetest sound handles [insert link] (in the order they came)
-- @function Guns4d.play_sounds
function Guns4d.play_sounds(soundspecs_list)
--print(dump(soundspecs_list))
--support a list of sounds to play
if not soundspecs_list[1] then --turn into iteratable format.
soundspecs_list = {soundspecs_list}
end
local applied = {}
--all fields that aren't numbers will be copied over, allowing you to set fields across all sounds (i.e. pos, target player.), if already present it will remain the same.
for field, v in pairs(soundspecs_list) do
if type(field) ~= "number" then
for _, spec in ipairs(soundspecs_list) do
if not spec[field] then
spec[field] = v
end
end
soundspecs_list[field] = nil --so it isn't iterated
end
end
--print(dump(soundspecs_list))
local out = {}
assert(args[1], "no arguments provided")
for i, soundspec in pairs(args) do
for i, soundspec in pairs(soundspecs_list) do
assert(not (soundspec.to_player and soundspec.min_distance), "in argument '"..tostring(i).."' `min_distance` and `to_player` are incompatible parameters.")
local sound
local sound = soundspec.sound
local outval
if type(soundspec.pitch) == "table" then
local pitch = soundspec.pitch
soundspec.pitch = pitch.min+(math.random()*(pitch.max-pitch.min))
for i, v in pairs(soundspec) do
if type(v) == "table" and v.min then
soundspec[i]=handle_min_max(v)
end
end
if type(soundspec.gain) == "table" then
local gain = soundspec.gain
soundspec.pitch = gain.min+(math.random()*(gain.max-gain.min))
end
if type(soundspec.sound) == "table" then
sound = math.weighted_randoms(soundspec.sound)
if type(sound) == "table" then
sound = Guns4d.math.weighted_randoms(sound)
end
assert(sound, "no sound found")
if soundspec.to_player then soundspec.pos = nil end
if soundspec.min_hear_distance then
local exclude_player_ref
@ -49,20 +89,28 @@ function Guns4d.play_sounds(...)
exclude_player_ref = minetest.get_player_by_name(soundspec.exclude_player)
end
for _, player in pairs(minetest.get_connected_players()) do
soundspec.sound = nil
local pos = player:get_pos()
local dist = sqrt( sqrt(pos.x^2+pos.y^2)^2 +pos.z^2 )
if (dist > soundspec.min_distance) and (player~=exclude_player_ref) then
local dist = sqrt( sqrt((pos.x-soundspec.pos.x)^2+(pos.y-soundspec.pos.y)^2)^2 + (pos.z-soundspec.pos.z)^2)
if (dist > soundspec.min_hear_distance) and (player~=exclude_player_ref) then
soundspec.exclude_player = nil --not needed anyway because we can just not play it for this player.
soundspec.to_player = player:get_player_name()
outval = minetest.play_sound(sound, soundspec)
outval = minetest.sound_play(sound, soundspec)
end
end
else
outval = minetest.play_sound(sound, soundspec)
soundspec.sound = nil
outval = minetest.sound_play(sound, soundspec)
end
out[i] = outval
end
return out
end
--- stops a list of sounds
-- @param handle_list a list of minetest sound handles to stop, this is the returned output of @{guns4d.play_sounds
-- @function Guns4d.stop_sounds
function Guns4d.stop_sounds(handle_list)
for i, v in pairs(handle_list) do
minetest.sound_stop(v)
end
end

View File

@ -0,0 +1,3 @@
ar_firing.ogg:
by SuperPhat on freesound.org
License: Creative Commons 0

View File

@ -0,0 +1,3 @@
ar_firing_far.ogg:
by (unknown) on opengameart.org (https://opengameart.org/content/the-free-firearm-sound-library)
License: Creative Commons

View File

@ -0,0 +1,3 @@
ar_mag_store.ogg:
by serøtōnin on freesound.org
License: Creative Commons

View File

BIN
sounds/ar_firing.ogg Normal file

Binary file not shown.

BIN
sounds/ar_firing_far.ogg Normal file

Binary file not shown.

BIN
sounds/ar_mag_load.ogg Normal file

Binary file not shown.

BIN
sounds/ar_mag_store.ogg Normal file

Binary file not shown.

BIN
sounds/ar_mag_unload.ogg Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
Files contained within this folder each have a respective license, their licenses are named in the following format:
"LICENSE [file].txt"
example:
for ar_firing.ogg the license would be named
"LICENSE ar_firing.ogg.txt"