Lunatic: player method fadecol(), an improved palfrom.

Also, an external 'minitext' with optional shade and pal. args and
documentation for ps:padecol().

git-svn-id: https://svn.eduke32.com/eduke32@3893 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-06-20 18:31:50 +00:00
parent e8c9bf044f
commit 5db04f585e
9 changed files with 234 additions and 21 deletions

View file

@ -98,9 +98,15 @@ function bcheck.top_level(funcname)
end end
end end
function bcheck.number(val) function bcheck.number(val, errlev)
if (type(val)~="number") then if (type(val)~="number" or val~=val) then
error("invalid argument: must be a number", 3) error("invalid argument: must be a non-NaN number", errlev or 3)
end
end
function bcheck.type(val, typestr)
if (type(val)~=typestr) then
error("invalid argument: must be a "..typestr, 3)
end end
end end

View file

@ -165,6 +165,7 @@ local check_tile_idx = bcheck.tile_idx
local check_sprite_idx = bcheck.sprite_idx local check_sprite_idx = bcheck.sprite_idx
local check_player_idx = bcheck.player_idx local check_player_idx = bcheck.player_idx
local check_sound_idx = bcheck.sound_idx local check_sound_idx = bcheck.sound_idx
local check_type = bcheck.type
-- Will contain [<label>]=number mappings after CON translation. -- Will contain [<label>]=number mappings after CON translation.
local D = { true } local D = { true }
@ -793,7 +794,15 @@ function _switch(swtab, testval, aci,pli,dist)
end end
-- text rendering --== Text rendering ==--
-- For external use. NOTE: <pal> comes before <shade>.
function minitext(x, y, str, pal, shade)
check_type(str, "string")
ffiC.minitext_(x, y, str, shade or 0, pal or 0, 2+8+16)
end
-- For CON only.
function _minitext(x, y, qnum, shade, pal) function _minitext(x, y, qnum, shade, pal)
local cstr = bcheck.quote_idx(qnum) local cstr = bcheck.quote_idx(qnum)
ffiC.minitext_(x, y, cstr, shade, pal, 2+8+16) ffiC.minitext_(x, y, cstr, shade, pal, 2+8+16)

View file

@ -291,6 +291,7 @@ __attribute__((packed)) struct {
uint8_t palette; uint8_t palette;
palette_t _pals; palette_t _pals;
int8_t _palsfadespeed, _palsfadenext, _palsfadeprio, _padding2;
// NOTE: In C, the struct type has no name. We only have it here to define // NOTE: In C, the struct type has no name. We only have it here to define
// a metatype later. // a metatype later.
@ -1147,6 +1148,18 @@ local weaponaccess_mt = {
} }
ffi.metatype("weaponaccess_t", weaponaccess_mt) ffi.metatype("weaponaccess_t", weaponaccess_mt)
local function clamp(num, min, max)
return num < min and min
or num > max and max
or num
end
local function clamp0to1(num)
check_number(num, 4)
return clamp(num, 0, 1)
end
local player_mt = { local player_mt = {
__index = { __index = {
-- CON-like addammo/addweapon, but without the non-local control flow -- CON-like addammo/addweapon, but without the non-local control flow
@ -1188,9 +1201,64 @@ local player_mt = {
p.look_ang = n p.look_ang = n
end, end,
--- Not fully specified, off-limits to users. Might disappear, change -- External, improved 'palfrom'.
--- signature, etc... -- <speed>: possibly fractional speed of tint fading, in pals.f decrements per gametic.
-- TODO: need to think about external API: 0-255 based? 0-1 based? -- XXX: exposes internals.
-- <prio>: a value from -128 to 127, higher ones trump lower or equal ones
fadecol = function(p, fadefrac, r, g, b, speed, prio)
-- Validate inargs: clamp f,r,g,b to [0 .. 1] first and multiply by
-- 63 for the internal handling.
fadefrac = clamp0to1(fadefrac)*63
-- NOTE: a fadefrac of now <1 is allowed, e.g. for clearing the tint.
r = clamp0to1(r)*63
g = clamp0to1(g)*63
b = clamp0to1(b)*63
if (speed ~= nil) then
check_number(speed)
-- Clamp to sensible values; the speed is stored in an int8_t
-- (see below).
speed = clamp(speed, 1/128, 127)
else
speed = 1
end
if (prio ~= nil) then
check_number(prio)
if (not (prio >= -128 and prio < 127)) then
error("invalid argument #6 (priority): must be in [-128 .. 127]", 2)
end
else
prio = 0
end
-- Check if a currently active tint has higher priority.
if (p._pals.f > 0 and p._palsfadeprio > prio) then
return
end
-- The passed tint can be applied now.
p:_palfrom(fadefrac, r, g, b)
p._palsfadeprio = prio
-- Calculate .palsfade{speed,next}
if (speed >= 1) then
-- Will round to the nearest integer ("banker's
-- rounding"). NOTE: This is not correct for all numbers, see
-- http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
p._palsfadespeed = speed + 0.5
p._palsfadenext = 0
else
-- NOTE: Values that round to 0 have are equivalent behavior to
-- passing a <speed> of 1.
local negspeedrecip = -((1/speed) + 0.5) -- [-128.5 .. 1/127+0.5]
p._palsfadespeed = negspeedrecip
p._palsfadenext = negspeedrecip
end
end,
-- INTERNAL and CON-only.
_palfrom = function(p, f, r,g,b) _palfrom = function(p, f, r,g,b)
local pals = p._pals local pals = p._pals
pals.f = f pals.f = f

View file

@ -138,13 +138,13 @@ dot contained in it with a directory separator. Then, it looks for a file with
that base name suffixed with `.lua` in the EDuke32 search path (virtual file that base name suffixed with `.lua` in the EDuke32 search path (virtual file
system, GRP, ZIP). Using directory separators directly is not allowed. system, GRP, ZIP). Using directory separators directly is not allowed.
The loaded module is ``protected'' so that write accesses to its table yield The loaded module is protected so that write accesses to its table yield
errors. Unlike Lua, our `module` does not return *true* when a module is errors. Unlike Lua, our `module` does not return *true* when a module is
++require++d that has not yet finished loading (that is, the inclusion chain ++require++d that has not yet finished loading (that is, the inclusion chain
contains a loop). Instead, an error is raised. contains a loop). Instead, an error is raised.
Issuing `require` for `'end_gamevars'` has a special meaning that is described Issuing `require` for ```end_gamevars`'' has a special meaning that is described
below. A `require` for `'CON.DEFS'` returns a table mapping labels ++define++d from below. A `require` for ```CON.DEFS`'' returns a table mapping labels ++define++d from
CON to their values, except for `NO`. CON to their values, except for `NO`.
@ -157,10 +157,10 @@ one call to `module`, which (if there is one) *must* be called at file scope.
===== Game variables ===== Game variables
Lunatic has a special mechanism to mark variables that represent some Lunatic has a special mechanism to mark variables that represent persistent
``persistent'' state and whose values should be stored in savegames. If such state and whose values should be stored in savegames. If such variables are
variables are desired, they must initialized between the `module` call in a Lua desired, they must be initialized between the `module` call in a Lua file and a
file and a closing `require("end_gamevars")`. These variables may also be *`local`*. closing `require("end_gamevars")`. These variables may also be *`local`*.
[icon="icons/din_w_collapse.png"] [icon="icons/din_w_collapse.png"]
CAUTION: A game variable must contain a non-nil value at any time. Otherwise, CAUTION: A game variable must contain a non-nil value at any time. Otherwise,
@ -728,6 +728,39 @@ Contains symbolic names of values applicable to <<gameactor,`gameactor`>>'s
===== `player` ===== `player`
===== `player` methods
`ps:fadecol(fadefrac, r, g, b [, speed [, prio]])`::
Initiates a tinting that linearly fades over time and is blended with the whole
screen contents whenever player `ps`'s view is displayed.footnote:[The behavior
is unspecified should more than one player's view be displayed at one time.]
The first argument `fadefrac` specifies the starting blending coefficient; `r`,
`g` and `b` specify the intensities of the red, green and blue color
components, respectively.
+
Both `fadefrac` and the component intensities are first clamped to the range
[0.0 .. 1.0]. The resulting values are interpreted as a fraction, 0.0 meaning
no blending/no color and 1.0 meaning full blending/full
color.footnote:[Currently, for implementation reasons, a value of 1.0 results
in only _almost_ full blending or presence of the specified color component.]
+
The fourth, optional argument `speed` controls the rate at which the tinting
diminishes. At a value of `1` (the default), a tint with a `fadefrac` of 0.5
finishes in approximately one second.
+
The last, optional argument `prio` must be an integer in the range [`-128`
.. `127`], the default being `0`. When a `fadecol` is issued in the presence of
another tint fading in progress, and the `prio` given by the arriving `fadecol`
is greater or equal than the `prio` of the ongoing one, the latter is canceled
and the arriving fading is initiated in its place. (There is no support for
tint fades that overlap in time.)
+
[icon="icons/din_w_crushing.png"]
CAUTION: If Lunatic code that uses `fadecol` is loaded together with CON code
that writes to the player's `pals` members directly at any point, the behavior
is undefined.
===== `projectile` ===== `projectile`
===== `g_tile` ===== `g_tile`

View file

@ -528,6 +528,70 @@ gameevent
end end
} }
local function testbit(num, b)
return bit.band(num,b)~=0 and 1 or 0
end
local FADE_SPEED = {
[gv.KNEE_WEAPON] = 1/2.5,
1/128,
1/5,
1/3,
1/2,
1, -- pipebomb: ~1 sec
2,
3,
5,
127, -- freezer; such a fast fade is not visible, but it clears any
-- existing one (if of higher priority)
[gv.GROW_WEAPON] = 9.9, -- test banker's rouding -- should be like 10
}
-- Test player[]:fadecol(), a better palfrom.
gameevent
{
"CHANGEWEAPON",
function (aci, pli)
local ps = player[pli]
local neww, curw = gv.g_RETURN, ps.curr_weapon
local r, g, b = testbit(neww, 1), testbit(neww, 2), testbit(neww, 4)
local speed = FADE_SPEED[neww] or 1
player[pli]:fadecol(0.5, r, g, b, speed, neww)
end
}
-- Time the above p:fadecol() test for verification of the <speed> argument.
local last_f, last_t = 0, 0
local last_secs = nil
gameevent
{
"DISPLAYREST",
function(aci, pli)
local ps = player[pli]
-- WARNING: This function uses INTERNAL interfaces.
local curf = ps._pals.f
if (curf > last_f) then
-- Starting a tint
last_secs = nil
last_f = curf
last_t = gv.getticks()
elseif (last_t > 0 and curf==0) then
-- Fade has ended
last_secs = (gv.getticks()-last_t)/1000
last_f, last_t = 0, 0
end
if (last_secs ~= nil) then
con.minitext(10, 10, string.format("Last fade time: %.03f s (%.03f gametics)",
last_secs, last_secs*30))
end
end,
}
printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines
local bittest = require "test.test_bitar" local bittest = require "test.test_bitar"

View file

@ -37,8 +37,6 @@ require "end_gamevars"
-- refer to locals defined prior to the gamevar section in it. -- refer to locals defined prior to the gamevar section in it.
local tag = tag local tag = tag
local Q = 1200
gameevent{"JUMP", actor.FLAGS.chain_beg, gameevent{"JUMP", actor.FLAGS.chain_beg,
function(aci, pli) function(aci, pli)
local ps = player[pli] local ps = player[pli]
@ -85,8 +83,6 @@ gameevent
"DISPLAYREST", "DISPLAYREST",
function() function()
con._definequote(Q, string.format("jumped %d times", ournumjumps)) con.minitext(160, 10, string.format("jumped %d times", ournumjumps))
-- NOTE: uses INTERNAL interface, don't copy!
con._minitext(160, 10, Q, 0,0)
end end
} }

View file

@ -4385,7 +4385,32 @@ void P_ProcessInput(int32_t snum)
} }
if (p->pals.f > 0) if (p->pals.f > 0)
{
#ifndef LUNATIC
p->pals.f--; p->pals.f--;
#else
if (p->palsfadespeed > 0)
{
// <palsfadespeed> is the tint fade speed is in
// decrements/P_ProcessInput() calls.
p->pals.f = max(p->pals.f - p->palsfadespeed, 0);
}
else
{
// <palsfadespeed> is a negated count of how many times we
// (P_ProcessInput()) should be called before decrementing the tint
// fading by one. <palsfadenext> is the live counter.
if (p->palsfadenext < 0)
p->palsfadenext++;
if (p->palsfadenext == 0)
{
p->palsfadenext = p->palsfadespeed;
p->pals.f--;
}
}
#endif
}
if (p->fta > 0 && --p->fta == 0) if (p->fta > 0 && --p->fta == 0)
{ {

View file

@ -230,6 +230,8 @@ typedef struct {
palette_t pals; palette_t pals;
#ifdef LUNATIC #ifdef LUNATIC
int8_t palsfadespeed, palsfadenext, palsfadeprio, padding2_;
// The player index. Always valid since we have no loose DukePlayer_t's // The player index. Always valid since we have no loose DukePlayer_t's
// anywhere (like with spritetype_t): g_player[i].ps->wa.idx == i. // anywhere (like with spritetype_t): g_player[i].ps->wa.idx == i.
struct { int32_t idx; } wa; struct { int32_t idx; } wa;

View file

@ -673,6 +673,14 @@ void P_RandomSpawnPoint(int32_t snum)
sprite[p->i].cstat = 1+256; sprite[p->i].cstat = 1+256;
} }
static inline void P_ResetTintFade(DukePlayer_t *ps)
{
ps->pals.f = 0;
#ifdef LUNATIC
ps->palsfadeprio = 0;
#endif
}
void P_ResetPlayer(int32_t snum) void P_ResetPlayer(int32_t snum)
{ {
vec3_t tmpvect; vec3_t tmpvect;
@ -708,7 +716,6 @@ void P_ResetPlayer(int32_t snum)
pl->wackedbyactor = -1; pl->wackedbyactor = -1;
pl->inv_amount[GET_SHIELD] = g_startArmorAmount; pl->inv_amount[GET_SHIELD] = g_startArmorAmount;
pl->dead_flag = 0; pl->dead_flag = 0;
pl->pals.f = 0;
pl->footprintcount = 0; pl->footprintcount = 0;
pl->weapreccnt = 0; pl->weapreccnt = 0;
pl->fta = 0; pl->fta = 0;
@ -718,6 +725,8 @@ void P_ResetPlayer(int32_t snum)
pl->runspeed = g_playerFriction; pl->runspeed = g_playerFriction;
pl->falling_counter = 0; pl->falling_counter = 0;
P_ResetTintFade(pl);
actor[pl->i].extra = -1; actor[pl->i].extra = -1;
actor[pl->i].owner = pl->i; actor[pl->i].owner = pl->i;
@ -897,7 +906,6 @@ static void resetprestat(int32_t snum,int32_t g)
p->hbomb_on = 0; p->hbomb_on = 0;
p->cheat_phase = 0; p->cheat_phase = 0;
p->pals.f = 0;
p->toggle_key_flag = 0; p->toggle_key_flag = 0;
p->secret_rooms = 0; p->secret_rooms = 0;
p->max_secret_rooms = 0; p->max_secret_rooms = 0;
@ -906,6 +914,8 @@ static void resetprestat(int32_t snum,int32_t g)
p->lastrandomspot = 0; p->lastrandomspot = 0;
p->weapon_pos = 6; p->weapon_pos = 6;
P_ResetTintFade(p);
if ((PWEAPON(snum, p->curr_weapon, WorksLike) == PISTOL_WEAPON) && if ((PWEAPON(snum, p->curr_weapon, WorksLike) == PISTOL_WEAPON) &&
(PWEAPON(snum, p->curr_weapon, Reload) > PWEAPON(snum, p->curr_weapon, TotalTime))) (PWEAPON(snum, p->curr_weapon, Reload) > PWEAPON(snum, p->curr_weapon, TotalTime)))
p->kickback_pic = PWEAPON(snum, p->curr_weapon, TotalTime); p->kickback_pic = PWEAPON(snum, p->curr_weapon, TotalTime);