Bat: Difference between revisions

From ZAMN Hacking
Content deleted Content added
No edit summary
No edit summary
Line 4: Line 4:
== Behavior ==
== Behavior ==


A bat starts out moving in a given direction specified in the entity arguments. The initial velocity of the bat is 6 px/frame in this direction. If the direction is diagonal, the velocity is 6 px/frame on both axes. Every frame, the bat will accelerate towards the nearest target at a rate of 1 px/frame<sup>2</sup>, but its speed will not exceed 6 px/frame. After the acceleration is applied, then the position is updated based on the current velocity.
A bat starts out moving in a given direction specified in the entity arguments. The initial velocity of the bat is 6 px/frame in this direction. If the direction is diagonal, the velocity is 6 px/frame on both axes. Every frame, the bat will accelerate towards the nearest target at a rate of 1 px/frame<sup>2</sup>, but its speed will not exceed 5 px/frame on each axis. After the acceleration is applied, then the position is updated based on the current velocity.


Every 3 frames the bat will switch to the next animation frame. There are 2 different sprites that it alternates between.
Every 3 frames the bat will switch to the next animation frame. There are 2 different sprites that it alternates between.
Line 14: Line 14:


Once the bat dies, it plays a 32 frame death animation, then the entity is destroyed.
Once the bat dies, it plays a 32 frame death animation, then the entity is destroyed.

== Bugs ==

* A bat starts at a speed of 6 px/frame on each axis, but the routine that updates the speed will not allow it to exceed 5 px/frame. Thus if a bat's speed ever goes below the initial 6 px/frame, it will not be able to reach it again. This is likely a mistake.


== RAM map ==
== RAM map ==
Line 56: Line 60:
| $1A || 2 || uint16 0-based || framesUntilDeath || Number of frames until death
| $1A || 2 || uint16 0-based || framesUntilDeath || Number of frames until death
|}
|}

== Pseudocode ==

main() {
{{RAM name|$7E:00DE}} += 8
this.sprite = init()
while (!this.dead) {
{{ROM name|$80:8353}}(1)
updateAnimation()
updatePosition()
}
this.sprite.tileData.bank = 0x8F
{{ROM name|$81:832C}}(deathAnimation)
{{RAM name|$7E:00DE}} -= 8
while ({{RAM name|$7E:00DE}} < 0) { }
{{ROM name|$80:BE41}}(this.sprite)
}
deathAnimation = { { 0xF6CB, 8 }, { 0xF6D4, 8 }, { 0xF6DD, 8 }, { 0xF6E6, 8 }, { 0 } }
updatePosition() {
targetSprite = {{ROM name|$80:B123}}(this.x, this.y)
targetDirection = {{ROM name|$80:B22A}}(this.sprite, targetSprite)
newVelocityX = this.velocityX + accelerationAmouont[targetDirection][0]
if (abs(newVelocityX) < 6) {
this.velocityX = newVelocityX
}
newVelocityY = this.velocityY + accelerationAmouont[targetDirection][1]
if (abs(newVelocityY) < 6) {
this.velocityY = newVelocityY
}
this.x += this.velocityX
this.y += this.velocityY
if ({{ROM name|$80:AF2C}}(this.x, this.y)) {
this.dead = true
} else {
this.sprite.x = this.x
this.sprite.y = this.y
this.framesUntilDeath -= 1
if (this.framesUntilDeath < 0) {
this.dead = true
}
}
}
accelerationAmouont = {
{ 0, 0 },
{ 0, -1 },
{ 1, -1 },
{ 1, 0 },
{ 1, 1 },
{ 0, 1 },
{ -1, 1 },
{ -1, 0 },
{ -1, -1 }
}
updateAnimation() {
this.framesUntilAnimUpdate -= 1
if (this.framesUntilAnimUpdate < 0) {
this.framesUntilAnimUpdate = 2
this.animationFrame += 1
this.sprite.tileData.lowBytes = animation[this.animationFrame % 2]
}
}
animation = { 0xDDB0, 0xDDB9 }
init() {
sprite = {{ROM name|$80:BE0C}}()
this.x = args.x
sprite.x = args.x
sprite.z = 12
this.y = args.y
sprite.y = args.y
this.unused12 = args.direction * 2
this.velocityX = startingVelocity[args.direction][0]
this.velocityY = startingVelocity[args.direction][1]
sprite.tileData = $90:DDB0
sprite.entity = {{RAM name|$7E:0008}}
sprite.visible = true
this.framesUntilDeath = 130
this.dead = false
this.animationFrame = 0
this.framesUntilAnimUpdate = 0
{{ROM name|$80:8475}}(collisionHandler)
return sprite
}
startingVelocity = {
{ 0, 0 },
{ 0, -6 },
{ 6, -6 },
{ 6, 0 },
{ 6, 6 },
{ 0, 6 },
{ -6, 6 },
{ -6, 0 },
{ -6, -6 }
}
collisionHandler(spriteType) {
if (spriteType == VICTIM || spriteType == ZEKE || spriteType == JULIE ||
(spriteType & 0x7FFF) >= SQUIRT_GUN) {
this.sprite.type = NONE
this.dead = true
}
}

[[Category:Bug]]

Revision as of 20:16, 14 July 2024

Entity data
Entity pointer $82:F03E

The bat is an entity that is spawned by vampires.

Behavior

A bat starts out moving in a given direction specified in the entity arguments. The initial velocity of the bat is 6 px/frame in this direction. If the direction is diagonal, the velocity is 6 px/frame on both axes. Every frame, the bat will accelerate towards the nearest target at a rate of 1 px/frame2, but its speed will not exceed 5 px/frame on each axis. After the acceleration is applied, then the position is updated based on the current velocity.

Every 3 frames the bat will switch to the next animation frame. There are 2 different sprites that it alternates between.

A bat will die if any of the following conditions are met:

  • It contacts a tile that is solid to weapons
  • It touches a player, victim, or any weapon shot
  • It has been alive for 131 frames

Once the bat dies, it plays a 32 frame death animation, then the entity is destroyed.

Bugs

  • A bat starts at a speed of 6 px/frame on each axis, but the routine that updates the speed will not allow it to exceed 5 px/frame. Thus if a bat's speed ever goes below the initial 6 px/frame, it will not be able to reach it again. This is likely a mistake.

RAM map

Entity arguments

Address Length Type Name Description
$00 2 int16 x X position
$02 2 int16 y Y position
$04 2 direction x2 direction Direction

Entity memory

Address Length Type Name Description
$08 2 pointer16 sprite Pointer to sprite
$0A 2 int16 x X position
$0C 2 int16 y Y position
$0E 2 int16 velocityX X velocity
$10 2 int16 velocityY Y velocity
$12 2 unused unused12 Value is set, but never used
$14 2 boolean dead Dead
$16 2 uint16 animationFrame Low bit is the current animation frame
$18 2 uint16 0-based framesUntilAnimUpdate Number of frames until the next animation frame
$1A 2 uint16 0-based framesUntilDeath Number of frames until death

Pseudocode

main() {
	$7E:00DE += 8
	this.sprite = init()

	while (!this.dead) {
		waitFrames(1)
		updateAnimation()
		updatePosition()
	}

	this.sprite.tileData.bank = 0x8F
	showAnimation(deathAnimation)
	$7E:00DE -= 8
	while ($7E:00DE < 0) { }
	deleteSprite(this.sprite)
}

deathAnimation = { { 0xF6CB, 8 }, { 0xF6D4, 8 }, { 0xF6DD, 8 }, { 0xF6E6, 8 }, { 0 } }

updatePosition() {
	targetSprite = findTarget(this.x, this.y)
	targetDirection = getDirectionToTarget(this.sprite, targetSprite)

	newVelocityX = this.velocityX + accelerationAmouont[targetDirection][0]
	if (abs(newVelocityX) < 6) {
		this.velocityX = newVelocityX
	}

	newVelocityY = this.velocityY + accelerationAmouont[targetDirection][1]
	if (abs(newVelocityY) < 6) {
		this.velocityY = newVelocityY
	}

	this.x += this.velocityX
	this.y += this.velocityY

	if (bgSolidToWeapons(this.x, this.y)) {
		this.dead = true
	} else {
		this.sprite.x = this.x
		this.sprite.y = this.y
		this.framesUntilDeath -= 1
		if (this.framesUntilDeath < 0) {
			this.dead = true
		}
	}
}

accelerationAmouont = {
	{  0,  0 },
	{  0, -1 },
	{  1, -1 },
	{  1,  0 },
	{  1,  1 },
	{  0,  1 },
	{ -1,  1 },
	{ -1,  0 },
	{ -1, -1 }
}

updateAnimation() {
	this.framesUntilAnimUpdate -= 1
	if (this.framesUntilAnimUpdate < 0) {
		this.framesUntilAnimUpdate = 2

		this.animationFrame += 1
		this.sprite.tileData.lowBytes = animation[this.animationFrame % 2]
	}
}

animation = { 0xDDB0, 0xDDB9 }

init() {
	sprite = createSprite()
	this.x = args.x
	sprite.x = args.x
	sprite.z = 12
	this.y = args.y
	sprite.y = args.y

	this.unused12 = args.direction * 2
	this.velocityX = startingVelocity[args.direction][0]
	this.velocityY = startingVelocity[args.direction][1]
	sprite.tileData = $90:DDB0
	sprite.entity = currentEntity
	sprite.visible = true
	this.framesUntilDeath = 130
	this.dead = false
	this.animationFrame = 0
	this.framesUntilAnimUpdate = 0
	setCollisionHandler(collisionHandler)
	return sprite
}

startingVelocity = {
	{  0,  0 },
	{  0, -6 },
	{  6, -6 },
	{  6,  0 },
	{  6,  6 },
	{  0,  6 },
	{ -6,  6 },
	{ -6,  0 },
	{ -6, -6 }
}

collisionHandler(spriteType) {
	if (spriteType == VICTIM || spriteType == ZEKE || spriteType == JULIE ||
	    (spriteType & 0x7FFF) >= SQUIRT_GUN) {
		this.sprite.type = NONE
		this.dead = true
	}
}