import "std.zh"
////////////////////////////////////////////////////////////////
//// Constants
//// Physics.
const float GRAV = 0.16;
const float TERM = 3.2;
//// Platform.
// In freefall.
const int PLATFORM_AIR = 0;
// Standing on a hard platform.
const int PLATFORM_HARD = 1;
// Standing on a soft platform.
const int PLATFORM_SOFT = 2;
//// Stairs
const int STAIR_NONE = 0;
// Standing on a up-left down-right stair.
const int STAIR_POS = 1;
// Standing on a up-right down-left stair.
const int STAIR_NEG = -1;
// Soft platforms
const int CF_SOFT = 98;
// Stairs.
const int CT_NEG = 142;
const int CT_CROSS = 143;
const int CT_POS = 144;
//// Link Movement
const int JUMP = -4;
const int LOW_JUMP = -1;
const float WALK = 1.5;
////////////////////////////////////////////////////////////////
//// Misc Indices
//// NPCs
// Vertical speed.
const int MISC_NPC_VY = 0;
////////////////////////////////////////////////////////////////
//// Global Variables.
//// Link State
// Link's actual location. Every frame Link->X and Link->Y are
// set to these values. This is so Link can have partial-pixel movement.
float LinkX = 0;
float LinkY = 0;
// Link's vertical velocity.
float LinkVy = 0;
// What Link is currently standing on.
int LinkPlatform = PLATFORM_AIR;
// If Link is on a stair.
int LinkStair = STAIR_NONE;
// The y offset for being on a stair.
int LinkStairY = 0;
// The left edge of the stair.
int LinkStairLeft = 0;
// The right edge of the stair.
int LinkStairRight = 0;
//// Link's State last frame.
int LinkLastX = -1;
int LinkLastY = -1;
int LinkLastAction = LA_NONE;
//// Inputs last frame.
bool LastInputUp = false;
bool LastInputDown = false;
bool LastInputLeft = false;
bool LastInputRight = false;
global script Active {
void run() {
LinkX = Link->X;
LinkY = Link->Y;
while (true) {
ScreenChange_Update();
LinkUpdate();
ActionJump();
//EnemyUpdate();
InputsUpdate();
Waitdraw();
LinkUpdate2();
InputsUpdate2();
Waitframe();}}}
int PlatformType(int x, int y) {
if (!Screen->isSolid(x, y)) {
return PLATFORM_AIR;}
else if (ComboFI(x, y, CF_SOFT) || IsStair(x, y)) {
return PLATFORM_SOFT;}
else {
return PLATFORM_HARD;}}
bool IsStair(int x, int y) {
return IsStair(ComboAt(x, y));}
bool IsStair(int loc) {
int ct = Screen->ComboT[loc];
return ct >= CT_NEG && ct <= CT_POS;}
// Update Link's variables.
void LinkUpdate() {
// Update Last frame variables.
LinkLastX = LinkX;
LinkLastY = LinkY;
LinkLastAction = Link->Action;
// If Link is in the frozen action, it probably means he is using
// the hookshot, so Update position based on where Link moved.
if (Link->Action == LA_FROZEN ||
Link->Action == LA_SCROLLING ||
Link->Action == LA_GOTHURTLAND ||
ScreenChanged ||
Abs(LinkX - Link->X) > 8 ||
Abs(LinkY - Link->Y) > 8) {
LinkX = Link->X;
LinkY = Link->Y;
LinkVy = 0;}
// Move by inputs.
if (Link->Action == LA_NONE ||
Link->Action == LA_WALKING ||
(Link->Action == LA_ATTACKING &&
LinkPlatform == PLATFORM_AIR)) {
if (Link->InputLeft) {
LinkX -= WALK;}
else if (Link->InputRight) {
LinkX += WALK;}}
bool movedLeft = false;
// Check if we can move left.
if (LinkX < LinkLastX) {
movedLeft = true;
int left1 = PlatformType(LinkX, LinkY);
int left2 = PlatformType(LinkX, LinkY + 8);
int left3 = PlatformType(LinkX, LinkY + 15);
if (LinkStair == STAIR_NONE &&
(left1 == PLATFORM_HARD ||
left2 == PLATFORM_HARD ||
left3 == PLATFORM_HARD)) {
LinkX += 8 - (LinkX % 8);}}
bool movedRight = false;
// Check if we can move right.
if (LinkX > LinkLastX) {
movedRight = true;
int left1 = PlatformType(LinkX + 15, LinkY);
int left2 = PlatformType(LinkX + 15, LinkY + 8);
int left3 = PlatformType(LinkX + 15, LinkY + 15);
if (LinkStair == STAIR_NONE &&
(left1 == PLATFORM_HARD ||
left2 == PLATFORM_HARD ||
left3 == PLATFORM_HARD)) {
LinkX &= 0x1FFF8;}}
// Move by vertical velocity.
LinkY += LinkVy;
// If we're on a stair.
if (LinkStair != STAIR_NONE) {
// We were hurt, so fall off.
if (Link->Action == LA_GOTHURTLAND) {
LinkStair = STAIR_NONE;}
else {
// Check if we're no longer on a stair.
int under = ComboAt(LinkX + 8, LinkY + 20);
int forward = -1;
if (LinkStair == STAIR_POS) {forward = ComboAt(LinkX - 2, LinkY + 12);}
else if (LinkStair == STAIR_NEG) {forward = ComboAt(LinkX + 18, LinkY + 12);}
int ct = Screen->ComboT[under];
bool hasStair = ct == CT_CROSS || ct == CT_CROSS + LinkStair;
if (!hasStair && forward != -1) {
ct = Screen->ComboT[forward];
hasStair = ct == CT_CROSS || ct == CT_CROSS + LinkStair;}
if (!hasStair) {
LinkStair = STAIR_NONE;}
// Otherwise move according to the stair.
if (hasStair) {
int x = Clamp(LinkX, LinkStairLeft, LinkStairRight);
LinkY = x * LinkStair + LinkStairY;}}}
// Not on a stair, check regular platforms
if (LinkStair == STAIR_NONE) {
// Check if we hit our head on something.
if (LinkVy < 0) {
int overLinkLeft = PlatformType(LinkX + 5, LinkY);
int overLinkRight = PlatformType(LinkX + 11, LinkY);
// We ran into a hard block.
if (overLinkLeft == PLATFORM_HARD || overLinkRight == PLATFORM_HARD) {
LinkVy = 0;
// Move to the next multiple of 8.
LinkY += 8 - (LinkY % 8);}}
// See if we're standing on a platform.
int underLinkLeft = PlatformType(LinkX + 5, LinkY + 16);
int underLinkRight = PlatformType(LinkX + 11, LinkY + 16);
// We're on a hard platform.
if (LinkVy >= 0 &&
(underLinkLeft == PLATFORM_HARD || underLinkRight == PLATFORM_HARD)) {
LinkPlatform = PLATFORM_HARD;
LinkVy = 0;
// Round down to nearest multiple of 8.
LinkY &= 0x1FFF8;}
// We're on a soft platform.
else if (LinkVy >= 0 &&
(underLinkLeft == PLATFORM_SOFT ||
underLinkRight == PLATFORM_SOFT)) {
// If we're falling and holding down, or we started below it,
// go through it.
if ((LinkVy > 0 && Link->InputDown) ||
(LinkLastY > (LinkY & 0x1FFF8))) {
LinkPlatform = PLATFORM_AIR;
// Accelerate from gravity.
LinkVy += GRAV;
if (LinkVy >= TERM) {
LinkVy = TERM;}}
// Otherwise stop.
else {
LinkPlatform = PLATFORM_SOFT;
LinkVy = 0;
// Round down to nearest multiple of 8.
LinkY &= 0x1FFF8;}}
// We're in freefall.
else {
LinkPlatform = PLATFORM_AIR;
// Accelerate from gravity.
LinkVy += GRAV;
if (LinkVy >= TERM) {
LinkVy = TERM;}}}
// Check for stairs.
if (LinkStair == STAIR_NONE &&
(LinkPlatform == PLATFORM_HARD ||
LinkPlatform == PLATFORM_SOFT)) {
// Up-going stairs.
if (movedLeft && Link->InputUp) {
int loc = ComboAt(LinkX, LinkY + 15);
int ct = Screen->ComboT[loc];
if (ct == CT_CROSS || ct == CT_POS) {
LinkStair = STAIR_POS;
StairFindEdges(loc);
LinkStairY = ComboY(loc) - ComboX(loc) - 16;}}
else if (movedRight && Link->InputUp) {
int loc = ComboAt(LinkX + 15, LinkY + 15);
int ct = Screen->ComboT[loc];
if (ct == CT_CROSS || ct == CT_NEG) {
LinkStair = STAIR_NEG;
StairFindEdges(loc);
LinkStairY = ComboY(loc) + ComboX(loc) - 16;}}
// Down-going stairs.
if (movedLeft && Link->InputDown) {
int loc = ComboAt(LinkX, LinkY + 18);
int ct = Screen->ComboT[loc];
if (ct == CT_CROSS || ct == CT_NEG) {
LinkStair = STAIR_NEG;
StairFindEdges(loc);
LinkStairY = ComboY(loc) + ComboX(loc) - 16;}}
else if (movedRight && Link->InputDown) {
int loc = ComboAt(LinkX + 15, LinkY + 18);
int ct = Screen->ComboT[loc];
if (ct == CT_CROSS || ct == CT_POS) {
LinkStair = STAIR_POS;
StairFindEdges(loc);
LinkStairY = ComboY(loc) - ComboX(loc) - 16;}}}
// If we've moved and have no action, set the action to walking.
if (Link->Action == LA_NONE &&
(Link->InputLeft || Link->InputRight)) {
Link->Action = LA_WALKING;}}
void LinkUpdate2() {
if (Link->Action == LA_FROZEN) {return;}
// Move Link to new location.
// If we're frozen, it probably means the hookshot is being used.
if (Link->Action != LA_FROZEN &&
Link->Action != LA_SCROLLING &&
Link->Action != LA_GOTHURTLAND &&
!ScreenChanged) {
Link->X = Round(LinkX);
Link->Y = Round(LinkY);}
// If we're on stairs, set the jump to 0.
if (LinkStair != STAIR_NONE) {
Link->Jump = 0;
Link->Z = 0;}
// Set direction.
if (Link->Action != LA_FROZEN) {
if (LastInputUp) {Link->Dir = DIR_UP;}
else if (LastInputDown) {Link->Dir = DIR_DOWN;}
else if (Link->InputLeft) {Link->Dir = DIR_LEFT;}
else if (Link->InputRight) {Link->Dir = DIR_RIGHT;}}}
// Jump if Link has been set to.
void ActionJump() {
if ((LinkPlatform != PLATFORM_AIR || LinkStair != STAIR_NONE) &&
Link->PressL &&
(Link->Action == LA_NONE || Link->Action == LA_WALKING)) {
LinkStair = STAIR_NONE;
if (Link->InputDown) {
LinkVy = LOW_JUMP;}
else {
LinkVy = JUMP;}
Game->PlaySound(SFX_JUMP);}}
// Clear inputs that we don't want the engine to use.
void InputsUpdate() {
LastInputUp = Link->InputUp;
LastInputDown = Link->InputDown;
LastInputLeft = Link->InputLeft;
LastInputRight = Link->InputRight;
if (Link->Action == LA_FROZEN) {return;}
Link->InputL = false;
Link->PressL = false;
// Cancel left/right movement for drawing if there is up/down movement.
if (Link->InputUp || Link->InputDown) {
Link->InputLeft = false;
Link->PressLeft = false;
Link->InputRight = false;
Link->PressRight = false;}
//Link->InputUp = false;
//Link->InputDown = false;
}
void StairFindEdges(int start) {
// Right edge.
int x = start % 16;
int y = start >> 4;
int ct = Screen->ComboT[start];
while (x <= 15 && y >= 0 && y <= 10 &&
(ct == CT_CROSS || ct == CT_CROSS + LinkStair)) {
x++;
y += LinkStair;
ct = Screen->ComboT[y * 16 + x];}
LinkStairRight = (x + Cond(LinkStair == STAIR_POS, 0, -1)) * 16;
// Left edge.
x = start % 16;
y = start >> 4;
ct = Screen->ComboT[start];
while (x >= 0 && y >= 0 && y <= 10 &&
(ct == CT_CROSS || ct == CT_CROSS + LinkStair)) {
x--;
y -= LinkStair;
ct = Screen->ComboT[y * 16 + x];}
LinkStairLeft = (x + Cond(LinkStair == STAIR_POS, 1, 0)) * 16;}
// Clear inputs that we don't want the engine to use.
void InputsUpdate2() {
if (Link->Action == LA_FROZEN) {return;}
Link->InputLeft = false;
Link->InputRight = false;}
void EnemyUpdate() {
for (int i = 1; i <= Screen->NumNPCs(); i++) {
EnemyUpdate(Screen->LoadNPC(i));}}
void EnemyUpdate(npc enemy) {
enemy->Z = 0;
// Don't let them go up or down.
if (enemy->Dir == DIR_UP || enemy->Dir == DIR_DOWN) {
int dir = DIR_LEFT + Rand(2);
if (CanWalk(enemy->X, enemy->Y, dir, Max(1, enemy->Step), true)) {
enemy->Dir = dir;}
else {
dir ^= 1;
if (CanWalk(enemy->X, enemy->Y, dir, Max(1, enemy->Step), true)) {
enemy->Dir = dir;}}}
// Check if we hit our head on something.
if (enemy->Misc[MISC_NPC_VY] < 0) {
int overLeft = PlatformType(enemy->X + 5, enemy->Y);
int overRight = PlatformType(enemy->Y + 11, enemy->Y);
// We ran into a hard block.
if (overLeft == PLATFORM_HARD || overRight == PLATFORM_HARD) {
enemy->Misc[MISC_NPC_VY] = 0;
// Move to the next multiple of 8.
enemy->Y += 8 - (enemy->Y % 8);}}
// See if we're standing on a platform.
int underLeft = PlatformType(enemy->X + 5, enemy->Y + 16);
int underRight = PlatformType(enemy->Y + 11, enemy->Y + 16);
int platform;
// We're on a hard platform.
if (enemy->Misc[MISC_NPC_VY] >= 0 &&
(underLeft == PLATFORM_HARD || underRight == PLATFORM_HARD)) {
platform = PLATFORM_HARD;
enemy->Misc[MISC_NPC_VY] = 0;
// Round down to nearest multiple of 8.
enemy->Y &= 0x1FFF8;}
// We're on a soft platform.
else if (enemy->Misc[MISC_NPC_VY] >= 0 &&
(underLeft == PLATFORM_SOFT ||
underRight == PLATFORM_SOFT)) {
platform = PLATFORM_SOFT;
enemy->Misc[MISC_NPC_VY] = 0;
// Round down to nearest multiple of 8.
enemy->Y &= 0x1FFF8;}
// We're in freefall.
else {
platform = PLATFORM_AIR;
enemy->Misc[MISC_NPC_VY] += GRAV;}
// Move by velocity.
enemy->Y += enemy->Misc[MISC_NPC_VY];}
// Test for changes in screen.
// Last noticed DMap.
int LastDMap = -1;
// Last noticed DScreen.
int LastDScreen = -1;
// If the screen has changed.
bool ScreenChanged = false;
// If the dmap has changed.
bool DMapChanged = false;
void ScreenChange_Update() {
int dMap = Game->GetCurDMap();
int dScreen = Game->GetCurDMapScreen();
DMapChanged = dMap != LastDMap;
ScreenChanged = DMapChanged || dScreen != LastDScreen;
LastDMap = dMap;
LastDScreen = dScreen;}