Hey, we have forums!

Author Topic: Form modification/Creation  (Read 33646 times)

0 Members and 1 Guest are viewing this topic.

Offline Titch

  • Bit Bit
  • ****
  • Posts: 55
  • Aquatic Rocket Scientist
    • View Profile
Re: Form modification/Creation
« Reply #15 on: January 09, 2009, 06:22:36 pm »
Thanks Edwards, any chance I could get a copy of that functions list?. By using setForm(9) with the costume change it's starting to look like a new form.

I'm using Songs.lua to call the new form, I'm not sure if putting a check to see if her form has changed will work there since it seems like songs is only executed when you are singing (unless of course reverting you form is a zero note song, then it might get called anyway). So I'll probably have to make like TimerBeast and stick a node on top of Naija to handle everything. Might be a little tricky to tie up neatly, but at least I could put the new ability functions inside the node so it's all neat like. :)

http://www.niftyhat.com/posted_stuff/ninja_form.zip

The transformation song is:
 
Mostly because I liked the melody it created.

After some fits and starts I got it working. Admittedly the form doesn't do anything right now, its just a costume that behaves like a form right now. I've got to do some intermediate functions to manage the new forms and stuff. You can see basically how I pulled it off (and look at the form skin :))
« Last Edit: January 10, 2009, 02:10:38 am by Titch »

Offline Particlese

  • Bit Bit
  • ****
  • Posts: 85
    • View Profile
Re: Form modification/Creation
« Reply #16 on: January 15, 2009, 05:29:15 am »
This is great news!  I'll have to take a look at it this weekend.  Are you having problems with Naija flipping when you change costumes?  There were two threads that sort of mentioned it, but, at least in the first one I linked to, they replaced Naija's graphics using another entity.  It sounds like you're taking a different approach that won't run into that problem, but I thought I'd ask anyway.

Offline Edwards

  • Bit Bit
  • ****
  • Posts: 93
    • View Profile
Re: Form modification/Creation
« Reply #17 on: January 15, 2009, 05:58:25 am »
No, there are no flipping problems with this, as it uses the built-in method of re-skinning Naija.  Those other threads were using even uglier hacks to re-skin Naija, as (a) the data files weren't accessible then, so the costume system couldn't be reverse-engineered, and (b) at least in some cases, they were attempting extreme changes, like swapping a sea turtle for Naija (this can't be done with the costume system).

- Edwards
You should only need one canister shell to bag your deer using your howitzer, but assemble more than one if  you have a mind to.1

Offline Titch

  • Bit Bit
  • ****
  • Posts: 55
  • Aquatic Rocket Scientist
    • View Profile
Re: Form modification/Creation
« Reply #18 on: January 15, 2009, 04:43:09 pm »
As Edwards said, this doesn't cause the flipping issue because I'm only doing a re-skin. It's worth noting that when re-skining for a form you MUST name the head files using the same string you used to name the costume xml file. For example in the mod I was using "ninja_naija.xml" so all the heads are "ninja_naija-normal" and so on, it doesn't matter what head entry you have in the xml itself, the only thing that matters is the string you are using to call the costume (and thus the name of the xml file you have used). I have been having some trouble with initHair (I've been trying to give Ninja form a bandanna. It works, but throws up an error message about creating a hair entity first. Also when I return to normal form Naija's cape seems to take on some of the properties I gave the bandanna. I probably need to store the cape off somewhere and restore it when you return to normal form.

Anyway, script updates. I'm writing a "switchForm" and "restoreForm" to help organize and clarify the code when you have more than one form as well as formsinclude.lua to store new static variables for the new forms. A big confustion right now is that switchForm is in Songs.lua while restoreForm I'm also adding a second form, just for demonstrating multiple form management. I've been working on a unique ability or two for Ninja form for actually doing something more than making ascetic changes. Having failed to alter Naija's max speed on transformation (I managed to change her max overall speed to 2000, with hilarious results) I'm trying to impliment a teleport ability where Naija just jumps directly to wherever you click on the screen instead of boosting.

Offline Particlese

  • Bit Bit
  • ****
  • Posts: 85
    • View Profile
Re: Form modification/Creation
« Reply #19 on: January 15, 2009, 06:37:36 pm »
Cool.

It might be useful to separate switchForm() from songs.lua and add it as an include instead.  This would allow you to force the form change when you defeat a boss, for example.

Offline Titch

  • Bit Bit
  • ****
  • Posts: 55
  • Aquatic Rocket Scientist
    • View Profile
Re: Form modification/Creation
« Reply #20 on: January 16, 2009, 02:12:10 am »
So I've gotten Ninja form doing this nifty teleport/dash thing when you click LMB to boost. Like the beast form bite it only works when you would usually be able to boost, so if you use it into the middle of and open area be prepared to wait a bit for a recharge time. Unlike the beast form it doesn't damage anything; but it ignores all the physical barriers between you and where you click the mouse. You can zip down tunnels like a demon as long as you can click accurately enough.

Which brings me to the current shortcomings of this move right now. It doesn't activate at all if you click somewhere Naija can't go, even if it's just overlapping the wall a little -too- much. I put in the fix I did to stop players from teleporting into the walls and it seems to have worked fairly well. I think I'll need to do some nifty vector tracing to find walls near the mouse cursor and use them. Additionally, the form_manager only initiates when songs.lua is called; which means it goes strange if you reset the level while still in form. A cleverer fix for this should be on the way. The Ninja costume still doesn't look right, I'll fix that soon too.

I'm working on a small-ish assault course level so people can put the form through it's paces, hopefully without too much fluff so it can be an example that other programmers can pick apart. It's not nearly finished yet, but there are a couple of areas the dodge really shines in already. I'm trying to decide on the secondary ability for the form, I want something else evasion/movement based rather than combit-ish. Although something that could be indirectly damaging would be nice.

http://www.niftyhat.com/posted_stuff/ninja_form.zip

Offline Chibi

  • Dream Bit
  • **********
  • Posts: 1443
  • Deus Ex Aquaria
    • View Profile
    • Prize Rebel ($50 dollars earned in two months)
Re: Form modification/Creation
« Reply #21 on: January 16, 2009, 05:37:35 am »
That is very neat - if it's as good as it sounds (and I believe it is) I'll be one of the first people to play the mod. I've an idea for the secondary shot - a shockwave that propels enemies out of your way (particularly useful for fast melee attackers).  :)

Life is a sexually transmitted disease with a 100% mortality rate. [Click the signature for +1 health!]

Offline TheBear

  • Extra Bit
  • *****
  • Posts: 199
    • View Profile
    • http://doconnell.wordpress.com
Re: Form modification/Creation
« Reply #22 on: January 16, 2009, 06:22:21 am »
Form works quite well and was fun. Being able to teleport definitely adds some new level design opportunities. Keep up the good work.

Offline Chibi

  • Dream Bit
  • **********
  • Posts: 1443
  • Deus Ex Aquaria
    • View Profile
    • Prize Rebel ($50 dollars earned in two months)
Re: Form modification/Creation
« Reply #23 on: January 16, 2009, 06:41:37 am »
Just a heads up - all the files have the same names as ones present in the game files: makes installing them a little annoying.

Life is a sexually transmitted disease with a 100% mortality rate. [Click the signature for +1 health!]

Offline Edwards

  • Bit Bit
  • ****
  • Posts: 93
    • View Profile
Re: Form modification/Creation
« Reply #24 on: January 16, 2009, 06:46:33 am »
Wow.  I could really get used to that form... :D

Anyway, that looks like a pretty decent set-up you have for keeping track of the form, its abilities, etc.  songs.lua will need a bit of clean-up before you release a version for developers to pick apart (and you might want to add a few comments), but otherwise, it's looking good.  I would suggest going back to just having a form_manager present in every level from the start, though.  postInit() fires after the level is created, regardless of whether the entity is on-screen, so if you move the n= getNaija(), entity_setPosition(...) lines into postInit(), the local form_manager will jump to be on top of Naija as soon as the level starts.  Then, you won't need to worry about duplicates (you're currently creating a new one every time a song is sung.  I hadn't meant for that duplicate-handling code to ever be used :P), and you won't have any trouble with reverting before singing, either after reloading or after entering a new map.

As for the problem of remembering the previous costume, good luck.  The best I can come up with is to have a n array of costume strings, and to save the index of the current costume in a flag when changing forms.  It's rather clunky, but at least it will maintain the memory across map transitions... :-\

- Edwards
You should only need one canister shell to bag your deer using your howitzer, but assemble more than one if  you have a mind to.1

Offline Titch

  • Bit Bit
  • ****
  • Posts: 55
  • Aquatic Rocket Scientist
    • View Profile
Re: Form modification/Creation
« Reply #25 on: January 18, 2009, 12:25:37 am »
I tried your fix Edwards, but the form manager still doesn't spawn until you sing a song of some kind, I think it's because the songs.lua doesn't execute until you sing a valid song of some kind.

Also I'm having some trouble with some nested loops. I've been trying to write a a simple while loop to test the best position to warp Naija to if the player clicks on solid rock using the dot_product function.

Code: [Select]
function getNearestPoint (cx,cy,i)
--cx and cy are target point, i is number of itterations to check for.
    cc = createEntity ("collision_check","",cx,cy)
    local bx =0
    local by =0
--cc is a dummy entity, all it has is a collision radius.
    if entity_isNearObstruction (cc, 1, OBSCHECK_RANGE) == false then
--if there isn't anything in the way just set the best spot to the target point
            bx= cx
            by= cy
    else
    -- Naija's x and y
        local nx,ny = entity_getPostion(n)
        -- dot product, half way between Naija and the target point
        local dx,dy = vector_dot (cx,cy,nx,ny)
        -- the best spot found.
        local bx,by = 0,0
        -- blocks eternal itterations.
        if i < 1 then i =1 end
            while i > 0 do
                -- put a collision checking object in where we want to test
                entity_setPositionX (cc,dx)
                entity_setPositionY (cc,dy)
                --this control hint never fires
                setControlHint("test 2", 0, 0, 0,1 )
                   if entity_isNearObstruction (cc, 1, OBSCHECK_RANGE) then
                       -- if it does collide, test a point closer to Naija
                       dx,dy = vector_dot (dx,dy,nx,ny)
                   else
                       -- if it doesn't collide, save that spot and test a spot futher away.
                       -- more itterations means you'll get closer to the obstruction.
                       bx,py = dx,dy
                       dx,dy = vector_dot (cx,cy,dx,dy)
                    end
                i = i-1
            end
    end
   --clean up the dummy checker.
    entity_delete(cc)
   --return the best spot to use
    return bx, by
end

The while loop works, the if loop works, they just don't work when I nest an if inside a while loop. It's quite frustrating. I've also probably used dot_vector() incorrectly based on my incredibly shaky memory of maths.

Since I'm on the subject of problems. I've started to try and implement a skill on the RMB, and for some reason isRightMouse () registers NOTHING right now. Perhaps I have to set some kind of state on the node or something.  I can kludge in using "entity_setActivation" but that means the cursor glows all the time, registering that it's over something that can be activated.
« Last Edit: January 18, 2009, 01:36:23 am by Titch »

Offline Edwards

  • Bit Bit
  • ****
  • Posts: 93
    • View Profile
Re: Form modification/Creation
« Reply #26 on: January 18, 2009, 06:26:49 am »
I tried your fix Edwards, but the form manager still doesn't spawn until you sing a song of some kind, I think it's because the songs.lua doesn't execute until you sing a valid song of some kind.
You do still need to place a form_manager entity in the level, first (and make sure you save the level afterward!).  What I was trying to say is that having songs.lua spawn form_managers would cause problems, and placing them manually when creating the levels would solve those problems.  For convenience' sake, I may come up with a modification to the entity's code to make it visible in the editor.

...
The while loop works, the if loop works, they just don't work when I nest an if inside a while loop. It's quite frustrating. I've also probably used dot_vector() incorrectly based on my incredibly shaky memory of maths.
I'll just do this the easy way- I've added comments (prefaced by --!) to your code, pointing out where you might be getting into trouble.
[EDIT] I don't have time to check this right now, but does anything bad happen if you feed a fraction into setPosition (say, entity_setPositionX(cc, 5000.35)?
Code: [Select]
function getNearestPoint (cx,cy,i)
--cx and cy are target point, i is number of itterations to check for.
    cc = createEntity ("collision_check","",cx,cy)
    local bx =0
    local by =0
--cc is a dummy entity, all it has is a collision radius.
    if entity_isNearObstruction (cc, 1, OBSCHECK_RANGE) == false then
--if there isn't anything in the way just set the best spot to the target point
            bx= cx
            by= cy
    else
    -- Naija's x and y
        local nx,ny = entity_getPostion(n)
        -- dot product, half way between Naija and the target point
        local dx,dy = vector_dot (cx,cy,nx,ny) --! No need for this, just use dx = nx + (cx-nx)/2, and its y equivalent
--! Furthermore, just start off with bx = nx, as that is guaranteed true
--! Although, I don't know what kind of dot product it's doing if it
--! returns two values- a dot product should return cx*nx+cy*ny
        -- the best spot found.
        local bx,by = 0,0
        -- blocks eternal itterations.
        if i < 1 then i =1 end --! Good point, given that I believe that 0 is passed if no argument is given.
--! This isn't "blocking eternal iterations", though.
         while i > 0 do
            -- put a collision checking object in where we want to test
            entity_setPositionX (cc,dx)
            entity_setPositionY (cc,dy)
            --this control hint never fires --! Sounds like a syntax error.
--! Does it fire if you put it at the start of the loop?
--! How about just before the loop?
--! Are you sure that dy has a value in it?  That might be the trouble.
            setControlHint("test 2", 0, 0, 0,1 )
--! What you're trying to do here is just a binary search,
--! with a limit on the number of iterations
--! I'll get back to you with a slightly more standard method in a bit
               if entity_isNearObstruction (cc, 1, OBSCHECK_RANGE) then
                   -- if it does collide, test a point closer to Naija
                   dx,dy = vector_dot (dx,dy,nx,ny)
               else
                   -- if it doesn't collide, save that spot and test a spot futher away.
                   -- more itterations means you'll get closer to the obstruction.
                   bx,py = dx,dy
                   dx,dy = vector_dot (cx,cy,dx,dy)
                end
            i = i-1
         end
    end
   --clean up the dummy checker.
    entity_delete(cc)
   --return the best spot to use
    return bx, by
end
[EDIT] Here's a re-written version of the script.  I haven't actually tried it, lacking the collision_check entity (and the rest of the testing framework you have), but I'm pretty sure it will work:
Code: [Select]
function getNearestPoint (endx,endy,i)
--cx and cy are target point, i is number of itterations to check for.
cc = createEntity ("collision_check","",endx,endy)
--cc is a dummy entity, all it has is a collision radius.
local destx = endx
local desty = endy

-- Check if the destination is bad.  If it isn't bad, the return values are already set up!
if entity_isNearObstruction(cc, 1, OBSCHECK_RANGE) then
-- The destination is blocked.  Run a search to find the furthest unblocked location.
-- Set the initial destination to Naija's current location, as this is guaranteed to be good
destx,desty = entity_getPostion(n)

if i < 1 then i = 1 end --! Force minimum iterations

-- while destx ~= endx and desty ~= endy and i > 0 do -- Try this line once the rest of the script works
while i > 0 do
-- Get a point half-way between the start and end of the area to check
local hx,hy = vector_half(destx, desty, endx, endy)
-- move the collision-checking entity to the half-way point
entity_setPositionX(cc, hx)
entity_setPositionY(cc, hy)

-- Just a debug string.  Note that only the last iteration will appear
setControlHint(string.format("Destination:%d,%d",hx,hy), 0, 0, 0,1 )
-- if it does collide, set this bad point to the end of the test range
if entity_isNearObstruction (cc, 1, OBSCHECK_RANGE) then
endx = hx
endy = hy
else
-- if it doesn't collide, set this good point to the beginning of the search range
-- and also to be the final destination
destx = hx
desty = hy
end
i = i-1
end
end
   --clean up the dummy checker.
entity_delete(cc)
   --return the best spot to use
return destx, desty
end

-- Returns a point halfway between the two given points (ax,ay) and (bx,by)
function vector_half(ax, ay, bx, by)
halfx = ax + (bx-ax)/2
halfy = ay + (by-ay)/2
return halfx, halfy
end


Since I'm on the subject of problems. I've started to try and implement a skill on the RMB, and for some reason isRightMouse () registers NOTHING right now. Perhaps I have to set some kind of state on the node or something.  I can kludge in using "entity_setActivation" but that means the cursor glows all the time, registering that it's over something that can be activated.
Odd.  I'll need to test that.  What version are you running, on what operating system?

[EDIT] Works fine for me.  Throw this snippet into an update(…) function somewhere- it's a basic display of what's going on with Naija:
   setControlHint(string.format("StillTimer:%d SpellCharge:%d Form:%d LeftMouse:%s RightMouse:%s", avatar_getStillTimer(),avatar_getSpellCharge(),getForm(),tostring(isLeftMouse()),tostring(isRightMouse())), 0, 0, 0, 2)

- Edwards
« Last Edit: January 18, 2009, 09:43:15 am by Edwards »
You should only need one canister shell to bag your deer using your howitzer, but assemble more than one if  you have a mind to.1

Offline Titch

  • Bit Bit
  • ****
  • Posts: 55
  • Aquatic Rocket Scientist
    • View Profile
Re: Form modification/Creation
« Reply #27 on: January 18, 2009, 05:46:15 pm »
Thanks Ed, your like the Guru of LUA for Aquaria :), or perhaps just LUA in general. The snippet for checking control inputs helped. I'm not quite sure how, I pasted it in and checked that it was registering the right mouse in the control hint and then just wrote out the if statement again, leaving out ==true and it just worked. Anyway, the snippet is pretty handy even though it can cause a bit of slowdown.

The iterating bit of code still isn't firing though. I don't get a hint output, but the teleport fires under normal conditions (destination clicked is reachable). All the code that is in the collision checker is this:-

Code: [Select]
function init(me)
    entity_setCollideRadius(me, 1)
end

There really isn't much to go wrong there, it's just to ensure it can register a collision, perhaps it's because theres no setup_entity() call. I'll be adding a msg callback at some point so that the form manager can cull them out if they somehow escape the cleanup in the function. I'll keep testing and see if I can work it out.

Anyway, form creation framework is moving foward. Now I can recognise right/left clicks I'll write a a function/callback structure for charging skills and the like. I'm not sure if there is a way to force Naija to charge, so I'll just have to fake it.
« Last Edit: January 18, 2009, 06:35:13 pm by Titch »

Offline Edwards

  • Bit Bit
  • ****
  • Posts: 93
    • View Profile
Re: Form modification/Creation
« Reply #28 on: January 18, 2009, 08:31:37 pm »
Thanks Ed, your like the Guru of LUA for Aquaria :), or perhaps just LUA in general.
Nah, I'm just a CS student who has about ten years of experience programming in various languages.  I'd never even touched Lua before I started working on Aquaria mods a month or so ago.

The iterating bit of code still isn't firing though. I don't get a hint output, but the teleport fires under normal conditions (destination clicked is reachable).
Half the problem there, now that I've tested it myself, is that you're calling "entity_setPostion(n), rather than entity_setPosition(n) just before the while loop.  My code is still causing random teleportation halfway across the map, but yours might work with that fixed.

[EDIT] Here's a fixed version of the code I posted last time.  I've tested it in-game and it works quite nicely (it may miss if Naija is on a wall, and there's a narrow passage behind that wall, and the player clicks beyond that passage, but that's inevitable with this search type.  Easy enough to fix, though, if you don't care too much about execution time- I'll post a variant in a  minute).
Code: [Select]
function teleport (x,y)
    x, y = getNearestPoint (x,y,20) -- changes to be offset down and left
spawnParticleEffect("shockwave",entity_getPosition(n))
entity_setPositionX (n,x)
entity_setPositionY (n,y)
entity_sound(n,"memory-flash",1000,0)
spawnParticleEffect("shockwave",entity_getPosition(n))
done_teleport=true
end


function getNearestPoint (endx,endy,i)
--cx and cy are target point, i is number of itterations to check for.
cc = createEntity ("collision_check","",endx,endy)
--cc is a dummy entity, all it has is a collision radius.
local destx = endx
local desty = endy

-- Check if the destination is bad.  If it isn't bad, the return values are already set up!
if entity_isNearObstruction(cc, 1, OBSCHECK_RANGE) then
-- The destination is blocked.  Run a search to find the furthest unblocked location.
-- Set the initial destination to Naija's current location, as this is guaranteed to be good
destx,desty = entity_getPosition(n)

if i < 1 then i = 10 end --! Force minimum iterations- I'd actually recommend forceing 10 iterations.
--i = 10-- This seems to work well
while i > 0 do
-- Get a point half-way between the start and end of the area to check
local hx,hy = vector_half(destx, desty, endx, endy)
-- move the collision-checking entity to the half-way point
entity_setPositionX(cc, hx)
entity_setPositionY(cc, hy)

-- Just a debug string.  Note that only the last iteration will appear
-- setControlHint(string.format("Destination:%d,%d",hx,hy), 0, 0, 0,1 )
-- if it does collide, set this bad point to the end of the test range
if entity_isNearObstruction (cc, 1, OBSCHECK_RANGE) then
endx = hx
endy = hy
else
-- if it doesn't collide, set this good point to the beginning of the search range
-- and also to be the final destination
destx = hx
desty = hy
end
i = i-1
end
end
   --clean up the dummy checker.
entity_delete(cc)
   --return the best spot to use
return destx,desty
end

-- Returns a point halfway between the two given points (ax,ay) and (bx,by)
function vector_half(ax, ay, bx, by)
halfx = ax + (bx-ax)/2
halfy = ay + (by-ay)/2
return halfx, halfy
end

[EDIT] Here's the code for a linear search, which fixes the occasional problems with being unable to teleport through walls.  It's actually nowhere near as inefficient as I'd expected- even long jumps work well with just 10 iterations, and fewer iterations might also work.  Just replace the while loop in the above code with this:
Code: [Select]
-- Linear search for open space
-- i=10 seems to work most of the time, although I'd like to have the step size be capped
for n = 0, i do
-- Get a point along the line beween Naija and the target
-- Starting the search from the target end of the line
local hx,hy = vector_fraction(endx, endy, destx, desty, i, n)
-- move the collision-checking entity to the half-way point
entity_setPositionX(cc, hx)
entity_setPositionY(cc, hy)
-- if it doesn't collide, set this to be the final destination,
-- as it is the furthest open point from Naija
if not entity_isNearObstruction (cc, 1, OBSCHECK_RANGE) then
destx = hx
desty = hy
break
end
end
And add this function:
Code: [Select]
function vector_fraction(ax,ay,bx,by,divs,numofdivs)
x = ax + (bx-ax)/divs*numofdivs
y = ay + (by-ay)/divs*numofdivs
return x,y
end

- Edwards
« Last Edit: January 18, 2009, 09:20:49 pm by Edwards »
You should only need one canister shell to bag your deer using your howitzer, but assemble more than one if  you have a mind to.1

Offline Titch

  • Bit Bit
  • ****
  • Posts: 55
  • Aquatic Rocket Scientist
    • View Profile
Re: Form modification/Creation
« Reply #29 on: January 18, 2009, 11:25:24 pm »
Again, thanks Edwards, I've been banging  my head on this for the past two days and not getting very far. With your help I can get on an try and focus on some of the other form related code, like charging levels and getting the flourish animation to work right.

As for limiting the step distance, I'm probably going to cap the jump distance. Right now since it's for the mouse only it jumps strait to wherever you can point the mouse, which gives widescreen and hi-res users a huge distance advantage (I run Aquaria at 640x480 in a window for testing  code, my laptop isn't exactly a powerhouse); plus I've got to bring it in line with how the jump is going to behave using the game pad. It's still going to be a fair distance, since I think the charm of it lies in the sheer speed you can move around areas.

So I'm building a forms include. So you can override forms from any bit of code that has the include, it's worth noting that dofile() seems to run from the Aquaria root, not the mod root. So it has to be dofile(_mods/myMod/scripts/myinclude.lua). I'm hoping to use scope to hide the old changeForm function like this:

Code: [Select]
do
    local __changeForm = changeForm
    changeForm = function (formID)
...code...
  __changeForm(formID)
end
end

So new forms can be swapped around with the same command. It will be a good place to stash new FORM_* constants. It's not working yet, but I'm nothing less than dogged ^_^

I've not used LUA before, I've been using ActionScript 2.0/3.0, UnrealScript and C# up until now. Hoping I can pick up enough about working LUA to work on something using C++/Allegro and LUA.