From ba0203169ed4f0c06a97f842871f45511ce54487 Mon Sep 17 00:00:00 2001 From: Mathew Velasquez Date: Thu, 28 Jan 2016 23:32:29 -0500 Subject: [PATCH] Added obscured/scratched window puzzle --- binding-mri/oneshot-binding.cpp | 15 +++++ binding-mri/sprite-binding.cpp | 2 + mkxp.pro | 1 + scripts/Ed_Message.rb | 4 +- scripts/Game_Map.rb | 28 +++++++++- scripts/Game_Temp.rb | 2 + scripts/Interpreter 1.rb | 10 ++++ scripts/Puzzle_Film.rb | 14 +++++ scripts/Script.rb | 15 +++-- scripts/Spriteset_Map.rb | 9 ++- scripts/Window_Item.rb | 11 ++++ scripts/Window_Message.rb | 2 +- shader/obscured.frag | 12 ++++ src/eventthread.cpp | 15 +++++ src/graphics.cpp | 56 ++++++++++++++----- src/graphics.h | 5 +- src/oneshot.cpp | 97 +++++++++++++++++++++++++++++++++ src/oneshot.h | 9 +++ src/shader.cpp | 15 +++++ src/shader.h | 13 +++++ src/sprite.cpp | 16 +++++- src/sprite.h | 1 + 22 files changed, 324 insertions(+), 28 deletions(-) create mode 100644 scripts/Puzzle_Film.rb create mode 100644 shader/obscured.frag diff --git a/binding-mri/oneshot-binding.cpp b/binding-mri/oneshot-binding.cpp index 8359405..c9b2e04 100644 --- a/binding-mri/oneshot-binding.cpp +++ b/binding-mri/oneshot-binding.cpp @@ -26,6 +26,19 @@ RB_METHOD(oneshotMsgBox) return rb_bool_new(shState->oneshot().msgbox(type, body, title)); } +RB_METHOD(oneshotResetObscured) +{ + RB_UNUSED_PARAM; + shState->oneshot().resetObscured(); + return Qnil; +} + +RB_METHOD(oneshotObscuredCleared) +{ + RB_UNUSED_PARAM; + return shState->oneshot().obscuredCleared() ? Qtrue : Qfalse; +} + void oneshotBindingInit() { VALUE module = rb_define_module("Oneshot"); @@ -43,4 +56,6 @@ void oneshotBindingInit() //Functions _rb_define_module_function(module, "set_yes_no", oneshotSetYesNo); _rb_define_module_function(module, "msgbox", oneshotMsgBox); + _rb_define_module_function(module, "reset_obscured", oneshotResetObscured); + _rb_define_module_function(module, "obscured_cleared?", oneshotObscuredCleared); } diff --git a/binding-mri/sprite-binding.cpp b/binding-mri/sprite-binding.cpp index dc7f902..ee95303 100644 --- a/binding-mri/sprite-binding.cpp +++ b/binding-mri/sprite-binding.cpp @@ -69,6 +69,7 @@ DEF_PROP_F(Sprite, Angle) DEF_PROP_F(Sprite, WavePhase) DEF_PROP_B(Sprite, Mirror) +DEF_PROP_B(Sprite, Obscured) RB_METHOD(spriteWidth) { @@ -121,6 +122,7 @@ spriteBindingInit() INIT_PROP_BIND( Sprite, BlendType, "blend_type" ); INIT_PROP_BIND( Sprite, Color, "color" ); INIT_PROP_BIND( Sprite, Tone, "tone" ); + INIT_PROP_BIND( Sprite, Obscured, "obscured" ); if (rgssVer >= 2) { diff --git a/mkxp.pro b/mkxp.pro index de82610..ccab387 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -242,6 +242,7 @@ EMBED = \ shader/simpleAlpha.frag \ shader/simpleAlphaUni.frag \ shader/flashMap.frag \ + shader/obscured.frag \ shader/minimal.vert \ shader/simple.vert \ shader/simpleColor.vert \ diff --git a/scripts/Ed_Message.rb b/scripts/Ed_Message.rb index 683debf..0e02e00 100644 --- a/scripts/Ed_Message.rb +++ b/scripts/Ed_Message.rb @@ -7,8 +7,8 @@ class Ed_Message def initialize @viewport = Viewport.new(0, 0, 640, 480) @sprite_bg = Sprite.new(@viewport) - @sprite_bg.bitmap = RPG::Cache.menu('ed') - @sprite_bg.blend_type = 2 + @sprite_bg.bitmap = Bitmap.new(640, 480) + @sprite_bg.bitmap.fill_rect(0, 0, 640, 480, Color.new(0, 0, 0, 128)) @sprite_text = Sprite.new(@viewport) @contents = Bitmap.new(640, HEIGHT) @sprite_text.bitmap = @contents diff --git a/scripts/Game_Map.rb b/scripts/Game_Map.rb index cd52efc..c2dd4f6 100644 --- a/scripts/Game_Map.rb +++ b/scripts/Game_Map.rb @@ -23,10 +23,13 @@ class Game_Map attr_accessor :battleback_name # battleback file name attr_accessor :display_x # display x-coordinate * 128 attr_accessor :display_y # display y-coordinate * 128 + attr_accessor :wrap_x # display x-coordinate * 128 for wrap + attr_accessor :wrap_y # display y-coordinate * 128 for wrap attr_accessor :need_refresh # refresh request flag attr_accessor :bg_name # bg file name attr_accessor :particles_type # particles name - attr_accessor :clamped_panorama # panorama is clamped? + attr_accessor :clamped_x # panorama is horizontally clamped? + attr_accessor :clamped_y # panorama is vertically clamped? attr_accessor :wrapping # map is wrapping? attr_accessor :ambient # ambient light attr_reader :passages # passage table @@ -39,10 +42,15 @@ class Game_Map #-------------------------------------------------------------------------- # * List of clamped panorama images #-------------------------------------------------------------------------- - CLAMPED_PANORAMAS = [ + CLAMPED = [ 'red', 'red_distort', ] + CLAMPED_X = [ + ] + CLAMPED_Y = [ + 'red_obsdesk', + ] #-------------------------------------------------------------------------- # * Object Initialization #-------------------------------------------------------------------------- @@ -85,6 +93,8 @@ class Game_Map # Initialize displayed coordinates @display_x = 0 @display_y = 0 + @wrap_x = 0 + @wrap_y = 0 # Clear refresh request flag @need_refresh = false # Set map event data @@ -114,7 +124,19 @@ class Game_Map # Clear particles @particles_type = nil # Unclamp panorama - @clamped_panorama = CLAMPED_PANORAMAS.include? @panorama_name + if CLAMPED.include? @panorama_name + @clamped_x = true + @clamped_y = true + elsif CLAMPED_X.include? @panorama_name + @clamped_x = true + @clamped_y = false + elsif CLAMPED_Y.include? @panorama_name + @clamped_x = false + @clamped_y = true + else + @clamped_x = false + @clamped_y = false + end # Unwrap map @wrapping = false # Full bright ambient light diff --git a/scripts/Game_Temp.rb b/scripts/Game_Temp.rb index e7be571..62cc598 100644 --- a/scripts/Game_Temp.rb +++ b/scripts/Game_Temp.rb @@ -54,6 +54,7 @@ class Game_Temp attr_accessor :debug_top_row # debug screen: for saving conditions attr_accessor :debug_index # debug screen: for saving conditions attr_accessor :footstep_sfx # current footstep sfx array + attr_accessor :filmsprite # film puzzle sprite #-------------------------------------------------------------------------- # * Object Initialization #-------------------------------------------------------------------------- @@ -104,5 +105,6 @@ class Game_Temp @debug_top_row = 0 @debug_index = 0 @footstep_sfx = nil + @filmsprite = nil end end diff --git a/scripts/Interpreter 1.rb b/scripts/Interpreter 1.rb index 24c21d9..82d75a6 100644 --- a/scripts/Interpreter 1.rb +++ b/scripts/Interpreter 1.rb @@ -6,6 +6,7 @@ #============================================================================== class Interpreter + @@chill_pill = false #-------------------------------------------------------------------------- # * Object Initialization # depth : nest depth @@ -129,6 +130,11 @@ class Interpreter Graphics.update @loop_count = 0 end + # Chill pill + if @@chill_pill + @@chill_pill = false + return + end # If map is different than event startup time if $game_map.map_id != @map_id # Change event ID to 0 @@ -306,4 +312,8 @@ class Interpreter end end end + # Chill out + def self.take_a_chill_pill + @@chill_pill = true + end end diff --git a/scripts/Puzzle_Film.rb b/scripts/Puzzle_Film.rb new file mode 100644 index 0000000..51de2c9 --- /dev/null +++ b/scripts/Puzzle_Film.rb @@ -0,0 +1,14 @@ +def film_puzzle_begin + Oneshot.reset_obscured + $game_temp.filmsprite.dispose if $game_temp.filmsprite + filmsprite = Sprite.new + filmsprite.bitmap = RPG::Cache.picture('numbersheet') + filmsprite.z = 9999 + filmsprite.obscured = true + $game_temp.filmsprite = filmsprite +end + +def film_puzzle_end + $game_temp.filmsprite.dispose if $game_temp.filmsprite + $game_temp.filmsprite = nil +end diff --git a/scripts/Script.rb b/scripts/Script.rb index 9f248d0..bd20b43 100644 --- a/scripts/Script.rb +++ b/scripts/Script.rb @@ -50,6 +50,13 @@ def has_lightbulb? $game_party.item_number(1) > 0 end +def button_pressed? + (1..18).each do |i| + return true if Input.trigger?(i) + end + return false +end + def enter_name $game_temp.name_calling = true end @@ -100,10 +107,6 @@ def clear_lights #$scene.clear_lights end -def clamp_panorama - $game_map.clamped_panorama = true -end - def wrap_map $game_map.wrapping = true end @@ -134,3 +137,7 @@ end def plight_update_timer Script.tmp_v1 = ((Time.now - $game_oneshot.plight_timer) / 60).to_i end + +def quit + $scene = nil +end diff --git a/scripts/Spriteset_Map.rb b/scripts/Spriteset_Map.rb index 7368e36..c447543 100644 --- a/scripts/Spriteset_Map.rb +++ b/scripts/Spriteset_Map.rb @@ -195,13 +195,16 @@ class Spriteset_Map @tilemap.oy = $game_map.display_y / 4 @tilemap.update # Update panorama plane - if $game_map.clamped_panorama + if $game_map.clamped_x x = ($game_player.real_x.to_f / (($game_map.width - 1) * 128)) * (@panorama.bitmap.width - 640) - y = ($game_player.real_y.to_f / (($game_map.height - 1) * 128)) * (@panorama.bitmap.height - 480) @panorama.ox = x < 0.0 ? 0.0 : x - @panorama.oy = y < 0.0 ? 0.0 : y else @panorama.ox = $game_map.display_x / 8 + end + if $game_map.clamped_y + y = ($game_player.real_y.to_f / (($game_map.height - 1) * 128)) * (@panorama.bitmap.height - 480) + @panorama.oy = y < 0.0 ? 0.0 : y + else @panorama.oy = $game_map.display_y / 8 end # Update fog plane diff --git a/scripts/Window_Item.rb b/scripts/Window_Item.rb index d03993e..b5459fc 100644 --- a/scripts/Window_Item.rb +++ b/scripts/Window_Item.rb @@ -139,6 +139,17 @@ class Window_Item < Window_Selectable $game_system.se_play($data_system.buzzer_se) return end + + # Run common event of item if valid + item = @data[@index] + if item.common_event_id > 0 + $game_temp.common_event_id = item.common_event_id + $game_system.se_play($data_system.decision_se) + @fade_out = true + return + end + + # Select or combine items item_a = $game_variables[1] item_b = @data[@index].id if item_a == item_b diff --git a/scripts/Window_Message.rb b/scripts/Window_Message.rb index d2bc141..0797d7f 100644 --- a/scripts/Window_Message.rb +++ b/scripts/Window_Message.rb @@ -194,7 +194,7 @@ class Window_Message < Window_Selectable @text.sub!(/\[([0-9]+)\]/, "") color = $1.to_i if color >= 0 and color <= 7 - self.contents.font.color = text_color(color) + self.contents.font.color = Window_Base.text_color(color) end # go to next text next diff --git a/shader/obscured.frag b/shader/obscured.frag new file mode 100644 index 0000000..84ad5b1 --- /dev/null +++ b/shader/obscured.frag @@ -0,0 +1,12 @@ + +uniform sampler2D texture; +uniform sampler2D obscured; + +varying vec2 v_texCoord; + +void main() +{ + vec4 color = texture2D(texture, v_texCoord); + color.a *= texture2D(obscured, v_texCoord).r; + gl_FragColor = color; +} diff --git a/src/eventthread.cpp b/src/eventthread.cpp index 3ea37fe..e175285 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -38,6 +38,8 @@ #include "al-util.h" #include "debugwriter.h" +#include "oneshot.h" + #include #include @@ -546,6 +548,19 @@ int EventThread::eventFilter(void *data, SDL_Event *event) Debug() << "SDL_APP_LOWMEMORY"; return 0; + /* Workaround for Windows pausing on drag */ + case SDL_WINDOWEVENT: + if (event->window.event == SDL_WINDOWEVENT_MOVED) + { + if (shState->rgssVersion > 0) + { + shState->oneshot().setWindowPos(event->window.data1, event->window.data2); + shState->graphics().update(false); + } + return 0; + } + return 1; + // case SDL_RENDER_TARGETS_RESET : // Debug() << "****** SDL_RENDER_TARGETS_RESET"; // return 0; diff --git a/src/graphics.cpp b/src/graphics.cpp index e8ef55e..651be5a 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -37,6 +37,7 @@ #include "intrulist.h" #include "binding.h" #include "debugwriter.h" +#include "oneshot.h" #include #include @@ -484,6 +485,8 @@ struct GraphicsPrivate * (disposed on reset) */ IntruList dispList; + TEX::ID obscuredTex; + GraphicsPrivate(RGSSThreadData *rtData) : scRes(DEF_SCREEN_W, DEF_SCREEN_H), scSize(scRes), @@ -516,6 +519,12 @@ struct GraphicsPrivate TEXFBO::linkFBO(transBuffer); fpsLimiter.resetFrameAdjust(); + + obscuredTex = TEX::gen(); + TEX::bind(obscuredTex); + TEX::setRepeat(false); + TEX::setSmooth(false); + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, 640, 480, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); } ~GraphicsPrivate() @@ -612,6 +621,12 @@ struct GraphicsPrivate void redrawScreen() { + if (shState->oneshot().obscuredDirty) + { + TEX::bind(obscuredTex); + TEX::uploadSubImage(0, 0, 640, 480, shState->oneshot().obscuredMap().data(), GL_LUMINANCE); + shState->oneshot().obscuredDirty = false; + } screen.composite(); GLMeta::blitBeginScreen(winSize); @@ -665,7 +680,7 @@ Graphics::~Graphics() delete p; } -void Graphics::update() +void Graphics::update(bool limitFps) { p->checkShutDownReset(); p->checkSyncLock(); @@ -673,23 +688,33 @@ void Graphics::update() if (p->frozen) return; - if (p->fpsLimiter.frameSkipRequired()) + if (limitFps) { - if (p->threadData->config.frameSkip) + if (p->fpsLimiter.frameSkipRequired()) { - /* Skip frame */ - p->fpsLimiter.delay(); - ++p->frameCount; - p->threadData->ethread->notifyFrame(); + if (p->threadData->config.frameSkip) + { + /* Skip frame */ + p->fpsLimiter.delay(); + ++p->frameCount; + p->threadData->ethread->notifyFrame(); - return; - } - else - { - /* Just reset frame adjust counter */ - p->fpsLimiter.resetFrameAdjust(); + return; + } + else + { + /* Just reset frame adjust counter */ + p->fpsLimiter.resetFrameAdjust(); + } } } + else + { + if (!p->fpsLimiter.frameSkipRequired()) + return; + } + + shState->oneshot().update(); p->checkResize(); p->redrawScreen(); @@ -1062,3 +1087,8 @@ void Graphics::remDisposable(Disposable *d) { p->dispList.remove(d->link); } + +const TEX::ID &Graphics::obscuredTex() const +{ + return p->obscuredTex; +} diff --git a/src/graphics.h b/src/graphics.h index aa30189..46f8204 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -23,6 +23,7 @@ #define GRAPHICS_H #include "util.h" +#include "gl-util.h" class Scene; class Bitmap; @@ -34,7 +35,7 @@ struct AtomicFlag; class Graphics { public: - void update(); + void update(bool limitFps = true); void freeze(); void transition(int duration = 8, const char *filename = "", @@ -70,6 +71,8 @@ public: void repaintWait(const AtomicFlag &exitCond, bool checkReset = true); + const TEX::ID &obscuredTex() const; + private: Graphics(RGSSThreadData *data); ~Graphics(); diff --git a/src/oneshot.cpp b/src/oneshot.cpp index 961a92e..27e4166 100644 --- a/src/oneshot.cpp +++ b/src/oneshot.cpp @@ -80,6 +80,12 @@ struct OneshotPrivate std::string txtYes; std::string txtNo; + //Alpha texture data for portions of window obscured by screen edges + int winX, winY; + bool winPosChanged; + std::vector obscuredMap; + bool obscuredCleared; + #if defined OS_LINUX //GTK+ void *libgtk; @@ -205,6 +211,11 @@ Oneshot::Oneshot(const RGSSThreadData &threadData) p = new OneshotPrivate(); p->window = threadData.window; p->savePath = threadData.config.commonDataPath.substr(0, threadData.config.commonDataPath.size() - 1); + p->obscuredMap.resize(640 * 480, 255); + obscuredDirty = true; + p->winX = 0; + p->winY = 0; + p->winPosChanged = false; /******************** * USERNAME/SAVE PATH @@ -337,6 +348,69 @@ Oneshot::~Oneshot() delete p; } +void Oneshot::update() +{ + if (p->winPosChanged) + { + p->winPosChanged = false; + + //Map of unobscured pixels in this frame + static std::vector obscuredFrame(p->obscuredMap.size()); + std::fill(obscuredFrame.begin(), obscuredFrame.end(), true); + + SDL_Rect screenRect; + screenRect.x = p->winX; + screenRect.y = p->winY; + screenRect.w = 640; + screenRect.h = 480; + + //Update obscured map and texture for window portion offscreen + for (int i = 0, max = SDL_GetNumVideoDisplays(); i < max; ++i) + { + SDL_Rect bounds; + SDL_GetDisplayBounds(i, &bounds); + + //If it's fully within the monitor, it's completely unobscured + //and no texture update is necessary + if (p->winX >= bounds.x && p->winY >= bounds.y && p->winX + 640 <= bounds.x + bounds.w && p->winY + 480 <= bounds.y + bounds.h) + return; + + //Update obscuredFrame otherwise + SDL_Rect intersect; + if (!SDL_IntersectRect(&screenRect, &bounds, &intersect)) + continue; + intersect.x -= p->winX; + intersect.y -= p->winY; + for (int y = intersect.y; y < intersect.y + intersect.h; ++y) + { + int start = y * 640 + intersect.x; + std::fill(obscuredFrame.begin() + start, obscuredFrame.begin() + (start + intersect.w), false); + } + } + + //Update the obscured map, and return prematurely if we don't have any changes + //to make to the texture + bool needsUpdate = false; + bool cleared = true; + for (size_t i = 0; i < obscuredFrame.size(); ++i) + { + if (obscuredFrame[i]) + { + p->obscuredMap[i] = 0; + needsUpdate = true; + } + if (p->obscuredMap[i] == 255) + cleared = false; + } + p->obscuredCleared = cleared; + if (!needsUpdate) + return; + + //Flag as dirty + obscuredDirty = true; + } +} + const std::string &Oneshot::lang() const { return p->lang; @@ -352,6 +426,16 @@ const std::string &Oneshot::savePath() const return p->savePath; } +const std::vector &Oneshot::obscuredMap() const +{ + return p->obscuredMap; +} + +bool Oneshot::obscuredCleared() const +{ + return p->obscuredCleared; +} + void Oneshot::setYesNo(const char *yes, const char *no) { p->txtYes = yes; @@ -458,3 +542,16 @@ bool Oneshot::msgbox(int type, const char *body, const char *title) return button ? true : false; #endif } + +void Oneshot::setWindowPos(int x, int y) +{ + p->winX = x; + p->winY = y; + p->winPosChanged = true; +} + +void Oneshot::resetObscured() +{ + std::fill(p->obscuredMap.begin(), p->obscuredMap.end(), 255); + obscuredDirty = true; +} diff --git a/src/oneshot.h b/src/oneshot.h index 8c10cbb..48cd644 100644 --- a/src/oneshot.h +++ b/src/oneshot.h @@ -42,17 +42,26 @@ public: GRADIENT_VERTICAL, }; + void update(); + //Accessors const std::string &lang() const; const std::string &userName() const; const std::string &savePath() const; + const std::vector &obscuredMap() const; + bool obscuredCleared() const; //Mutators void setYesNo(const char *yes, const char *no); + void setWindowPos(int x, int y); + void resetObscured(); //Functions bool msgbox(int type, const char *body, const char *title); + //Dirty flag for obscured texture + bool obscuredDirty; + private: OneshotPrivate *p; }; diff --git a/src/shader.cpp b/src/shader.cpp index 3b22483..9c2f183 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -52,6 +52,7 @@ #include "blurH.vert.xxd" #include "blurV.vert.xxd" #include "tilemapvx.vert.xxd" +#include "obscured.frag.xxd" #define INIT_SHADER(vert, frag, name) \ @@ -641,3 +642,17 @@ void BltShader::setOpacity(float value) { gl.Uniform1f(u_opacity, value); } + +ObscuredShader::ObscuredShader() +{ + INIT_SHADER(simple, obscured, ObscuredShader); + + ShaderBase::init(); + + GET_U(obscured); +} + +void ObscuredShader::setObscured(const TEX::ID value) +{ + setTexUniform(u_obscured, 1, value); +} diff --git a/src/shader.h b/src/shader.h index 323b10a..9ced82c 100644 --- a/src/shader.h +++ b/src/shader.h @@ -304,6 +304,18 @@ private: GLint u_source, u_destination, u_subRect, u_opacity; }; +/* Obscured graphic */ +class ObscuredShader : public ShaderBase +{ +public: + ObscuredShader(); + + void setObscured(const TEX::ID value); + +private: + GLint u_obscured; +}; + /* Global object containing all available shaders */ struct ShaderSet { @@ -325,6 +337,7 @@ struct ShaderSet SimpleMatrixShader simpleMatrix; BlurShader blur; TilemapVXShader tilemapVX; + ObscuredShader obscured; }; #endif // SHADER_H diff --git a/src/sprite.cpp b/src/sprite.cpp index 760bb0b..51b57e4 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -33,6 +33,8 @@ #include "shader.h" #include "glstate.h" #include "quadarray.h" +#include "config.h" +#include "debugwriter.h" #include @@ -64,6 +66,8 @@ struct SpritePrivate * the screen if drawn? */ bool isVisible; + bool obscured; + Color *color; Tone *tone; @@ -95,6 +99,7 @@ struct SpritePrivate opacity(255), blendType(BlendNormal), isVisible(false), + obscured(false), color(&tmp.color), tone(&tmp.tone) @@ -322,6 +327,7 @@ DEF_ATTR_SIMPLE(Sprite, Opacity, int, p->opacity) DEF_ATTR_SIMPLE(Sprite, SrcRect, Rect&, *p->srcRect) DEF_ATTR_SIMPLE(Sprite, Color, Color&, *p->color) DEF_ATTR_SIMPLE(Sprite, Tone, Tone&, *p->tone) +DEF_ATTR_SIMPLE(Sprite, Obscured, bool, p->obscured) void Sprite::setBitmap(Bitmap *bitmap) { @@ -518,7 +524,15 @@ void Sprite::draw() flashing || p->bushDepth != 0; - if (renderEffect) + if (p->obscured) + { + ObscuredShader &shader = shState->shaders().obscured; + shader.bind(); + shader.applyViewportProj(); + shader.setObscured(shState->graphics().obscuredTex()); + base = &shader; + } + else if (renderEffect) { SpriteShader &shader = shState->shaders().sprite; diff --git a/src/sprite.h b/src/sprite.h index 3b82f04..fa63f70 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -66,6 +66,7 @@ public: DECL_ATTR( WaveLength, int ) DECL_ATTR( WaveSpeed, int ) DECL_ATTR( WavePhase, float ) + DECL_ATTR( Obscured, bool ) void initDynAttribs();