static void stalagmiteAttack() { Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add a stalagmite"); } loadProperties("item/stalagmite", e); setEntityAnimation(e, "STAND"); e->x = self->x + self->w / 2; e->y = self->y + self->h / 2; e->targetX = player.x + player.w / 2 - e->w / 2; e->targetY = getMapFloor(self->x, self->y); e->startY = e->targetY - e->h; calculatePath(e->x, e->y, e->targetX, e->targetY, &e->dirX, &e->dirY); e->flags |= (NO_DRAW|HELPLESS|TELEPORTING|NO_END_TELEPORT_SOUND); playSoundToMap("sound/common/spell", -1, self->x, self->y, 0); e->head = self; e->face = RIGHT; setEntityAnimation(e, "STAND"); e->action = &stalagmiteRise; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->damage = 2; e->takeDamage = &stalagmiteTakeDamage; e->die = &stalagmiteDie; e->head = self; e->face = self->face; e->type = ENEMY; e->thinkTime = 0; e->flags |= DO_NOT_PERSIST; self->mental--; if (self->mental <= 0) { self->thinkTime = 60; self->action = &stalagmiteAttackFinish; } else { self->thinkTime = 180; } } self->maxThinkTime--; checkToMap(self); }
static void entityWait() { int i; Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { self->mental = self->mental == 0 ? 1 : 0; self->thinkTime = 120; for (i=0;i<2;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add a Flame Statue Flame"); } loadProperties("enemy/horizontal_flame", e); if (i == 0) { e->face = RIGHT; e->x = self->x + self->w; e->y = self->y + (self->mental == 1 ? self->offsetY : self->offsetX); } else { e->face = LEFT; e->x = self->x - e->w; e->y = self->y + (self->mental == 0 ? self->offsetY : self->offsetX); } e->action = &flameWait; e->draw = &drawLoopingAnimationToMap; e->touch = &flameTouch; e->head = self; e->thinkTime = self->thinkTime; if (i == 0) { e->endY = playSoundToMap("sound/enemy/fire_burner/flame", -1, self->x, self->y, -1); } else { e->endY = -1; } e->type = ENEMY; e->flags |= DO_NOT_PERSIST|PLAYER_TOUCH_ONLY; setEntityAnimation(e, "STAND"); } self->action = &doFlame; } self->dirX = self->standingOn != NULL ? self->standingOn->dirX : 0; checkToMap(self); }
static void lightningCageCreate() { int i; Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { setEntityAnimation(self, "LIGHTNING_CAGE"); self->x = player.x + player.w / 2 - self->w / 2; addParticleExplosion(self->x + self->w / 2, self->y + self->h / 2); playSoundToMap("sound/common/spell", -1, self->x, self->y, 0); self->flags &= ~NO_DRAW; self->face = prand() % 2 == 0 ? LEFT : RIGHT; self->mental = 0; self->thinkTime = 60; self->maxThinkTime = 0; for (i=0;i<2;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add a lightning cage"); } loadProperties("boss/azriel_lightning_cage_spell", e); e->action = &lightningCageWait; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->head = self; e->face = i == 0 ? LEFT : RIGHT; setEntityAnimation(e, "STAND"); if (e->face == LEFT) { e->x = self->x + self->w - e->w - e->offsetX; } else { e->x = self->x + e->offsetX; } e->y = self->y + e->offsetY; } self->action = &lightningCageMoveAbovePlayer; } becomeTransparent(); }
static void acidStream() { Entity *e; self->endY--; if (self->endY <= 0) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add Acid"); } loadProperties("boss/cave_boss_acid", e); setEntityAnimation(e, "DOWN"); if (self->face == LEFT) { e->x = self->x + self->w - self->offsetX; } else { e->x = self->x + self->offsetX; } e->y = self->y + self->offsetY; e->x -= e->w / 2; e->action = &fireFall; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->face = self->face; e->type = ENEMY; e->health = 0; self->endY = 6; } if (self->mental == 0) { self->thinkTime--; if (self->thinkTime <= 0) { setEntityAnimation(self, "NORMAL_ATTACK_DOWN_WALK"); self->thinkTime = 60; self->mental = 1; self->dirX = self->face == LEFT ? self->speed : -self->speed; } } else { if (self->dirX == 0) { setEntityAnimation(self, "NORMAL_ATTACK_DOWN"); self->thinkTime--; if (self->thinkTime <= 0) { self->targetX = getMapStartX() + SCREEN_WIDTH / 2 - self->w / 2; self->dirX = self->targetX < self->x ? -self->speed : self->speed; self->action = &acidStreamFinish; setEntityAnimation(self, "NORMAL_WALK"); } } } checkToMap(self); }
static void ceilingBurn() { Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add Fire"); } loadProperties("enemy/fire", e); setEntityAnimation(e, "UP"); if (self->face == LEFT) { e->x = self->x + self->w - self->offsetX; } else { e->x = self->x + self->offsetX; } e->y = self->y + self->offsetY; e->x -= e->w / 2; e->action = &fireFall; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->face = self->face; e->type = ENEMY; e->flags |= FLY; e->thinkTime = 600; e->dirY = -15; self->mental--; if (self->mental <= 0) { self->endY += TILE_SIZE / 2 - e->w / 2; self->thinkTime = 30; self->action = &ceilingBurnWait; } else { self->thinkTime = 6; } } checkToMap(self); }
static void createBody() { char bodyName[MAX_VALUE_LENGTH]; int i; Entity **body, *head; body = malloc(self->mental * sizeof(Entity *)); if (body == NULL) { showErrorAndExit("Failed to allocate a whole %d bytes for Snake Boss body...", self->mental * (int)sizeof(Entity *)); } snprintf(bodyName, sizeof(bodyName), "%s_body", self->name); for (i=self->mental-1; i>=0; i--) { body[i] = getFreeEntity(); if (body[i] == NULL) { showErrorAndExit("No free slots to add a Snake Boss body part"); } loadProperties(bodyName, body[i]); body[i]->x = self->x; body[i]->y = self->y; body[i]->action = &bodyWait; body[i]->draw = &drawLoopingAnimationToMap; body[i]->touch = &entityTouch; body[i]->die = &entityDieNoDrop; body[i]->takeDamage = &bodyTakeDamage; body[i]->creditsAction = &bodyWait; body[i]->type = ENEMY; body[i]->active = FALSE; body[i]->flags |= NO_DRAW; setEntityAnimation(body[i], "STAND"); } /* Recreate the head so that it's on top */ head = getFreeEntity(); if (head == NULL) { showErrorAndExit("No free slots to add a Snake Boss head"); } *head = *self; self->inUse = FALSE; self = head; /* Link the sections */ for (i=self->mental-1; i>=0; i--) { if (i == 0) { self->target = body[i]; } else { body[i - 1]->target = body[i]; } body[i]->head = self; } free(body); }
static void addSegments() { char name[MAX_VALUE_LENGTH]; int i, frameCount, mental; Entity *e, *prev; prev = self; if (strcmpignorecase(self->name, "enemy/green_centipede") == 0) { mental = 2; } else if (strcmpignorecase(self->name, "enemy/red_centipede") == 0) { mental = 1; } else { mental = 0; } snprintf(name, MAX_VALUE_LENGTH, "%s_segment", self->name); for (i=0;i<5;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add a Centipede Segment"); } loadProperties(name, e); e->action = &segmentInit; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->takeDamage = &segmentTakeDamage; e->die = &entityDieNoDrop; e->creditsAction = &segmentInit; e->type = ENEMY; e->face = self->face; e->target = prev; e->head = self; e->speed = self->speed; setEntityAnimation(e, "STAND"); frameCount = getFrameCount(e); e->currentFrame = prand() % frameCount; e->x = self->x + self->w / 2 - e->w / 2; e->y = self->y; e->mental = mental; STRNCPY(e->objectiveName, self->name, sizeof(e->objectiveName)); prev = e; if (i == 0) { self->head = e; } if (self->flags & SPAWNED_IN) { e->flags |= SPAWNED_IN; e->spawnTime = self->spawnTime; } } self->mental = mental; }
static void init() { int i, x, y, col; Entity *e, *prev; prev = self; x = self->x; y = self->y + self->h; col = 1; if (self->mental != 2) { for (i=0;i<50;i++) { /* 4 pegs per row, plus 1 score tile and 10 rows */ e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add a Mastermind Peg"); } if (col == 5) { loadProperties("item/mastermind_score", e); col = 0; e->mental = 1; } else { loadProperties("item/mastermind_peg", e); e->mental = 0; } setEntityAnimation(e, "STAND"); if (i == 0) { y -= TILE_SIZE; } if (i != 0 && i % 5 == 0) { x = self->x; y -= TILE_SIZE; } else if (i != 0) { x += TILE_SIZE; } e->face = RIGHT; e->x = x; e->y = y; e->action = &pegWait; e->draw = &drawLoopingAnimationToMap; prev->target = e; prev = e; e->target = NULL; col++; } generateSolution(); addCursor(); } self->action = &entityWait; }
static void holdPersonSpellMove() { Entity *e; if (self->target == NULL) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add the Sorceror's Hold Person"); } loadProperties("boss/sorceror_hold_person_spell", e); setEntityAnimation(e, "BACK"); e->face = RIGHT; e->action = &holdPersonBackWait; e->draw = &drawLoopingAnimationToMap; e->x = self->x; e->y = self->y; e->layer = BACKGROUND_LAYER; self->target = e; e->head = self; } switch (self->mental) { case 3: self->targetY = player.y + player.h / 2 - self->h / 2 - 16; break; case 2: self->targetY = player.y + player.h / 2 - self->h / 2; break; default: self->targetY = player.y + player.h / 2 - self->h / 2 + 16; break; } setCustomAction(&player, &stickToFloor, 3, 0, 0); if (self->y < self->targetY) { self->y += 12; if (self->y >= self->targetY) { self->y = self->targetY; self->face = player.face; } } else { if (self->mental == 3) { setInfoBoxMessage(0, 255, 255, 255, _("Quickly turn left and right to remove the hold person spell!")); } if (self->face != player.face) { self->face = player.face; self->health--; if (self->health <= 0) { setEntityAnimation(self, "LEFT_PIECE"); self->layer = FOREGROUND_LAYER; self->dirX = -4; self->action = &holdPersonPieceMove; self->thinkTime = 30; } } } player.x = self->x + self->w / 2 - player.w / 2; }
static void createIceBlock() { int x, y; Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add Ice Block"); } loadProperties("edgar/edgar_frozen", e); setEntityAnimation(e, "STAND"); x = player.x + player.w / 2; y = self->y - 100 - (prand() % 60); e->x = x; e->y = y; if (isValidOnMap(e) == TRUE) { e->x = self->x + self->w / 2; e->y = self->y + self->h / 2; e->x -= e->w / 2; e->y -= e->h / 2; e->targetX = x; e->targetY = y; e->damage = 1; e->health = 50; calculatePath(e->x, e->y, e->targetX, e->targetY, &e->dirX, &e->dirY); e->flags |= (NO_DRAW|HELPLESS|TELEPORTING|NO_END_TELEPORT_SOUND); playSoundToMap("sound/common/spell", -1, self->x, self->y, 0); e->action = &iceBlockDrop; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->pain = &enemyPain; e->die = &iceBlockDie; e->head = self; e->face = self->face; e->type = ENEMY; e->thinkTime = 30; e->flags |= FLY|DO_NOT_PERSIST; self->endX--; if (self->endX <= 0) { self->thinkTime = 0; self->action = &castIceFinish; } else { self->thinkTime = 60; } } else { e->inUse = FALSE; } } checkToMap(self); hover(); }
static void entityWait() { int frameCount = getFrameCount(self); Entity *e; if (self->active == TRUE) { self->thinkTime--; if (self->thinkTime % 120 == 0) { self->currentFrame++; if (self->currentFrame >= frameCount) { self->currentFrame = frameCount - 1; } } else if (self->thinkTime % 5 == 0) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add the Fire"); } loadProperties("boss/phoenix_die_fire", e); setEntityAnimation(e, "STAND"); e->x = self->x + prand() % self->w; e->y = self->y + self->h - e->h; e->action = &fireWait; e->draw = &drawLoopingAnimationToMap; e->type = ENEMY; e->flags |= DO_NOT_PERSIST|FLY; e->thinkTime = 30; e->health = 0; e->maxHealth = 3 + prand() % 3; e->mental = 1; e->head = self; } if (self->thinkTime <= 0) { self->inUse = FALSE; } } else { self->frameSpeed = 0; self->thinkTime = frameCount * 120; } checkToMap(self); }
static void createIceWall() { int i, mapFloor; Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { mapFloor = getMapFloor(self->x, self->y); for (i=0;i<2;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add Ice Wall"); } loadProperties("enemy/ice_wall", e); e->x = player.x + player.w / 2 + (i == 0 ? -200 : 200); e->y = mapFloor + 50; e->face = (i == 0 ? RIGHT : LEFT); e->targetY = mapFloor - e->h; e->x -= e->w / 2; e->action = &iceWallMove; e->draw = &drawLoopingAnimationToMap; e->touch = &iceWallTouch; e->head = self; e->type = ENEMY; e->flags |= DO_NOT_PERSIST; e->head = self; e->thinkTime = 60; setEntityAnimation(e, "STAND"); } self->endX--; if (self->endX <= 0) { self->thinkTime = 0; self->action = &teleportToOtherSide; } else { self->thinkTime = 90; } } checkToMap(self); hover(); }
static void createLightningWave() { int i, top, bottom, valid; Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { bottom = getMapFloor(self->x, self->y); top = getMapCeiling(self->x, self->y); valid = TRUE; playSoundToMap("sound/enemy/thunder_cloud/lightning", -1, self->x, self->y, 0); for (i=top;i<bottom;i+=32) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add lightning"); } loadProperties("enemy/lightning", e); setEntityAnimation(e, "STAND"); e->x = self->targetX + self->w / 2 - e->w / 2; e->y = i; e->action = &lightningWait; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->head = self; e->currentFrame = prand() % 6; e->face = RIGHT; e->thinkTime = 15; if (isValidOnMap(e) == FALSE) { valid = FALSE; e->inUse = FALSE; break; } } if (valid == TRUE) { e = addSmallRock(self->targetX, bottom, "common/small_rock"); e->x += (self->w - e->w) / 2; e->y -= e->h; e->dirX = -3; e->dirY = -8; e = addSmallRock(self->targetX, bottom, "common/small_rock"); e->x += (self->w - e->w) / 2; e->y -= e->h; e->dirX = 3; e->dirY = -8; self->targetX += self->face == RIGHT ? 64 : -64; self->thinkTime = 30; } else { self->action = &teleportToOtherSide; } } checkToMap(self); hover(); }
static void createBody(Entity *trapHead, Entity *trapBase) { int i; Entity **body, *head; trapHead->x = trapHead->endX; trapHead->y = trapHead->endY; body = malloc(trapHead->mental * sizeof(Entity *)); if (body == NULL) { showErrorAndExit("Failed to allocate a whole %d bytes for Fly Trap body...", trapHead->mental * (int)sizeof(Entity *)); } for (i=trapHead->mental-1;i>=0;i--) { body[i] = getFreeEntity(); if (body[i] == NULL) { showErrorAndExit("No free slots to add a Fly Trap body part"); } loadProperties("enemy/fly_trap_body", body[i]); body[i]->x = trapHead->x; body[i]->y = trapHead->y; body[i]->action = &bodyWait; body[i]->draw = &drawLoopingAnimationToMap; body[i]->touch = &entityTouch; body[i]->die = &entityDieNoDrop; body[i]->takeDamage = &bodyTakeDamage; body[i]->creditsAction = &bodyWait; body[i]->type = ENEMY; body[i]->health = trapBase->health; setEntityAnimation(body[i], "STAND"); } /* Recreate the head so that it's on top */ head = getFreeEntity(); if (head == NULL) { showErrorAndExit("No free slots to add a Fly Trap head"); } *head = *trapHead; trapHead->inUse = FALSE; trapHead = head; /* Link the sections */ for (i=trapHead->mental-1;i>=0;i--) { if (i == 0) { trapHead->target = body[i]; } else { body[i - 1]->target = body[i]; if (i == trapHead->mental - 1) { body[i]->endX = trapBase->x; body[i]->endY = trapBase->y; } } body[i]->head = trapHead; } free(body); trapHead->action = &headWait; trapHead->creditsAction = &headWait; }
static void dieSplit() { Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add a Blob Boss Part"); } loadProperties("boss/blob_boss_part", e); e->flags |= LIMIT_TO_SCREEN; setEntityAnimation(e, "STAND"); e->draw = &drawLoopingAnimationToMap; e->type = ENEMY; e->damage = 0; e->touch = NULL; e->head = self; e->action = &partFinalDie; e->die = &entityDieNoDrop; e->x = self->x + self->box.x + self->box.w / 2 - e->w / 2; e->y = self->y + self->box.y + self->box.h / 2 - e->h / 2; e->thinkTime = 120; e->dirX = 10 + prand() % 80; e->dirX /= 10; e->dirX *= prand() % 2 == 0 ? -1 : 1; e->dirY = -4 - prand() % 12; e->targetX = self->x + self->w / 2; self->mental--; if (self->mental == 0) { if (self->currentFrame == 0) { self->flags |= NO_DRAW; self->thinkTime = 120; self->action = &dieWait; return; } else { self->mental = 30; self->currentFrame--; setFrameData(self); } } self->thinkTime = 3; } checkToMap(self); }
static void castLightningBolt() { Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add lightning"); } loadProperties("enemy/lightning", e); setEntityAnimation(e, "STAND"); e->x = self->x + self->w / 2; e->y = self->y + self->h / 2; e->x -= e->w / 2; e->y -= e->h / 2; e->targetX = player.x + player.w / 2 - e->w / 2; e->targetY = getMapCeiling(self->x, self->y); e->startY = e->targetY; e->endY = getMapFloor(e->targetX, e->targetY); calculatePath(e->x, e->y, e->targetX, e->targetY, &e->dirX, &e->dirY); e->flags |= (NO_DRAW|HELPLESS|TELEPORTING|NO_END_TELEPORT_SOUND); playSoundToMap("sound/common/spell", -1, self->x, self->y, 0); e->head = self; e->face = RIGHT; e->action = &lightningBolt; e->draw = &drawLoopingAnimationToMap; e->head = self; e->face = self->face; e->type = ENEMY; e->thinkTime = 0; e->flags |= FLY|DO_NOT_PERSIST; self->mental--; if (self->mental <= 0) { self->thinkTime = 60; self->action = &attackFinished; } else { self->thinkTime = 30; } } hover(); }
static void eatExplode() { int i; Entity *e; Target *t; self->maxThinkTime = 0; t = getCenterTarget(); self->startX = 0; for (i=0;i<60;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add a Blob Boss Part"); } loadProperties("boss/blob_boss_part", e); setEntityAnimation(e, "STAND"); e->flags |= LIMIT_TO_SCREEN; e->x = self->x; e->y = self->y; e->x += prand() % self->w; e->y += prand() % (self->h - e->h); e->dirX = (10 + prand() % 20) * (prand() % 2 == 0 ? 1 : -1); e->dirX /= 10; e->dirY = -6 - prand() % 4; e->touch = &entityTouch; e->damage = 0; e->health = 600; e->action = &partWait; e->pain = &enemyPain; e->draw = &drawLoopingAnimationToMap; e->head = self; e->type = ENEMY; e->targetX = t->x; e->targetY = t->y; self->startX++; } self->target = NULL; self->thinkTime = 120; self->action = &explodeWait; setEntityAnimation(self, "WALK"); self->flags |= NO_DRAW; self->frameSpeed = 0; self->maxThinkTime = 1; self->touch = NULL; }
static void createShield() { int i, x, y; Entity *shield, *e; self->thinkTime--; if (self->thinkTime <= 0) { if (self->mental == 0) { setEntityAnimation(self, "ATTACK_1"); shield = getFreeEntity(); if (shield == NULL) { showErrorAndExit("No free slots to add the Sorceror's Shield"); } loadProperties("boss/sorceror_shield", shield); shield->x = self->x; shield->y = self->y; shield->action = &shieldCreateWait; shield->draw = &drawLoopingAnimationToMap; shield->touch = &entityTouch; shield->takeDamage = &shieldTakeDamage; shield->pain = &enemyPain; shield->die = &shieldDie; shield->type = ENEMY; shield->head = self; shield->alpha = 128; shield->face = RIGHT; shield->maxHealth = self->health; setEntityAnimation(shield, "STAND"); shield->x = self->x + self->w / 2 - shield->w / 2; shield->y = self->y + self->h / 2 - shield->h / 2; shield->flags |= NO_DRAW; self->startX = 1; self->head = shield; shield->mental = 6; x = shield->x; y = shield->y; for (i=0;i<6;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add the Sorceror's Shield Piece"); } loadProperties("boss/sorceror_shield_piece", e); setEntityAnimationByID(e, i); e->x = x; e->y = y; e->startX = e->x; e->startY = e->y; e->action = &pieceMoveToShield; e->draw = &drawLoopingAnimationToMap; e->type = ENEMY; e->alpha = 128; e->face = RIGHT; e->head = shield; if (x == shield->x) { x += e->w; } else { x = shield->x; y += e->h; } switch (i) { case 0: e->dirX = -12; e->dirY = -6; break; case 1: e->dirX = 12; e->dirY = -6; break; case 2: e->dirX = -12; e->dirY = 0; break; case 3: e->dirX = 12; e->dirY = 0; break; case 4: e->dirX = -12; e->dirY = 6; break; default: e->dirX = 12; e->dirY = 6; break; } e->x += e->dirX * 60; e->y += e->dirY * 60; e->dirX *= -1; e->dirY *= -1; } self->mental = 1; } else if (self->mental == 2) { self->thinkTime = 30; self->action = &createShieldFinish; } } }
static void addChains() { int i; Entity **chains, *chaos; chaos = getEntityByObjectiveName("CHAOS"); if (chaos == NULL) { showErrorAndExit("Chaos Chain Base cannot find Chaos"); } chains = malloc(self->mental * sizeof(Entity *)); if (chains == NULL) { showErrorAndExit("Failed to allocate a whole %d bytes for Chaos's chains...", self->mental * (int)sizeof(Entity *)); } for (i=self->mental-1;i>=0;i--) { chains[i] = getFreeEntity(); if (chains[i] == NULL) { showErrorAndExit("No free slots to add a Chaos Chain"); } loadProperties("item/chaos_chain", chains[i]); chains[i]->face = self->face; chains[i]->action = &chainWait; chains[i]->creditsAction = &creditsChainWait; chains[i]->draw = &drawLoopingAnimationToMap; setEntityAnimation(chains[i], "STAND"); } /* Link the sections */ for (i=self->mental-1;i>=0;i--) { if (i == 0) { self->target = chains[i]; } else { chains[i - 1]->target = chains[i]; } chains[i]->head = self; } free(chains); self->head = chaos; self->action = &chainBaseWait; self->creditsAction = &creditsChainBaseWait; }
static void shieldDie() { int i, x, y; Entity *e; x = self->x; y = self->y; playSoundToMap("sound/boss/sorceror/shield_die", -1, self->x, self->y, 0); for (i=0;i<6;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add the Sorceror's Shield Piece"); } loadProperties("boss/sorceror_shield_piece", e); e->x = x; e->y = y; e->action = &pieceWait; e->draw = &drawLoopingAnimationToMap; e->type = ENEMY; e->alpha = 128; e->face = RIGHT; setEntityAnimationByID(e, i); if (x == self->x) { x += e->w; } else { x = self->x; y += e->h; } switch (i) { case 0: e->dirX = -3; e->dirY = -1.5f; break; case 1: e->dirX = 3; e->dirY = -1.5f; break; case 2: e->dirX = -3; break; case 3: e->dirX = 3; break; case 4: e->dirX = -3; e->dirY = 1.5f; break; default: e->dirX = 3; e->dirY = 1.5f; break; } } self->head->startY = 0; self->head->startX = 0; self->inUse = FALSE; }
static void incinerate() { int x, startX, startY; Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add Fire"); } loadProperties("enemy/fire", e); setEntityAnimation(e, "DOWN"); if (self->face == LEFT) { e->x = self->x + self->w - self->offsetX; } else { e->x = self->x + self->offsetX; } e->y = self->y + self->offsetY; e->x -= e->w / 2; e->action = &fireFall; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->face = self->face; e->type = ENEMY; e->health = 0; self->mental++; if (self->mental == 60) { startX = getMapStartX(); startY = getMapStartY() + SCREEN_HEIGHT - TILE_SIZE; x = 0; while (x < SCREEN_WIDTH) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add the Fire"); } loadProperties("boss/phoenix_die_fire", e); setEntityAnimation(e, "STAND"); e->x = startX + x; e->y = startY - e->h; e->action = &fireWait; e->touch = &entityTouch; e->draw = &drawLoopingAnimationToMap; e->type = ENEMY; e->flags |= FLY; e->layer = FOREGROUND_LAYER; e->thinkTime = 30; e->damage = 1; e->health = 0; e->maxHealth = 5; e->mental = 1; e->head = self; x += e->w; self->endY++; } } else if (self->mental > 100) { self->thinkTime = 60; self->action = &incinerateWait; } else { self->thinkTime = 3; } } checkToMap(self); }
static void finalAttack() { Entity *temp; self->thinkTime--; if (self->thinkTime <= 0) { switch (self->mental) { case 0: setEntityAnimation(self, "ATTACK_4"); addParticleExplosion(self->x + self->w / 2, self->y + self->h / 2); playSoundToMap("sound/common/spell", -1, self->x, self->y, 0); self->flags &= ~NO_DRAW; self->face = RIGHT; createAutoDialogBox(_("Sorceror"), _("Enough! Prepare to die..."), 180); self->thinkTime = 180; self->mental = 1; temp = getFreeEntity(); if (temp == NULL) { showErrorAndExit("No free slots to add a the staff"); } loadProperties("boss/sorceror_staff", temp); temp->action = &staffMoveAroundSorceror; temp->draw = &drawLoopingAnimationToMap; temp->type = ENEMY; setEntityAnimationByID(temp, 0); temp->x = self->x + self->w / 2 - temp->w / 2; temp->y = self->y + self->h / 2 - temp->h / 2; temp->startX = temp->x; temp->startY = temp->y; temp->head = self; temp->health = self->health; temp->mental = self->box.w; break; case 1: temp = getFreeEntity(); if (temp == NULL) { showErrorAndExit("No free slots to add a the Final Attack spell"); } loadProperties("boss/sorceror_final_spell", temp); temp->action = &finalSpellWait; temp->draw = &drawLoopingAnimationToMap; temp->type = ENEMY; setEntityAnimationByID(temp, 0); temp->x = self->x + temp->offsetX; temp->y = self->y + temp->offsetY; temp->head = self; temp->thinkTime = 60; self->thinkTime = 660; self->mental = 2; temp->health = self->health; break; default: self->mental = 3; playSoundToMap("sound/common/massive_explosion", -1, self->x, self->y, 0); fadeFromColour(0, 0, 200, 30); /* Gib the player */ temp = self; self = &player; freeEntityList(playerGib()); self = temp; self->thinkTime = 120; self->action = &finalAttackFinish; break; } } else if (self->thinkTime <= 420 && self->mental == 2) { self->startX = 2; } }
static void stunned() { int i; long onGround = self->flags & ON_GROUND; Entity *e; checkToMap(self); if (self->flags & ON_GROUND) { if (onGround == 0) { for (i=0;i<2;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add the Cave Boss's Star"); } loadProperties("boss/armour_boss_star", e); e->x = self->x; e->y = self->y; e->action = &starWait; e->draw = &drawLoopingAnimationToMap; e->thinkTime = self->thinkTime; e->head = self; setEntityAnimation(e, "STAND"); e->currentFrame = (i == 0 ? 0 : 6); if (self->face == LEFT) { e->x = self->x + self->w - e->w - e->offsetX; } else { e->x = self->x + e->offsetX; } e->y = self->y + e->offsetY; } for (i=0;i<20;i++) { addSmoke(self->x + prand() % self->w, self->y + self->h - prand() % 10, "decoration/dust"); } playSoundToMap("sound/common/crash", BOSS_CHANNEL, self->x, self->y, 0); } self->action = &stunFinish; } }
static void riftAttack() { Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { if (self->mental == 0) { self->y = getMapFloor(self->x + self->w / 2, self->y) - self->h; self->flags &= ~NO_DRAW; addParticleExplosion(self->x + self->w / 2, self->y + self->h / 2); self->mental = 1; self->thinkTime = 60; } else { setEntityAnimation(self, "ATTACK_1"); e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add an Energy Rift"); } loadProperties("enemy/energy_rift", e); e->damage = 1; e->action = &riftMove; e->touch = &entityTouch; e->draw = &drawLoopingAnimationToMap; e->type = ENEMY; setEntityAnimation(e, "STAND"); if (self->face == LEFT) { e->x = self->x - e->w; } else { e->x = self->x + self->w; } e->thinkTime = 15; e->y = self->y; e->dirX = self->face == LEFT ? -e->speed : e->speed; e->head = self; e->targetX = getMapStartX() + SCREEN_WIDTH / 2 - e->w / 2; e->targetY = e->y; e->health = 0; playSoundToMap("sound/common/spell", -1, self->x, self->y, 0); self->mental = -1; self->action = &riftAttackWait; self->thinkTime = 60; } } checkToMap(self); }
static void lightningBolt() { int i, middle; Entity *e; self->flags |= NO_DRAW; self->thinkTime--; middle = -1; if (self->thinkTime <= 0) { playSoundToMap("sound/enemy/thunder_cloud/lightning", -1, self->targetX, self->startY, 0); for (i=self->startY;i<self->endY;i+=32) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add lightning"); } loadProperties("enemy/lightning", e); setEntityAnimation(e, "STAND"); if (i == self->startY) { middle = self->targetX + self->w / 2 - e->w / 2; } e->x = middle; e->y = i; e->action = &lightningWait; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->head = self; e->currentFrame = prand() % 6; e->face = RIGHT; e->thinkTime = 15; } e = addSmallRock(self->x, self->endY, "common/small_rock"); e->x += (self->w - e->w) / 2; e->y -= e->h; e->dirX = -3; e->dirY = -8; e = addSmallRock(self->x, self->endY, "common/small_rock"); e->x += (self->w - e->w) / 2; e->y -= e->h; e->dirX = 3; e->dirY = -8; self->inUse = FALSE; } }
static void disintegrationAttack() { Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { if (self->mental == 0) { self->y = getMapFloor(self->x + self->w / 2, self->y) - self->h; self->flags &= ~NO_DRAW; addParticleExplosion(self->x + self->w / 2, self->y + self->h / 2); self->mental = 1; self->thinkTime = 30; } else { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add the Disintegration Spell"); } setEntityAnimation(self, "ATTACK_2"); loadProperties("boss/sorceror_disintegration_spell", e); setEntityAnimation(e, "STAND"); e->face = self->face; if (self->face == LEFT) { e->x = self->x + self->w - e->w - e->offsetX; } else { e->x = self->x + e->offsetX; } e->y = self->y + e->offsetY; e->action = &disintegrationSpellInit; e->thinkTime = 120; e->startX = e->x; e->startY = e->y; e->head = self; e->endX = player.x + player.w / 2; e->endY = player.y + player.h / 2; e->draw = &drawLoopingAnimationToMap; self->mental = 1; self->action = &disintegrationAttackWait; self->thinkTime = 30; } } }
static void beamWait() { int i, x, startX, floor; Entity *e; self->thinkTime--; if (self->thinkTime <= 0) { if (self->mental == 1) { self->thinkTime = 60; setEntityAnimation(self, "STAND_RED"); self->action = &redBeamWait; } else { i = 0; floor = getMapFloor(self->head->x + self->head->w / 2, self->head->y); /* Left side of beam */ x = self->x; startX = getMapStartX(); for (;x>=startX;) { e = getFreeEntity(); loadProperties("boss/azriel_ground_spikes", e); e->head = self; e->x = x - e->w; e->y = floor; e->startY = e->y - e->h; e->endY = e->y; e->action = &spikeRise; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->face = RIGHT; e->type = ENEMY; e->thinkTime = prand() % 30; x = e->x; i++; } /* Right side of beam */ x = self->x + self->w; startX = getMapStartX() + SCREEN_WIDTH; for (;x<startX;) { e = getFreeEntity(); loadProperties("boss/azriel_ground_spikes", e); e->head = self; e->x = x; e->y = floor; e->startY = e->y - e->h; e->endY = e->y; e->action = &spikeRise; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->face = RIGHT; e->type = ENEMY; e->thinkTime = prand() % 30; x = e->x + e->w; i++; } self->mental = i; self->thinkTime = 30; self->action = &beamFinish; } } }
static void splitAttackInit() { int i; Entity *e; Target *t; self->maxThinkTime = 0; t = getCenterTarget(); for (i=0;i<60;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add a Blob Boss Part"); } loadProperties("boss/blob_boss_part", e); e->flags |= LIMIT_TO_SCREEN; setEntityAnimation(e, "WALK"); e->x = self->x; e->y = self->y; e->x += prand() % self->w; e->y += prand() % (self->h - e->h); e->dirX = (10 + prand() % 20) * (prand() % 2 == 0 ? 1 : -1); e->dirX /= 10; e->dirY = -6; e->action = &partAttack; e->touch = &partGrab; e->die = &partDie; e->pain = &enemyPain; e->draw = &drawLoopingAnimationToMap; e->takeDamage = &partTakeDamage; e->head = self; e->type = ENEMY; e->thinkTime = 60; e->targetX = t->x; e->targetY = t->y; } self->maxThinkTime = 60; self->action = &headWait; setEntityAnimation(self, "BLOCK"); self->flags |= NO_DRAW; self->frameSpeed = 0; self->touch = NULL; }
static void lightningCageWait() { int i, middle; Entity *e; if (self->face == LEFT) { self->x = self->head->x + self->head->w - self->w - self->offsetX; } else { self->x = self->head->x + self->offsetX; } self->y = self->head->y + self->offsetY; middle = 0; e = NULL; if (self->head->mental == 1) { if (self->mental == 0) { self->endY = getMapFloor(self->x + self->w / 2, self->y); for (i=self->y;i<self->endY;i+=32) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add lightning"); } loadProperties("enemy/lightning", e); setEntityAnimation(e, "STAND"); if (i == self->startY) { middle = self->targetX + self->w / 2 - e->w / 2; } e->x = middle; e->y = i; e->action = &cageLightningWait; e->draw = &drawLoopingAnimationToMap; e->touch = &entityTouch; e->head = self; e->currentFrame = prand() % 6; e->face = RIGHT; e->thinkTime = 15; } e->mental = 1; self->mental = 1; if (self->face == LEFT) { self->targetX = playSoundToMap("sound/boss/azriel/azriel_lightning_cage", -1, self->x, self->y, -1); } } } else if (self->head->mental == 2) { if (self->face == LEFT) { stopSound(self->targetX); } self->inUse = FALSE; } }
static void riftAttack() { int i, width; Entity *e; Target *t1, *t2; self->thinkTime--; if (self->thinkTime <= 0) { self->mental = 2; t1 = getTargetByName("CHAOS_TARGET_LEFT"); t2 = getTargetByName("CHAOS_TARGET_RIGHT"); if (t1 == NULL || t2 == NULL) { showErrorAndExit("Chaos cannot find target"); } width = (t2->x - t1->x) / 2; for (i=0;i<self->mental;i++) { e = getFreeEntity(); if (e == NULL) { showErrorAndExit("No free slots to add an Energy Rift"); } loadProperties("enemy/energy_rift", e); e->damage = 2; e->action = &riftOpen; e->touch = &entityTouch; e->draw = &drawLoopingAnimationToMap; e->type = ENEMY; setEntityAnimation(e, "STAND"); e->x = (i == 0 ? t1->x : t2->x) - e->w / 2; e->y = (i == 0 ? t1->y : t2->y); e->speed = width; e->thinkTime = 15; e->head = self; e->health = 0; } self->action = &riftAttackWait; self->thinkTime = 15; } self->maxThinkTime--; checkToMap(self); }