This engine is gently modified Shaman King's engine factically - the walking and battle functions was unsplitted, the battles became real-time, and interaction with joystick buttons has returned to main function. But there is a serious problem - during the hit getting enemy loses 3 HP which is prescribed in code not once but after every screen refresh for the all duration of hit getting animation first frame (5 system screen frames); i. e. enemy loses 15 HP after each hit instead of 3 planned.

SDK: by Sebastian Mihai.
Uses a DATlib v0.3.

Roms+source+XML file for MAME.

The gameplay video is here.


3 damage * 5 timing frame = 15


HPMAN (./2):
3 damage * 5 timing frame = 15

I just said about it, and I want to know, how to make the program to plus the damage after hit only once - not after every timing frame.


Games susually put an ID on hit boxes to make sure they only process once.


HPMAN (./4):
Games susually put an ID on hit boxes to make sure they only process once.

How can I realise it?


Now this engine is restructed for a mix of visual novel, RPG, point'n'klick adventure, and date- or life-sim (Inspired by few relatively old concept artworks - 1, 2 - of hentai mangaka nicknamed Chinbotsu, and also by MLP and Super Drags: Red Band Slayage cartoon series).
At present I trying to make the NPCs sprites to behave as a solid objects interfering player's movement during the collision (this might be very useful in Shaman King also), but for now I'm not very successful in this thing.
I used this way to create the "barrier" around every NPC:
if((p1&JOY_UP)&&y>=176) { if(((y-RoxyY>=16)&&(RoxyX-x>=16)&&(x-RoxyX>=16))&&((y-GyaruY>=16)&&(GyaruX-x>=16)&&(x-GyaruX>=16))) { y--; } } if((p1&JOY_DOWN)&&y<=220) { if(((RoxyY-y>=16)&&(RoxyX-x>=16)&&(x-RoxyX>=16))&&((GyaruY-y>=16)&&(GyaruX-x>=16)&&(x-GyaruX>=16))) { y++; } } if((p1&JOY_LEFT)&&x>=32) { if(((x-RoxyX>=16)&&(y-RoxyY>=16)&&(RoxyY-y>=16))&&((x-GyaruX>=16)&&(y-GyaruY>=16)&&(GyaruY-y>=16))) { x--; } flipMode|=FLIP_X; aSpriteSetFlip(&demoSpr,flipMode); } if((p1&JOY_RIGHT)&&x<=288) { if(((RoxyX-x>=16)&&(y-RoxyY>=16)&&(RoxyY-y>=16))&&((GyaruX-x>=16)&&(y-GyaruY>=16)&&(GyaruY-y>=16))) { x++; } flipMode&=~FLIP_X; aSpriteSetFlip(&demoSpr,flipMode); }if((p1&JOY_UP)&&((y<=112)&&(y>0))) { if(((y-RoxyY>=16)&&(RoxyX-x>=16)&&(x-RoxyX>=16))&&((y-GyaruY>=16)&&(GyaruX-x>=16)&&(x-GyaruX>=16))) { scrl_y--; } } if((p1&JOY_DOWN)&&((y>=208)&&(y<224))) { if(((RoxyY-y>=16)&&(RoxyX-x>=16)&&(x-RoxyX>=16))&&((GyaruY-y>=16)&&(GyaruX-x>=16)&&(x-GyaruX>=16))) { scrl_y++; } } if((p1&JOY_LEFT)&&((x<=32)&&(x>0))) { if(((x-RoxyX>=16)&&(y-RoxyY>=16)&&(RoxyY-y>=16))&&((x-GyaruX>=16)&&(y-GyaruY>=16)&&(GyaruY-y>=16))) { scrl_x--; if (scrl_x>FRONT_MIN_X) { RoxyX++; GyaruX++; } } } if((p1&JOY_RIGHT)&&((x>=288)&&(x<320))) { if(((RoxyX-x>=16)&&(y-RoxyY>=16)&&(RoxyY-y>=16))&&((GyaruX-x>=16)&&(y-GyaruY>=16)&&(GyaruY-y>=16))) { scrl_x++; if (scrl_x<FRONT_MAX_X) { RoxyX--; GyaruX--; } } }But in practice the player's character is just stupidly "stuck".

Can you advice me a more smart way to relize a solid objects in the game?


I don't know what the specific problem with your code is, but I think you need to restructure it. At the moment it's very hard to read, and it will only get worse if you add more features later.

Try to use named constants, remove duplication (if very similar code appears more than one time, it's a sign you should create a function) and split up things into several lines so that you don't have complex expressions.

If you're stuck, testing your algorithm manually on grid paper can help you find the problems, too.

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo


Now this code look like this:

if((RoxyX-x<=16)||(GyaruX-x<=16)) obstacle_right=1; if((x-RoxyX<=16)||(x-GyaruX<=16)) obstacle_left=1; if((y-RoxyY<=16)||(y-GyaruY<=16)) obstacle_up=1; if((RoxyY-y<=16)||(GyaruY-y<=16)) obstacle_down=1; //if(attacking==0) //{ if((p1&JOY_UP)&&y>=176) { if((obstacle_up==0)&&(obstacle_left==0)&&(obstacle_right==0)) { y--; } } if((p1&JOY_DOWN)&&y<=220) { if((obstacle_down==0)&&(obstacle_left==0)&&(obstacle_right==0)) { y++; } } if((p1&JOY_LEFT)&&x>=32) { if((obstacle_left==0)&&(obstacle_up==0)&&(obstacle_down==0)) { x--; } flipMode|=FLIP_X; aSpriteSetFlip(&demoSpr,flipMode); } if((p1&JOY_RIGHT)&&x<=288) { if((obstacle_right==0)&&(obstacle_up==0)&&(obstacle_down==0)) { x++; } flipMode&=~FLIP_X; aSpriteSetFlip(&demoSpr,flipMode); }if((p1&JOY_UP)&&((y<=112)&&(y>0))) { if((obstacle_up==0)&&(obstacle_left==0)&&(obstacle_right==0)) { scrl_y--; } } if((p1&JOY_DOWN)&&((y>=208)&&(y<224))) { if((obstacle_down==0)&&(obstacle_left==0)&&(obstacle_right==0)) { scrl_y++; } } if((p1&JOY_LEFT)&&((x<=32)&&(x>0))) { if((obstacle_left==0)&&(obstacle_up==0)&&(obstacle_down==0)) { scrl_x--; if (scrl_x>FRONT_MIN_X) { RoxyX++; GyaruX++; } } } if((p1&JOY_RIGHT)&&((x>=288)&&(x<320))) { if((obstacle_right==0)&&(obstacle_up==0)&&(obstacle_down==0)) { scrl_x++; if (scrl_x<FRONT_MAX_X) { RoxyX--; GyaruX--; } } }
It became more readable and understandable, but bug wasn't removed.


The bug is finally fixed (the archive file by the link is updated, but link by itself is stayed the same). Now the solid objects properties code looks that:
if(((x>(RoxyX-48))&&(x<(RoxyX-32)))) { if(((y<(RoxyY+8))&&(y>(RoxyY-8)))) { x--; } } if(((x>(RoxyX+32))&&(x<(RoxyX+48)))) { if(((y<(RoxyY+8))&&(y>(RoxyY-8)))) { x++; } } if(((y>(RoxyY+4))&&(y<(RoxyY+8)))) { if(((x<(RoxyX+16))&&(x>(RoxyX-16)))) { y++; } } if(((y>(RoxyY-8))&&(y<(RoxyY-4)))) { if(((x<(RoxyX+16))&&(x>(RoxyX-16)))) { y--; } } if(((x>(GyaruX-48))&&(x<(GyaruX-32)))) { if(((y<(GyaruY+8))&&(y>(GyaruY-8)))) { x--; } } if(((x>(GyaruX+32))&&(x<(GyaruX+48)))) { if(((y<(GyaruY+8))&&(y>(GyaruY-8)))) { x++; } } if(((y>(GyaruY+4))&&(y<(GyaruY+8)))) { if(((x<(GyaruX+16))&&(x>(GyaruX-16)))) { y++; } } if(((y>(GyaruY-8))&&(y<(GyaruY-4)))) { if(((x<(GyaruX+16))&&(x>(GyaruX-16)))) { y--; } }
However, the pieces of code responsible for each NPC hitzones is still diplicates each other for now, so I'll need to make this code more common for all NPCs (this also applies to dialogues code in future).

I also had to "centerize" the every frame of player sprite animation horizonatally to solve the hitzones detection problem.

But it's a still unclear, what's happening with Gyaru sprite palettes, and what mistakes I made during the definition of FRONT_MAX_X.


Now I added the dialogue system (the link is stayed the same again), but for some reason this dialogues are refising to work. Where a made a mistake at this time?

if(talking==0) { if((p1&JOY_UP)&&y>=176) y--; if((p1&JOY_DOWN)&&y<=220) y++; if((p1&JOY_LEFT)&&x>=32) { x--; flipMode|=FLIP_X; aSpriteSetFlip(&demoSpr,flipMode); } if((p1&JOY_RIGHT)&&x<=288) { x++; flipMode&=~FLIP_X; aSpriteSetFlip(&demoSpr,flipMode); } aSpriteSetAnim(&demoSpr,p1&(JOY_UP|JOY_DOWN|JOY_LEFT|JOY_RIGHT)?1:0); } if(p1e&JOY_A) { if(talking==0) { if(((flipMode==FLIP_NONE)&&(RoxyX-x<64))||((flipMode==FLIP_X)&&(x-RoxyX<64))) { if(((y<(RoxyY+8))&&(y>(RoxyY-8)))) { talking=1; action=0; choice=0; } } if(((flipMode==FLIP_NONE)&&(GyaruX-x<64))||((flipMode==FLIP_X)&&(x-GyaruX<64))) { if(((y<(GyaruY+8))&&(y>(GyaruY-8)))) { talking=2; action=0; choice=0; } } } if(talking>0) { action++; clearFixLayer(); pictureSetPos(&InterlocutorPortrait,0,16-(VerticalAlign*8)); } } if(talking>0) { if(demoSpr.currentAnim!=0) aSpriteSetAnim(&demoSpr,0); TextWindow(VerticalAlign); if(dialogue_menu>0) { if(p1e&JOY_UP) { if(choice>=1) choice--; } if(p1e&JOY_DOWN) { if(choice<dialogue_menu) choice++; } } } if(talking==1) { pictureInit(&InterlocutorPortrait, &roxypict,22, 16 + ffbg_b.palInfo->count, 0, 16-(VerticalAlign*8),FLIP_NONE); palJobPut(16 + ffbg_b.palInfo->count, roxypict.palInfo->count, roxypict.palInfo->data); if(action==0) { VerticalAlign=7; fixPrint(1,22,0,1,"Добро пожаловать на наш прекрасный"); fixPrint(1,23,0,1,"остров трансов! Мы тут с моими"); fixPrint(1,24,0,1,"\"подружками\" все весело, беспечно и"); fixPrint(1,25,0,1,"раздолбайски проводим ВСЁ наше время!"); fixPrint(1,26,0,1,"Присоединяйся к нам,"); fixPrint(1,27,0,1,"красавчик - уверена, тебе у нас"); fixPrint(1,28,0,1,"О-О-ОО-ООО-ОО-ОЧЕНЬ понравится!"); } if(action==1) { talking=0; clearFixLayer(); clearSprites(1,21); } } if(talking==2) { pictureInit(&InterlocutorPortrait, &gyarupict,22, 16 + ffbg_b.palInfo->count, 0, 16-(VerticalAlign*8),FLIP_NONE); palJobPut(16 + ffbg_b.palInfo->count, gyarupict.palInfo->count, gyarupict.palInfo->data); if(action==0) { VerticalAlign=10; dialogue_menu=1; fixPrint(1,19,0,1,"Привет, сладенький! Как же я рада, что"); fixPrint(1,20,0,1,"ты всё-таки выжил во время того"); fixPrint(1,21,0,1,"крушения!"); fixPrint(1,22,0,1,"Надеюсь, тебя не смущает, красавчик,"); fixPrint(1,23,0,1,"что я... ненастоящая девушка? :)"); fixPrint(3,24,choice==1?2:4,1,"НУ... Э-Э-ЭЭМ... НЕ ОЧЕНЬ."); fixPrint(3,25,choice==2?2:4,1,"НЕ, НЕ, НЕ, НЕ, НЕ! НЕ НАДО, НЕ"); fixPrint(3,26,choice==2?2:4,1,"НАДО!... СПАСИБО!... ВЫ УЖ МЕНЯ,"); fixPrint(3,27,choice==2?2:4,1,"КОНЕЧНО, ИЗВИНИТЕ, НО Я... Я ВСЁ-ТАКИ"); fixPrint(3,28,choice==2?2:4,1,"ПРЕДПОЧИТАЮ НАСТОЯЩИХ!"); } if(action==1) { VerticalAlign=12; if(choice==0) { fixPrint(1,17,0,1,"Вот и славненько! Знаешь,"); fixPrint(1,18,0,1,"ты - пожалуй, первый за всю историю"); fixPrint(1,19,0,1,"острова парень, попавший сюда, не"); fixPrint(1,20,0,1,"пройдя предварительно процедуру"); fixPrint(1,21,0,1,"феминизации! Хотя... в последнем я"); fixPrint(1,22,0,1,"особой проблемы не вижу - здесь это"); fixPrint(1,23,0,1,"очень даже легко поправимо, всего лишь"); fixPrint(1,24,0,1,"вопрос времени. Было бы классно, если"); fixPrint(1,25,0,1,"бы ты смог \"влиться в нашу"); fixPrint(1,26,0,1,"компанию\" - как говорят у нас на"); fixPrint(1,27,0,1,"острове, партнёрш много не бывает! \x3"); fixPrint(1,28,0,1,"Не правда ли?"); } if(choice==1) { fixPrint(1,17,0,1,"Жа-а-аа-ааа-аль!... А ведь мы с тобой"); fixPrint(1,18,0,1,"могли бы стать отличными подругами,"); fixPrint(1,19,0,1,"если бы ты всё же решился на операцию"); fixPrint(1,20,0,1,"по перемене пола! Я уж, конечно,"); fixPrint(1,21,0,1,"постараюсь это пережить, но... Ты"); fixPrint(1,22,0,1,"всё-таки мне очень понравился, парень."); fixPrint(1,23,0,1,"Понравился с самого первого"); fixPrint(1,24,0,1,"взгляда. :("); fixPrint(1,25,0,1,"Ну да ладно, если вдруг резко"); fixPrint(1,26,0,1,"усомнишься в своих сексуальных"); fixPrint(1,27,0,1,"предпочтениях, возвращайся - буду"); fixPrint(1,28,0,1,"ждать тебя на этом же месте."); } } if(action==2) { talking=0; clearFixLayer(); clearSprites(1,21); } } if(talking==0) { if((p1&JOY_UP)&&((y<=112)&&(y>0))) scrl_y--; if((p1&JOY_DOWN)&&((y>=208)&&(y<224))) scrl_y++; if((p1&JOY_LEFT)&&((x<=32)&&(x>0))) { scrl_x--; if (scrl_x>FRONT_MIN_X) { RoxyX++; GyaruX++; } } if((p1&JOY_RIGHT)&&((x>=288)&&(x<320))) { scrl_x++; if (scrl_x<FRONT_MAX_X) { RoxyX--; GyaruX--; } } }void TextWindow(short WindowHeight) { short tilex; short tiley; fixPrint(0,29-WindowHeight,0,1,"\xD"); fixPrint(39,29-WindowHeight,0,1,"\xF"); fixPrint(0,29,0,1,"\x7"); fixPrint(39,29,0,1,"\x9"); for(tiley=29-WindowHeight;tiley=29-WindowHeight;tiley++) { for(tilex=1;tilex<39;tilex++) { fixPrint(tilex,tiley,0,1,"\xE"); } } for(tiley=(29-WindowHeight)+1;tiley<29;tiley++) { for(tilex=0;tilex=0;tilex++) { fixPrint(tilex,tiley,0,1,"\xC"); } } for(tiley=(29-WindowHeight)+1;tiley<29;tiley++) { for(tilex=39;tilex=39;tilex++) { fixPrint(tilex,tiley,0,1,"\xB"); } } for(tiley=29;tiley=29;tiley++) { for(tilex=1;tilex<39;tilex++) { fixPrint(tilex,tiley,0,1,"\x8"); } } for(tiley=(29-WindowHeight)+1;tiley<29;tiley++) { for(tilex=0;tilex<39;tilex++) { fixPrint(tilex,tiley,0,1,"\xA"); } } }