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
function bcheck.number(val)
if (type(val)~="number") then
error("invalid argument: must be a number", 3)
function bcheck.number(val, errlev)
if (type(val)~="number" or val~=val) then
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

View file

@ -165,6 +165,7 @@ local check_tile_idx = bcheck.tile_idx
local check_sprite_idx = bcheck.sprite_idx
local check_player_idx = bcheck.player_idx
local check_sound_idx = bcheck.sound_idx
local check_type = bcheck.type
-- Will contain [<label>]=number mappings after CON translation.
local D = { true }
@ -793,7 +794,15 @@ function _switch(swtab, testval, aci,pli,dist)
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)
local cstr = bcheck.quote_idx(qnum)
ffiC.minitext_(x, y, cstr, shade, pal, 2+8+16)

View file

@ -291,6 +291,7 @@ __attribute__((packed)) struct {
uint8_t palette;
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
// a metatype later.
@ -1147,6 +1148,18 @@ local 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 = {
__index = {
-- CON-like addammo/addweapon, but without the non-local control flow
@ -1188,9 +1201,64 @@ local player_mt = {
p.look_ang = n
end,
--- Not fully specified, off-limits to users. Might disappear, change
--- signature, etc...
-- TODO: need to think about external API: 0-255 based? 0-1 based?
-- External, improved 'palfrom'.
-- <speed>: possibly fractional speed of tint fading, in pals.f decrements per gametic.
-- 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)
local pals = p._pals
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
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
++require++d that has not yet finished loading (that is, the inclusion chain
contains a loop). Instead, an error is raised.
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
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
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
Lunatic has a special mechanism to mark variables that represent some
``persistent'' state and whose values should be stored in savegames. If such
variables are desired, they must initialized between the `module` call in a Lua
file and a closing `require("end_gamevars")`. These variables may also be *`local`*.
Lunatic has a special mechanism to mark variables that represent persistent
state and whose values should be stored in savegames. If such variables are
desired, they must be initialized between the `module` call in a Lua file and a
closing `require("end_gamevars")`. These variables may also be *`local`*.
[icon="icons/din_w_collapse.png"]
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` 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`
===== `g_tile`

View file

@ -528,6 +528,70 @@ gameevent
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
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.
local tag = tag
local Q = 1200
gameevent{"JUMP", actor.FLAGS.chain_beg,
function(aci, pli)
local ps = player[pli]
@ -85,8 +83,6 @@ gameevent
"DISPLAYREST",
function()
con._definequote(Q, string.format("jumped %d times", ournumjumps))
-- NOTE: uses INTERNAL interface, don't copy!
con._minitext(160, 10, Q, 0,0)
con.minitext(160, 10, string.format("jumped %d times", ournumjumps))
end
}

View file

@ -4385,7 +4385,32 @@ void P_ProcessInput(int32_t snum)
}
if (p->pals.f > 0)
{
#ifndef LUNATIC
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)
{

View file

@ -230,6 +230,8 @@ typedef struct {
palette_t pals;
#ifdef LUNATIC
int8_t palsfadespeed, palsfadenext, palsfadeprio, padding2_;
// 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.
struct { int32_t idx; } wa;

View file

@ -673,6 +673,14 @@ void P_RandomSpawnPoint(int32_t snum)
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)
{
vec3_t tmpvect;
@ -708,7 +716,6 @@ void P_ResetPlayer(int32_t snum)
pl->wackedbyactor = -1;
pl->inv_amount[GET_SHIELD] = g_startArmorAmount;
pl->dead_flag = 0;
pl->pals.f = 0;
pl->footprintcount = 0;
pl->weapreccnt = 0;
pl->fta = 0;
@ -718,6 +725,8 @@ void P_ResetPlayer(int32_t snum)
pl->runspeed = g_playerFriction;
pl->falling_counter = 0;
P_ResetTintFade(pl);
actor[pl->i].extra = -1;
actor[pl->i].owner = pl->i;
@ -897,7 +906,6 @@ static void resetprestat(int32_t snum,int32_t g)
p->hbomb_on = 0;
p->cheat_phase = 0;
p->pals.f = 0;
p->toggle_key_flag = 0;
p->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->weapon_pos = 6;
P_ResetTintFade(p);
if ((PWEAPON(snum, p->curr_weapon, WorksLike) == PISTOL_WEAPON) &&
(PWEAPON(snum, p->curr_weapon, Reload) > PWEAPON(snum, p->curr_weapon, TotalTime)))
p->kickback_pic = PWEAPON(snum, p->curr_weapon, TotalTime);