Aller au contenu

Photo

Shoot the Moon


  • Veuillez vous connecter pour répondre
27 réponses à ce sujet

#1
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages

<squeaking...>

 

So, yeah. I'm busy lately. But I made these cool Celestial Orbs for someone...

moondialsun.png

 

Only someone else would really appreciate it if I wrote the scripts intended to work with them :-P

 

I may, one day, but not anytime soon ;-(

 

So, does anyone want to contribute to this project?

 

Write a script that will:

  • Activate the Sun at Dawn and deactivate it at Dusk.
  • Activate the Moon at moonrise and deactivate it at moonset.
  • Advance the moon (change facing) around the moondial based on the day of the month.

Credit will of course be given and I'm sure at least three people will be very grateful =)

 

<...with rust>


  • OldTimeRadio aime ceci

#2
Tarot Redhand

Tarot Redhand
  • Members
  • 2 675 messages

What is the length of the lunar month? Earth's is approx 29 days or from wikipedia

 

A synodic month is the most familiar lunar cycle, defined as the time interval between two consecutive occurrences of a particular phase (such as new moon or full moon) as seen by an observer on Earth. The mean length of the synodic month is 29.53059 days (29 days, 12 hours,44 minutes, 2.8 seconds).

 

TR



#3
meaglyn

meaglyn
  • Members
  • 808 messages

What is the length of the lunar month? Earth's is approx 29 days or from wikipedia

 

 

TR

I think that would want to be settable by the builder ...



#4
Tarot Redhand

Tarot Redhand
  • Members
  • 2 675 messages

The reason for asking that is because to do the last you need three pieces of information - two 2d vectors and an angle.

 

  • vector 1 - current x,y of the moon
  • vector 2 - location (x,y) of the centre of the circle that the moon orbits around
  • angle - the angle that the moon needs rotate (haven't checked but probably in radians). To get this angle calculate (number describing one whole travel around the centre)/(number of days in month

 

TR



#5
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages

<complicating...>

 

Actually, the origin of the moon is the center of the circle, so all you need to do is calculate the new facing based on % of lunar cycle * 360 degrees 

SetFacing(fDirection); With fDirection being in degrees. An offset might be provided for moondials not aligned to true north, also.

 

If *I* were doing the script, Lunar period (I think is the right term for how fast it goes around the planet, and determining Moonrise and Moonset) and Lunar cycle (how long it takes for one complete cycle of phases) would be local vars, letting builders customize things.

 

<...simpler things>



#6
Tarot Redhand

Tarot Redhand
  • Members
  • 2 675 messages
<sounds of sobbing & hair being torn out>
 
From your illustration there are a number of symbols on the floor, each representing a particular phase of the moon. These symbols are arranged in a circle. You referred to it as a moondial somewhat akin to what some old mechanical clocks had. Therefore the moon is required to travel around the circumference of said circle. In order to do that I need the moon to be a totally separate object with its own centre and not an identical centre to the damned dial!!!!! I have just spent an hour and a half trying to figure out why the pretty standard small routine I use for such a thing didn't work. Doing it in code has many advantages over the locked version.
 
Here is the code
#include "x0_i0_position"
 
location orbit(float fAngle, location lcurrent, location lcentre)
{
    float fFacing = GetFacingFromLocation(lcurrent);
    object oArea = GetAreaFromLocation(lcurrent);
    vector vStartPosition = GetPositionFromLocation(lcurrent);
    vector vCentrePos = GetPositionFromLocation(lcentre);
    vector vNewPosition;
 
//theoretically move moon to centre
 
    float fOldX = vStartPosition.x - vCentrePos.x;
    float fOldY = vStartPosition.y - vCentrePos.y;
 
//rotate about the origin (0,0)
 
    float fSine = sin(fAngle);
    float fCosine = cos(fAngle);
    float fWorkingX = fOldX * fCosine - fOldY * fSine;
    float fWorkingY = fOldX * fSine + fOldY * fCosine;
 
//theoretically move moon to new position
 
    vNewPosition = Vector(fWorkingX + vCentrePos.x, fWorkingY + vCentrePos.y, vStartPosition.z);
 
    return (Location(oArea, vNewPosition, fFacing));
}
 
void main()
{
    object oMoon = GetObjectByTag("CelestialOrbMoon");
    object oDial = GetObjectByTag("CelestialOrbMoondial");
    location lMoon = GetLocation(oMoon);
    location lDial = GetLocation(oDial);
    location lNewLocation = orbit(5.0f, lMoon, lDial);
    MoveToNewLocation(lNewLocation, oMoon);
}

That would have worked (might have needed tweaking but...)

 

<wanders off shaking head and muttering imprecations under breath>

 

TR



#7
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages

<looks totally confused...>

 

Um, the dial *is* a separate model. All three parts are separate, but all have an origin at the same center point. So you place/spawn all three at the same location. Then you setfacing on the moon... either continuously or every 45 degrees... You don't *move* the moon... The sphere is hanging off to the side of the origin by 5m...

 

I'm soooo confused!

 

<...and feels like it, too>



#8
Grymmly

Grymmly
  • Members
  • 3 messages

Okay, I haven't read all of the posts fully, but here is what I know:  Because of the offset of the moon object from it's actual center, just rotating the object is all that is needed to move the moon around the dial.

I'm not a scripter by any means, but I think all that is needed is to detect what day it is (start with a day 0 and go from there up to 28 days, which is the approximate cycle of the moon) and for every X days (X being the number of moon symbols on the dial divided into 28) rotate the object the appropriate number of degrees (I think rolo said 45 degrees?).



#9
Tarot Redhand

Tarot Redhand
  • Members
  • 2 675 messages

In computer graphics the origin is set to either 0,0 for 2d or 0,0,0 for 3d. When you perform a vector rotation it is in relation to that origin. In NwN the origin is set to be the south west corner, with no elevation, of an area. By making the moon, the sun and the dial all have the same location (as in adjust location) I can only rotate the moon about it's own axis.

 

TR



#10
Tarot Redhand

Tarot Redhand
  • Members
  • 2 675 messages

I have just posted what I hope is an even clearer explanation in the existing thread on using vectors.

 

TR



#11
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages
<spinning...>

Good explanation, TR, but... Not wanting to waste valuable time, I designed the moon orb so that the only thing a script needs to do is rotate it. The moon's spherical mesh is located 5 meters away from its pivot point (which was what I meant by origin).

Load up the demo mod and hit rotate in the toolset and you'll see what I meant.

I wanted to keep the scripting as simple as possible, doing the geometry in the model, rather than the game ;-/

<...in place>
  • henesua aime ceci

#12
Tarot Redhand

Tarot Redhand
  • Members
  • 2 675 messages
The saga of "The Moving Moon" or "How I Learned to Love Pulling my Hair Out" (apologies to the late Stanley Kubrick)
 
So Rolo wants his moon to travel around that dial. He keeps mentioning rotation. Now I know that rotation won't work but... So I write the code to do rotation just to prove it. As expected the moon duly rotates about the SW corner of the area. Here is the demo code. It goes in the OnHeartbeat of the area.
#include "x0_i0_position"
 
location Rotate(float fAngle, location lcurrent, object oArea);
void MovePlaceable(location lNewLocation, object oPlaceable, string sResRef);
 
void main()
{
    object oMoon = GetObjectByTag("CelestialOrbMoon");
    location lMoon = GetLocation(oMoon);
    object oArea = GetAreaFromLocation(lMoon);
    location lNewLocation = Rotate(12.0f, lMoon, oArea);
    vector vToHere = GetPositionFromLocation(lNewLocation);
 
// only move to a location if it is inside the area
 
    if((vToHere.x >= 0.0f) && (vToHere.y >= 0.0f))
    {
        if(vToHere.x < IntToFloat(GetAreaSize(AREA_WIDTH, oArea) * 10 - 1))
        {
            if(vToHere.y < IntToFloat(GetAreaSize(AREA_HEIGHT, oArea) * 10 - 1))
                MovePlaceable(lNewLocation, oMoon, "blueprint001");
        }
    }
}
 
location Rotate(float fAngle, location lcurrent, object oArea)
{
    float fFacing = GetFacingFromLocation(lcurrent);
    vector vStartPosition = GetPositionFromLocation(lcurrent);
 
//rotate about the origin (0,0)
 
    float fSine = sin(fAngle);
    float fCosine = cos(fAngle);
    float fNewX = vStartPosition.x * fCosine - vStartPosition.y * fSine;
    float fNewY = vStartPosition.x * fSine + vStartPosition.y * fCosine;
    float fNewZ = vStartPosition.z;
 
    vector vNewPosition = Vector(fNewX,fNewY,fNewZ);
 
    return (Location(oArea, vNewPosition, fFacing));
}
 
void MovePlaceable(location lNewLocation, object oPlaceable, string sResRef)
{
    int iStatic = GetUseableFlag(oPlaceable);
    object oNew = CreateObject(OBJECT_TYPE_PLACEABLE, sResRef, lNewLocation, FALSE);
    if(iStatic)
        SetUseableFlag(oPlaceable, 1);
    DestroyObject(oPlaceable, 0.5);
}
I did however have an idea of what might work. If you look at the area in the toolset with the moon selected, you will see the directional arrow which tells you the facing of the placeable. If you don't spot it straight away, that's OK. Unlike most placeables, with this moon it is not in the immediate vicinity of it but in the (rough) centre of the dial and pointing at the (x,y) centre of the moon. So I thought that if I could change the facing I could make the moon orbit the centre of the dial. And it works (but see later). Again, here is the demo code. It goes in the OnHeartbeat of the area.
void main()
{
    object oMoon = GetObjectByTag("CelestialOrbMoon");
    location lMoon = GetLocation(oMoon);
    object oArea = GetAreaFromLocation(lMoon);
 
    int iStatic = GetUseableFlag(oMoon);
    if(GetLocalInt(oArea, "iIsFirstTime"))
    {
        SetLocalInt(oArea, "iIsFirstTime", FALSE);
        SetLocalFloat(oArea, "fFaceDirection", GetFacing(oMoon));
        return;
    }
 
    float fNewDirection = GetLocalFloat(oArea, "fFaceDirection");
    fNewDirection = fNewDirection - 12.0f;
 
    if(fNewDirection < 0.0f)
        fNewDirection = 360.0f - fNewDirection;
 
    SetLocalFloat(oArea, "fFaceDirection", fNewDirection);
 
    if(iStatic)
        SetUseableFlag(oMoon, 1);
 
    DestroyObject(oMoon, 0.01f);
 
    object oNew = CreateObject(OBJECT_TYPE_PLACEABLE, "blueprint001", lMoon, FALSE);
    AssignCommand(oNew, SetFacing(fNewDirection));
 
}
The above code could do with optimizing of course. The main problem is that you can only change the facing of an object once it exists and the OnSpawn event doesn't exist for placeables. So what happens is that the new moon is created in exactly the same position as the old moon. Even putting the call to DestroyObject before the call to CreateObject doesn't help as objects do not disappear immediately but rather fade away. Now this wouldn't matter too much except when hb code is called there is a doubling of light because of 2 moons being there instead of one.
 
Hair restorer anyone, please...
 
TR


#13
Proleric

Proleric
  • Members
  • 2 350 messages

To create a new placeable with a different facing,

 

vector vMoon = GetPositionFromLocation(lMoon);
object oNew = CreateObject(OBJECT_TYPE_PLACEABLE, "blueprint001", Location(oArea, vMoon, fNewDirection), FALSE);

 

You've probably seen the Orrery and Moons of Krynn, which use animation instead.



#14
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages

<feels like he is...>

 

Ok, let me try a demo...

 

I wrote a simple script just to demonstrate the rotation of the moon. I used GetTimeSecond() because Minute only counts to 1 (counts realtime minutes 2 per game hour, whereas hour counts game hours... Be nice to get game minutes...) and GetTimeHour is too slow for the demo (and too fast for the orb :-P Which should be based on day of the month (& hour?)).

 

Spoiler

 

And in game...

HuT0DS5.png

 

What I was hoping for was a low impact way to trigger the setfacing once a game-hour, and to check the time for moonrise/set and sunrise/set...

 

<...speaking in tongues>



#15
Tarot Redhand

Tarot Redhand
  • Members
  • 2 675 messages

Well I didn't think mine was too bad considering that the facing routine was written in just a few minutes during a bout of insomnia at  5am this morning.

 

TR


  • Rolo Kipp aime ceci

#16
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages
<sneaking in...>

It wasn't bad at all, in fact it was what I've been getting at. :-P

What's been throwing me is why destroy and create the placeable...?. I'm not getting that.

Anyway, just to be clear, I'm really happy you've been posting this stuff, because these kind of threads are so valuable to our knowledgebase. I'd rather you pull some hair out (and I yank on my beard a bit) than no dialog at all.

<...a little forumula>

#17
Proleric

Proleric
  • Members
  • 2 350 messages

...why destroy and create the placeable...?. I'm not getting that....

I have to admit that I didn't expect SetFacing to work for dynamic placeables, but it does!

 

I guess the misconception arises because placeables can't move to a new location.


  • Rolo Kipp aime ceci

#18
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages

<initializing...>

 

Ok, I have this on area onEnter. It initializes the temple array based on month, day, hour and the moons variable setting iMoonPeriod (how long it takes to pass through all phases - default 28 days) and iMoonCycle (time from one moonrise to the next - default 23 hours): It will rotate the moon to the correct position on the moon dial, set it activated if up, deactivated if set and do the same for the sun:

 

Spoiler
 
Note: This is working fine. Placeables can not be set static, but do not need to be useable.
 
Next up is to put in something to track the time while the PC is in the temple and rise/set the orbs by time of day... ideas?
 
Edit: this would only check the hours to determine whether to activate/deactivate the orbs... it wouldn't need to advance rotation, as I don't see the need to cover the possibility of a PC waiting 24 hours just to see the moon inch a bit around the dial... :-P

 

<...an update>



#19
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages

<tries to still...>

 

Here's my first approach. This is on the Area Hb. It compares the current hour against a a variable stored on the area (initially null) and, if they don't match, it updates the sun & moon states.

 

Spoiler
 
This works, though I can't help but feel there is a more elegant way to do this.
 
But I sat in the temple and watched the sun rise and the moon set and the sun set and then the moon rise... Was nice with the changing light values... :-)
 
Edit: Added demo mod. Experiment with the calendar and time and the period/cycle settings... nifty :-)

 

<...his beating heart>


  • Zwerkules aime ceci

#20
meaglyn

meaglyn
  • Members
  • 808 messages

Rolo, I just put a version of the CelestialOrbs module in the dropbox folder we share. Let me know if you can't find it.   I took your script above and put it in a framework to run from the area HB.  It can additionally run in the area enter sctript to make the update happen without waiting for the first HB after there's a PC in the area since the script won't do anything in that case.

 

It still allows for the period to be changed but it won't work quite right if you change it (back pedaling on my initial comment in this thread). It does not really allow for the cycle to change anymore since the moonrise time is really a function of the phase. I.e. you get full moon rise at sunset. You can't have a full moon while the sun is in the sky.  The code in there is an approximation of that.

 

One note, it looks like the moon should not go down quite as far when it is off. It looks like it settles into the floor a bit and flickers sometimes.

 

And there is debug print out enabled which you may want to remove.

 

Edit: Here's a public link to it in case anyone else wants to poke at it:

https://db.tt/IUQ7IL8B


  • Rolo Kipp aime ceci

#21
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages

<playing with...>

 

I'm liking that a lot, Meaglyn :-)

Didn't find it in dropbox, btw... ;-/

 

I put wrappers around the debug statements, but otherwise left it as is... replacing the demo mod now...

 

<...stars>



#22
meaglyn

meaglyn
  • Members
  • 808 messages

<playing with...>

 

I'm liking that a lot, Meaglyn :-)

Didn't find it in dropbox, btw... ;-/

 

I put wrappers around the debug statements, but otherwise left it as is... replacing the demo mod now...

 

<...stars>

 

You found it in the public link I assume. That other folder was one you shared with me a long time ago, maybe defunct. I'll just remove it from there...



#23
Tarot Redhand

Tarot Redhand
  • Members
  • 2 675 messages

I think I may have a better way of doing this and it doesn't involve the Heartbeat event, only OnEnter and OnExit for the area. I haven't the time to fully implement and test this at the moment but here is a pseudo-code version.

 

OnEnter script
 
main
 
set variable bNotInArea to false
set moon to current position
set sun to current position
calc time to next moon rise/set
calc time to next sun rise/set
 
delayCommand( call MoonTimePseudoHB(MoonTime))
delayCommand( call SunTimePseudoHB(SunTime))
 
END Main
 
MoonTimePseudoHB(MoonTime)
 
if(bNotInArea)
EXIT
toggle moon up/down
calc time to next moon rise/set
delayCommand( call MoonTimePseudoHB(MoonTime))
 
END MoonTimePseudoHB
 
SunTimePseudoHB(SunTime)
 
if(bNotInArea)
      EXIT
toggle moon up/down
calc time to next sun rise/set
delayCommand( call SunTimePseudoHB(SunTime))
 
END SunTimePseudoHB
 
------------------------------------------------------------------------
 
OnExit script
 
main
 
set variable bNotInArea to true
 
END Main
 
------------------------------------------------------------------------
 
As you can see the onenter script uses a pair of pseudo-heartbeat functions. This allows for the moon to be in the sky at the same time as the sun, just like in real life.
 
Anyway, what do you think?
 
TR

  • Rolo Kipp aime ceci

#24
meaglyn

meaglyn
  • Members
  • 808 messages

A PHB would work fine.  The HB is not very intensive as it only really does anything if there are PCs in the area and then only does something at most every hour.  This could be tracked by a variable as you are doing on enter/exit instead of searching, which would make it a bit more efficient.   You'll want the bNotInArea to be a counter, though, for MP cases. Right now tracking if a PC is in the area or not is a function of how many PCs there are in the module. In single player that's 1 obviously.

The sun and the moon can be in the sky at the same time already. Just not the FULL moon.  I.e. if you can see both the sun and the moon the there must be part of the moon you can see (or facing you at least) that is not being lit by the sun, hence not full.

 

The pseudo code you posted only deals with rise and set, not phase. You'd need another to run once a day to move the moon...

 

BTW, I was not meaning to step on toes. I just got interested and took Rolo's posting as a starting point.


  • Rolo Kipp aime ceci

#25
Rolo Kipp

Rolo Kipp
  • Members
  • 2 791 messages

<hopping about...>

 

Ow! Ow! Ow! *hams it up*

 

Actually, Tarot's approach has merit.

 

Also, thinking about it, I've been treating phase and state as independent things, but really, phase is driven by moonrise.

When the moon rises at sunrise, the moon is new.

When the moon rises at sunset, the moon is (Edit) FULL.

 

So we use the modulo approach combined with a moon-defined cycle local variable to determine moonrise and use moonrise to determine phase.

 

That way, systems with more than one moon can use the same script, yet have very different periods.

 

<...holding his foot>


  • Killmonger aime ceci