00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "kgrfigure.h"
00019
00020 #include "kgrconsts.h"
00021 #include "kgrobject.h"
00022 #include "kgrcanvas.h"
00023
00024 #include <stdio.h>
00025 #include <QList>
00026
00027 KGrFigure :: KGrFigure (int px, int py) : direction(RIGHT)
00028 {
00029 x = mem_x = px;
00030 y = mem_y = py;
00031 relx = mem_relx = 0;
00032 rely = mem_rely = 0;
00033
00034 absx = px*16;
00035 absy = py*16;
00036
00037 nuggets = 0;
00038 status = STANDING;
00039
00040 walkTimer = new QTimer (this);
00041 fallTimer = new QTimer (this);
00042 }
00043
00044
00045 bool KGrFigure::variableTiming = true;
00046 bool KGrFigure::alwaysCollectNugget = true;
00047 bool KGrFigure::runThruHole = true;
00048 bool KGrFigure::reappearAtTop = true;
00049 SearchStrategy KGrFigure::searchStrategy = LOW;
00050
00051 int KGrFigure::herox = 0;
00052 int KGrFigure::heroy = 0;
00053
00054
00055 int KGrFigure::speed = NSPEED;
00056 int KGrBrick::speed = NSPEED;
00057
00058
00059
00060
00061
00062 Timing KGrFigure::fixedTiming = {45, 50, 55, 100, 500, 40};
00063
00064 Timing KGrFigure::varTiming[6] = {
00065 {40, 58, 78, 88, 170, 23},
00066 {50, 68, 78, 88, 170, 32},
00067 {57, 67, 114, 128, 270, 37},
00068 {60, 70, 134, 136, 330, 40},
00069 {63, 76, 165, 150, 400, 46},
00070 {70, 80, 189, 165, 460, 51}
00071 };
00072
00073 int KGrBrick::HOLETIME = 0;
00074
00075 int KGrFigure::getx()
00076 {
00077 return absx;
00078 }
00079
00080 int KGrFigure::gety()
00081 {
00082 return absy;
00083 }
00084
00085 Status KGrFigure::getStatus()
00086 {
00087 return status;
00088 }
00089
00090 void KGrFigure::init(int a,int b)
00091 {
00092 walkTimer->stop();
00093 fallTimer->stop();
00094 x = mem_x = a;
00095 y = mem_y = b;
00096 relx = mem_relx = 0;
00097 rely = mem_rely = 0;
00098 nuggets = 0;
00099 status = STANDING;
00100 }
00101
00102 void KGrFigure:: setNuggets(int n)
00103 {
00104 nuggets = n;
00105 }
00106
00107
00108 bool KGrFigure::canWalkRight()
00109 {
00110 return (((*playfield)[x+1][y]->whatIam() != BRICK) &&
00111 ((*playfield)[x+1][y]->whatIam() != BETON) &&
00112 ((*playfield)[x+1][y]->whatIam() != FBRICK));
00113 }
00114
00115 bool KGrFigure::canWalkLeft()
00116 {
00117 return (((*playfield)[x-1][y]->whatIam() != BRICK) &&
00118 ((*playfield)[x-1][y]->whatIam() != BETON) &&
00119 ((*playfield)[x-1][y]->whatIam() != FBRICK));
00120 }
00121
00122 bool KGrFigure::canWalkUp()
00123 {
00124 return (((*playfield)[x][y-1]->whatIam() != BRICK) &&
00125 ((*playfield)[x][y-1]->whatIam() != BETON) &&
00126 ((*playfield)[x][y-1]->whatIam() != FBRICK) &&
00127 ((*playfield)[x][y]->whatIam() == LADDER));
00128 }
00129
00130 bool KGrFigure::canWalkDown()
00131 {
00132 return (((*playfield)[x][y+1]->whatIam() != BRICK) &&
00133 ((*playfield)[x][y+1]->whatIam() != BETON) &&
00134
00135
00136 (((*playfield)[x][y+1]->whatIam() == LADDER)||
00137 ((*playfield)[x][y]->whatIam() == LADDER)));
00138 }
00139
00140 bool KGrFigure::canStand()
00141 {
00142 return (((*playfield)[x][y+1]->whatIam() == BRICK) ||
00143 ((*playfield)[x][y+1]->whatIam() == BETON) ||
00144 ((*playfield)[x][y+1]->whatIam() == USEDHOLE)||
00145 ((*playfield)[x][y+1]->whatIam() == LADDER)||
00146 ((*playfield)[x][y]->whatIam() == LADDER)||
00147 standOnEnemy());
00148 }
00149
00150 bool KGrFigure::hangAtPole()
00151 {
00152 return ((*playfield)[x][y]->whatIam() == POLE);
00153 }
00154
00155 void KGrFigure::walkUp(int WALKDELAY)
00156 {
00157 actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1;
00158 if (walkCounter++ < gameCycle) {
00159
00160 if (canWalkUp()) {
00161 rely -= STEP;
00162 absy -= STEP;
00163 }
00164 walkTimer->setSingleShot( true );
00165 walkTimer->start ((WALKDELAY * NSPEED) / speed);
00166 }
00167 else {
00168
00169 if (canWalkUp()) {
00170 y--;
00171 }
00172
00173 rely = 0;
00174 absy = y*16;
00175
00176
00177 status = STANDING;
00178 }
00179 }
00180
00181 void KGrFigure::walkDown(int WALKDELAY, int FALLDELAY)
00182 {
00183 if (hangAtPole() || (! canStand())) {
00184
00185 initFall (FALL2, FALLDELAY);
00186 }
00187 else {
00188 actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1;
00189 if (walkCounter++ < gameCycle) {
00190
00191 if (canWalkDown()) {
00192 rely += STEP;
00193 absy += STEP;
00194 }
00195 walkTimer->setSingleShot( true );
00196 walkTimer->start ((WALKDELAY * NSPEED) / speed);
00197 }
00198 else {
00199
00200 if (canWalkDown()) {
00201 y++;
00202 }
00203
00204 rely = 0;
00205 absy = y*16;
00206
00207
00208 if (! (canStand() || hangAtPole()))
00209 initFall(FALL2, FALLDELAY);
00210 else
00211
00212 status = STANDING;
00213 }
00214 }
00215 }
00216
00217 void KGrFigure::walkLeft (int WALKDELAY, int FALLDELAY)
00218 {
00219
00220 if (walkCounter++ != 0) {
00221
00222 if ((++actualPixmap % gameCycle) != 0) {
00223
00224 if (canWalkLeft()) {
00225 relx -= STEP;
00226 absx -=STEP;
00227 }
00228 walkTimer->setSingleShot( true );
00229 walkTimer->start ((WALKDELAY * NSPEED) / speed);
00230 }
00231 else {
00232
00233 alternateStepGraphics = true;
00234 if ((actualPixmap % graphicsCycle) == 0) {
00235 alternateStepGraphics = false;
00236 actualPixmap -= graphicsCycle;
00237 }
00238
00239 if (canWalkLeft()) {
00240 x--;
00241 }
00242
00243 relx = 0;
00244 absx = x*16;
00245
00246
00247 if (! (canStand() || hangAtPole()))
00248 initFall (FALL1, FALLDELAY);
00249 else
00250 status = STANDING;
00251 }
00252 }
00253 else {
00254 status = STANDING;
00255 }
00256 }
00257
00258 void KGrFigure::walkRight(int WALKDELAY, int FALLDELAY)
00259 {
00260 if (walkCounter++) {
00261 if ((++actualPixmap % gameCycle) != 0) {
00262
00263 if (canWalkRight()) {
00264 relx += STEP;
00265 absx += STEP;
00266 }
00267 walkTimer->setSingleShot( true );
00268 walkTimer->start ((WALKDELAY * NSPEED) / speed);
00269 }
00270 else {
00271
00272 alternateStepGraphics = true;
00273 if ((actualPixmap % graphicsCycle) == 0) {
00274 alternateStepGraphics = false;
00275 actualPixmap -= graphicsCycle;
00276 }
00277
00278 if (canWalkRight()) {
00279 x++;
00280 }
00281
00282 relx = 0;
00283 absx = x*16;
00284
00285 if (!(canStand()||hangAtPole()))
00286 initFall (FALL2, FALLDELAY);
00287 else
00288 status = STANDING;
00289 }
00290 }
00291 else {
00292 status = STANDING;
00293 }
00294 }
00295
00296 void KGrFigure::initFall(int apm, int FALLDELAY)
00297 {
00298 status = FALLING;
00299 actualPixmap = apm;
00300 walkCounter=1;
00301 walkTimer->stop();
00302 fallTimer->setSingleShot( true );
00303 fallTimer->start((FALLDELAY * NSPEED) / speed);
00304 }
00305
00306 void KGrFigure::showFigure ()
00307 {
00308 }
00309
00310 void KGrFigure::setPlayfield (KGrObject * (*p)[30][22])
00311 {
00312 playfield = p;
00313 }
00314
00315 KGrFigure :: ~KGrFigure ()
00316 {
00317 }
00318
00319 KGrHero :: KGrHero (KGrCanvas * view, int x, int y)
00320 :KGrFigure (x, y)
00321 {
00322 heroView = view;
00323 status = STANDING;
00324 actualPixmap = FALL1;
00325
00326 herox = x;
00327 heroy = y;
00328
00329 started = false;
00330 mouseMode = true;
00331 walkCounter = 1;
00332
00333 walkFrozen = false;
00334 fallFrozen = false;
00335 alternateStepGraphics = false;
00336
00337 connect (walkTimer, SIGNAL (timeout ()), SLOT (walkTimeDone ()));
00338 connect (fallTimer, SIGNAL (timeout ()), SLOT (fallTimeDone ()));
00339 }
00340
00341 int KGrHero::WALKDELAY = 0;
00342 int KGrHero::FALLDELAY = 0;
00343
00344
00345
00346
00347 void KGrHero::startWalk ()
00348 {
00349 switch (nextDir) {
00350 case UP:
00351 if ((*playfield)[x][y]->whatIam () == LADDER)
00352 {walkCounter = 1;
00353 direction = UP;}
00354 break;
00355 case RIGHT:
00356 if (hangAtPole())
00357 actualPixmap = (alternateStepGraphics) ? RIGHTCLIMB5 : RIGHTCLIMB1;
00358 else
00359 actualPixmap = (alternateStepGraphics) ? RIGHTWALK5 : RIGHTWALK1;
00360 if (direction != RIGHT)
00361 walkCounter = 0;
00362 else
00363 walkCounter = 1;
00364 direction = RIGHT;
00365 break;
00366 case DOWN:
00367 if (((*playfield)[x][y]->whatIam () == LADDER)||
00368 ((*playfield)[x][y+1]->whatIam () == LADDER))
00369 {walkCounter = 1;
00370 direction = DOWN;}
00371 else
00372
00373 if (hangAtPole() && (!canStand()))
00374 { status = STANDING;
00375 actualPixmap = (direction==RIGHT)?FALL2:FALL1;
00376 walkCounter=1;
00377 direction=STAND;
00378 walkTimer->stop();
00379 }
00380 break;
00381 case LEFT:
00382 if (hangAtPole())
00383 actualPixmap = (alternateStepGraphics) ? LEFTCLIMB5 : LEFTCLIMB1;
00384 else
00385 actualPixmap = (alternateStepGraphics) ? LEFTWALK5 : LEFTWALK1;
00386 if (direction != LEFT)
00387 walkCounter = 0;
00388 else
00389 walkCounter = 1;
00390 direction = LEFT;
00391 break;
00392 default :
00393 direction = STAND;
00394 status = FALLING;
00395 break;
00396 }
00397 nextDir = STAND;
00398 if (status != FALLING)
00399 { status = WALKING;
00400 showFigure ();
00401 }
00402 }
00403
00404 void KGrHero::setKey(Direction key)
00405 {
00406
00407
00408 mouseMode = false;
00409 stopped = false;
00410 switch (key) {
00411 case UP: mousex = x; mousey = 0; break;
00412 case DOWN: mousex = x; mousey = FIELDHEIGHT + 1; break;
00413 case LEFT: mousex = 0; mousey = y; break;
00414 case RIGHT: mousex = FIELDWIDTH + 1; mousey = y; break;
00415 case STAND: stopped = true; mousex = x; mousey = y; break;
00416 }
00417 }
00418
00419 void KGrHero::setDirection(int i, int j)
00420 {
00421
00422 mouseMode = true;
00423 stopped = false;
00424 mousex = i;
00425 mousey = j;
00426 }
00427
00428 void KGrHero::setNextDir()
00429 {
00430 int dx, dy;
00431
00432 if (! mouseMode) {
00433
00434 if (stopped) {
00435 mousex = x;
00436 mousey = y;
00437 }
00438 if ((mousey < 1) || (mousey > FIELDHEIGHT)) {
00439 mousex = x;
00440 }
00441 else if ((mousex < 1) || (mousex > FIELDWIDTH)) {
00442 mousey = y;
00443 }
00444 }
00445
00446 dx = mousex - x; dy = mousey - y;
00447
00448 if ((dy == 0) && (y == 1) && (nuggets <= 0)) {
00449 nextDir = UP;
00450 }
00451 else if ((dy > 0) &&
00452 (canWalkDown() ||
00453
00454
00455
00456
00457
00458 (hangAtPole() && ((*playfield)[x][y+1]->whatIam() != BRICK) &&
00459 ((*playfield)[x][y+1]->whatIam() != BETON)))) {
00460 nextDir = DOWN;
00461 }
00462 else if ((dy < 0) && canWalkUp ()) {
00463 nextDir = UP;
00464 }
00465 else if (dx > 0) {
00466 nextDir = RIGHT;
00467 }
00468 else if (dx < 0) {
00469 nextDir = LEFT;
00470 }
00471 else if (dx == 0) {
00472 nextDir = STAND;
00473 }
00474 }
00475
00476 void KGrHero::doStep() {
00477 if (walkFrozen) {
00478 walkFrozen = false;
00479 walkTimeDone();
00480 }
00481 if (fallFrozen) {
00482 fallFrozen = false;
00483 fallTimeDone();
00484 }
00485 }
00486
00487 void KGrHero::showState(char option)
00488 {
00489 printf("(%02d,%02d) - Hero ", x, y);
00490 switch (option) {
00491 case 'p': printf ("\n"); break;
00492 case 's': printf (" nuggets %02d status %d walk-ctr %d ",
00493 nuggets, status, walkCounter);
00494 printf ("dirn %d next dirn %d\n", direction, nextDir);
00495 printf (" rel (%02d,%02d) abs (%03d,%03d)",
00496 relx, rely, absx, absy);
00497 printf (" pix %02d", actualPixmap);
00498 printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely);
00499 if (walkFrozen) printf (" wBlock");
00500 if (fallFrozen) printf (" fBlock");
00501 printf ("\n");
00502 break;
00503 }
00504 }
00505
00506 void KGrHero::init(int a,int b)
00507 {
00508 walkTimer->stop();
00509 fallTimer->stop();
00510 walkCounter = 1;
00511 started = false;
00512
00513 x = mem_x = a;
00514 y = mem_y = b;
00515 relx = mem_relx = 0;
00516 rely = mem_rely = 0;
00517
00518 absx = 16*x;
00519 absy = 16*y;
00520
00521 nuggets = 0;
00522
00523 if (herox < 1) {
00524 heroView->makeHeroSprite (x, y, actualPixmap);
00525 }
00526 herox = x;
00527 heroy = y;
00528
00529 actualPixmap = FALL2;
00530 heroView->moveHero (absx, absy, actualPixmap);
00531 }
00532
00533 void KGrHero::start()
00534 {
00535 started = true;
00536 walkFrozen = false;
00537 fallFrozen = false;
00538
00539 if (!(canStand()||hangAtPole())) {
00540 status = FALLING;
00541 fallTimeDone();
00542 }
00543 else {
00544 status = STANDING;
00545 walkTimeDone();
00546 }
00547 }
00548
00549 void KGrHero::setSpeed (int gamespeed)
00550 {
00551 if (gamespeed >= 0) {
00552 if (gamespeed < MINSPEED)
00553 speed++;
00554 else
00555 speed = gamespeed;
00556 if (speed > MAXSPEED)
00557 speed = MAXSPEED;
00558 }
00559 else {
00560 speed--;
00561 if (speed < MINSPEED)
00562 speed = MINSPEED;
00563 }
00564
00565 KGrBrick::speed = speed;
00566 }
00567
00568 void KGrHero::walkTimeDone ()
00569 {
00570 if (! started) return;
00571 if (KGrObject::frozen) {walkFrozen = true; return; }
00572
00573 if ((*playfield)[x][y]->whatIam() == BRICK) {
00574 emit caughtHero();
00575 return;
00576 }
00577
00578 if ((y==1)&&(nuggets<=0)) {
00579 emit leaveLevel();
00580 return;
00581 }
00582
00583 if (status == STANDING)
00584 setNextDir();
00585 if ((status == STANDING) && (nextDir != STAND)) {
00586 if ((standOnEnemy()) && (nextDir == DOWN)) {
00587 emit caughtHero();
00588 return;
00589 }
00590 startWalk();
00591 }
00592 if (status != STANDING) {
00593 switch (direction) {
00594 case UP: walkUp (WALKDELAY); break;
00595 case DOWN: walkDown (WALKDELAY, FALLDELAY); break;
00596 case RIGHT: walkRight (WALKDELAY, FALLDELAY); break;
00597 case LEFT: walkLeft (WALKDELAY, FALLDELAY); break;
00598 default :
00599
00600
00601 if (!canStand()||hangAtPole())
00602 initFall(FALL1, FALLDELAY);
00603 else status = STANDING;
00604 break;
00605 }
00606 herox=x; heroy=y;
00607 if ((relx==0)&&(rely==0)) {
00608 collectNugget();
00609 }
00610 }
00611 if (status == STANDING)
00612 if (!canStand()&&!hangAtPole())
00613 initFall(FALL1, FALLDELAY);
00614 else
00615 {
00616 walkTimer->setSingleShot( true );
00617 walkTimer->start ((WALKDELAY * NSPEED) / speed);
00618 }
00619
00620
00621
00623 showFigure();
00624 if(isInEnemy()) {
00625 walkTimer->stop();
00626 emit caughtHero();
00627 }
00628 }
00629
00630 void KGrHero::fallTimeDone()
00631 {
00632 if (! started) return;
00633 if (KGrObject::frozen) {fallFrozen = true; return; }
00634
00635 if (!standOnEnemy()) {
00636 if (walkCounter++ < gameCycle) {
00637 fallTimer->setSingleShot(true);
00638 fallTimer->start((FALLDELAY * NSPEED) / speed);
00639 rely+=STEP;
00640 absy+=STEP;
00641 }
00642 else {
00643
00644 heroy = ++y;
00645 rely = 0;
00646 absy = y*16;
00647 collectNugget();
00648 if (! (canStand()||hangAtPole())) {
00649 fallTimer->setSingleShot(true);
00650 fallTimer->start((FALLDELAY * NSPEED) / speed);
00651 walkCounter = 1;
00652 }
00653 else {
00654 status = STANDING;
00655 walkTimer->setSingleShot(true);
00656 walkTimer->start((WALKDELAY * NSPEED) / speed);
00657 direction = (actualPixmap == FALL2) ? RIGHT : LEFT;
00658 if ((*playfield)[x][y]->whatIam() == POLE)
00659 actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1;
00660
00661
00662
00663 }
00664 }
00665 showFigure();
00666 }
00667 else {
00668 if (rely == 0) {
00669
00670 status = STANDING;
00671 direction = (actualPixmap == FALL2) ? RIGHT : LEFT;
00672 if ((*playfield)[x][y]->whatIam() == POLE)
00673 actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1;
00674
00675
00676
00677 walkTimer->setSingleShot( true );
00678 walkTimer->start((WALKDELAY * NSPEED) / speed);
00679 }
00680 else {
00681
00682 fallTimer->setSingleShot(true);
00683 fallTimer->start((FALLDELAY * NSPEED) / speed);
00684 }
00685 }
00686 if (isInEnemy() && (! standOnEnemy()))
00687 emit caughtHero();
00688 }
00689
00690
00691 void KGrHero::showFigure () {
00692
00693 heroView->moveHero (absx, absy, actualPixmap);
00694
00695
00696 mem_x = x;
00697 mem_y = y;
00698 mem_relx = relx;
00699 mem_rely = rely;
00700 }
00701
00702 void KGrHero::dig(){
00703 if (direction == LEFT)
00704 digLeft();
00705 else
00706 if (direction == RIGHT)
00707 digRight();
00708 }
00709
00710 void KGrHero::digLeft(){
00711 int i = 1;
00712 if (status == STANDING)
00713 setNextDir();
00714 if ((status == WALKING) ||
00715 ((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) {
00716 if ((direction == LEFT) && canWalkLeft())
00717 i = 2;
00718 else if ((direction == RIGHT) && canWalkRight())
00719 i = 0;
00720 }
00721 if (((*playfield)[x-i][y+1]->whatIam() == BRICK)&&
00722 (((*playfield)[x-i][y]->whatIam() == HLADDER)||
00723 ((*playfield)[x-i][y]->whatIam() == FREE)||
00724 ((*playfield)[x-i][y]->whatIam() == HOLE)))
00725 ((KGrBrick*)(*playfield)[x-i][y+1])->dig();
00726 }
00727
00728 void KGrHero::digRight(){
00729 int i = 1;
00730 if (status == STANDING)
00731 setNextDir();
00732 if ((status == WALKING) ||
00733 ((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) {
00734 if ((direction == LEFT) && canWalkLeft())
00735 i = 0;
00736 else if ((direction == RIGHT) && canWalkRight())
00737 i = 2;
00738 }
00739 if (((*playfield)[x+i][y+1]->whatIam() == BRICK)&&
00740 (((*playfield)[x+i][y]->whatIam() == HLADDER)||
00741 ((*playfield)[x+i][y]->whatIam() == FREE)||
00742 ((*playfield)[x+i][y]->whatIam() == HOLE)))
00743 ((KGrBrick*)(*playfield)[x+i][y+1])->dig();
00744 }
00745
00746 void KGrHero::setEnemyList(QList<KGrEnemy *> * e)
00747 {
00748 enemies = e;
00749 }
00750
00751 bool KGrHero::standOnEnemy()
00752 {
00753 int c = 0;
00754 int range = enemies->count();
00755 if (range > 0) {
00756 for (KGrEnemy * enemy = enemies->at (c); c < range; ) {
00757 enemy = enemies->at(c++);
00758
00759
00760 if ((((absy + 16) == enemy->gety()) ||
00761 ((absy + 12) == enemy->gety())) &&
00762 (((absx - 16) < enemy->getx()) &&
00763 ((absx + 16) > enemy->getx()))) {
00764 if (((absy + 12) == enemy->gety()) &&
00765 (enemy->getStatus() != FALLING)) {
00766 absy = absy - rely;
00767 rely = 0;
00768 walkCounter = 1;
00769 }
00770 return true;
00771 }
00772 }
00773 }
00774 return false;
00775 }
00776
00777 void KGrHero::collectNugget(){
00778
00779 if ((*playfield)[x][y]->whatIam() == NUGGET)
00780 {
00781 ((KGrFree *)(*playfield)[x][y])->setNugget(false);
00782 if (!(--nuggets))
00783 emit haveAllNuggets();
00784
00785 emit gotNugget(250);
00786
00787 }
00788 }
00789
00790 void KGrHero::loseNugget() {
00791
00792
00793 if (! (--nuggets))
00794 emit haveAllNuggets();
00795
00796 }
00797
00798 bool KGrHero::isInEnemy(){
00799
00800 int c=0;
00801 int range=enemies->count();
00802 if (range)
00803 for (KGrEnemy *enemy=enemies->at(c);c<range; )
00804 {enemy = enemies->at(c++);
00805 if (isInside(enemy->getx(),enemy->gety())||
00806 isInside(enemy->getx()-15,enemy->gety())||
00807 isInside(enemy->getx(),enemy->gety()-15))
00808 return true;}
00809 return false;
00810 }
00811
00812 bool KGrHero::isInside(int enemyx, int enemyy){
00813
00814 return ((absx >= enemyx)&&
00815 (absx <= enemyx+15)&&
00816 (absy >= enemyy)&&
00817 (absy