Copy to Clipboard Test

Battleship Minigame Code

const int CF_BATTLESHIP_TEMP_ADJ_BAN = 46;//Combo Flag used internally to define ship positions
const int CF_BATTLESHIP_TEMP_BAN = 45;//Combo Flag to define hint aura generated by sunk ships if no two ships can touch each other.

const int CT_BALLESHIP_BOARD = 142;//Combo type used to define board area. It can be any shape.

const int SFX_BATTLESHIP_MISS = 42;//Sound to play, when player shoots and it`s miss.
const int SFX_BATTLESHIP_HIT = 3;//Sound to play, when player shoots and it`s hit.
const int SFX_BATTLESHIP_SUNK = 37;//Sound to play, when player shoots and it`s hit, resulting in destroying the ship.
const int SFX_BATTLESHIP_FAIL = 38;//Sound to play, when player runs out of shots and loses the game.

const int FONT_BATTLESHIP = 0;//Font used to render all game`s HUD stuff.

//Battleship, straight from Wind Waker.

//You are given a grid that hides some ships of various sizes (2,3 cells long etc). Your goal is to destroy all those hidden ships within limited number of shots.
//Stand on targetted space and press Ex1 to shoot.

//1. Set up 5 consecutive combos: Space(must use CT_BALLESHIP_BOARD combo type), then Miss/empty, then Damaged(to indicate destroyed spaces of still alive ships), then Sunk(to render spaces of sunk ships) and one that reveals shipsupon losing the game.
//2. Build board using 1st combo from step 1.
//3. Fleet configuration: set up separate screen. From topleft corner, use set consecutive lines of combos>0, to define ship sizes, separated by 1 combo 0. Sequences of combos ending at rightmost space and beginning on leftmost position of next row count as one ship. Max ship size is 8, Max number of ships of the same size is 10.
//4. Place invisible FFC where youy want to render remaining shot counter to be.
// D0 - Map ID where screens with fleet data is located(step 3).
// D1 - ID of screen (decimal), where fleet data is located (step 3).
// D2 - shoot count limit
// D3 - if >0 - ships cannot be adjacent even diagonally.
// D4 - string to display at game startup.
// D5 - minimum number of moves to be left when winning the game for bonus prize item.
// D6 - ID of bonus item.

ffc script BattleshipMinigame{
	void run(int map, int scr, int moves, int flags, int str, int bonus, int itm){
		int ships[8];
		for (int i=0;i<8;i++){
			ships[i]=0;
		}
		int shipstate[640];
		for (int i=0;i<640;i++){
			shipstate[i]=0;
		}
		LoadBattleshipFleetData(ships,map, scr);
		bool banadj = (flags&1)>0;		
		GenerateBattleshipPosition(ships, banadj, shipstate);
		LoadBattleshipFleetData(ships,map, scr);
		int cmb = -1;
		int shippos = 0;
		// bool sunk=false;
		int adjcmb = 0;
		int size=0;
		Screen->Message(str);
		int strui1[] = "SHOTS";
		while(true){
			cmb = ComboAt (CenterLinkX(), CenterLinkY());
			if (moves>0){
				if (Screen->ComboT[cmb]==CT_BALLESHIP_BOARD && Link->PressEx1){
					moves--;
					if (Screen->ComboF[cmb]==CF_BATTLESHIP_TEMP_BAN){
						Game->PlaySound(SFX_BATTLESHIP_HIT);
						Screen->ComboD[cmb]=this->Data+2;
						shippos = ArrayMatch(shipstate, cmb);
						if (shippos<0){
							int err[] = "Battleship: 404 Damaged ship info not found";
							TraceS(err);
							Quit();
						}
						shipstate[shippos]=-cmb;
						shippos -= shippos%8;
						for (int i=0;i<=8;i++){
							if (i==8){
								Game->PlaySound(SFX_BATTLESHIP_SUNK);
								size=0;
								for (int s=0;s<8;s++){
									if (shipstate[shippos+s]==0){
										if (size==0)size=s;
										continue;
									}
									adjcmb = Abs(shipstate[shippos+s]);
									Screen->ComboD[adjcmb] = this->Data+3;
									for (int d=0;d<8;d++){
										adjcmb = AdjacentComboFix(Abs(shipstate[shippos+s]), d);
										if (adjcmb<0) continue;
										if (Screen->ComboT[adjcmb]!=CT_BALLESHIP_BOARD) continue;
										if (Screen->ComboF[adjcmb]==CF_BATTLESHIP_TEMP_ADJ_BAN) Screen->ComboD[adjcmb] = this->Data+1;
									}
									shipstate[shippos+s]=0;
								}
								ships[size-1]--;
								for (int tr=0; tr<=8;tr++){
									if (tr==8){
										if (!Screen->State[ST_ITEM]&&(moves>=bonus)&&(bonus>0)){
											item it = CreateItemAt(itm, Link->X, Link->Y);
											it->Pickup = 6;
											if (ScreenFlag(SF_ITEMS, SFI_FALLS)){
												Game->PlaySound(7);
												it->Z=256;
											}
										}
										if (Screen->State[ST_SECRET])break;
										Game->PlaySound(SFX_SECRET);
										Screen->TriggerSecrets();
										Screen->State[ST_SECRET]=true;
									}
									else if (ships[tr]>0)break;
								}
							}
							if (shipstate[shippos+i]>0)break;
						}
					}
					else{
						Game->PlaySound(SFX_BATTLESHIP_MISS);
						Screen->ComboD[cmb]=this->Data+1;
					}
					if (moves<=0){
						for (int tr=0; tr<640;tr+=8){							
							if (shipstate[tr]!=0){
								Game->PlaySound(SFX_BATTLESHIP_FAIL);
								for (int i=0;i<176;i++){
									if (Screen->ComboT[i]!=CT_BALLESHIP_BOARD) continue;
									if (Screen->ComboF[i]==CF_BATTLESHIP_TEMP_BAN) Screen->ComboD[i] = this->Data+4;
								}
								break;
							}
						}
					}
				}
			}
			Screen->DrawString(3, this->X-8, this->Y,FONT_BATTLESHIP, 1, 0, 2, strui1, OP_OPAQUE);
			Screen->DrawInteger(3, this->X, this->Y, FONT_BATTLESHIP,1,0, -1, -1, moves, 0, OP_OPAQUE);
			Waitframe();
		}
	}
}

void LoadBattleshipFleetData(int ships, int map, int scr){
	int curship=0;
	int cd=0;
	for (int i=0;i<176;i++){
	cd = Game->GetComboData(map, scr, i);
	if (cd>0)curship++;
	else {
		if (curship==0)return;
		else {
			ships[curship-1]++;
			curship=0;
		}
	}
}
}

void GenerateBattleshipPosition(int ships, bool banadj, int shipstate){
	int legal[704];
	int shippos = 7;
	int pos=-1;;
	int size=0;
	int dir=0;
	int numlegal = 0;
	while(shippos>=0){
		if (ships[shippos]==0){
			shippos--;
			continue;
		}
		numlegal = ListAllowedPositions (legal, shippos+1, banadj);
		if (numlegal==0){
			int err[] = "Battleship: failed to generate fleet configuration. No legal place for a battleship";
			TraceS(err);
			for (int i=0;i<176;i++){
				if (Screen->ComboT[i]==CT_BALLESHIP_BOARD) Screen->ComboF[i]=0;
			}			
			Quit();
		}
		pos = PickRandomShipPosDir(legal);
		dir = pos%4;
		pos -=dir;
		pos /=4;
		PlaceBattleShip(pos, shippos+1, dir, banadj, shipstate);
		ships[shippos]--;
	}
}

int PickRandomShipPosDir(int legal){
	int pos[704];
	int curpos = 0;
	for (int i=0;i<704;i++){
		if (legal[i]>=0){
			pos[curpos]=legal[i];
			curpos++;
		}
	}
	int ret = Rand(curpos);
	return pos[ret];
}

int ListAllowedPositions (int legal, int size, bool banadj){
	int ret = 0;
	for (int i=0;i<704;i++){
		legal[i]=-1;
	}
	for (int i=0;i<176;i++){
		if (Screen->ComboT[i]!=CT_BALLESHIP_BOARD){
			for (int d=0;d<4;d++){
				legal[i*4+d]=-1;
			}
			continue;
		}
		for (int d=0;d<4;d++){
			if (IsLegalShipPosition (size, i, d, banadj)){
				ret++;
				legal[i*4+d]=i*4+d;
			}
		}
	}
	return ret;
}

void PlaceBattleShip(int pos, int size, int dir, bool banadj, int shipstate){
	int adjcmb = -1;
	int state = 0;
	while (shipstate[state]>0)state+=8;
	for (int i=0;i<size;i++){
		Screen->ComboF[pos] = CF_BATTLESHIP_TEMP_BAN;
		// Screen->ComboD[pos] = 1019;//Debug
		for (int d=0;d<8;d++){
			adjcmb = AdjacentComboFix(pos, d);
			if (adjcmb<0) continue;
			if (Screen->ComboT[adjcmb]!=CT_BALLESHIP_BOARD) continue;
			if (Screen->ComboF[adjcmb]==CF_BATTLESHIP_TEMP_BAN) continue;
			Screen->ComboF[adjcmb] = CF_BATTLESHIP_TEMP_ADJ_BAN;
		}
		shipstate[state+i]=pos;
		pos = AdjacentComboFix(pos, dir);
	}
}

bool IsLegalShipPosition (int size, int pos, int dir, bool banadj){
	int adjcmb = 0;
	for (int s = 0;s<size;s++){
		if (pos<0)return false;
		if (Screen->ComboT[pos]!=CT_BALLESHIP_BOARD) return false;
		if (ComboFI(pos, CF_BATTLESHIP_TEMP_BAN)) return false;
		if (banadj && ComboFI(pos, CF_BATTLESHIP_TEMP_ADJ_BAN)) return false;
		pos = AdjacentComboFix(pos, dir);
	}
	return true;
}

//Fixed variant of AdjacentCombo function from std_extension.zh
int AdjacentComboFix(int cmb, int dir){
	int combooffsets[13]={-0x10, 0x10, -1, 1, -0x11, -0x0F, 0x0F, 0x11};
	if ( cmb % 16 == 0 ) combooffsets[9] = -1;//if it's the left edge
	if ( (cmb % 16) == 15 ) combooffsets[10] = -1; //if it's the right edge
	if ( cmb < 0x10 ) combooffsets[11] = -1; //if it's the top row
	if ( cmb > 0x9F ) combooffsets[12] = -1; //if it's on the bottom row
	if ( combooffsets[9]==-1 && ( dir == DIR_LEFT || dir == DIR_LEFTUP || dir == DIR_LEFTDOWN ) ) return -1; //if the left columb
	if ( combooffsets[10]==-1 && ( dir == DIR_RIGHT || dir == DIR_RIGHTUP || dir == DIR_RIGHTDOWN ) ) return -1; //if the right column
	if ( combooffsets[11]==-1 && ( dir == DIR_UP || dir == DIR_RIGHTUP || dir == DIR_LEFTUP ) ) return -1; //if the top row
	if ( combooffsets[12]==-1 && ( dir == DIR_DOWN || dir == DIR_RIGHTDOWN || dir == DIR_LEFTDOWN ) ) return -1; //if the bottom row
	if ( cmb >= 0 && cmb < 176 ) return cmb + combooffsets[dir];
	else return -1;
}	

//Returns index of the given element that exists in the given array or -1, if it does not exist.
int ArrayMatch(int arr, int value){
	for (int i=0; i<SizeOfArray(arr); i++){
		if (arr[i] == value) return i;
	}
	return -1;
}