[Lua] Palette Based Graphics Programming

Started by Scavenger, Wed 27/11/2013 15:35:39

Previous topic - Next topic

Scavenger

I'm trying to write a plugin to speed up pixel level functions in AGS, by converting an image into a string (AGS has the String.Chars[] array, which would be much faster to manipulate than Get/SetPixel). I hope to fake a kind of array setup when manipulating pixels, so I can speed up my translucency and colourisation functions for my next game, and maybe even write some more graphics programming type stuff.

I am ludicrously underskilled in C++. I did a bit of C in the old DOS days, but nothing fancy. I at least managed to get the plugin to compile in VS2008 Express. I suppose I gotta start somewhere!


Here's the code I wrote.
Code: C++
const char SerialiseSprite (int id)
{
	unsigned char serialised_image;
	serialised_image = new signed char [engine->GetSpriteWidth (id) * engine->GetSpriteHeight (id)];
	BITMAP *temp = engine->GetSpriteGraphic (id);
	serialised_image = engine->GetRawBitmapSurface (temp);
	engine->ReleaseBitmapSurface (temp);
	return engine->CreateScriptString (serialised_image);
}

int UnSerialiseSprite (const char string, int width,int height)
{
	int sprite = engine->CreateDynamicSprite (8,width,height);
	BITMAP *temp = engine->GetSpriteGraphic (sprite);
	unsigned char imagecontent = engine->GetRawBitmapSurface (sprite);
	imagecontent = string;
	engine->ReleaseBitmapSurface (sprite);
	return sprite;
}

void DestroySprite (int id)
{
	engine->DeleteDynamicSprite (id);
}


Quote.\dllmain.cpp(96) : error C2440: '=' : cannot convert from 'signed char *' to 'unsigned char'
1>        There is no context in which this conversion is possible
1>.\dllmain.cpp(98) : error C2440: '=' : cannot convert from 'unsigned char **' to 'unsigned char'
1>        There is no context in which this conversion is possible
1>.\dllmain.cpp(100) : error C2664: 'IAGSEngine::CreateScriptString' : cannot convert parameter 1 from 'unsigned char' to 'const char *'
1>        Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
1>.\dllmain.cpp(107) : error C2664: 'IAGSEngine::GetRawBitmapSurface' : cannot convert parameter 1 from 'int' to 'BITMAP *'
1>        Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
1>.\dllmain.cpp(109) : error C2664: 'IAGSEngine::ReleaseBitmapSurface' : cannot convert parameter 1 from 'int' to 'BITMAP *'
1>        Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast

This is the error it kicks out. I know this is simple stuff, but I don't understand it at all. Stuff I take for granted in AGSScript doesn't seem to carry over to C++, and it's a little embarrassing. Especially the "char" types. I'm not sure how to fix this?

Thankyou in advance :)

Wyz

There are some issues with types being incompatible. For instance you're trying to store an array in a scalar typed variable. Also you are mixing signed and unsigned types which is not really a problem but also something that makes the compiler spit errors. But there is also a general design issue: Converting something to a script string is very unlikely to make it faster. The engine will allocate new memory every time you use the CreateScriptString function which is pretty inefficient. There are also caveat with the null character, as that is used as a terminating character for C-strings. You're better of moving the actual calculation to the plugin and update the bitmap from there. Sorry to break it to you :)
Life is like an adventure without the pixel hunts.

Scavenger

That's pretty lame. I was hoping to make a pretty generic function so I could do much of the calculations in AGSScript and just convert it back and forth from array and sprite as needed. But if I really have to do it in-plugin, I probably could handle that given some practice.

Crimson Wizard

#3
Scavenger, I can suggest for you to look into Calin's Blend and SpriteFont plugins: they work with bitmaps, so you could have an example.
https://github.com/adventuregamestudio/ags/blob/master/Plugins/agsblend/AGSBlend.cpp
https://github.com/CalinLeafshade/ags-spritefont

Regarding your code, you are not using a pointer type:
Code: cpp

unsigned char * serialised_image; // see the '*' added here
serialised_image = new signed char [engine->GetSpriteWidth (id) * engine->GetSpriteHeight (id)];


More importantly
Code: cpp

serialised_image = engine->GetRawBitmapSurface (temp);
engine->ReleaseBitmapSurface (temp);

when you assign the result of GetRawBitmapSurface to your pointer, you are not making a copy of bitmap, but a copy of bitmap's address in memory.
The following call to ReleaseBitmapSurface will destroy bitmap, leaving you with a pointer addressing freed memory. Using that address further won't do any good (if not crash your game right away).

monkey0506

I also noticed that serialised_image was being allocated with new but never deleted anywhere. That's a memory leak! 8-0

For what you're trying to do, you would have to go through the image and copy the raw image data a line at a time into your buffer. Really though, the buffer isn't even necessary here since CreateScriptString is already going to make a copy, you could just pass the raw data directly through. As Wyz mentions though, any null bytes (decimal value 0) in your image data (which would not really be uncommon) would truncate the rest of the entire image. The speed you were hoping to gain would also have been severely diminished by the fact that AGS' String type is immutable, so you would constantly be making copies of the entire String just to change a single byte.

Scavenger

Interesting! I'm learning a lot here that I didn't know before.

I think, going forward what would be better is for me to try and implement the main bulk of my image processing (that is, the giant loops that iterate through the entire image and change it pixel by pixel) in the plugin. It'd be less flexible, but probably a lot faster for what I'm trying to do with it. Since it's basically just substituting values based on other values (in a look up table), and returning the resulting sprite, it might be easier to do, as well!

(I probably need to look up some more books on the language if I'm to fully understand what I'm doing.)

Calin Leafshade

@Scavenger

I did some benchmarking on this and the Get/Set pixel functions are not the bottleneck, it's the ags script language itself. It's *really* slow, especially in loops.

The AGS compiler, AFAIK, has no optimisation and uses no advanced CPU instructions. If you made a function to serialise it to a string it would be *far* slower than Get/Setpixel.

You could use Lua though. I used Lua to do the Mode7 emulation in the mario kart clone and I could get 40fps with no problem. That was using Get/SetPixel.
Doing the exact same calculations in AGS gave me about 7fps.

Crimson Wizard

Quote from: Calin Leafshade on Wed 27/11/2013 23:21:56
The AGS compiler, AFAIK, has no optimisation and uses no advanced CPU instructions.
Unfortunately, that's true. It produces very formal sequence of instructions, mimicing how real x86 processor (IIRC) works: putting values to registers, getting them back, calculating, putting result to register, getting it back once again for other use, etc, etc., and every action is processed separately. I think it does at least about 3-5 things more than needed per each primitive operation.

Scavenger

That's cool. While I'm more experienced with AGSScript, I may as well learn some Lua syntax and try that out. The plugin appears to be open source, so porting games made with it to other platforms can't be that hard.

If the speed of it is all that it's cracked up to be, this is exactly what I've been looking for! Though, it may take me some time to get used to it. At least it won't require me to wrangle with VS anymore! Ahaha. Does the Lua plugin have a manual I can look up to see how calling functions differs from AGSScript?

Calin Leafshade

This wiki is useful:

http://lua-for-ags.wikispaces.com/agsTable

but its basically like this for your purposes:

Code: ags

function DoSomethingToDynamicSprite(DynamicSprite* myDS)
{
  LuaValueList *list = Lua.NewValueList()
  list.Add(myDS.AsLuaValue())
  Lua.Call("myLuaFunction", list)
}


Code: lua

function myLuaFunction(ds)
    local surface = ds:GetDrawingSurface()
    for y=0,ds.Height - 1 do
        for x=0,ds.Width - 1 do
            --Do stuff with pixel here
        end
    end
    surface:Release()
end

Scavenger

Okay, I've been pottering around in Lua for a bit, it's not as daunting as I thought it would initially be. I managed to convert a function from AGSScript to Lua, and Lua, having 2D arrays, seems to be a better fit for it:

Code: lua


function GetCLUTForRoom () 
    clut[0][0] = 0;
    local i,x,y,cdata
    x=0
    y=0
    i=0
    cdata = 0
    local CLUTSPR = ags.DynamicSprite.CreateFromExistingSprite (ags.GetRoomProperty ("TransCLUTSlot"), false)
    local surface = ags.CLUTSPR.GetDrawingSurface ()
    while (i < 8)
    do
        while (cdata < 65536)
        do
            clut[i][cdata] = ags.surface.GetPixel (x, y)
            if (x == 255 and y <2047) then 
                y = y + 1 end
            if (x < 255) then 
                x = x + 1 
            else x = 0 end
            cdata = cdata + 1
        end
        i = i + 1
        cdata = 0
    end
    ags.surface.Release()
    ags.CLUTSPR.Delete()
end


And the syntax checked out! Hopefully I'm not doing this terribly wrong. I'll continue to convert some other functions later, and look up specific syntax when needed.

Calin Leafshade

Tidied up with comments:

I made some style changes just to make it easier for me to read. The thing about nil tables is the only issue I saw.

Code: lua


function GetCLUTForRoom () 
    local i,x,y,cdata = 0,0,0,0
    local CLUTSPR = ags.DynamicSprite.CreateFromExistingSprite (ags.GetRoomProperty ("TransCLUTSlot"), false)
    local surface = ags.CLUTSPR.GetDrawingSurface ()
    while (i < 8) do
        clut[i] = clut[i] or {} -- make a new table if one doesnt exist or clut[x][y] will fail because clut[x] is nil.
        while (cdata < 65536) do
            clut[i][cdata] = ags.surface.GetPixel (x, y)
            if (x == 255 and y <2047) then 
                y = y + 1 
            end
            if (x < 255) then 
                x = x + 1 
            else 
                x = 0 
            end
            cdata = cdata + 1
        end
        i = i + 1
        cdata = 0
    end
    ags.surface.Release()
    ags.CLUTSPR.Delete()
end


Scavenger

EDIT: Okay, no more crashes, as I fixed some of the syntax, but still, nothing happens when I run the Lua version of the code, where it DOES work with the AGS version.

My code looks like this:

Code: AGS
function ProcessTranslucency(DynamicSprite* sprite, DynamicSprite* background, int ld, int sd, int trans)
{
  LuaValueList *list = Lua.NewValueList();
  list.Add(sprite.AsLuaValue());
  list.Add(background.AsLuaValue());
  list.Add (Lua.IntValue (ld));
  list.Add (Lua.IntValue (sd));
  list.Add (Lua.IntValue (trans));
  Lua.Call("ProcessTranslucency", list); //This is line 156
}

function noloopcheck DrawObject(this DrawingSurface*, Object *theObject) {
  if ((theObject == null) || (!theObject.Graphic) || (!theObject.Visible)) return;
  DynamicSprite *sprite;
  int graphic;
  if (objecttransparent [theObject.ID])
  {
    graphic = objecttruesprite [theObject.ID];
  }
  else graphic = theObject.Graphic;
  if (theObject.View) {
    ViewFrame *frame = Game.GetViewFrame(theObject.View, theObject.Loop, theObject.Frame);
    if (frame.Flipped) {
      sprite = DynamicSprite.CreateFromExistingSprite(frame.Graphic, true);
      sprite.Flip(eFlipLeftToRight);
    }
  }
  int scale = GetScalingAt(theObject.X, theObject.Y);
  if ((!theObject.IgnoreScaling) && (scale != 100)) {
    if (sprite == null) sprite = DynamicSprite.CreateFromExistingSprite(graphic, true);
    sprite.Resize((Game.SpriteWidth[graphic] * scale) / 100, (Game.SpriteHeight[graphic] * scale) / 100);
  }
  if (sprite != null) graphic = sprite.Graphic;
  {
    int ox = theObject.X;
    int oy = theObject.Y - Game.SpriteHeight[graphic];
    DynamicSprite *bgcopy = DynamicSprite.CreateFromDrawingSurface (this, ox, oy, Game.SpriteWidth [graphic], Game.SpriteHeight [graphic]);
    origspr[theObject.ID] = DynamicSprite.CreateFromExistingSprite (graphic);
    int sd = smallest_digit (objecttransparent[theObject.ID]);
    int ld = largest_digit  (objecttransparent[theObject.ID]);
    ProcessTranslucency(origspr[theObject.ID], bgcopy, ld, sd, objecttransparent[theObject.ID]); //Line 225. This function replaces the much slower code below.
    /*
    DrawingSurface *Surface = bgcopy.GetDrawingSurface ();
    DrawingSurface *OSSurface = origspr[theObject.ID].GetDrawingSurface ();
    int i;
    int j;
    
    int OSpixel;
    int BGpixel;
    while (j < origspr[theObject.ID].Height)
    {
      while (i < origspr[theObject.ID].Width)
      {
        OSpixel = OSSurface.GetPixel (i, j);
        if (OSpixel != COLOR_TRANSPARENT)
        {
        OSpixel = AbsInt (OSpixel); 
         BGpixel = AbsInt (Surface.GetPixel (i, j));
                  if (objecttransparent[theObject.ID] > 90)
                  {
                    
                    bool alternate = true;
                    if (alternate == false)
                    {

                              OSSurface.DrawingColor = clut [ld -1].data[(BGpixel << 8) + OSpixel];
                              OSSurface.DrawPixel (i, j);
                              alternate = true;
                    }
                    else if (alternate == true)
                    {
                      OSSurface.DrawingColor = OSpixel;
                      OSSurface.DrawPixel (i, j);
                      alternate = false;
                    }
                  }
                  else if (objecttransparent[theObject.ID] > 10) 
                 {

                      if (Random (sd) < sd)
                          {
                                        OSSurface.DrawingColor = clut [ld].data[(BGpixel <<8) + OSpixel];
                                        OSSurface.DrawPixel (i, j);
                          }
                      else 
                          {
                  
                                        OSSurface.DrawingColor = clut [ld-1].data[(BGpixel <<8) + OSpixel];
                                        OSSurface.DrawPixel (i, j);
                          }
                 }
                 else     {
                              if (Random (sd) < sd)
                              {
                    
                                              OSSurface.DrawingColor = clut [0].data[(BGpixel <<8) + OSpixel];
                                              OSSurface.DrawPixel (i, j);
                              }
                              else {
                                              OSSurface.DrawingColor = COLOR_TRANSPARENT;
                                              OSSurface.DrawPixel (i, j);
                                   }
                          }
        }
        i++;
      }
      i = 0;
      j++;
    } 
    Surface.Release ();
    OSSurface.Release ();
    */
  }
  this.DrawImage(theObject.X, theObject.Y - Game.SpriteHeight[graphic], origspr[theObject.ID].Graphic, theObject.Transparency);
  if (theObject.IgnoreScaling && objecttransparent[theObject.ID]) { theObject.Graphic = origspr[theObject.ID].Graphic; objecttranssprite[theObject.ID] = origspr[theObject.ID].Graphic; }
}


Code: Lua

function GetCLUTForRoom ()
    local i,x,y,cdata = 0,0,0,0
    local CLUTSPR = ags.DynamicSprite.CreateFromExistingSprite (ags.GetRoomProperty ("TransCLUTSlot"), false)
    local surface = ags.CLUTSPR.GetDrawingSurface ()
    while (i < 8) do
        clut[i] = clut[i] or {} -- make a new table if one doesnt exist or clut[x][y] will fail because clut[x] is nil.
        while (cdata < 65536) do
            clut[i][cdata] = ags.surface.GetPixel (x, y)
            if (x == 255 and y <2047) then
                y = y + 1
            end
            if (x < 255) then
                x = x + 1
            else
                x = 0
            end
            cdata = cdata + 1
        end
        i = i + 1
        cdata = 0
    end
    ags.surface.Release()
    ags.CLUTSPR.Delete()
end


function ProcessTranslucency(ds,bg,ld,sd,trans)
    local OSSsurface = ds:GetDrawingSurface()
    local BGSurface = bg:GetDrawingSurface ()
    for y=0,ds.Height - 1 do
        for x=0,ds.Width - 1 do
        local OSpixel = ags.OSSurface.GetPixel (x, y)
        if (OSpixel > 0)
            then
                  local BGpixel = ags.BGSurface.GetPixel (x, y)
                  if (trans > 90)
                    then
                          if (alternate == nil)
                            then
                                alternate = 0
                            end
                    
                          if (alternate == 0)
                              then
                                  ags.OSSurface.DrawingColor = clut [ld -1][bit.blshift(BGPixel,8) + OSpixel]
                                  ags.OSSurface.DrawPixel (x, y)
                                  alternate = 1;
                          else if (alternate == 1)
                              then
                                  ags.OSSurface.DrawingColor = OSpixel;
                                  ags.OSSurface.DrawPixel (x, y)
                                  alternate = 0;
                              end
                     end
                  else if (trans > 10) 
                    then

                          if (ags.Random (sd) < sd) then
                               ags.OSSurface.DrawingColor = clut [ld][bit.blshift(BGPixel,8) + OSpixel]
                               ags.OSSurface.DrawPixel (x, y)
                          else 
                               ags.OSSurface.DrawingColor = clut [ld-1][bit.blshift(BGPixel,8) + OSpixel]
                               ags.OSSurface.DrawPixel (x, y)
                          end
                    else
                          if (Random (sd) < sd) then
                               ags.OSSurface.DrawingColor = clut [0][bit.blshift(BGPixel,8) + OSpixel]
                               ags.OSSurface.DrawPixel (x, y)
                          else
                               ags.OSSurface.DrawingColor = 0
                               ags.OSSurface.DrawPixel (x, y)
                          end
                    end
               end
            end
        end
    end
    surface:Release()
end

-- stuff
---function ColouriseArea(ds,bg,ld,sd)
--    local surface = ds:GetDrawingSurface()
--    local bgsurface
--    for y=0,ds.Height - 1 do
--        for x=0,ds.Width - 1 do
--            --Do stuff with pixel here
--        end
--    end
--    surface:Release()
--end


--function ColouriseSprite(ds,bg,ld,sd)
--    local surface = ds:GetDrawingSurface()
--    local bgsurface
--    for y=0,ds.Height - 1 do
--       for x=0,ds.Width - 1 do
--            --Do stuff with pixel here
--        end
--    end
--    surface:Release()
--end


What thing did I do wrong? I'm a little confused.

EDIT EDIT:

Quote
Illegal exception
An exception 0xC0000005 occurred in ACWIN.EXE at EIP = 0x01EB6D93 ; program pointer is +1004, ACI version 3.3.0.1148, gtags (0,50)

AGS cannot continue, this exception was fatal. Please note down the numbers above, remember what you were doing at the time and post the details on the AGS Technical Forum.

in "PAlgorithms2.asc", line 151 (list.Add(sprite.AsLuaValue()); )
from "PAlgorithms2.asc", line 225
from "PAlgorithms2.asc", line 837

I have no idea what's going on.


Scavenger

Not yet, but I can quickly set something up in 3.2.1.

EDIT: Okay, tried it. Nothing happens. No crashes, but nothing happens.

I packaged both the 330 and 321 one in this zip: https://dl.dropboxusercontent.com/u/50882197/Game/Paltest.zip

Calin Leafshade

That's odd. I've used the Lua plugin extensively and very rarely get crashes.

I will investigate with your packaged versions

EDIT:

Ok the problem is that you never actually run the script.. although I admit it doesnt fail very nicely.

You need to run the script to actually process the file.

Code: ags

Lua.RunScript("ProcessTranslucency.lua");


in your game start

You also need to run the function like this if you want to see any errors:

Code: ags

Lua.Call("ProcessTranslucency", list, eLuaUnprotectedMode);


otherwise Lua catches the errors.

EDIT AGAIN:

Ok there are actually a lot of errors with your script. I will fix them and post again.

Calin Leafshade

Here's the fixed script. Works wonderfully at 40fps for me on linux. Looks really cool.

A few things you need to be mindful of:

- Watch case errors. Don't use the AGS built in Lua editor use ZeroBrane Studio or Notepad++ or something else with "special word" catching. It saves a lot of time.
- Colon syntax. Theres no such thing as a "method" in Lua. Objects have fields some of which may be functions but they are not methods like in an OOP language. So when calling a function which acts on the object of which it's a field it is called like this: object.function(object). This is a little messy though so Lua provides a shorthand which is object:function(). They mean the same thing and are entirely synonymous.
- Variable access. You don't need to prefix every variable with "ags." You only need do that when it's an AGS object you are interested in like ags.Display() or ags.DynamicSprite.Create. Once a variable has a local name you can refer to it by that. You can even do this to save typing and speed up access (Lua is faster at accessing local variables than globals)

Code: lua

    local ds = ags.DynamicSprite
    ds.Create(320,200)


- Bitshifting. Lua doesnt have bitshifting built in so I got a module that does it and included it. The one I included is pure Lua so it's much slower than pure bitshifting but you can get the C library and include that instead if you find yourself needing the extra speed. I doubt you will.


Code: lua

bit = require("bit").bit

function GetCLUTForRoom ()
    local i,x,y,cdata = 0,0,0,0
    local CLUTSPR = ags.DynamicSprite.CreateFromExistingSprite (ags.GetRoomProperty ("TransCLUTSlot"), false)
    local surface = CLUTSPR:GetDrawingSurface () -- Use : for method (i.e non static) calls. It's shorthand for object.method(object)
    clut = {} -- init clut
    while (i < 8) do
        clut[i] = clut[i] or {} -- make a new table if one doesnt exist or clut[x][y] will fail because clut[x] is nil.
        while (cdata < 65536) do
            clut[i][cdata] = surface:GetPixel (x, y)
            if (x == 255 and y <2047) then
                y = y + 1
            end
            if (x < 255) then
                x = x + 1
            else
                x = 0
            end
            cdata = cdata + 1
        end
        i = i + 1
        cdata = 0
    end
    surface:Release() -- you dont need to prefix everything with ags. only ags functions.
    CLUTSPR:Delete()
end
 
 
function ProcessTranslucency(ds,bg,ld,sd,trans)

    local OSSurface = ds:GetDrawingSurface()
    local BGSurface = bg:GetDrawingSurface ()
    for y=0,ds.Height - 1 do
        for x=0,ds.Width - 1 do
        local OSpixel = OSSurface:GetPixel (x, y) -- Colon notation again
        if (OSpixel > 0)
            then
                  local BGpixel = BGSurface:GetPixel (x, y)
                  
                  if (trans > 90)
                    then
                          if (alternate == nil)
                            then
                                alternate = 0
                            end
                   
                          if (alternate == 0)
                              then
                                  OSSurface.DrawingColor = clut [ld -1][bit.lshift(BGpixel,8) + OSpixel]
                                  OSSurface:DrawPixel (x, y)
                                  alternate = 1;
                          else if (alternate == 1)
                              then
                                  OSSurface.DrawingColor = OSpixel;
                                  OSSurface:DrawPixel (x, y)
                                  alternate = 0;
                              end
                     end
                  else if (trans > 10)
                    then
 
                          if (ags.Random (sd) < sd) then
                               OSSurface.DrawingColor = clut [ld][bit.lshift(BGpixel,8) + OSpixel]
                               ags.OSSurface:DrawPixel (x, y)
                          else
                              OSSurface.DrawingColor = clut [ld-1][bit.lshift(BGpixel,8) + OSpixel]
                               OSSurface:DrawPixel (x, y)
                          end
                    else
                          if (Random (sd) < sd) then
                               OSSurface.DrawingColor = clut [0][bit.lshift(BGpixel,8) + OSpixel]
                               OSSurface:DrawPixel (x, y)
                          else
                               aOSSurface.DrawingColor = 0
                               OSSurface:DrawPixel (x, y)
                          end
                    end
               end
            end
        end
    end
    
    OSSurface:Release()
    BGSurface:Release()
end
 
-- stuff
---function ColouriseArea(ds,bg,ld,sd)
--    local surface = ds:GetDrawingSurface()
--    local bgsurface
--    for y=0,ds.Height - 1 do
--        for x=0,ds.Width - 1 do
--            --Do stuff with pixel here
--        end
--    end
--    surface:Release()
--end
 
 
--function ColouriseSprite(ds,bg,ld,sd)
--    local surface = ds:GetDrawingSurface()
--    local bgsurface
--    for y=0,ds.Height - 1 do
--       for x=0,ds.Width - 1 do
--            --Do stuff with pixel here
--        end
--    end
--    surface:Release()
--end


And the bitshifting lib:

name it "bit.lua"

Code: lua

--[[

LUA MODULE

  bit.numberlua - Bitwise operations implemented in pure Lua as numbers,
    with Lua 5.2 'bit32' and (LuaJIT) LuaBitOp 'bit' compatibility interfaces.

SYNOPSIS

  local bit = require 'bit.numberlua'
  print(bit.band(0xff00ff00, 0x00ff00ff)) --> 0xffffffff
  
  -- Interface providing strong Lua 5.2 'bit32' compatibility
  local bit32 = require 'bit.numberlua'.bit32
  assert(bit32.band(-1) == 0xffffffff)
  
  -- Interface providing strong (LuaJIT) LuaBitOp 'bit' compatibility
  local bit = require 'bit.numberlua'.bit
  assert(bit.tobit(0xffffffff) == -1)
  
DESCRIPTION
  
  This library implements bitwise operations entirely in Lua.
  This module is typically intended if for some reasons you don't want
  to or cannot  install a popular C based bit library like BitOp 'bit' [1]
  (which comes pre-installed with LuaJIT) or 'bit32' (which comes
  pre-installed with Lua 5.2) but want a similar interface.
  
  This modules represents bit arrays as non-negative Lua numbers. [1]
  It can represent 32-bit bit arrays when Lua is compiled
  with lua_Number as double-precision IEEE 754 floating point.

  The module is nearly the most efficient it can be but may be a few times
  slower than the C based bit libraries and is orders or magnitude
  slower than LuaJIT bit operations, which compile to native code.  Therefore,
  this library is inferior in performane to the other modules.

  The `xor` function in this module is based partly on Roberto Ierusalimschy's
  post in http://lua-users.org/lists/lua-l/2002-09/msg00134.html .
  
  The included BIT.bit32 and BIT.bit sublibraries aims to provide 100%
  compatibility with the Lua 5.2 "bit32" and (LuaJIT) LuaBitOp "bit" library.
  This compatbility is at the cost of some efficiency since inputted
  numbers are normalized and more general forms (e.g. multi-argument
  bitwise operators) are supported.
  
STATUS

  WARNING: Not all corner cases have been tested and documented.
  Some attempt was made to make these similar to the Lua 5.2 [2]
  and LuaJit BitOp [3] libraries, but this is not fully tested and there
  are currently some differences.  Addressing these differences may
  be improved in the future but it is not yet fully determined how to
  resolve these differences.
  
  The BIT.bit32 library passes the Lua 5.2 test suite (bitwise.lua)
  http://www.lua.org/tests/5.2/ .  The BIT.bit library passes the LuaBitOp
  test suite (bittest.lua).  However, these have not been tested on
  platforms with Lua compiled with 32-bit integer numbers.

API

  BIT.tobit(x) --> z
  
    Similar to function in BitOp.
    
  BIT.tohex(x, n)
  
    Similar to function in BitOp.
  
  BIT.band(x, y) --> z
  
    Similar to function in Lua 5.2 and BitOp but requires two arguments.
  
  BIT.bor(x, y) --> z
  
    Similar to function in Lua 5.2 and BitOp but requires two arguments.

  BIT.bxor(x, y) --> z
  
    Similar to function in Lua 5.2 and BitOp but requires two arguments.
  
  BIT.bnot(x) --> z
  
    Similar to function in Lua 5.2 and BitOp.

  BIT.lshift(x, disp) --> z
  
    Similar to function in Lua 5.2 (warning: BitOp uses unsigned lower 5 bits of shift),
  
  BIT.rshift(x, disp) --> z
  
    Similar to function in Lua 5.2 (warning: BitOp uses unsigned lower 5 bits of shift),

  BIT.extract(x, field [, width]) --> z
  
    Similar to function in Lua 5.2.
  
  BIT.replace(x, v, field, width) --> z
  
    Similar to function in Lua 5.2.
  
  BIT.bswap(x) --> z
  
    Similar to function in Lua 5.2.

  BIT.rrotate(x, disp) --> z
  BIT.ror(x, disp) --> z
  
    Similar to function in Lua 5.2 and BitOp.

  BIT.lrotate(x, disp) --> z
  BIT.rol(x, disp) --> z

    Similar to function in Lua 5.2 and BitOp.
  
  BIT.arshift
  
    Similar to function in Lua 5.2 and BitOp.
    
  BIT.btest
  
    Similar to function in Lua 5.2 with requires two arguments.

  BIT.bit32
  
    This table contains functions that aim to provide 100% compatibility
    with the Lua 5.2 "bit32" library.
    
    bit32.arshift (x, disp) --> z
    bit32.band (...) --> z
    bit32.bnot (x) --> z
    bit32.bor (...) --> z
    bit32.btest (...) --> true | false
    bit32.bxor (...) --> z
    bit32.extract (x, field [, width]) --> z
    bit32.replace (x, v, field [, width]) --> z
    bit32.lrotate (x, disp) --> z
    bit32.lshift (x, disp) --> z
    bit32.rrotate (x, disp) --> z
    bit32.rshift (x, disp) --> z

  BIT.bit
  
    This table contains functions that aim to provide 100% compatibility
    with the LuaBitOp "bit" library (from LuaJIT).
    
    bit.tobit(x) --> y
    bit.tohex(x [,n]) --> y
    bit.bnot(x) --> y
    bit.bor(x1 [,x2...]) --> y
    bit.band(x1 [,x2...]) --> y
    bit.bxor(x1 [,x2...]) --> y
    bit.lshift(x, n) --> y
    bit.rshift(x, n) --> y
    bit.arshift(x, n) --> y
    bit.rol(x, n) --> y
    bit.ror(x, n) --> y
    bit.bswap(x) --> y
    
DEPENDENCIES

  None (other than Lua 5.1 or 5.2).
    
DOWNLOAD/INSTALLATION

  If using LuaRocks:
    luarocks install lua-bit-numberlua

  Otherwise, download <https://github.com/davidm/lua-bit-numberlua/zipball/master>.
  Alternately, if using git:
    git clone git://github.com/davidm/lua-bit-numberlua.git
    cd lua-bit-numberlua
  Optionally unpack:
    ./util.mk
  or unpack and install in LuaRocks:
    ./util.mk install 

REFERENCES

  [1] http://lua-users.org/wiki/FloatingPoint
  [2] http://www.lua.org/manual/5.2/
  [3] http://bitop.luajit.org/
  
LICENSE

  (c) 2008-2011 David Manura.  Licensed under the same terms as Lua (MIT).

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  (end license)

--]]

local M = {_TYPE='module', _NAME='bit.numberlua', _VERSION='0.3.1.20120131'}

local floor = math.floor

local MOD = 2^32
local MODM = MOD-1

local function memoize(f)
  local mt = {}
  local t = setmetatable({}, mt)
  function mt:__index(k)
    local v = f(k); t[k] = v
    return v
  end
  return t
end

local function make_bitop_uncached(t, m)
  local function bitop(a, b)
    local res,p = 0,1
    while a ~= 0 and b ~= 0 do
      local am, bm = a%m, b%m
      res = res + t[am][bm]*p
      a = (a - am) / m
      b = (b - bm) / m
      p = p*m
    end
    res = res + (a+b)*p
    return res
  end
  return bitop
end

local function make_bitop(t)
  local op1 = make_bitop_uncached(t,2^1)
  local op2 = memoize(function(a)
    return memoize(function(b)
      return op1(a, b)
    end)
  end)
  return make_bitop_uncached(op2, 2^(t.n or 1))
end

-- ok?  probably not if running on a 32-bit int Lua number type platform
function M.tobit(x)
  return x % 2^32
end

M.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4}
local bxor = M.bxor

function M.bnot(a)   return MODM - a end
local bnot = M.bnot

function M.band(a,b) return ((a+b) - bxor(a,b))/2 end
local band = M.band

function M.bor(a,b)  return MODM - band(MODM - a, MODM - b) end
local bor = M.bor

local lshift, rshift -- forward declare

function M.rshift(a,disp) -- Lua5.2 insipred
  if disp < 0 then return lshift(a,-disp) end
  return floor(a % 2^32 / 2^disp)
end
rshift = M.rshift

function M.lshift(a,disp) -- Lua5.2 inspired
  if disp < 0 then return rshift(a,-disp) end 
  return (a * 2^disp) % 2^32
end
lshift = M.lshift

function M.tohex(x, n) -- BitOp style
  n = n or 8
  local up
  if n <= 0 then
    if n == 0 then return '' end
    up = true
    n = - n
  end
  x = band(x, 16^n-1)
  return ('%0'..n..(up and 'X' or 'x')):format(x)
end
local tohex = M.tohex

function M.extract(n, field, width) -- Lua5.2 inspired
  width = width or 1
  return band(rshift(n, field), 2^width-1)
end
local extract = M.extract

function M.replace(n, v, field, width) -- Lua5.2 inspired
  width = width or 1
  local mask1 = 2^width-1
  v = band(v, mask1) -- required by spec?
  local mask = bnot(lshift(mask1, field))
  return band(n, mask) + lshift(v, field)
end
local replace = M.replace

function M.bswap(x)  -- BitOp style
  local a = band(x, 0xff); x = rshift(x, 8)
  local b = band(x, 0xff); x = rshift(x, 8)
  local c = band(x, 0xff); x = rshift(x, 8)
  local d = band(x, 0xff)
  return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d
end
local bswap = M.bswap

function M.rrotate(x, disp)  -- Lua5.2 inspired
  disp = disp % 32
  local low = band(x, 2^disp-1)
  return rshift(x, disp) + lshift(low, 32-disp)
end
local rrotate = M.rrotate

function M.lrotate(x, disp)  -- Lua5.2 inspired
  return rrotate(x, -disp)
end
local lrotate = M.lrotate

M.rol = M.lrotate  -- LuaOp inspired
M.ror = M.rrotate  -- LuaOp insipred


function M.arshift(x, disp) -- Lua5.2 inspired
  local z = rshift(x, disp)
  if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end
  return z
end
local arshift = M.arshift

function M.btest(x, y) -- Lua5.2 inspired
  return band(x, y) ~= 0
end

--
-- Start Lua 5.2 "bit32" compat section.
--

M.bit32 = {} -- Lua 5.2 'bit32' compatibility


local function bit32_bnot(x)
  return (-1 - x) % MOD
end
M.bit32.bnot = bit32_bnot

local function bit32_bxor(a, b, c, ...)
  local z
  if b then
    a = a % MOD
    b = b % MOD
    z = bxor(a, b)
    if c then
      z = bit32_bxor(z, c, ...)
    end
    return z
  elseif a then
    return a % MOD
  else
    return 0
  end
end
M.bit32.bxor = bit32_bxor

local function bit32_band(a, b, c, ...)
  local z
  if b then
    a = a % MOD
    b = b % MOD
    z = ((a+b) - bxor(a,b)) / 2
    if c then
      z = bit32_band(z, c, ...)
    end
    return z
  elseif a then
    return a % MOD
  else
    return MODM
  end
end
M.bit32.band = bit32_band

local function bit32_bor(a, b, c, ...)
  local z
  if b then
    a = a % MOD
    b = b % MOD
    z = MODM - band(MODM - a, MODM - b)
    if c then
      z = bit32_bor(z, c, ...)
    end
    return z
  elseif a then
    return a % MOD
  else
    return 0
  end
end
M.bit32.bor = bit32_bor

function M.bit32.btest(...)
  return bit32_band(...) ~= 0
end

function M.bit32.lrotate(x, disp)
  return lrotate(x % MOD, disp)
end

function M.bit32.rrotate(x, disp)
  return rrotate(x % MOD, disp)
end

function M.bit32.lshift(x,disp)
  if disp > 31 or disp < -31 then return 0 end
  return lshift(x % MOD, disp)
end

function M.bit32.rshift(x,disp)
  if disp > 31 or disp < -31 then return 0 end
  return rshift(x % MOD, disp)
end

function M.bit32.arshift(x,disp)
  x = x % MOD
  if disp >= 0 then
    if disp > 31 then
      return (x >= 0x80000000) and MODM or 0
    else
      local z = rshift(x, disp)
      if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end
      return z
    end
  else
    return lshift(x, -disp)
  end
end

function M.bit32.extract(x, field, ...)
  local width = ... or 1
  if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
  x = x % MOD
  return extract(x, field, ...)
end

function M.bit32.replace(x, v, field, ...)
  local width = ... or 1
  if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
  x = x % MOD
  v = v % MOD
  return replace(x, v, field, ...)
end


--
-- Start LuaBitOp "bit" compat section.
--

M.bit = {} -- LuaBitOp "bit" compatibility

function M.bit.tobit(x)
  x = x % MOD
  if x >= 0x80000000 then x = x - MOD end
  return x
end
local bit_tobit = M.bit.tobit

function M.bit.tohex(x, ...)
  return tohex(x % MOD, ...)
end

function M.bit.bnot(x)
  return bit_tobit(bnot(x % MOD))
end

local function bit_bor(a, b, c, ...)
  if c then
    return bit_bor(bit_bor(a, b), c, ...)
  elseif b then
    return bit_tobit(bor(a % MOD, b % MOD))
  else
    return bit_tobit(a)
  end
end
M.bit.bor = bit_bor

local function bit_band(a, b, c, ...)
  if c then
    return bit_band(bit_band(a, b), c, ...)
  elseif b then
    return bit_tobit(band(a % MOD, b % MOD))
  else
    return bit_tobit(a)
  end
end
M.bit.band = bit_band

local function bit_bxor(a, b, c, ...)
  if c then
    return bit_bxor(bit_bxor(a, b), c, ...)
  elseif b then
    return bit_tobit(bxor(a % MOD, b % MOD))
  else
    return bit_tobit(a)
  end
end
M.bit.bxor = bit_bxor

function M.bit.lshift(x, n)
  return bit_tobit(lshift(x % MOD, n % 32))
end

function M.bit.rshift(x, n)
  return bit_tobit(rshift(x % MOD, n % 32))
end

function M.bit.arshift(x, n)
  return bit_tobit(arshift(x % MOD, n % 32))
end

function M.bit.rol(x, n)
  return bit_tobit(lrotate(x % MOD, n % 32))
end

function M.bit.ror(x, n)
  return bit_tobit(rrotate(x % MOD, n % 32))
end

function M.bit.bswap(x)
  return bit_tobit(bswap(x % MOD))
end

return M

Crimson Wizard

I am getting crashes in both 3.2.1 and 3.3.0 all the time, even if used last Calin's version of the lua script.

Can this be some bad version of lua plugin? Just a random thought. :-\

EDIT: Hmm, no, not all time. I get crash if I run the game from Compiled folder, and nothing at all if test-run from Editor. But the latter may be simply because lua scripts don't get loaded?

EDIT2: I honestly tried to debug the engine, but all I see so far is that the engine tries to call plugin functions at abnormal address.

EDIT3: And suddenly, after couple of more rebuilds, there are no crashes... But I do not see anything except for a static scene with some characters. What is supposed to happen?

Calin Leafshade

It mimics transparency for paletted games by picking a palette colour close to the required blend. It's very clever and looks kinda cool.

To load the lua script you need to call

Code: ags

Lua.RunScript("ProcessTranslucency.lua");


in game_start

Crimson Wizard

I am using Scavenger's game project (AGS 3.2.1) with lua scripts from your previous post pasted over. I am getting crashes with exactly same error as Scavenger posted as soon as the loop counter reaches 100.

Sometimes it stops crashing after rebuild, but I suspect that may be because lua scripts get out of date? Not sure.
I do not see any transparency changes.

Wait a sec... You said you are running on Linux? Do you use JJS's port? In such case the engine uses built-in lua plugin plugin version from repository that may have some differences from the Windows plugin we are using.

Scavenger

Thank you so much Calin! I never dreamed I'd get my module running fullspeed! Now I can use translucent images in my next game without losing any speed!

Now all I have to do is fiddle with it a bit to get it to work all across the spectrum, and possibly convert all the other parts of the module to Lua as well! I am so excited about the possibilities!
(And the CLUT generator definitely, that thing takes like an hour on this computer to render a CLUT.)

Though, now I'm getting this error:

Quote---------------------------
Adventure Game Studio
---------------------------
An internal error has occurred. Please note down the following information.
If the problem persists, post the details on the AGS Technical Forum.
(ACI version 3.3.0.1148)

Error: [Lua] [string "local select = select;local error = error;l..."]:1: bad argument #2 to 'setter' (number expected, got nil)

This happens every time there's an unclean crash in the AGS engine or editor. The project is thereafter ruined, even if the code is okay.

Calin Leafshade

No, I'm using Wine.

I loaded the 3.2.1 project in the version of AGS I happen to have on my laptop which is:

AGS Editor .NET (Build 3.3.0.1132) ** BETA VERSION **
v3.3.0, April 2013


Calin Leafshade

Quote from: Scavenger on Fri 29/11/2013 22:36:26
Though, now I'm getting this error:

Quote---------------------------
Adventure Game Studio
---------------------------
An internal error has occurred. Please note down the following information.
If the problem persists, post the details on the AGS Technical Forum.
(ACI version 3.3.0.1148)

Error: [Lua] [string "local select = select;local error = error;l..."]:1: bad argument #2 to 'setter' (number expected, got nil)

This happens every time there's an unclean crash in the AGS engine or editor. The project is thereafter ruined, even if the code is okay.

I have seen that error before but never found out what causes it. Maybe a Rebuild All Files might fix it, i'm not sure.

Crimson Wizard

There's some mystery here.
3.2.1 crashes at loop count 100.
3.3.0 betas 1 - 9 do not crash, but I see no blending effect (just two opaque characters).
3.3.0 beta 10 crashes like 3.2.1.
:(

I'll check again tomorrow, maybe I am doing something wrong, or using older version of plugin or something.

Scavenger

Rebuild All Files didn't work. Hmm. I just wish it was a little more stable on my end. :P This makes it very hard to want to use it in a real game, if there's a danger of an editor or engine crash that messes everything up. Not all script errors quit neatly!

I'll upload the project file - It works, you can see the translucency, but on Loop 17 it falls dead.

https://dl.dropboxusercontent.com/u/50882197/Game/paltest_ruined.zip (3.3.0)

And the version of Lua for AGS I'm using is the one from here: http://lua-for-ags.wikispaces.com/Downloads

Calin Leafshade

My version works well into loop 500 with no issues.

I think this might be some issue with AGS itself. It does seem to be behaving oddly.

Posting your error into the Lua plugin thread might be helpful since I think the author monitors that thread.

Khris


Scavenger

Okay, I've moved all of the necessary files over to 3.2.1 and I'll just work on this module from there until it can be confirmed that Lua and 3.3.0 can work nicely together. (And I'll hit up the Lua plugin thread to give my bug reports.)

I'm now converting this code:

Code: AGS

static function PALgorithms::ProcessPalette (int palsiz, int palindex[])
{
  int paltemp [];
  paltemp = new int [palsiz];
  char i = 0;
  int i2 = 1;
  int upperboundary;
  while (i < palsiz)
  {
    paltemp [i] = (palette[palindex[i]].r +  // This algorithm produces an accurate, fast approximation of
                   palette[palindex[i]].r +  // The luminescence value of each of our palette slots.
                   palette[palindex[i]].r +  //
                   palette[palindex[i]].b +  // This ensures that when we colourise the final product, there 
                   palette[palindex[i]].g +  // will be little to no weird artifacts or lightness shifts.
                   palette[palindex[i]].g +  //
                   palette[palindex[i]].g +  // By using addition and bit shifts instead of multiplication, 
                   palette[palindex[i]].g    // the code will run a lot faster and more efficiently.
                   )>>3;
    i++;
  }
  i = 0;
  while (i < palsiz)
  {
      //Display ("%d , %d", i,  i+1);
      if (i > palsiz -2) upperboundary = 255;
      else upperboundary = (paltemp [i] + paltemp [i + 1])/2;
      //Display ("%d",upperboundary);

      while (i2 < upperboundary)
      {
        String output;
        _C8palgorithm [i2] = palindex[i];
        i2++;
      }
    i++;
    }
    
    i= 0;
    paltemp = new int [256];
    while (i < 255)
      {
       paltemp [i] = (palette[i].r +  // This algorithm produces an accurate, fast approximation of
                      palette[i].r +  // The luminescence value of each of our palette slots.
                      palette[i].r +  //
                      palette[i].b +  // This ensures that when we colourise the final product, there 
                      palette[i].g +  // will be little to no weird artifacts or lightness shifts.
                      palette[i].g +  //
                      palette[i].g +  // By using addition and bit shifts instead of multiplication, 
                      palette[i].g    // the code will run a lot faster and more efficiently.
                      )>>3;
                      i++;
      }
    i = 0;
    while (i < 255)
      {
       _C8PaletteMap [i] = _C8palgorithm [paltemp[i]];
       i++;
      }
  } 


But I'm not sure how to pass a dynamic array to Lua. The wiki doesn't seem much help. :( Everything else is fine, I can deal with that, but there isn't much documentation on transferring arrays between AGS and Lua.

Calin Leafshade

Make a new LuaValueList and add the values 1 by 1. It's not ideal but its probably the best you'll get.

(all code untested and done from memory)
Code: ags

    LuaValueList *list = Lua.NewValueList();
    int i = 0;
    while (i < arrayCount)
    {
        list.Add(Lua.IntValue(array[i]))
        i ++;
    }
    Lua.Call("func", list)


Code: lua

    function func(...) -- This means it has an arbitrary number of args
        local array = {...} -- This encapsulates those args into a table.
        --do shit with array
    end


If you want to have other values too you can do this:

Code: lua

    function func(valA, valB, ...)
        -- now you can use valA and valB as normal and still encap the ... args.
    end

Crimson Wizard

Quote from: Scavenger on Sat 30/11/2013 19:01:28
Okay, I've moved all of the necessary files over to 3.2.1 and I'll just work on this module from there until it can be confirmed that Lua and 3.3.0 can work nicely together.
Do you have a 3.2.1 project that works well for you? Having that will help to debug 3.3.0.

Scavenger

The 3.2.1 files are here: https://dl.dropboxusercontent.com/u/50882197/Game/paltest321.zip

Also, how do you access the palette array with Lua? I've tried ags.palette.r et al but it just returns:
Error: [Lua][string "ProcessTranslucency.lua"]:99 attempt to index field 'palette' (a nil value)

Code: Lua
function ProcessPalette (...)
  local palindex = {...}
  local paltemp = {...}
  local i = 0
  local i2 = 1
  local upperboundary
  C8Palgorithm = {}
  C8PaletteMap = {}
  while (i < table.getn(palindex))
  do
    paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
    paltemp [i] = bit.rshift(paltemp [i],3) 
    i = i + 1
  end
  i = 0
  C8palgorithm = {}
  while (i < palsiz)
  do
      if (i > palsiz -2) then upperboundary = 255
      else upperboundary = (paltemp [i] + paltemp [i + 1])/2; end
      
      while (i2 < upperboundary)
      do
        C8palgorithm [i2] = palindex[i]
        i2 = i2 + 1
      end
    i = i + 1
  end
    
   i= 0
   paltemp = {}
   while (i < 255)
      do
       paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
       paltemp [i] = bit.rshift(paltemp [i],3)
       i = i+ 1
      end
    i = 0
    while (i < 255)
      do
       C8PaletteMap [i] = C8palgorithm [paltemp[i]]
       i = i + 1
      end
  
end

Calin Leafshade

It doesn't look like there is a way to access the palette. I'd suggest posting that in the Lua Plugin thread to get it added.

You could just pass the palette to the plugin in the way I described in my last post though.

Crimson Wizard

Hmm, interesting.

First of all, the effect I see in this game is different, the Jacob (name is right?) character is not translucent, but he has some weird effect applied on it, looks like running vertical scanlines. Is this what is expected to happen?

Secondly, I made a copy of the project, opened it in 3.3.0.1148 (beta 10) and made full rebuild. The game runs fine with same effect. No crashes so far  (waited till loop 500).

Scavenger

I was testing the very edges of the translucency effect, since I only have 8 CLUTs at the moment, change the SetTransparency function in Room 1.asc to 50 to see a more spectacular effect.

And I'll do that, thanks Calin!

Crimson Wizard

Quote from: Scavenger on Sat 30/11/2013 21:48:08
I was testing the very edges of the translucency effect, since I only have 8 CLUTs at the moment, change the SetTransparency function in Room 1.asc to 50 to see a more spectacular effect.
Ah, I see now. Yes, that's nice, although his legs do not look translucent. Maybe because the background color is too close, or that table missing some colors?
Anyway still works well in 3.2.1 and 3.3.0. Maybe the previous errors was due "project corruption" you mentioned?
I'll keep an eye on how this progresses, I must make sure that 3.3.0 works well with plugins.

Scavenger

Yeah, there's probably not enough purples and purple-like colours in the palette for it to work properly. It DOES, however, work really nicely for a wide variety of effects - I'm not quite sure how to make characters translucent yet as that requires fiddling around with the ViewFrame functions and that is some deep stuff I don't want to mess around with.

I'm trying to code a workaround for passing the palette to Lua, but I don't think Lua likes having 256 parameters in it's functions.

Code: AGS
function GiveLuaPalette ()
{
  LuaValueList *r = Lua.NewValueList();
    int i = 0;
    while (i < 255)
    {
        r.Add(Lua.IntValue(palette[i].r));
        i ++;
    }
    Lua.Call("CatchPalette_R", r, eLuaUnprotectedMode);
    
    LuaValueList *b = Lua.NewValueList();
    i = 0;
    while (i < 255)
    {
        b.Add(Lua.IntValue(palette[i].b));
        i ++;
    }
    Lua.Call("CatchPalette_B", b, eLuaUnprotectedMode);
    
      LuaValueList *g = Lua.NewValueList();
    i = 0;
    while (i < 255)
    {
        g.Add(Lua.IntValue(palette[i].g));
        i ++;
    }
    Lua.Call("CatchPalette_G", g, eLuaUnprotectedMode);
}


function ProcessPaletteLua (int palsiz, int palindex [])
{
  GiveLuaPalette ();
  LuaValueList *list = Lua.NewValueList();
    int i = 0;
    while (i < palsiz)
    {
        list.Add(Lua.IntValue(palindex[i]));
        i ++;
    }
    Lua.Call("ProcessPalette", list, eLuaUnprotectedMode);
}

static function PALgorithms::ProcessPalette (int palsiz, int palindex[])
{
  /*
  int paltemp [];
  paltemp = new int [palsiz];
  char i = 0;
  int i2 = 1;
  int upperboundary;
  while (i < palsiz)
  {
    paltemp [i] = (palette[palindex[i]].r +  // This algorithm produces an accurate, fast approximation of
                   palette[palindex[i]].r +  // The luminescence value of each of our palette slots.
                   palette[palindex[i]].r +  //
                   palette[palindex[i]].b +  // This ensures that when we colourise the final product, there 
                   palette[palindex[i]].g +  // will be little to no weird artifacts or lightness shifts.
                   palette[palindex[i]].g +  //
                   palette[palindex[i]].g +  // By using addition and bit shifts instead of multiplication, 
                   palette[palindex[i]].g    // the code will run a lot faster and more efficiently.
                   )>>3;
    i++;
  }
  i = 0;
  while (i < palsiz)
  {
      //Display ("%d , %d", i,  i+1);
      if (i > palsiz -2) upperboundary = 255;
      else upperboundary = (paltemp [i] + paltemp [i + 1])/2;
      //Display ("%d",upperboundary);

      while (i2 < upperboundary)
      {
        String output;
        _C8palgorithm [i2] = palindex[i];
        i2++;
      }
    i++;
    }
    
    i= 0;
    paltemp = new int [256];
    while (i < 255)
      {
       paltemp [i] = (palette[i].r +  // This algorithm produces an accurate, fast approximation of
                      palette[i].r +  // The luminescence value of each of our palette slots.
                      palette[i].r +  //
                      palette[i].b +  // This ensures that when we colourise the final product, there 
                      palette[i].g +  // will be little to no weird artifacts or lightness shifts.
                      palette[i].g +  //
                      palette[i].g +  // By using addition and bit shifts instead of multiplication, 
                      palette[i].g    // the code will run a lot faster and more efficiently.
                      )>>3;
                      i++;
      }
    i = 0;
    while (i < 255)
      {
       _C8PaletteMap [i] = _C8palgorithm [paltemp[i]];
       i++;
      }
      */
      ProcessPaletteLua (palsiz, palindex);
  } 
  
function Colourise (DynamicSprite* sprite)
{
  LuaValueList *list = Lua.NewValueList();
  list.Add(sprite.AsLuaValue());
  Lua.Call("Colourise", list, eLuaUnprotectedMode);
}

function ColouriseArea (this DynamicSprite*, int x1,  int y1,  int w,  int h)
{
  DrawingSurface *Surface;
  DynamicSprite *Area;
  //Display ("Init C8");
  mouse.Visible = false;
  Wait (1);
  Area = DynamicSprite.CreateFromScreenShot (320, 200);
  //Display ("Screenshot created.");
  mouse.Visible = true;
  if (Area.ColorDepth != 8) AbortGame ("Error in COLOR8: Isn't an indexed image!");
  int realwidth = w;
  int realheight = h;
  if (x1 + w > 320) realwidth = 320 - x1;
  if (y1 + h > 200) realheight = 200 - y1;
  int realx = x1;
  int realy = y1;
  if (x1 == 320) 
  {
    realx = 319;
    realwidth = 1;
  }
  if (y1 == 200)
  {
    realy = 199;
    realheight = 1;
  }
  Area.Crop (realx, realy, realwidth, realheight);
  Colourise (Area);
  /*Surface = Area.GetDrawingSurface ();
  int i;
  int j;
  int cpixel;
  while (j < h)
  {
    while (i < w)
    {
      cpixel = Surface.GetPixel (i, j);
      //Display ("Coordinates: %d, %d. Colour: %d",i, j,  Surface.GetPixel (i, j));
      if (cpixel == COLOR_TRANSPARENT) Surface.DrawingColor = COLOR_TRANSPARENT;
      else Surface.DrawingColor = _C8PaletteMap [cpixel];
      Surface.DrawPixel (i, j);
      i++;
    }
    i = 0;
    j++;
  } 
  this.Crop (x1, y1, w, h);
  Surface.Release ();
  */
  this.Crop (realx, realy, realwidth, realheight);
  Surface = this.GetDrawingSurface ();
  Surface.DrawImage (0, 0, Area.Graphic);
  Surface.Release ();
  Area.Delete ();
  return this.Graphic;
}


Code: Lua

function CatchPalette_R (...)
  local i = 0
  local r = {...}
  palette_r = {}
  while (i < 255)
  do
      palette_r [i] = r[i]
      i = i + 1
  end
end

function CatchPalette_B (...)
  local i = 0
  local b = {...}
  palette_b = {}
  while (i < 255)
  do
      palette_b [i] = b[i]
      i = i + 1
  end
end

function CatchPalette_G (...)
  local i = 0
  local g = {...}
  palette_g = {}
  while (i < 255)
  do
      palette_g [i] = g[i]
      i = i + 1
  end
end


function ProcessPalette (...)
  local palindex = {...}
  local paltemp = {...}
  local i = 0
  local i2 = 1
  local upperboundary
  C8Palgorithm = {}
  C8PaletteMap = {}
  while (i < table.getn(palindex))
  do
    --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
    paltemp [i] = palette_r [i] + palette_r [i] + palette_r [i] + palette_b [i] +  palette_g [i] + palette_g [i] + palette_g [i] + palette_g [i]
    paltemp [i] = bit.rshift(paltemp [i],3) 
    i = i + 1
  end
  i = 0
  C8palgorithm = {}
  while (i < palsiz)
  do
      if (i > palsiz -2) then upperboundary = 255
      else upperboundary = (paltemp [i] + paltemp [i + 1])/2; end
      
      while (i2 < upperboundary)
      do
        C8palgorithm [i2] = palindex[i]
        i2 = i2 + 1
      end
    i = i + 1
  end
    
   i= 0
   paltemp = {}
   while (i < 255)
      do
       --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
       paltemp [i] = palette_r [i] + palette_r [i] + palette_r [i] + palette_b [i] +  palette_g [i] + palette_g [i] + palette_g [i] + palette_g [i]
       paltemp [i] = bit.rshift(paltemp [i],3)
       i = i+ 1
      end
    i = 0
    while (i < 255)
      do
       C8PaletteMap [i] = C8palgorithm [paltemp[i]]
       i = i + 1
      end
  
end

function Colourise (ds)
  local surface = ds:GetDrawingSurface ()
  local i;
  local j;
  local h = ds.Height
  local w = ds.Width
  local cpixel;
  while (j < h)
  do
    while (i < w)
    do
      cpixel = surface:GetPixel (i, j)
      if (cpixel == 0)
        then
          surface.DrawingColor = 0
      else 
          surface.DrawingColor = C8PaletteMap [cpixel]
        end
      surface:DrawPixel (i, j)
      i = j + 1
    end
    i = 0
    j = j + 1
  end
  surface:Release ()
end


It just quits with an unspecified error.

Calin Leafshade

Oh apparently you can pass arrays:

Code: ags

    LuaValueList *r = Lua.NewValueList();
    int i = 0;
    while (i < 255)
    {
        r.Add(Lua.IntValue(palette[i].r));
        i ++;
    }
    Lua.Call("CatchPalette_R", r.ToArray(), eLuaUnprotectedMode);


Code: lua

function CatchPalette_R (r)
  palette_r = r
end



Scavenger

I get:

Quotepal2.asc(569): Error (line 569): '.ToArray' is not a public member of 'LuaValueList'. Are you sure you spelt it correctly (remember, capital letters are important)?

Maybe it's in a newer version than what I've got? Where's the latest version of the plugin?

Calin Leafshade


Scavenger

Funny, I do have that version (LuaForAGS_v6_b), but it's not got ToArray in it. I'm not sure what's going on there. Perhaps it wasn't implemented yet?

Calin Leafshade

It's not mentioned in the code so it looks like Denzil planned to implement it but never did.

you could try Lua.Evaluate.

Code: ags

int i = 0;
while(i < 255)
    Lua.Evaluate(String.Format("palette_r[%d] = %d", i, value));
    i++;
end


Scavenger

Got this:

QuoteScript link failed: Runtime Error: Unresolved import 'Lua::Evaluate^2'

I am really sorry I am having so much trouble with this. x3

Calin Leafshade

Not a problem dude.

Seems that function is also missing.

try:

Code: ags

Lua.SetVar(String.Format("palette_r[%d]",i), value)

Scavenger

Okay, I managed to make ProcessPalette start working! It's pretty smooth up until Next error is this particularily nebulous one:

QuoteAttempt to compare nil with number

Code: Lua
function ProcessPalette (...)
  local palindex = {...}
  local paltemp = {...}
  local i = 0
  local i2 = 1
  local upperboundary = 0
  C8Palgorithm = {}
  C8PaletteMap = {}
  while (i < table.getn(palindex))
  do
    --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
    paltemp [i] = palette_r [i] + palette_r [i] + palette_r [i] + palette_b [i] +  palette_g [i] + palette_g [i] + palette_g [i] + palette_g [i]
    paltemp [i] = bit.rshift(paltemp [i],3) 
    i = i + 1
  end
  i = 0
  C8palgorithm = {}
  while (i < table.getn(palindex))
  do
      if (i > table.getn(palindex)-2) then upperboundary = 255
      else upperboundary = (paltemp [i] + paltemp [i + 1])/2  --THIS IS THE LINE IT CHOKES AT
      end
      
      while (i2 < upperboundary)
      do
        C8palgorithm [i2] = palindex[i]
        i2 = i2 + 1
      end
    i = i + 1
  end
    
   i= 0
   paltemp = {}
   while (i < 255)
      do
       --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
       paltemp [i] = palette_r [i] + palette_r [i] + palette_r [i] + palette_b [i] +  palette_g [i] + palette_g [i] + palette_g [i] + palette_g [i]
       paltemp [i] = bit.rshift(paltemp [i],3)
       i = i+ 1
      end
    i = 0
    while (i < 255)
      do
       C8PaletteMap [i] = C8palgorithm [paltemp[i]]
       i = i + 1
      end
  
end


Splitting the line into two:

Code: Lua
        upperboundary = paltemp [i] + paltemp [i + 1]
        upperboundary = upperboundary /2 --It chokes here now.


I'm sure I'll be able to figure out errors like these when I get more used to Lua, but... choking on a simple division?

Calin Leafshade

dont use table.getn use the # operator instead. #palindex returns the number of values in the array (numberically indexed) part of a table.

Quote
Attempt to compare nil with number

That error is for a comparison (i.e < > <= >= == ~=). If the value were nil you'd get an arithmatic error.
Are you sure thats the offending line?

Scavenger

#45
Okay, copied the lua file into Notepad++ to count the lines there, solved that issue.

But there are so many other issues cropping up I am beginning to lose hope that I'll ever get this done. I don't think I'm quite qualified to even do this.

1) The colourisation function crashes AGS with the same
Quote
Error: [Lua] [string "local select = select;local error = error;l..."]:1: bad argument #2 to 'setter' (number expected, got nil)
Error. I have no idea what is going wrong there. I added a UsingLua bool you can switch on and off to see the intended effect there.

2) Only Object 0 is affected by translucency. Even though in repeatedly_execute_always it iterates through all of the objects on screen, only Object 0 will be translucent.

3) Other objects are invisible behind a translucent object. I think this is related to 2, as somewhere along the line the function is just choking. Characters render fine. This is odd, as I knew I tested this in the AGSScript version. It just... stopped working.

I'll put the project here:
https://dl.dropboxusercontent.com/u/50882197/Game/paltest321_errors.zip
If anyone wants to take a look. It uses the FakeScreen functions (DrawObject, DrawCharacter) to build the background and the characters on it so that the translucency drawing functions know what is behind the object at the time. The workflow goes:

repeatedly execute always: Is there translucency to be rendered? If so, create a surface and start building on it until a translucent object needs to be rendered. Then, render the translucent object (DrawObject) and set that to be the object's graphic.

Denzil Quixode

Hi, hi, really sorry you've been having all these issues with the Lua plugin! Please bear with me a little. This is a really cool project and I always wanted people to use the plugin for ambitious interesting things so I feel like I've really dropped the ball on this one. I have posted some stuff in the plugin thread about where the problem seems to be in issue 1.) there. I will address missing API functions and the lousy documentation as soon as I can. Sorry again!

Denzil Quixode

#47
Two problems I noticed in the Lua Colourise() function:

  • cpixel is sometimes -1, and C8PaletteMap[-1] doesn't exist, so surface.DrawingColor is being set to nil, which is invalid
  • there's an infinite loop problem in there:
Code: lua
while (i < w) do

  -- ( . . . ) --

  i = j + 1
end
    (...should be i = i + 1)


Also, this bit in ProcessPalette:
Code: Lua
while (i < palsiz)
do
  if (i > palsiz) then upperboundary = 255
  -- ... --


...looks like it should probably have "-2" added, to be equivalent to the AGSScript version in pal2.asc:

Code: AGS
while (i < palsiz)
{
  if (i > palsiz -2) upperboundary = 255;

Scavenger

It's cool, Denzil, I'm really liking this plugin and it can do some amazing things compared to plain AGSScript. It's mostly my newness to Lua that is holding me back.

I corrected those errors, but I'm still getting the same problem:

Code: Lua
function Colourise (ds)
  local surface = ds:GetDrawingSurface ()
  local i = 0;
  local j= 0;
  local h = ds.Height
  local w = ds.Width
  local cpixel;
  while (j < h)
  do
    while (i < w)
    do
      cpixel = surface:GetPixel (i, j)
      if (cpixel == -1)
        then
          surface.DrawingColor = 0
      else 
          surface.DrawingColor = C8PaletteMap [cpixel]
        end
      surface:DrawPixel (i, j)
      i = i + 1
    end
    i = 0
    j = j + 1
  end
  surface:Release ()
end

Calin Leafshade

Here are some tricks to simplify your code:

Code: lua

function Colourise (ds)
  local surface = ds:GetDrawingSurface ()
  local h = ds.Height
  local w = ds.Width
  local cpixel;
  for j=0, h-1 do
    for i=0, w-1 do
        cpixel = surface:GetPixel (i, j)
        surface.DrawingColor = C8PaletteMap[cpixel] or 0
        surface:DrawPixel (i, j)
    end
  end
  surface:Release ()
end


See if that crashes. It makes it so that DrawingColor can never be nil.

Denzil Quixode

^^ Yeah, a good suggestion from Calin there. So it looks like Lua's C8PaletteMap has some holes in it, not all palette slots are being set in ProcessPalette().

One thing I notice about ProcessPalette() is that you're taking an array of parameters using palindex = {...} but this array will start from 1 rather than 0, and later code adapted from AGSScript naturally isn't expecting this. So to change it to a 0-based array, you could change the first few lines of ProcessPalette() from:

Code: Lua
function ProcessPalette (...)
  local palindex = {...}
  local paltemp = {...}
  local palsiz = #palindex


...to:

Code: Lua
function ProcessPalette (...)
  local palindex = {}
  local paltemp = {}
  local palsiz = select('#', ...) -- get number of arguments
  for n = 1, palsiz do
    palindex[n - 1] = select(n, ...) -- get nth argument
    paltemp[n - 1] = select(n, ...)
  end


A second thing in ProcessPalette(): it looks like, in the first while-loop, where the original AGSScript version of the function uses ags.palette[palindex[ i ]].r etc. the Lua version uses (the equivalent of) ags.palette[ i ].r

Scavenger

Alright! With Calin and Denzil's amendments to the code, it no longer crashes, and it shows the effect! I fixed the palette[palindex[ i ]].r bit and the colours got more accurate, but they still aren't correct compared to the AGSScript version.

Code: AGS
int paltemp [];
  paltemp = new int [palsiz];
  char i = 0;
  int i2 = 1;
  int upperboundary;
  while (i < palsiz)
  {
    paltemp [i] = (palette[palindex[i]].r +  // This algorithm produces an accurate, fast approximation of
                   palette[palindex[i]].r +  // The luminescence value of each of our palette slots.
                   palette[palindex[i]].r +  //
                   palette[palindex[i]].b +  // This ensures that when we colourise the final product, there 
                   palette[palindex[i]].g +  // will be little to no weird artifacts or lightness shifts.
                   palette[palindex[i]].g +  //
                   palette[palindex[i]].g +  // By using addition and bit shifts instead of multiplication, 
                   palette[palindex[i]].g    // the code will run a lot faster and more efficiently.
                   )>>3;
    i++;
  }
  i = 0;
  while (i < palsiz)
  {
      //Display ("%d , %d", i,  i+1);
      if (i > palsiz -2) upperboundary = 255;
      else upperboundary = (paltemp [i] + paltemp [i + 1])/2;
      //Display ("%d",upperboundary);

      while (i2 < upperboundary)
      {
        _C8palgorithm [i2] = palindex[i];
        i2++;
      }
    i++;
    }
    
    i= 0;
    paltemp = new int [256];
    while (i < 255)
      {
       paltemp [i] = (palette[i].r +  // This algorithm produces an accurate, fast approximation of
                      palette[i].r +  // The luminescence value of each of our palette slots.
                      palette[i].r +  //
                      palette[i].b +  // This ensures that when we colourise the final product, there 
                      palette[i].g +  // will be little to no weird artifacts or lightness shifts.
                      palette[i].g +  //
                      palette[i].g +  // By using addition and bit shifts instead of multiplication, 
                      palette[i].g    // the code will run a lot faster and more efficiently.
                      )>>3;
                      i++;
      }
    i = 0;
    while (i < 255)
      {
       _C8PaletteMap [i] = _C8palgorithm [paltemp[i]];
       i++;
      }


Code: Lua
function ProcessPalette (...)
  local palindex = {}
  local paltemp = {}
  local palsiz = select('#', ...) -- get number of arguments
  for n = 1, palsiz do
     palindex[n - 1] = select(n, ...) -- get nth argument
     paltemp[n - 1] = select(n, ...)
  end
  local i = 0
  local i2 = 1
  local upperboundary = 0
  C8palgorithm = {}
  C8PaletteMap = {}
  C8PaletteMap [0] = C8PaletteMap [0] or {}
  C8palgorithm [0] = C8palgorithm [0] or {}
  while (i < palsiz)
  do
    --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
    paltemp [i] = palette_r [palindex[i]] + palette_r [palindex[i]] + palette_r [palindex[i]] + palette_b [palindex[i]] +  palette_g [palindex[i]] + palette_g [palindex[i]] + palette_g [palindex[i]] + palette_g [palindex[i]]
    paltemp [i] = bit.rshift(paltemp [i],3) 
    i = i + 1
  end
  i = 0
  C8palgorithm = {}
  while (i < palsiz)
  do
      if (i > palsiz - 2) then upperboundary = 255
      else 
        upperboundary = paltemp [i] + paltemp [i + 1]
        upperboundary = upperboundary /2
        end
      
      while (i2 < upperboundary)
      do
        C8palgorithm [i2] = palindex[i]
        i2 = i2 + 1
      end
    i = i + 1
  end
   i= 0
   paltemp = {}
   while (i < 255)
      do
       --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
       paltemp [i] = palette_r [i] + palette_r [i] + palette_r [i] + palette_b [i] +  palette_g [i] + palette_g [i] + palette_g [i] + palette_g [i]
       paltemp [i] = bit.rshift(paltemp [i],3)
       i = i+ 1
      end
    i = 0
    while (i < 255)
      do
       C8PaletteMap [i] = C8palgorithm [paltemp[i]]
       i = i + 1
      end
  
end


Is the problem that Lua is counting from 1 again in the latter part of it?

The effect now looks like this:


The code looks identical, but the effect is very off. I'm not even sure how it's getting those non-blue colours.

ALSO, I'm trying to fix the translucency things, and I've noticed something very odd about the FakeScreen code I'm using:

Code: AGS
while ((i < Game.CharacterCount) || (i < Room.ObjectCount)) {
    if ((i < Game.CharacterCount) && (character[i].Room == player.Room)) surface.DrawCharacter(character[i]);
    if (i < Room.ObjectCount) {
      if (object[i].Visible) surface.DrawObject(object[i]);
      if (object[i].Graphic && object[i].Visible) {
        int scale = GetScalingAt(object[i].X, object[i].Y);
        int ow = (Game.SpriteWidth[object[i].Graphic] * scale) / 100;
        int oh = (Game.SpriteHeight[object[i].Graphic] * scale) / 100;
        if (object[i].IgnoreScaling) {
          ow = Game.SpriteWidth[object[i].Graphic];
          oh = Game.SpriteHeight[object[i].Graphic];
        }
        int ox1 = object[i].X;
        int ox2 = ox1 + ow;
        int j = 0;
        while (j < Game.CharacterCount) {
          if (character[j].Room == player.Room) {
            ViewFrame *frame = Game.GetViewFrame(character[j].View, character[j].Loop, character[j].Frame);
            int cw = (Game.SpriteWidth[frame.Graphic] * character[j].Scaling) / 100;
            int cx1 = character[j].x - (cw / 2);
            if ((((cx1 + cw) >= ox1) && (cx1 <= ox2)) && (character[j].y > object[i].Baseline) && ((character[j].y - character[j].z) >= (object[i].Y - oh))) surface.DrawCharacter(character[j]);
          }
          j++;
        }
      }
    }
    i++;
  }


It doesn't take into account the baseline of the objects, and it only seems to ever draw one object on it's surface. How do I sort objects by their baseline and draw them top-to-bottom, instead of by their object IDs? Otherwise I get stuff like this:



Which makes the effect kind of pointless. (The other object is invisible even when it's Y coordinate is above the translucent sprite. I have no idea what's going on!)

I'm really sorry for being so useless at solving these problems. This must be getting really irritating. x3

Denzil Quixode

#52
Quote from: Scavenger on Tue 03/12/2013 17:07:55
The effect now looks like this:


The code looks identical, but the effect is very off. I'm not even sure how it's getting those non-blue colours.
I think there's something odd going on when you use DrawingColor zero. I tried changing Lua's Colourise() to draw every pixel to zero, and got a lot more of that colourful effect:



Then I tried the same as Calin's except with or 255 instead of or 0:



So it looks like (I'm guessing here) drawing with DrawingColor 0 is adding transparency, punching a "hole" in the image, but what is showing up "behind" it happens to be a lot of weird pixels when Lua does it, but solid black when AGSScript does it. I've got no idea why though.

Scavenger

Yeah, that works! Weird that Lua is doing that with DrawingColor=0. I'm guessing it doesn't show up on the Translucency because Lua doesn't actually write pixels of the 0 colour.

This is a weird bug - does it do the same thing in 16 bit and 32bit with magic pink?

SMF spam blocked by CleanTalk