9ce5b2a2ed
The implementation of the "show_selection_marker" option in lists isn't great. It's a cosmetic option used to hide the selection, but it causes the list to do funny things to the selected_item and doesn't play nice with voiced menus, since these rely on the selection to determine what item is spoken. There are only two user-facing lists that use the option, the "Rockbox Info" screen and a menu in the superdom plugin. The rest are debug screens, and cosmetics don't matter much there. Given how little used the option is, and its issues, removing it seems reasonable. Change-Id: I2c70b3e4c74ff3cc6dbac46366a371d271dd2d58
2572 lines
75 KiB
C
2572 lines
75 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2006 Will Robertson
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
/* TODO list:
|
|
- improve AI
|
|
- buy/use nukes - DONE
|
|
- build farms/factories
|
|
*/
|
|
|
|
|
|
#include "plugin.h"
|
|
#include "lib/display_text.h"
|
|
#include "lib/playback_control.h"
|
|
#include "lib/pluginlib_actions.h"
|
|
#include "pluginbitmaps/superdom_boarditems.h"
|
|
|
|
char buf[255];
|
|
|
|
/* key mappings */
|
|
|
|
#define SUPERDOM_OK PLA_SELECT
|
|
#define SUPERDOM_CANCEL PLA_CANCEL
|
|
#define SUPERDOM_RIGHT PLA_RIGHT
|
|
#define SUPERDOM_LEFT PLA_LEFT
|
|
#define SUPERDOM_UP PLA_UP
|
|
#define SUPERDOM_DOWN PLA_DOWN
|
|
|
|
#define SUPERDOM_RIGHT_REPEAT PLA_RIGHT_REPEAT
|
|
#define SUPERDOM_LEFT_REPEAT PLA_LEFT_REPEAT
|
|
#define SUPERDOM_UP_REPEAT PLA_UP_REPEAT
|
|
#define SUPERDOM_DOWN_REPEAT PLA_DOWN_REPEAT
|
|
|
|
/***** game settings *****/
|
|
|
|
/* Some defines for the prices */
|
|
#define PRICE_MEN 1
|
|
#define PRICE_MOVE 100
|
|
#define PRICE_TANK 300
|
|
#define PRICE_PLANE 600
|
|
#define PRICE_FARM 1150
|
|
#define PRICE_FACTORY 1300
|
|
#define PRICE_NUKE 2000
|
|
|
|
#define STRINGIZE_2(X) #X
|
|
#define STRINGIZE(X) STRINGIZE_2(X)
|
|
|
|
#define PRICE_MEN_STR STRINGIZE(PRICE_MEN)
|
|
#define PRICE_MOVE_STR STRINGIZE(PRICE_MOVE)
|
|
#define PRICE_TANK_STR STRINGIZE(PRICE_TANK)
|
|
#define PRICE_PLANE_STR STRINGIZE(PRICE_PLANE)
|
|
#define PRICE_FARM_STR STRINGIZE(PRICE_FARM)
|
|
#define PRICE_FACTORY_STR STRINGIZE(PRICE_FACTORY)
|
|
#define PRICE_NUKE_STR STRINGIZE(PRICE_NUKE)
|
|
|
|
/* surrender thresholds */
|
|
#define HUMAN_SURRENDER_THRESHOLD 15
|
|
#define COMPUTER_SURRENDER_THRESHOLD 15
|
|
#define COMPUTER_HARD_SURRENDER_THRESHOLD 25
|
|
|
|
/* AI settings */
|
|
#define AI_INVESTING_LEVEL 2
|
|
#define AI_BUILD_NUKES_LEVEL 3
|
|
#define AI_BUILD_INDS_FARMS_LEVEL 2
|
|
|
|
/* board size */
|
|
#define BOARD_SIZE 10
|
|
#define NUM_SPACES (BOARD_SIZE*BOARD_SIZE)
|
|
#define COLOUR_DARK 0
|
|
#define COLOUR_LIGHT 1
|
|
|
|
/* drawing presets */
|
|
|
|
#define MARGIN 5
|
|
|
|
#if (LCD_DEPTH >= 16)
|
|
#define MY_BITMAP_PART rb->lcd_bitmap_transparent_part
|
|
#else
|
|
#define MY_BITMAP_PART rb->lcd_mono_bitmap_part
|
|
#endif
|
|
|
|
#if LCD_WIDTH > LCD_HEIGHT
|
|
#define BOX_WIDTH ((LCD_WIDTH-(MARGIN*2))/BOARD_SIZE)
|
|
#define BOX_HEIGHT ((BOX_WIDTH*2)/3)
|
|
|
|
#else
|
|
#define BOX_HEIGHT ((LCD_HEIGHT-(MARGIN*2)-15)/BOARD_SIZE)
|
|
#define BOX_WIDTH ((BOX_HEIGHT*2)/3)
|
|
|
|
#endif
|
|
|
|
/* NUM_BOX HEIGHT and WIDTH are used for the number pad in the game. The height
|
|
* calculation includes spacing for the text placed above and below the number
|
|
* pad (it divides by 6 instead of just 4). The width calculation gives extra
|
|
* spacing on the sides of the pad too (divides by 5 instead of 3).
|
|
*/
|
|
#define NUM_BOX_HEIGHT (LCD_HEIGHT/6)
|
|
#define NUM_BOX_WIDTH (LCD_WIDTH/5)
|
|
|
|
#define NUM_MARGIN_X (LCD_WIDTH-3*NUM_BOX_WIDTH)/2
|
|
#define NUM_MARGIN_Y (LCD_HEIGHT-4*NUM_BOX_HEIGHT)/2
|
|
|
|
/* These parameters define the piece image dimensions, Stride is the total width
|
|
* of the bitmap.
|
|
*/
|
|
#define ICON_STRIDE STRIDE(SCREEN_MAIN, BMPWIDTH_superdom_boarditems, BMPHEIGHT_superdom_boarditems)
|
|
#define ICON_HEIGHT (BMPHEIGHT_superdom_boarditems/6)
|
|
#define ICON_WIDTH (BMPWIDTH_superdom_boarditems/2)
|
|
|
|
enum {
|
|
RET_VAL_OK,
|
|
RET_VAL_USB,
|
|
RET_VAL_QUIT_ERR, /* quit or error */
|
|
};
|
|
|
|
static void gen_interest(void);
|
|
static void init_resources(void);
|
|
static int select_square(void);
|
|
static void update_score(void);
|
|
static void gen_resources(void);
|
|
static void draw_cursor(void);
|
|
static void draw_board(void);
|
|
|
|
struct tile {
|
|
signed int colour; /* -1 = Unset */
|
|
bool tank;
|
|
bool plane;
|
|
bool nuke;
|
|
bool ind;
|
|
bool farm;
|
|
int men;
|
|
};
|
|
|
|
struct resources {
|
|
int cash;
|
|
int food;
|
|
int farms;
|
|
int inds;
|
|
int men;
|
|
int tanks;
|
|
int planes;
|
|
int nukes;
|
|
int bank;
|
|
int moves;
|
|
};
|
|
|
|
static struct settings {
|
|
int compstartfarms;
|
|
int compstartinds;
|
|
int humanstartfarms;
|
|
int humanstartinds;
|
|
int startcash;
|
|
int startfood;
|
|
int movesperturn;
|
|
/* 1=easy 2=medium 3=hard */
|
|
/* AI difficulty works like this:
|
|
easy:
|
|
- no movement
|
|
- no investing
|
|
- will build factories if it has none
|
|
medium:
|
|
- movement
|
|
- investing
|
|
- can build factories/farms if it has money
|
|
hard:
|
|
- can buy/use nukes
|
|
- will hold out longer (surrender threshold 25)
|
|
*/
|
|
int compdiff;
|
|
bool spoil_enabled;
|
|
bool persistent_units;
|
|
} superdom_settings;
|
|
|
|
static struct resources humanres;
|
|
static struct resources compres;
|
|
enum { GS_PROD, GS_MOVE, GS_WAR };
|
|
static int gamestate;
|
|
|
|
static struct cursor {
|
|
int x;
|
|
int y;
|
|
} cursor;
|
|
|
|
static struct tile board[BOARD_SIZE+2][BOARD_SIZE+2];
|
|
|
|
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
|
|
|
|
static void init_board(void)
|
|
{
|
|
int i,j;
|
|
rb->srand(*rb->current_tick);
|
|
for(i=0;i<12;i++)
|
|
{ /* Hopefully about 50% each colour */
|
|
for(j=0;j<BOARD_SIZE+2;j++)
|
|
{
|
|
if((i<1)||(j<1)||(i>BOARD_SIZE)||(j>BOARD_SIZE))
|
|
board[i][j].colour = -1; /* Unset */
|
|
else
|
|
board[i][j].colour = rb->rand()%2;
|
|
board[i][j].tank = false;
|
|
board[i][j].plane = false;
|
|
board[i][j].nuke = false;
|
|
board[i][j].ind = false;
|
|
board[i][j].farm = false;
|
|
board[i][j].men = 0;
|
|
}
|
|
}
|
|
|
|
while(compres.farms < superdom_settings.compstartfarms)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if((board[i][j].colour == COLOUR_DARK) && (board[i][j].farm == false))
|
|
{
|
|
board[i][j].farm = true;
|
|
compres.farms++;
|
|
}
|
|
}
|
|
while(compres.inds < superdom_settings.compstartinds)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if((board[i][j].colour == COLOUR_DARK) && (board[i][j].ind == false))
|
|
{
|
|
board[i][j].ind = true;
|
|
compres.inds++;
|
|
}
|
|
}
|
|
while(humanres.farms < superdom_settings.humanstartfarms)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if((board[i][j].colour == COLOUR_LIGHT)&&(board[i][j].farm == false))
|
|
{
|
|
board[i][j].farm = true;
|
|
humanres.farms++;
|
|
}
|
|
}
|
|
while(humanres.inds < superdom_settings.humanstartinds)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if((board[i][j].colour == COLOUR_LIGHT) && (board[i][j].ind == false))
|
|
{
|
|
board[i][j].ind = true;
|
|
humanres.inds++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw_board(void)
|
|
{
|
|
int i,j;
|
|
rb->lcd_clear_display();
|
|
for(i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if(board[i][j].colour == COLOUR_DARK)
|
|
{
|
|
rb->lcd_set_foreground(LCD_DARKGRAY);
|
|
}
|
|
else
|
|
{
|
|
rb->lcd_set_foreground(LCD_LIGHTGRAY);
|
|
}
|
|
rb->lcd_fillrect(MARGIN+(BOX_WIDTH*(i-1)),
|
|
MARGIN+(BOX_HEIGHT*(j-1)), BOX_WIDTH,
|
|
BOX_HEIGHT);
|
|
#if LCD_DEPTH != 16
|
|
rb->lcd_set_drawmode(DRMODE_BG | DRMODE_INVERSEVID);
|
|
#endif
|
|
if(board[i][j].ind)
|
|
{
|
|
MY_BITMAP_PART(superdom_boarditems,
|
|
board[i][j].colour?ICON_WIDTH:0, 0, ICON_STRIDE,
|
|
#if LCD_WIDTH > LCD_HEIGHT
|
|
MARGIN+(BOX_WIDTH*(i-1))+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+ICON_HEIGHT+1,
|
|
#else
|
|
MARGIN+(BOX_WIDTH*(i-1))+1+ICON_WIDTH,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+1,
|
|
#endif
|
|
ICON_WIDTH, ICON_HEIGHT);
|
|
}
|
|
if(board[i][j].farm)
|
|
{
|
|
MY_BITMAP_PART(superdom_boarditems,
|
|
board[i][j].colour?ICON_WIDTH:0, ICON_HEIGHT,
|
|
ICON_STRIDE, MARGIN+(BOX_WIDTH*(i-1))+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+1,
|
|
ICON_WIDTH, ICON_HEIGHT);
|
|
}
|
|
if(board[i][j].tank)
|
|
{
|
|
MY_BITMAP_PART(superdom_boarditems,
|
|
board[i][j].colour?ICON_WIDTH:0, ICON_HEIGHT*2,
|
|
ICON_STRIDE, MARGIN+(BOX_WIDTH*(i-1))+ICON_WIDTH+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+ICON_HEIGHT+1,
|
|
ICON_WIDTH, ICON_HEIGHT);
|
|
}
|
|
if(board[i][j].men)
|
|
{
|
|
MY_BITMAP_PART(superdom_boarditems,
|
|
board[i][j].colour?ICON_WIDTH:0, ICON_HEIGHT*3,
|
|
#if LCD_WIDTH > LCD_HEIGHT
|
|
ICON_STRIDE, MARGIN+(BOX_WIDTH*(i-1))+ICON_WIDTH+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+1,
|
|
#else
|
|
ICON_STRIDE, MARGIN+(BOX_WIDTH*(i-1))+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+1+ICON_HEIGHT,
|
|
#endif
|
|
ICON_WIDTH, ICON_HEIGHT);
|
|
}
|
|
if(board[i][j].plane)
|
|
{
|
|
MY_BITMAP_PART(superdom_boarditems,
|
|
board[i][j].colour?ICON_WIDTH:0, ICON_HEIGHT*4,
|
|
#if LCD_WIDTH > LCD_HEIGHT
|
|
ICON_STRIDE,MARGIN+(BOX_WIDTH*(i-1))+ICON_WIDTH*2+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+ICON_HEIGHT+1,
|
|
#else
|
|
ICON_STRIDE,MARGIN+(BOX_WIDTH*(i-1))+ICON_WIDTH+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+ICON_HEIGHT*2+1,
|
|
#endif
|
|
ICON_WIDTH, ICON_HEIGHT);
|
|
}
|
|
if(board[i][j].nuke)
|
|
{
|
|
MY_BITMAP_PART(superdom_boarditems,
|
|
board[i][j].colour?ICON_WIDTH:0, ICON_HEIGHT*5,
|
|
#if LCD_WIDTH > LCD_HEIGHT
|
|
ICON_STRIDE,MARGIN+(BOX_WIDTH*(i-1))+ICON_WIDTH*2+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+1,
|
|
#else
|
|
ICON_STRIDE,MARGIN+(BOX_WIDTH*(i-1))+1,
|
|
MARGIN+(BOX_HEIGHT*(j-1))+ICON_HEIGHT*2+1,
|
|
#endif
|
|
ICON_WIDTH, ICON_HEIGHT);
|
|
}
|
|
#if LCD_DEPTH != 16
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
#endif
|
|
}
|
|
}
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
/* Draw Horizontal lines */
|
|
for(i=0;i<=BOARD_SIZE;i++)
|
|
{
|
|
rb->lcd_hline(MARGIN, MARGIN+(BOX_WIDTH*BOARD_SIZE), MARGIN+(BOX_HEIGHT*i));
|
|
}
|
|
/* Draw Vertical lines */
|
|
for(i=0;i<=BOARD_SIZE;i++)
|
|
{
|
|
rb->lcd_vline(MARGIN+(BOX_WIDTH*i), MARGIN, MARGIN+(BOX_HEIGHT*BOARD_SIZE));
|
|
}
|
|
rb->lcd_update();
|
|
}
|
|
|
|
static int calc_strength(int colour, int x, int y)
|
|
{
|
|
int a, b, score=0;
|
|
for (a = -1; a < 2; a++)
|
|
{
|
|
for (b = -1; b < 2; b++)
|
|
{
|
|
if(board[x+a][y+b].colour==colour)
|
|
{
|
|
if(a && b) /* diagonally adjacent, give less influence */
|
|
{
|
|
score += 5;
|
|
if(board[x + a][y + b].tank || board[x + a][y + b].farm)
|
|
score += 15;
|
|
if(board[x + a][y + b].plane || board[x + a][y + b].ind)
|
|
score += 20;
|
|
if(board[x + a][y + b].nuke)
|
|
score += 10;
|
|
if(board[x + a][y + b].men)
|
|
score += (board[x + a][y + b].men*133/1000);
|
|
}
|
|
else
|
|
{
|
|
score += 10;
|
|
if(board[x + a][y + b].tank || board[x + a][y + b].farm)
|
|
score += 30;
|
|
if(board[x + a][y + b].plane || board[x + a][y + b].ind)
|
|
score += 40;
|
|
if(board[x + a][y + b].nuke)
|
|
score += 20;
|
|
if(board[x + a][y + b].men)
|
|
score += (board[x + a][y + b].men*133/1000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return score;
|
|
}
|
|
|
|
void gen_interest(void)
|
|
{
|
|
/* Interest should be around 10% */
|
|
int interest = 7+rb->rand()%6;
|
|
humanres.bank = humanres.bank+(interest*humanres.bank/100);
|
|
/* Different interest for AI player */
|
|
interest = 7+rb->rand()%6;
|
|
compres.bank = compres.bank+(interest*compres.bank/100);
|
|
}
|
|
|
|
void draw_cursor(void)
|
|
{
|
|
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
|
|
rb->lcd_fillrect(MARGIN+((cursor.x-1)*BOX_WIDTH),
|
|
MARGIN+((cursor.y-1)*BOX_HEIGHT), BOX_WIDTH+1, BOX_HEIGHT+1);
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
rb->lcd_update();
|
|
}
|
|
|
|
void gen_resources(void)
|
|
{
|
|
int inccash = 0;
|
|
int incfood = 0;
|
|
int ratecash = 0;
|
|
int ratefood = 0;
|
|
int i;
|
|
gen_interest();
|
|
/* Generate Human's resources */
|
|
for(i=0;i<humanres.inds;i++)
|
|
{
|
|
inccash += (300+rb->rand()%200);
|
|
}
|
|
for(i=0;i<humanres.farms;i++)
|
|
{
|
|
incfood += (200+rb->rand()%200);
|
|
}
|
|
if(humanres.inds)
|
|
ratecash = inccash/humanres.inds;
|
|
if(humanres.farms)
|
|
ratefood = incfood/humanres.farms;
|
|
if(ratecash > 450)
|
|
{
|
|
if(ratefood > 350)
|
|
{
|
|
rb->splash(HZ*2, "Patriotism sweeps the land, all production"
|
|
" is up this year!");
|
|
}
|
|
else
|
|
{
|
|
rb->splash(HZ*2, "Factories working at maximum efficiency,"
|
|
" cash production up this year!");
|
|
}
|
|
}
|
|
else if(ratecash > 350)
|
|
{
|
|
if(ratefood > 350)
|
|
{
|
|
rb->splash(HZ*2, "Record crop harvest this year!");
|
|
}
|
|
else if(ratefood > 250)
|
|
{
|
|
rb->splash(HZ*2, "Production continues as normal");
|
|
}
|
|
else
|
|
{
|
|
rb->splash(HZ*2, "Spoilage of crops leads to reduced farm"
|
|
" output this year");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(ratefood > 350)
|
|
{
|
|
rb->splash(HZ*2, "Record crop harvest this year!");
|
|
}
|
|
else if(ratefood > 250)
|
|
{
|
|
rb->splash(HZ*2, "Factory unions introduced. Industrial"
|
|
" production is down this year.");
|
|
}
|
|
else
|
|
{
|
|
rb->splash(HZ*2, "Internet created. All production is down"
|
|
" due to time wasted.");
|
|
}
|
|
}
|
|
humanres.cash += inccash;
|
|
humanres.food += incfood;
|
|
|
|
/* Generate Computer's resources */
|
|
inccash = 0;
|
|
incfood = 0;
|
|
for(i=0;i<compres.inds;i++)
|
|
{
|
|
inccash += (300+rb->rand()%200);
|
|
}
|
|
for(i=0;i<compres.farms;i++)
|
|
{
|
|
incfood += (200+rb->rand()%200);
|
|
}
|
|
compres.cash += inccash;
|
|
compres.food += incfood;
|
|
}
|
|
|
|
static void update_score(void)
|
|
{
|
|
int strength;
|
|
|
|
rb->lcd_setfont(FONT_SYSFIXED);
|
|
rb->lcd_set_drawmode(DRMODE_BG|DRMODE_INVERSEVID);
|
|
rb->lcd_fillrect(5,LCD_HEIGHT-20,105,20);
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
strength = calc_strength(COLOUR_LIGHT, cursor.x, cursor.y);
|
|
rb->lcd_putsxyf(5,LCD_HEIGHT-20,"Your power: %d.%d",strength/10,strength%10);
|
|
strength = calc_strength(COLOUR_DARK, cursor.x, cursor.y);
|
|
rb->lcd_putsxyf(5,LCD_HEIGHT-10,"Comp power: %d.%d",strength/10,strength%10);
|
|
rb->lcd_setfont(FONT_UI);
|
|
}
|
|
|
|
static int settings_menu(void)
|
|
{
|
|
int selection = 0;
|
|
|
|
MENUITEM_STRINGLIST(menu, "Super Domination Settings", NULL,
|
|
"Computer starting farms", "Computer starting factories",
|
|
"Human starting farms", "Human starting factories",
|
|
"Starting cash", "Starting food", "Computer difficulty","Food spoilage", "Persistent units", "Moves per turn");
|
|
|
|
while(1)
|
|
{
|
|
switch(rb->do_menu(&menu, &selection, NULL, false))
|
|
{
|
|
case 0:
|
|
rb->set_int("Computer starting farms", "", UNIT_INT,
|
|
&superdom_settings.compstartfarms, NULL,
|
|
1, 0, 5, NULL);
|
|
break;
|
|
case 1:
|
|
rb->set_int("Computer starting factories", "", UNIT_INT,
|
|
&superdom_settings.compstartinds, NULL,
|
|
1, 0, 5, NULL);
|
|
break;
|
|
case 2:
|
|
rb->set_int("Human starting farms", "", UNIT_INT,
|
|
&superdom_settings.humanstartfarms, NULL,
|
|
1, 0, 5, NULL);
|
|
break;
|
|
case 3:
|
|
rb->set_int("Human starting factories", "", UNIT_INT,
|
|
&superdom_settings.humanstartinds, NULL,
|
|
1, 0, 5, NULL);
|
|
break;
|
|
case 4:
|
|
rb->set_int("Starting cash", "", UNIT_INT,
|
|
&superdom_settings.startcash, NULL,
|
|
250, 0, 5000, NULL);
|
|
break;
|
|
case 5:
|
|
rb->set_int("Starting food", "", UNIT_INT,
|
|
&superdom_settings.startfood, NULL,
|
|
250, 0, 5000, NULL);
|
|
break;
|
|
case 6:
|
|
{
|
|
static const struct opt_items difficulty_options[3]={
|
|
{"Easy", -1},
|
|
{"Intermediate", -1},
|
|
{"Hard", -1}
|
|
};
|
|
static int sel=1;
|
|
rb->set_option("Computer difficulty", &sel,
|
|
INT, difficulty_options, 3, NULL);
|
|
superdom_settings.compdiff=sel+1;
|
|
break;
|
|
}
|
|
case 7:
|
|
rb->set_bool_options("Food spoilage", &superdom_settings.spoil_enabled, "Enabled",
|
|
0, "Disabled", 0, NULL);
|
|
break;
|
|
case 8:
|
|
rb->set_bool_options("Persistent units", &superdom_settings.persistent_units, "Enabled", 0,
|
|
"Disabled", 0, NULL);
|
|
break;
|
|
case 9:
|
|
rb->set_int("Moves per turn", "", UNIT_INT,
|
|
&superdom_settings.movesperturn, NULL,
|
|
1, 1, 5, NULL);
|
|
break;
|
|
case MENU_ATTACHED_USB:
|
|
return RET_VAL_USB;
|
|
break;
|
|
case GO_TO_PREVIOUS:
|
|
return RET_VAL_OK;
|
|
break;
|
|
}
|
|
}
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static int superdom_help(void)
|
|
{
|
|
static char* help_text[] = {"Super Domination","",
|
|
"Aim", "",
|
|
"Super", "Domination", "is", "a", "turn-based",
|
|
"strategy", "game", "where", "the", "aim", "is",
|
|
"to", "overpower", "the", "computer.", "", "",
|
|
"How", "to", "Play", "",
|
|
"Each", "year", "you", "are", "allocated", "an", "amount", "of", "cash",
|
|
"and", "food,", "depending", "on", "how", "many", "farms", "and",
|
|
"factories", "you", "control.", "",
|
|
"Use", "this", "cash", "and", "food", "to", "build", "and", "feed", "your",
|
|
"army.", "Each", "tile", "has", "a", "strength,", "calculated", "by",
|
|
"the", "ownership", "of", "surrounding", "tiles,", "and", "the", "type",
|
|
"and", "number", "of", "troops", "on", "them."};
|
|
static struct style_text style[]={
|
|
{0, TEXT_CENTER|TEXT_UNDERLINE},
|
|
{2, C_RED},
|
|
{21, C_RED},
|
|
{22, C_RED},
|
|
{23, C_RED},
|
|
LAST_STYLE_ITEM
|
|
};
|
|
#ifdef HAVE_LCD_COLOR
|
|
rb->lcd_set_foreground(LCD_WHITE);
|
|
rb->lcd_set_background(LCD_BLACK);
|
|
#endif
|
|
int ret=display_text(ARRAYLEN(help_text), help_text, style, NULL, true);
|
|
#ifdef HAVE_LCD_COLOR
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
rb->lcd_set_background(LCD_WHITE);
|
|
#endif
|
|
if(ret)
|
|
return RET_VAL_USB;
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static int start_menu(void)
|
|
{
|
|
int selection = 0;
|
|
|
|
MENUITEM_STRINGLIST(menu, "Super Domination Menu", NULL,
|
|
"Play Super Domination", "Settings",
|
|
"Help", "Playback Control", "Quit");
|
|
|
|
while(1)
|
|
{
|
|
switch(rb->do_menu(&menu, &selection, NULL, false))
|
|
{
|
|
case 0:
|
|
return RET_VAL_OK; /* start playing */
|
|
break;
|
|
case 1:
|
|
if(settings_menu()==RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 2:
|
|
if(superdom_help()==RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 3:
|
|
if(playback_control(NULL))
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 4:
|
|
return RET_VAL_QUIT_ERR;
|
|
break;
|
|
}
|
|
}
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
|
|
static int save_game(void)
|
|
{
|
|
int fd;
|
|
char savepath[MAX_PATH];
|
|
|
|
rb->snprintf(savepath, sizeof(savepath), "/Savegame.ssg");
|
|
if(rb->kbd_input(savepath, MAX_PATH, NULL))
|
|
{
|
|
DEBUGF("Keyboard input failed\n");
|
|
return -1;
|
|
}
|
|
|
|
fd = rb->open(savepath, O_WRONLY|O_CREAT, 0666);
|
|
DEBUGF("savepath: %s\n", savepath);
|
|
if(fd < 0)
|
|
{
|
|
DEBUGF("Couldn't create/open file\n");
|
|
return -1;
|
|
}
|
|
|
|
rb->write(fd, "SSGv3", 5);
|
|
rb->write(fd, &gamestate, sizeof(gamestate));
|
|
rb->write(fd, &humanres.cash, sizeof(humanres.cash));
|
|
rb->write(fd, &humanres.food, sizeof(humanres.food));
|
|
rb->write(fd, &humanres.bank, sizeof(humanres.bank));
|
|
rb->write(fd, &humanres.planes, sizeof(humanres.planes));
|
|
rb->write(fd, &humanres.tanks, sizeof(humanres.tanks));
|
|
rb->write(fd, &humanres.men, sizeof(humanres.men));
|
|
rb->write(fd, &humanres.nukes, sizeof(humanres.nukes));
|
|
rb->write(fd, &humanres.inds, sizeof(humanres.inds));
|
|
rb->write(fd, &humanres.farms, sizeof(humanres.farms));
|
|
rb->write(fd, &humanres.moves, sizeof(humanres.moves));
|
|
rb->write(fd, &compres.cash, sizeof(compres.cash));
|
|
rb->write(fd, &compres.food, sizeof(compres.food));
|
|
rb->write(fd, &compres.bank, sizeof(compres.bank));
|
|
rb->write(fd, &compres.planes, sizeof(compres.planes));
|
|
rb->write(fd, &compres.tanks, sizeof(compres.tanks));
|
|
rb->write(fd, &compres.men, sizeof(compres.men));
|
|
rb->write(fd, &compres.nukes, sizeof(compres.nukes));
|
|
rb->write(fd, &compres.inds, sizeof(compres.inds));
|
|
rb->write(fd, &compres.farms, sizeof(compres.farms));
|
|
rb->write(fd, &compres.moves, sizeof(compres.moves));
|
|
rb->write(fd, board, sizeof(board));
|
|
rb->write(fd, &superdom_settings.compstartfarms, sizeof(int));
|
|
rb->write(fd, &superdom_settings.compstartinds, sizeof(int));
|
|
rb->write(fd, &superdom_settings.humanstartfarms, sizeof(int));
|
|
rb->write(fd, &superdom_settings.humanstartinds, sizeof(int));
|
|
rb->write(fd, &superdom_settings.startcash, sizeof(int));
|
|
rb->write(fd, &superdom_settings.startfood, sizeof(int));
|
|
rb->write(fd, &superdom_settings.movesperturn, sizeof(int));
|
|
rb->close(fd);
|
|
return 0;
|
|
}
|
|
|
|
static int ingame_menu(void)
|
|
{
|
|
MENUITEM_STRINGLIST(menu, "Super Domination Menu", NULL,
|
|
"Return to game", "Save Game",
|
|
"Playback Control", "Quit");
|
|
|
|
switch(rb->do_menu(&menu, NULL, NULL, false))
|
|
{
|
|
case 0:
|
|
return RET_VAL_OK;
|
|
break;
|
|
case 1:
|
|
if(!save_game())
|
|
rb->splash(HZ, "Game saved");
|
|
else
|
|
rb->splash(HZ, "Error in save");
|
|
break;
|
|
case 2:
|
|
if(playback_control(NULL))
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 3:
|
|
return RET_VAL_QUIT_ERR;
|
|
break;
|
|
case MENU_ATTACHED_USB:
|
|
return RET_VAL_USB;
|
|
break;
|
|
case GO_TO_PREVIOUS:
|
|
return RET_VAL_OK;
|
|
break;
|
|
}
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static int get_number(char* param, int* value, int max)
|
|
{
|
|
static const char *button_labels[4][3] = {
|
|
{ "1", "2", "3" },
|
|
{ "4", "5", "6" },
|
|
{ "7", "8", "9" },
|
|
{ "DEL", "0", "OK" }
|
|
};
|
|
int i,j,x=0,y=0;
|
|
int height, width;
|
|
int button = 0, ret = RET_VAL_OK;
|
|
bool done = false;
|
|
rb->lcd_clear_display();
|
|
rb->lcd_getstringsize("DEL", &width, &height);
|
|
if(width > NUM_BOX_WIDTH || height > NUM_BOX_HEIGHT)
|
|
rb->lcd_setfont(FONT_SYSFIXED);
|
|
/* Draw a 3x4 grid */
|
|
for(i=0;i<=3;i++)
|
|
{ /* Vertical lines */
|
|
rb->lcd_vline(NUM_MARGIN_X+(NUM_BOX_WIDTH*i), NUM_MARGIN_Y,
|
|
NUM_MARGIN_Y+(4*NUM_BOX_HEIGHT));
|
|
}
|
|
/* Horizontal lines */
|
|
for(i=0;i<=4;i++)
|
|
{
|
|
rb->lcd_hline(NUM_MARGIN_X, NUM_MARGIN_X+(3*NUM_BOX_WIDTH),
|
|
NUM_MARGIN_Y+(NUM_BOX_HEIGHT*i));
|
|
}
|
|
for(i=0;i<4;i++)
|
|
{
|
|
for(j=0;j<3;j++)
|
|
{
|
|
rb->lcd_getstringsize(button_labels[i][j], &width, &height);
|
|
rb->lcd_putsxy(NUM_MARGIN_X+(j*NUM_BOX_WIDTH)+NUM_BOX_WIDTH/2-width/2,
|
|
NUM_MARGIN_Y+(i*NUM_BOX_HEIGHT)+NUM_BOX_HEIGHT/2-height/2,
|
|
button_labels[i][j]);
|
|
}
|
|
}
|
|
rb->lcd_putsxyf(NUM_MARGIN_X+10, NUM_MARGIN_Y+4*NUM_BOX_HEIGHT+10,"%d",*value);
|
|
rb->lcd_getstringsize(param, &width, &height);
|
|
if(width < LCD_WIDTH)
|
|
rb->lcd_putsxy((LCD_WIDTH-width)/2, (NUM_MARGIN_Y-height)/2, param);
|
|
else
|
|
rb->lcd_puts_scroll(0, (NUM_MARGIN_Y/height-1)/2, param);
|
|
|
|
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
|
|
rb->lcd_fillrect(NUM_MARGIN_X+(NUM_BOX_WIDTH*x),
|
|
NUM_MARGIN_Y+(NUM_BOX_HEIGHT*y),
|
|
NUM_BOX_WIDTH+1, NUM_BOX_HEIGHT+1);
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
rb->lcd_update();
|
|
|
|
while(!done)
|
|
{
|
|
button=pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts));
|
|
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
|
|
rb->lcd_fillrect(NUM_MARGIN_X+(NUM_BOX_WIDTH*x),
|
|
NUM_MARGIN_Y+(NUM_BOX_HEIGHT*y),
|
|
NUM_BOX_WIDTH+1, NUM_BOX_HEIGHT+1);
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
switch(button)
|
|
{
|
|
case SUPERDOM_OK:
|
|
if(y!=3)
|
|
{
|
|
*value *= 10;
|
|
*value += button_labels[y][x][0] - '0';
|
|
}
|
|
else if(x==0)
|
|
{
|
|
*value /= 10;
|
|
}
|
|
else if(x==1)
|
|
{
|
|
*value *= 10;
|
|
}
|
|
else if(x==2)
|
|
{
|
|
done = true;
|
|
break;
|
|
}
|
|
if ((unsigned) *value > (unsigned) max)
|
|
*value = max;
|
|
rb->lcd_set_drawmode(DRMODE_BG|DRMODE_INVERSEVID);
|
|
rb->lcd_fillrect(0, NUM_MARGIN_Y+4*NUM_BOX_HEIGHT+10,
|
|
LCD_WIDTH, 30);
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
rb->lcd_putsxyf(NUM_MARGIN_X+10,NUM_MARGIN_Y+4*NUM_BOX_HEIGHT+10,
|
|
"%d", *value);
|
|
break;
|
|
case SUPERDOM_CANCEL:
|
|
*value = 0;
|
|
done = true;
|
|
ret = RET_VAL_QUIT_ERR;
|
|
break;
|
|
#if CONFIG_KEYPAD != IRIVER_H10_PAD
|
|
case SUPERDOM_LEFT:
|
|
if(x==0)
|
|
{
|
|
x=2;
|
|
}
|
|
else
|
|
{
|
|
x--;
|
|
}
|
|
break;
|
|
case SUPERDOM_RIGHT:
|
|
if(x==2)
|
|
{
|
|
x=0;
|
|
}
|
|
else
|
|
{
|
|
x++;
|
|
}
|
|
break;
|
|
#endif
|
|
case SUPERDOM_UP:
|
|
if(y==0)
|
|
{
|
|
#if CONFIG_KEYPAD == IRIVER_H10_PAD
|
|
if(x > 0)
|
|
x--;
|
|
else
|
|
x=2;
|
|
#endif
|
|
y=3;
|
|
}
|
|
else
|
|
{
|
|
y--;
|
|
}
|
|
break;
|
|
case SUPERDOM_DOWN:
|
|
if(y==3)
|
|
{
|
|
#if CONFIG_KEYPAD == IRIVER_H10_PAD
|
|
if(x < 2)
|
|
x++;
|
|
else
|
|
x=0;
|
|
#endif /* CONFIG_KEYPAD == IRIVER_H10_PAD */
|
|
y=0;
|
|
}
|
|
else
|
|
{
|
|
y++;
|
|
}
|
|
break;
|
|
default:
|
|
if(rb->default_event_handler(button) == SYS_USB_CONNECTED)
|
|
{
|
|
done = true;
|
|
ret = RET_VAL_USB;
|
|
}
|
|
break;
|
|
}
|
|
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
|
|
rb->lcd_fillrect(NUM_MARGIN_X+(NUM_BOX_WIDTH*x),
|
|
NUM_MARGIN_Y+(NUM_BOX_HEIGHT*y),
|
|
NUM_BOX_WIDTH+1, NUM_BOX_HEIGHT+1);
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
rb->lcd_update();
|
|
}
|
|
rb->lcd_setfont(FONT_UI);
|
|
rb->lcd_scroll_stop();
|
|
if (ret == RET_VAL_QUIT_ERR)
|
|
rb->splash(HZ, "Cancelled");
|
|
return ret;
|
|
}
|
|
|
|
static bool tile_has_item(int type, int x, int y)
|
|
{
|
|
switch(type)
|
|
{
|
|
case 0:
|
|
return (board[x][y].men > 0);
|
|
break;
|
|
case 1:
|
|
return board[x][y].tank;
|
|
break;
|
|
case 2:
|
|
return board[x][y].plane;
|
|
break;
|
|
case 3:
|
|
return board[x][y].farm;
|
|
break;
|
|
case 4:
|
|
return board[x][y].ind;
|
|
break;
|
|
case 5:
|
|
return board[x][y].nuke;
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int buy_resources(int colour, int type, int x, int y, int nummen)
|
|
{
|
|
const char *itemnames[][6] = {
|
|
{
|
|
"them",
|
|
"the tank",
|
|
"the plane",
|
|
"the farm",
|
|
"the industrial plant",
|
|
"the nuke",
|
|
},
|
|
{
|
|
"place men",
|
|
"place a tank",
|
|
"place a plane",
|
|
"build a farm",
|
|
"build an industrial plant",
|
|
"place a nuke",
|
|
},
|
|
{
|
|
NULL,
|
|
"a tank",
|
|
"a plane",
|
|
"a farm",
|
|
"an industrial plant",
|
|
"a nuke",
|
|
},
|
|
};
|
|
|
|
bool human = (colour == COLOUR_LIGHT);
|
|
int price = 0;
|
|
int temp;
|
|
struct resources *res;
|
|
|
|
if(human)
|
|
{
|
|
res = &humanres;
|
|
}
|
|
else
|
|
{
|
|
res = &compres;
|
|
}
|
|
switch(type)
|
|
{
|
|
case 0: /* men */
|
|
price = PRICE_MEN*nummen;
|
|
break;
|
|
case 1: /* tank */
|
|
price = PRICE_TANK;
|
|
break;
|
|
case 2: /* plane */
|
|
price = PRICE_PLANE;
|
|
break;
|
|
case 3: /* Farm */
|
|
price = PRICE_FARM;
|
|
break;
|
|
case 4: /* Factory */
|
|
price = PRICE_FACTORY;
|
|
break;
|
|
case 5: /* nuke */
|
|
price = PRICE_NUKE;
|
|
break;
|
|
}
|
|
if(res->cash < price)
|
|
{
|
|
if(human)
|
|
rb->splash(HZ, "Not enough money!");
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
if(human)
|
|
{
|
|
rb->splashf(HZ, "Where do you want to place %s?", itemnames[0][type]);
|
|
if((temp = select_square()) != RET_VAL_OK)
|
|
return temp;
|
|
x = cursor.x;
|
|
y = cursor.y;
|
|
}
|
|
if(board[x][y].colour != colour)
|
|
{
|
|
if(human)
|
|
rb->splashf(HZ, "Can't %s on enemy territory", itemnames[1][type]);
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
if(type != 0 && tile_has_item(type, x, y))
|
|
{
|
|
if(human)
|
|
rb->splashf(HZ, "There is already %s there", itemnames[2][type]);
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
switch(type)
|
|
{
|
|
case 0:
|
|
board[x][y].men += nummen;
|
|
res->men += nummen;
|
|
break;
|
|
case 1:
|
|
board[x][y].tank = true;
|
|
res->tanks++;
|
|
break;
|
|
case 2:
|
|
board[x][y].plane = true;
|
|
res->planes++;
|
|
break;
|
|
case 3:
|
|
board[x][y].farm = true;
|
|
res->farms++;
|
|
break;
|
|
case 4:
|
|
board[x][y].ind = true;
|
|
res->inds++;
|
|
break;
|
|
case 5:
|
|
board[x][y].nuke = true;
|
|
res->nukes++;
|
|
break;
|
|
}
|
|
res->cash -= price;
|
|
|
|
draw_board();
|
|
rb->sleep(HZ);
|
|
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static int buy_resources_menu(void)
|
|
{
|
|
int selection = 0,nummen;
|
|
|
|
MENUITEM_STRINGLIST(menu, "Buy Resources", NULL,
|
|
"Buy men ($" PRICE_MEN_STR ")", "Buy tank ($" PRICE_TANK_STR ")", "Buy plane ($" PRICE_PLANE_STR ")",
|
|
"Buy Farm ($" PRICE_FARM_STR ")", "Buy Factory ($" PRICE_FACTORY_STR ")",
|
|
"Buy Nuke ($" PRICE_NUKE_STR ")",
|
|
"Finish buying");
|
|
|
|
while(1) {
|
|
switch(rb->do_menu(&menu, &selection, NULL, false)) {
|
|
case 0:
|
|
nummen = 0;
|
|
if(get_number("How many men would you like?", &nummen,
|
|
humanres.cash) == RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
if(!nummen)
|
|
break;
|
|
/* fall through */
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
if(buy_resources(COLOUR_LIGHT, selection, 0, 0, nummen)== RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 6:
|
|
return RET_VAL_OK;
|
|
break;
|
|
case MENU_ATTACHED_USB:
|
|
return RET_VAL_USB;
|
|
break;
|
|
case GO_TO_PREVIOUS:
|
|
return RET_VAL_OK;
|
|
break;
|
|
}
|
|
}
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static int move_unit(int colour, int type, int fromx, int fromy,
|
|
int tox, int toy, int nummen)
|
|
{
|
|
const char *itemnames[][3] = {
|
|
{
|
|
"troops",
|
|
"the tank",
|
|
"the plane",
|
|
},
|
|
{
|
|
"any troops",
|
|
"a tank",
|
|
"a plane",
|
|
},
|
|
{
|
|
"the troops",
|
|
"the tank",
|
|
"the plane",
|
|
}
|
|
};
|
|
bool human = (colour == COLOUR_LIGHT);
|
|
int temp;
|
|
|
|
if(human)
|
|
{
|
|
rb->splashf(HZ, "Select where you want to move %s from",
|
|
itemnames[0][type]);
|
|
if((temp = select_square()) != RET_VAL_OK)
|
|
return temp;
|
|
fromx = cursor.x;
|
|
fromy = cursor.y;
|
|
}
|
|
if(board[fromx][fromy].colour != colour)
|
|
{
|
|
if(human)
|
|
rb->splash(HZ, "That isn't your territory");
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
if(!tile_has_item(type, fromx, fromy))
|
|
{
|
|
if(human)
|
|
rb->splashf(HZ, "You don't have %s there", itemnames[1][type]);
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
if(type == 0)
|
|
{
|
|
if(human)
|
|
{
|
|
nummen = board[fromx][fromy].men;
|
|
if((temp = get_number("How many men do you want to move?", &nummen,
|
|
nummen)) != RET_VAL_OK)
|
|
return temp;
|
|
}
|
|
if(nummen > board[fromx][fromy].men)
|
|
{
|
|
if(human)
|
|
rb->splash(HZ, "You don't have that many troops.");
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
}
|
|
if(human)
|
|
{
|
|
rb->splashf(HZ, "Select where you want to move %s to",
|
|
itemnames[2][type]);
|
|
if((temp = select_square()) != RET_VAL_OK)
|
|
return temp;
|
|
tox = cursor.x;
|
|
toy = cursor.y;
|
|
}
|
|
if((tox == fromx && toy == fromy) ||
|
|
board[tox][toy].colour != colour ||
|
|
(type != 2 && (abs(tox - fromx) > 1 ||
|
|
abs(toy - fromy) > 1)))
|
|
{
|
|
if(human)
|
|
rb->splash(HZ, "Invalid move");
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
if(type != 0 && tile_has_item(type, tox, toy))
|
|
{
|
|
if(human)
|
|
rb->splashf(HZ, "There is already %s there", itemnames[1][type]);
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
switch(type)
|
|
{
|
|
case 0:
|
|
board[fromx][fromy].men -= nummen;
|
|
board[tox][toy].men += nummen;
|
|
break;
|
|
case 1:
|
|
board[fromx][fromy].tank = false;
|
|
board[tox][toy].tank = true;
|
|
break;
|
|
case 2:
|
|
board[fromx][fromy].plane = false;
|
|
board[tox][toy].plane = true;
|
|
break;
|
|
}
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static int move_unit_menu(void)
|
|
{
|
|
int selection = 0;
|
|
|
|
MENUITEM_STRINGLIST(menu, "Move unit", NULL,
|
|
"Move men", "Move tank", "Move plane");
|
|
switch(rb->do_menu(&menu, &selection, NULL, false))
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
switch(move_unit(COLOUR_LIGHT, selection, 0, 0, 0, 0, 0))
|
|
{
|
|
case RET_VAL_OK:
|
|
humanres.moves--;
|
|
break;
|
|
case RET_VAL_USB:
|
|
return RET_VAL_USB;
|
|
break;
|
|
}
|
|
break;
|
|
case MENU_ATTACHED_USB:
|
|
return RET_VAL_USB;
|
|
}
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static int launch_nuke(int colour, int nukex, int nukey, int targetx, int targety)
|
|
{
|
|
bool human = (colour == COLOUR_LIGHT);
|
|
int temp;
|
|
struct resources *res;
|
|
|
|
if(board[nukex][nukey].colour != colour)
|
|
{
|
|
if(human)
|
|
rb->splash(HZ, "That isn't your territory");
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
if(! board[nukex][nukey].nuke)
|
|
{
|
|
if(human)
|
|
rb->splashf(HZ, "You don't have a nuke there");
|
|
return RET_VAL_QUIT_ERR;
|
|
}
|
|
if(human)
|
|
{
|
|
rb->splash(HZ, "Select place to target with nuke");
|
|
if((temp = select_square()) != RET_VAL_OK)
|
|
return temp;
|
|
targetx = cursor.x;
|
|
targety = cursor.y;
|
|
}
|
|
if(human)
|
|
{
|
|
humanres.nukes--;
|
|
}
|
|
else
|
|
{
|
|
compres.nukes--;
|
|
}
|
|
board[nukex][nukey].nuke = false;
|
|
|
|
if(board[targetx][targety].colour == COLOUR_LIGHT)
|
|
{
|
|
res = &humanres;
|
|
}
|
|
else
|
|
{
|
|
res = &compres;
|
|
}
|
|
res->men -= board[targetx][targety].men;
|
|
res->tanks -= board[targetx][targety].tank;
|
|
res->planes -= board[targetx][targety].plane;
|
|
res->nukes -= board[targetx][targety].nuke;
|
|
res->farms -= board[targetx][targety].farm;
|
|
res->inds -= board[targetx][targety].ind;
|
|
board[targetx][targety].men = 0;
|
|
board[targetx][targety].tank = false;
|
|
board[targetx][targety].plane = false;
|
|
board[targetx][targety].ind = false;
|
|
board[targetx][targety].nuke = false;
|
|
board[targetx][targety].farm = false;
|
|
/* TODO: Fallout carried by wind */
|
|
draw_board();
|
|
if(human)
|
|
rb->sleep(HZ*2);
|
|
else
|
|
rb->sleep(HZ);
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static int movement_menu(void)
|
|
{
|
|
int selection = 0, temp;
|
|
|
|
MENUITEM_STRINGLIST(menu, "Movement", NULL,
|
|
"Move unit", "Buy additional moves ($" PRICE_MOVE_STR ")",
|
|
"Launch nuclear missile", "Check map",
|
|
"Finish moving", "Game menu");
|
|
|
|
while(1)
|
|
{
|
|
switch(rb->do_menu(&menu, &selection, NULL, false))
|
|
{
|
|
case 0:
|
|
if(humanres.moves)
|
|
{
|
|
if(move_unit_menu()==RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
}
|
|
else
|
|
{
|
|
rb->splash(HZ, "You have no more moves left."
|
|
" You can buy more for $" PRICE_MOVE_STR " each.");
|
|
}
|
|
break;
|
|
case 1:
|
|
if(humanres.cash > PRICE_MOVE)
|
|
{
|
|
humanres.moves++;
|
|
humanres.cash -= PRICE_MOVE;
|
|
rb->snprintf(buf, sizeof(buf), "You now have %d moves",
|
|
humanres.moves);
|
|
rb->splash(HZ, buf);
|
|
}
|
|
else
|
|
{
|
|
rb->splash(HZ, "Not enough money!");
|
|
}
|
|
break;
|
|
case 2:
|
|
if(humanres.nukes==0)
|
|
{
|
|
rb->splash(HZ, "You do not have any nukes to launch");
|
|
}
|
|
else
|
|
{
|
|
rb->splash(HZ, "Select place to launch nuke from");
|
|
switch(select_square())
|
|
{
|
|
case RET_VAL_OK:
|
|
if(launch_nuke(COLOUR_LIGHT, cursor.x, cursor.y,
|
|
0, 0) == RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
break;
|
|
case RET_VAL_USB:
|
|
return RET_VAL_USB;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
if(select_square() == RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 4:
|
|
return RET_VAL_OK;
|
|
break;
|
|
case 5:
|
|
if((temp = ingame_menu()) != RET_VAL_OK)
|
|
return temp;
|
|
break;
|
|
case MENU_ATTACHED_USB:
|
|
return RET_VAL_USB;
|
|
break;
|
|
}
|
|
}
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static const char* inventory_data(int selected_item, void * data,
|
|
char * buffer, size_t buffer_len)
|
|
{
|
|
(void)data;
|
|
switch(selected_item)
|
|
{
|
|
case 0:
|
|
rb->snprintf(buffer,buffer_len,"Men: %d", humanres.men);
|
|
break;
|
|
case 1:
|
|
rb->snprintf(buffer,buffer_len,"Tanks: %d", humanres.tanks);
|
|
break;
|
|
case 2:
|
|
rb->snprintf(buffer,buffer_len,"Planes: %d", humanres.planes);
|
|
break;
|
|
case 3:
|
|
rb->snprintf(buffer,buffer_len,"Factories: %d", humanres.inds);
|
|
break;
|
|
case 4:
|
|
rb->snprintf(buffer,buffer_len,"Farms: %d", humanres.farms);
|
|
break;
|
|
case 5:
|
|
rb->snprintf(buffer,buffer_len,"Nukes: %d", humanres.nukes);
|
|
break;
|
|
case 6:
|
|
rb->snprintf(buffer,buffer_len,"Cash: %d", humanres.cash);
|
|
break;
|
|
case 7:
|
|
rb->snprintf(buffer,buffer_len,"Food: %d", humanres.food);
|
|
break;
|
|
case 8:
|
|
rb->snprintf(buffer,buffer_len,"Bank: %d", humanres.bank);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
static int show_inventory(void)
|
|
{
|
|
struct simplelist_info info;
|
|
rb->simplelist_info_init(&info, "Inventory", 9, NULL);
|
|
info.get_name = inventory_data;
|
|
if(rb->simplelist_show_list(&info))
|
|
{
|
|
return RET_VAL_USB;
|
|
}
|
|
else
|
|
{
|
|
return RET_VAL_OK;
|
|
}
|
|
}
|
|
|
|
static int production_menu(void)
|
|
{
|
|
int selection = 0, temp;
|
|
|
|
MENUITEM_STRINGLIST(menu, "Production", NULL,
|
|
"Buy resources", "Show inventory", "Check map",
|
|
"Invest money", "Withdraw money",
|
|
"Finish turn", "Game menu");
|
|
|
|
while(1)
|
|
{
|
|
switch(rb->do_menu(&menu, &selection, NULL, false))
|
|
{
|
|
case 0:
|
|
if(buy_resources_menu() == RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 1:
|
|
if(show_inventory() == RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 2:
|
|
if(select_square() == RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
break;
|
|
case 3:
|
|
temp = humanres.cash;
|
|
if(get_number("How much do you want to invest?", &temp,
|
|
humanres.cash) == RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
if(temp > humanres.cash)
|
|
{
|
|
rb->splash(HZ, "You don't have that much cash to invest");
|
|
}
|
|
else
|
|
{
|
|
humanres.cash -= temp;
|
|
humanres.bank += temp;
|
|
}
|
|
break;
|
|
case 4:
|
|
temp = humanres.bank;
|
|
if(get_number("How much do you want to withdraw?", &temp,
|
|
humanres.bank) == RET_VAL_USB)
|
|
return RET_VAL_USB;
|
|
if(temp > humanres.bank)
|
|
{
|
|
rb->splash(HZ, "You don't have that much cash to withdraw");
|
|
}
|
|
else
|
|
{
|
|
humanres.cash += temp;
|
|
humanres.bank -= temp;
|
|
}
|
|
break;
|
|
case 5:
|
|
return RET_VAL_OK;
|
|
break;
|
|
case 6:
|
|
if((temp = ingame_menu()) != RET_VAL_OK)
|
|
return temp;
|
|
break;
|
|
case MENU_ATTACHED_USB:
|
|
return RET_VAL_USB;
|
|
break;
|
|
}
|
|
}
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
static void init_resources(void)
|
|
{
|
|
humanres.cash = superdom_settings.startcash;
|
|
humanres.food = superdom_settings.startfood;
|
|
humanres.tanks = 0;
|
|
humanres.planes = 0;
|
|
humanres.nukes = 0;
|
|
humanres.inds = 0;
|
|
humanres.farms = 0;
|
|
humanres.men = 0;
|
|
humanres.bank = 0;
|
|
humanres.moves = 0;
|
|
compres.cash = superdom_settings.startcash;
|
|
compres.food = superdom_settings.startfood;
|
|
compres.tanks = 0;
|
|
compres.planes = 0;
|
|
compres.nukes = 0;
|
|
compres.inds = 0;
|
|
compres.farms = 0;
|
|
compres.men = 0;
|
|
compres.bank = 0;
|
|
compres.moves = 0;
|
|
}
|
|
|
|
static int select_square(void)
|
|
{
|
|
int button = 0;
|
|
draw_board();
|
|
draw_cursor();
|
|
update_score();
|
|
#if LCD_WIDTH >= 220
|
|
rb->lcd_setfont(FONT_SYSFIXED);
|
|
rb->lcd_putsxyf(125, LCD_HEIGHT-20,"Cash: %d", humanres.cash);
|
|
rb->lcd_putsxyf(125, LCD_HEIGHT-10,"Food: %d", humanres.food);
|
|
rb->lcd_setfont(FONT_UI);
|
|
#endif
|
|
rb->lcd_update();
|
|
while(1)
|
|
{
|
|
button=pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts));
|
|
switch(button)
|
|
{
|
|
case SUPERDOM_CANCEL:
|
|
rb->splash(HZ, "Cancelled");
|
|
return RET_VAL_QUIT_ERR;
|
|
break;
|
|
case SUPERDOM_OK:
|
|
return RET_VAL_OK;
|
|
break;
|
|
case SUPERDOM_LEFT:
|
|
case SUPERDOM_LEFT_REPEAT:
|
|
draw_cursor(); /* Deselect the current tile */
|
|
if(cursor.x>1)
|
|
{
|
|
cursor.x--;
|
|
}
|
|
else
|
|
{
|
|
cursor.x = BOARD_SIZE;
|
|
}
|
|
update_score();
|
|
draw_cursor();
|
|
break;
|
|
case SUPERDOM_RIGHT:
|
|
case SUPERDOM_RIGHT_REPEAT:
|
|
draw_cursor(); /* Deselect the current tile */
|
|
if(cursor.x<BOARD_SIZE)
|
|
{
|
|
cursor.x++;
|
|
}
|
|
else
|
|
{
|
|
cursor.x = 1;
|
|
}
|
|
update_score();
|
|
draw_cursor();
|
|
break;
|
|
case SUPERDOM_UP:
|
|
case SUPERDOM_UP_REPEAT:
|
|
draw_cursor(); /* Deselect the current tile */
|
|
if(cursor.y>1)
|
|
{
|
|
cursor.y--;
|
|
}
|
|
else
|
|
{
|
|
#if CONFIG_KEYPAD == IRIVER_H10_PAD
|
|
if(cursor.x > 1)
|
|
cursor.x--;
|
|
else
|
|
cursor.x = BOARD_SIZE;
|
|
#endif
|
|
cursor.y = BOARD_SIZE;
|
|
}
|
|
update_score();
|
|
draw_cursor();
|
|
break;
|
|
case SUPERDOM_DOWN:
|
|
case SUPERDOM_DOWN_REPEAT:
|
|
draw_cursor(); /* Deselect the current tile */
|
|
if(cursor.y<BOARD_SIZE)
|
|
{
|
|
cursor.y++;
|
|
}
|
|
else
|
|
{
|
|
#if CONFIG_KEYPAD == IRIVER_H10_PAD
|
|
if(cursor.x < BOARD_SIZE)
|
|
cursor.x++;
|
|
else
|
|
cursor.x = 1;
|
|
#endif
|
|
cursor.y = 1;
|
|
}
|
|
update_score();
|
|
draw_cursor();
|
|
break;
|
|
default:
|
|
if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
|
|
{
|
|
return RET_VAL_USB;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int killmen(int colour)
|
|
{
|
|
bool human = (colour == COLOUR_LIGHT);
|
|
int menkilled,i,j;
|
|
int percent;
|
|
if(human)
|
|
{
|
|
percent = (humanres.food*1000)/humanres.men;
|
|
humanres.food = 0;
|
|
}
|
|
else
|
|
{
|
|
percent = (compres.food*1000)/compres.men;
|
|
compres.food = 0;
|
|
}
|
|
menkilled = 0;
|
|
for(i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if(board[i][j].colour == colour)
|
|
{
|
|
int nummen = ((board[i][j].men * percent)/1000);
|
|
menkilled += board[i][j].men - nummen;
|
|
board[i][j].men = nummen;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(human)
|
|
humanres.men -= menkilled;
|
|
else
|
|
compres.men -= menkilled;
|
|
return menkilled;
|
|
}
|
|
|
|
/* return -1 if error, 1 if attack is succeeded, 0 otherwise */
|
|
static int attack_territory(int colour, int x, int y)
|
|
{
|
|
bool human = (colour == COLOUR_LIGHT);
|
|
int str_diff;
|
|
|
|
if(board[x][y].colour == colour)
|
|
{
|
|
if(human)
|
|
rb->splash(HZ, "You can't attack your own territory");
|
|
return -1;
|
|
}
|
|
str_diff = calc_strength(COLOUR_DARK, x, y) -
|
|
calc_strength(COLOUR_LIGHT, x, y);
|
|
if(human)
|
|
{
|
|
str_diff = -str_diff;
|
|
}
|
|
if(str_diff > 0 || (str_diff == 0 && rb->rand()%2))
|
|
{
|
|
struct resources *offres, *defres;
|
|
if(human)
|
|
{
|
|
offres = &humanres;
|
|
defres = &compres;
|
|
}
|
|
else
|
|
{
|
|
offres = &compres;
|
|
defres = &humanres;
|
|
}
|
|
defres->men -= board[x][y].men;
|
|
defres->tanks -= board[x][y].tank;
|
|
defres->planes -= board[x][y].plane;
|
|
defres->nukes -= board[x][y].nuke;
|
|
defres->farms -= board[x][y].farm;
|
|
defres->inds -= board[x][y].ind;
|
|
offres->farms += board[x][y].farm;
|
|
offres->inds += board[x][y].ind;
|
|
offres->nukes += board[x][y].nuke;
|
|
board[x][y].colour = colour;
|
|
if(!superdom_settings.persistent_units)
|
|
{
|
|
board[x][y].men = 0;
|
|
board[x][y].tank = false;
|
|
board[x][y].plane = false;
|
|
}
|
|
draw_board();
|
|
if(human)
|
|
rb->sleep(HZ*2);
|
|
else
|
|
rb->sleep(HZ);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if(human)
|
|
rb->splash(HZ, "Your troops were unable to overcome"
|
|
" the enemy troops");
|
|
else
|
|
rb->splash(HZ*2, "The computer attempted to "
|
|
"attack, but the invasion was"
|
|
" pushed back");
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void spoil_food(void)
|
|
{
|
|
/* spoil 0-10% of food, different amounts for computer/player */
|
|
int spoil_amount=humanres.food*(0.1*rb->rand()/RAND_MAX);
|
|
if(spoil_amount)
|
|
rb->splashf(2*HZ, "Spoilage claims %d units of food", spoil_amount);
|
|
humanres.food-=spoil_amount;
|
|
|
|
/* now for computer */
|
|
spoil_amount=compres.food*(0.1*rb->rand()/RAND_MAX);
|
|
compres.food-=spoil_amount;
|
|
}
|
|
|
|
static int war_menu(void)
|
|
{
|
|
int selection = 0, temp;
|
|
|
|
MENUITEM_STRINGLIST(menu, "War!", NULL,
|
|
"Select territory to attack",
|
|
"Finish turn", "Game menu");
|
|
|
|
while(humanres.moves)
|
|
{
|
|
switch(rb->do_menu(&menu, &selection, NULL, false))
|
|
{
|
|
case 0:
|
|
switch(select_square())
|
|
{
|
|
case RET_VAL_OK:
|
|
if(attack_territory(COLOUR_LIGHT, cursor.x, cursor.y) >= 0)
|
|
humanres.moves--;
|
|
break;
|
|
case RET_VAL_USB:
|
|
return RET_VAL_USB;
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
return RET_VAL_OK;
|
|
break;
|
|
case 2:
|
|
if((temp = ingame_menu()) != RET_VAL_OK)
|
|
return temp;
|
|
break;
|
|
}
|
|
}
|
|
return RET_VAL_OK;
|
|
}
|
|
|
|
struct threat {
|
|
int x;
|
|
int y;
|
|
int str_diff;
|
|
};
|
|
|
|
static bool place_adjacent(bool tank, int x, int y)
|
|
{
|
|
int type = (tank ? 1: 2);
|
|
if(!buy_resources(COLOUR_DARK, type, x, y, 0))
|
|
{
|
|
return true;
|
|
}
|
|
if(!buy_resources(COLOUR_DARK, type, x-1, y, 0))
|
|
{
|
|
return true;
|
|
}
|
|
if(!buy_resources(COLOUR_DARK, type, x+1, y, 0))
|
|
{
|
|
return true;
|
|
}
|
|
if(!buy_resources(COLOUR_DARK, type, x, y-1, 0))
|
|
{
|
|
return true;
|
|
}
|
|
if(!buy_resources(COLOUR_DARK, type, x, y+1, 0))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool has_adjacent(int x, int y)
|
|
{
|
|
if ((board[x][y].colour == COLOUR_LIGHT) &&
|
|
((board[x-1][y].colour == COLOUR_DARK) ||
|
|
(board[x+1][y].colour == COLOUR_DARK) ||
|
|
(board[x][y+1].colour == COLOUR_DARK) ||
|
|
(board[x][y-1].colour == COLOUR_DARK)))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void find_adjacent(int x, int y, int* adj_x, int* adj_y)
|
|
{
|
|
/* Finds adjacent squares, returning squares without tanks on them
|
|
* in preference to those with them */
|
|
if(board[x-1][y].colour == COLOUR_DARK)
|
|
{
|
|
*adj_x = x-1;
|
|
*adj_y = y;
|
|
return;
|
|
}
|
|
if(board[x+1][y].colour == COLOUR_DARK)
|
|
{
|
|
*adj_x = x+1;
|
|
*adj_y = y;
|
|
return;
|
|
}
|
|
if(board[x][y-1].colour == COLOUR_DARK)
|
|
{
|
|
*adj_x = x;
|
|
*adj_y = y-1;
|
|
return;
|
|
}
|
|
if(board[x][y+1].colour == COLOUR_DARK)
|
|
{
|
|
*adj_x = x;
|
|
*adj_y = y+1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void computer_allocate(void)
|
|
{
|
|
/* Firstly, decide whether to go offensive or defensive.
|
|
* This is primarily decided by the human player posing a threat to either
|
|
* the computer's farms, factories or nukes */
|
|
int i, j, k;
|
|
bool offensive = true;
|
|
struct threat threats[4];
|
|
int numthreats = 0;
|
|
int total_str_diff = 0;
|
|
int numterritory = 0;
|
|
int str_diff;
|
|
int men_needed;
|
|
struct threat targets[2];
|
|
int numtargets;
|
|
struct cursor adj = { 0, 0 };
|
|
|
|
compres.cash += compres.bank;
|
|
compres.bank = 0;
|
|
for(i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if(board[i][j].colour == COLOUR_DARK)
|
|
{
|
|
numterritory++;
|
|
str_diff = calc_strength(COLOUR_LIGHT,i,j) -
|
|
calc_strength(COLOUR_DARK,i,j);
|
|
if(str_diff > 0 && (board[i][j].ind || board[i][j].farm || board[i][j].nuke))
|
|
{
|
|
/* computer's farm/factory/nuke is being threatened */
|
|
if(numthreats < 3)
|
|
{
|
|
offensive = false;
|
|
threats[numthreats].x = i;
|
|
threats[numthreats].y = j;
|
|
threats[numthreats].str_diff = str_diff;
|
|
numthreats++;
|
|
}
|
|
}
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
/* if the computer has no factories, build some ASAP */
|
|
if(!compres.inds)
|
|
{
|
|
while(compres.cash >= PRICE_FACTORY && compres.inds < numterritory)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if(board[i][j].colour == COLOUR_DARK)
|
|
{
|
|
buy_resources(COLOUR_DARK, 4, i, j, 0);
|
|
}
|
|
}
|
|
}
|
|
if(superdom_settings.compdiff>=AI_BUILD_INDS_FARMS_LEVEL && compres.cash>=PRICE_FACTORY)
|
|
{
|
|
while(compres.cash>=PRICE_FACTORY)
|
|
{
|
|
if(compres.farms<compres.inds)
|
|
{
|
|
while(compres.farms<compres.inds && compres.cash>=PRICE_FARM)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if(board[i][j].colour == COLOUR_DARK && !board[i][j].farm)
|
|
{
|
|
buy_resources(COLOUR_DARK, 3, i, j, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(compres.inds<compres.farms && compres.cash>=PRICE_FACTORY)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if(board[i][j].colour == COLOUR_DARK && !board[i][j].ind)
|
|
{
|
|
buy_resources(COLOUR_DARK, 4, i, j, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* AI will buy nukes first if possible */
|
|
if(compres.cash > PRICE_NUKE + PRICE_TANK && superdom_settings.compdiff>=AI_BUILD_NUKES_LEVEL)
|
|
{
|
|
while(compres.cash >= PRICE_NUKE && compres.nukes < numterritory)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if(board[i][j].colour == COLOUR_DARK)
|
|
{
|
|
buy_resources(COLOUR_DARK, 5, i, j, 0);
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
if(offensive)
|
|
{
|
|
/* The AI is going to go straight for the throat here and attack
|
|
* the player's farms, nukes, and factories. The amount of cash
|
|
* the AI has to spend will determine how many targets there are */
|
|
if(compres.cash > 1200)
|
|
{
|
|
/* 1200 is a figure I pulled out of nowhere. Adjust as needed */
|
|
numtargets = 2;
|
|
}
|
|
else
|
|
{
|
|
numtargets = 1;
|
|
}
|
|
/* Work out which target(s) to attack. They must have adjacent squares
|
|
* owned by the computer. If none are found just place troops in
|
|
* random places around the map until we run out of money */
|
|
k = 0;
|
|
for(i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if(has_adjacent(i,j) &&
|
|
(board[i][j].ind || board[i][j].farm || board[i][j].nuke))
|
|
{
|
|
if(k<numtargets)
|
|
{
|
|
targets[k].x = i;
|
|
targets[k].y = j;
|
|
targets[k].str_diff =
|
|
calc_strength(COLOUR_LIGHT, i, j) -
|
|
calc_strength(COLOUR_DARK, i, j);
|
|
k++;
|
|
}
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
if(k == 0)
|
|
{
|
|
/* randomly place tanks */
|
|
while(compres.cash >= PRICE_TANK && compres.tanks < numterritory)
|
|
{
|
|
i = rb->rand()%BOARD_SIZE + 1;
|
|
j = rb->rand()%BOARD_SIZE + 1;
|
|
if(board[i][j].colour == COLOUR_DARK)
|
|
{
|
|
buy_resources(COLOUR_DARK, 1, i, j, 0);
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<k;i++)
|
|
{
|
|
str_diff = targets[i].str_diff;
|
|
while(str_diff + 20 > 0 && compres.cash > 0)
|
|
{
|
|
/* While we still need them keep placing men */
|
|
if(!place_adjacent(true, targets[i].x, targets[i].y))
|
|
{
|
|
find_adjacent(targets[i].x, targets[i].y,
|
|
&adj.x, &adj.y);
|
|
men_needed = (str_diff + 20)*1000/133;
|
|
if(compres.cash < men_needed)
|
|
{
|
|
men_needed = compres.cash;
|
|
}
|
|
buy_resources(COLOUR_DARK, 0, adj.x, adj.y,
|
|
men_needed);
|
|
break;
|
|
}
|
|
str_diff = calc_strength(COLOUR_LIGHT,
|
|
targets[i].x, targets[i].y) -
|
|
calc_strength(COLOUR_DARK,
|
|
targets[i].x, targets[i].y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Work out what to place on each square to defend it.
|
|
* Tanks are preferential because they do not require food,
|
|
* but if the budget is tight then we fall back onto troops.
|
|
* Conversely if cash is not an issue and there are already tanks in
|
|
* place planes will be deployed. We would like a margin of at least
|
|
* 20 points to be safe. */
|
|
|
|
for(i=0;i<numthreats;i++)
|
|
{
|
|
total_str_diff += threats[i].str_diff;
|
|
}
|
|
if((total_str_diff+20)*10 > compres.cash)
|
|
{
|
|
/* Not enough cash to accomodate all threats using tanks alone -
|
|
* use men as a backup */
|
|
for(i=0;i<numthreats;i++)
|
|
{
|
|
men_needed = ((threats[i].str_diff + 20)*1000)/133;
|
|
if(compres.cash < men_needed)
|
|
{
|
|
men_needed = compres.cash;
|
|
}
|
|
buy_resources(COLOUR_DARK, 0, threats[i].x, threats[i].y,
|
|
men_needed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Tanks it is */
|
|
/* Enough money to pay their way by planes? */
|
|
bool tank = ((total_str_diff+20)*15 >= compres.cash);
|
|
for(i=0;i<numthreats;i++)
|
|
{
|
|
str_diff = threats[i].str_diff;
|
|
while(str_diff + 20 > 0)
|
|
{
|
|
if(!place_adjacent(tank, threats[i].x, threats[i].y))
|
|
{
|
|
/* No room for any more planes or tanks, revert to
|
|
* men */
|
|
find_adjacent(threats[i].x, threats[i].y,
|
|
&adj.x, &adj.y);
|
|
men_needed = (str_diff + 20)*1000/133;
|
|
if(compres.cash < men_needed)
|
|
{
|
|
men_needed = compres.cash;
|
|
}
|
|
buy_resources(COLOUR_DARK, 0, threats[i].x,
|
|
threats[i].y, men_needed);
|
|
break;
|
|
}
|
|
str_diff = calc_strength(COLOUR_LIGHT,
|
|
threats[i].x, threats[i].y) -
|
|
calc_strength(COLOUR_DARK,
|
|
threats[i].x, threats[i].y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* no investing in easy mode */
|
|
if(superdom_settings.compdiff>=AI_INVESTING_LEVEL)
|
|
{
|
|
compres.bank += compres.cash;
|
|
compres.cash = 0;
|
|
}
|
|
}
|
|
|
|
static int find_adj_target(int x, int y, struct cursor* adj)
|
|
{
|
|
/* Find a square next to a computer's farm or factory owned by the player
|
|
* that is vulnerable. Return 1 on success, 0 otherwise */
|
|
if(board[x+1][y].colour == COLOUR_LIGHT &&
|
|
calc_strength(COLOUR_LIGHT,x+1,y)<=calc_strength(COLOUR_DARK,x+1,y))
|
|
{
|
|
adj->x = x+1;
|
|
adj->y = y;
|
|
return 1;
|
|
}
|
|
if(board[x-1][y].colour == COLOUR_LIGHT &&
|
|
calc_strength(COLOUR_LIGHT,x-1,y)<=calc_strength(COLOUR_DARK,x-1,y))
|
|
{
|
|
adj->x = x-1;
|
|
adj->y = y;
|
|
return 1;
|
|
}
|
|
if(board[x][y+1].colour == COLOUR_LIGHT &&
|
|
calc_strength(COLOUR_LIGHT,x,y+1)<=calc_strength(COLOUR_DARK,x,y+1))
|
|
{
|
|
adj->x = x;
|
|
adj->y = y+1;
|
|
return 1;
|
|
}
|
|
if(board[x][y-1].colour == COLOUR_LIGHT &&
|
|
calc_strength(COLOUR_LIGHT,x,y-1)<=calc_strength(COLOUR_DARK,x,y-1))
|
|
{
|
|
adj->x = x;
|
|
adj->y = y-1;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void computer_movement(void)
|
|
{
|
|
/* use nukes */
|
|
if(superdom_settings.compdiff>=3)
|
|
{
|
|
struct cursor nukes[10]; /* 10 for now, change as needed */
|
|
int nukes_back=0;
|
|
if(compres.nukes>0)
|
|
{
|
|
for(int i=1;i<=BOARD_SIZE && nukes_back<compres.nukes && nukes_back<10;i++)
|
|
{
|
|
for(int j=1;j<BOARD_SIZE && nukes_back<compres.nukes && nukes_back<10 ;j++)
|
|
{
|
|
if(board[i][j].nuke)
|
|
{
|
|
nukes[nukes_back].x=i;
|
|
nukes[nukes_back].y=j;
|
|
nukes_back++;
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
bool found_target=true;
|
|
struct cursor adj;
|
|
int next_nuke=0;
|
|
/* first, use nukes for defence */
|
|
while(found_target && next_nuke<nukes_back)
|
|
{
|
|
found_target = false;
|
|
for(int i=1;i<=BOARD_SIZE && nukes_back<compres.nukes && nukes_back<10;i++)
|
|
{
|
|
for(int j=1;j<=BOARD_SIZE && nukes_back<compres.nukes && nukes_back<10;j++)
|
|
{
|
|
if((board[i][j].colour == COLOUR_DARK) &&
|
|
(board[i][j].farm || board[i][j].ind || board[i][j].nuke) &&
|
|
find_adj_target(i, j, &adj))
|
|
{
|
|
found_target = true;
|
|
rb->splashf(2*HZ, "Launching nuke for defence, from (%d, %d) to (%d, %d)", nukes[next_nuke].x, nukes[next_nuke].y, adj.x, adj.y);
|
|
launch_nuke(COLOUR_DARK, nukes[next_nuke].x, nukes[next_nuke].y, adj.x, adj.y);
|
|
next_nuke++;
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if we still have any left over, use those for offence */
|
|
found_target = true;
|
|
while(found_target && next_nuke<nukes_back)
|
|
{
|
|
found_target = false;
|
|
for(int i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(int j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if(board[i][j].colour == COLOUR_LIGHT &&
|
|
(board[i][j].ind || board[i][j].farm || board[i][j].nuke) &&
|
|
(calc_strength(COLOUR_DARK, i, j) >= calc_strength(COLOUR_LIGHT, i, j)))
|
|
{
|
|
found_target = true;
|
|
rb->splashf(2*HZ, "Launching nuke for offence, nuke index %d", next_nuke);
|
|
launch_nuke(COLOUR_DARK, nukes[next_nuke].x, nukes[next_nuke].y, i, j);
|
|
next_nuke++;
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
/* TODO: move other units */
|
|
}
|
|
|
|
static void computer_war(void)
|
|
{
|
|
/* Work out where to attack - prioritise the defence of buildings and nukes */
|
|
int i, j;
|
|
bool found_target = true;
|
|
struct cursor adj;
|
|
|
|
while(found_target)
|
|
{
|
|
found_target = false;
|
|
for(i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if((board[i][j].colour == COLOUR_DARK) &&
|
|
(board[i][j].farm || board[i][j].ind || board[i][j].nuke) &&
|
|
find_adj_target(i, j, &adj))
|
|
{
|
|
found_target = true;
|
|
if(attack_territory(COLOUR_DARK, adj.x, adj.y) >= 0)
|
|
{
|
|
compres.moves--;
|
|
if(!compres.moves)
|
|
return;
|
|
}
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
}
|
|
/* Defence stage done, move on to OFFENCE */
|
|
found_target = true;
|
|
while(found_target)
|
|
{
|
|
found_target = false;
|
|
for(i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if(board[i][j].colour == COLOUR_LIGHT &&
|
|
(board[i][j].ind || board[i][j].farm || board[i][j].nuke) &&
|
|
(calc_strength(COLOUR_DARK, i, j) >= calc_strength(COLOUR_LIGHT, i, j)))
|
|
{
|
|
found_target = true;
|
|
if(attack_territory(COLOUR_DARK, i, j) >= 0)
|
|
{
|
|
compres.moves--;
|
|
if(!compres.moves)
|
|
return;
|
|
}
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
}
|
|
/* Spend leftover moves wherever attacking randomly */
|
|
found_target = true;
|
|
while(found_target)
|
|
{
|
|
found_target = false;
|
|
for(i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if(board[i][j].colour == COLOUR_LIGHT &&
|
|
(calc_strength(COLOUR_DARK, i, j) >=
|
|
calc_strength(COLOUR_LIGHT, i, j)))
|
|
{
|
|
found_target = true;
|
|
if(attack_territory(COLOUR_DARK, i, j) >= 0)
|
|
{
|
|
compres.moves--;
|
|
if(!compres.moves)
|
|
return;
|
|
}
|
|
}
|
|
rb->yield();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int load_game(const char* file)
|
|
{
|
|
int fd;
|
|
|
|
fd = rb->open(file, O_RDONLY);
|
|
if(fd < 0)
|
|
{
|
|
DEBUGF("Couldn't open savegame\n");
|
|
return -1;
|
|
}
|
|
rb->read(fd, buf, 5);
|
|
if(rb->strcmp(buf, "SSGv3"))
|
|
{
|
|
rb->splash(HZ, "Invalid/incompatible savegame");
|
|
return -1;
|
|
}
|
|
rb->read(fd, &gamestate, sizeof(gamestate));
|
|
rb->read(fd, &humanres.cash, sizeof(humanres.cash));
|
|
rb->read(fd, &humanres.food, sizeof(humanres.food));
|
|
rb->read(fd, &humanres.bank, sizeof(humanres.bank));
|
|
rb->read(fd, &humanres.planes, sizeof(humanres.planes));
|
|
rb->read(fd, &humanres.tanks, sizeof(humanres.tanks));
|
|
rb->read(fd, &humanres.men, sizeof(humanres.men));
|
|
rb->read(fd, &humanres.nukes, sizeof(humanres.nukes));
|
|
rb->read(fd, &humanres.inds, sizeof(humanres.inds));
|
|
rb->read(fd, &humanres.farms, sizeof(humanres.farms));
|
|
rb->read(fd, &humanres.moves, sizeof(humanres.moves));
|
|
rb->read(fd, &compres.cash, sizeof(humanres.cash));
|
|
rb->read(fd, &compres.food, sizeof(humanres.food));
|
|
rb->read(fd, &compres.bank, sizeof(humanres.bank));
|
|
rb->read(fd, &compres.planes, sizeof(humanres.planes));
|
|
rb->read(fd, &compres.tanks, sizeof(humanres.tanks));
|
|
rb->read(fd, &compres.men, sizeof(humanres.men));
|
|
rb->read(fd, &compres.nukes, sizeof(humanres.nukes));
|
|
rb->read(fd, &compres.inds, sizeof(humanres.inds));
|
|
rb->read(fd, &compres.farms, sizeof(humanres.farms));
|
|
rb->read(fd, &compres.moves, sizeof(humanres.moves));
|
|
rb->read(fd, board, sizeof(board));
|
|
rb->read(fd, &superdom_settings.compstartfarms, sizeof(int));
|
|
rb->read(fd, &superdom_settings.compstartinds, sizeof(int));
|
|
rb->read(fd, &superdom_settings.humanstartfarms, sizeof(int));
|
|
rb->read(fd, &superdom_settings.humanstartinds, sizeof(int));
|
|
rb->read(fd, &superdom_settings.startcash, sizeof(int));
|
|
rb->read(fd, &superdom_settings.startfood, sizeof(int));
|
|
rb->read(fd, &superdom_settings.movesperturn, sizeof(int));
|
|
rb->close(fd);
|
|
return 0;
|
|
}
|
|
|
|
static void default_settings(void)
|
|
{
|
|
superdom_settings.compstartfarms = 1;
|
|
superdom_settings.compstartinds = 1;
|
|
superdom_settings.humanstartfarms = 2;
|
|
superdom_settings.humanstartinds = 2;
|
|
superdom_settings.startcash = 0;
|
|
superdom_settings.startfood = 0;
|
|
superdom_settings.movesperturn = 2;
|
|
superdom_settings.compdiff=2;
|
|
superdom_settings.spoil_enabled=false;
|
|
superdom_settings.persistent_units=false;
|
|
}
|
|
|
|
static int average_strength(int colour)
|
|
{
|
|
/* This function calculates the average strength of the given player,
|
|
* used to determine when the computer wins or loses. */
|
|
int i,j;
|
|
int totalpower = 0;
|
|
for(i=1;i<=BOARD_SIZE;i++)
|
|
{
|
|
for(j=1;j<=BOARD_SIZE;j++)
|
|
{
|
|
if(board[i][j].colour != -1)
|
|
{
|
|
totalpower += calc_strength(colour, i, j);
|
|
}
|
|
}
|
|
}
|
|
return totalpower/NUM_SPACES;
|
|
}
|
|
|
|
enum plugin_status plugin_start(const void* parameter)
|
|
{
|
|
rb->srand(*rb->current_tick);
|
|
#if LCD_DEPTH > 1
|
|
rb->lcd_set_backdrop(NULL);
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
rb->lcd_set_background(LCD_WHITE);
|
|
#endif
|
|
|
|
cursor.x = 1;
|
|
cursor.y = 1;
|
|
default_settings();
|
|
if(parameter)
|
|
{
|
|
if(load_game(parameter) != 0)
|
|
{
|
|
DEBUGF("Loading failed, generating new game\n");
|
|
}
|
|
else
|
|
{
|
|
switch(gamestate)
|
|
{
|
|
case GS_PROD:
|
|
goto startprod;
|
|
break;
|
|
case GS_MOVE:
|
|
goto startmove;
|
|
break;
|
|
case GS_WAR:
|
|
goto startwar;
|
|
break;
|
|
default:
|
|
goto startyear;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(start_menu())
|
|
{
|
|
case RET_VAL_OK: /* start playing */
|
|
break;
|
|
case RET_VAL_QUIT_ERR: /* quit */
|
|
return PLUGIN_OK;
|
|
break;
|
|
case RET_VAL_USB:
|
|
return PLUGIN_USB_CONNECTED;
|
|
break;
|
|
}
|
|
|
|
init_resources();
|
|
init_board();
|
|
gen_resources();
|
|
startyear:
|
|
while(1)
|
|
{
|
|
int avg_str_diff = (average_strength(COLOUR_LIGHT) -
|
|
average_strength(COLOUR_DARK));
|
|
/* computer will hold out longer in hard mode */
|
|
if(avg_str_diff > (superdom_settings.compdiff>=3 ?
|
|
COMPUTER_HARD_SURRENDER_THRESHOLD :
|
|
COMPUTER_SURRENDER_THRESHOLD))
|
|
{
|
|
rb->splash(HZ*4, "The computer has surrendered. You win.");
|
|
return PLUGIN_OK;
|
|
}
|
|
if(-avg_str_diff > HUMAN_SURRENDER_THRESHOLD)
|
|
{
|
|
rb->splash(HZ*4, "Your army has suffered terrible morale from"
|
|
" the bleak prospects of winning. You lose.");
|
|
return PLUGIN_OK;
|
|
}
|
|
|
|
/* production */
|
|
startprod:
|
|
gamestate = GS_PROD;
|
|
switch(production_menu())
|
|
{
|
|
case RET_VAL_USB:
|
|
return PLUGIN_USB_CONNECTED;
|
|
break;
|
|
case RET_VAL_QUIT_ERR:
|
|
return PLUGIN_OK;
|
|
break;
|
|
}
|
|
computer_allocate();
|
|
|
|
/* movement */
|
|
humanres.moves = superdom_settings.movesperturn;
|
|
startmove:
|
|
gamestate = GS_MOVE;
|
|
switch(movement_menu())
|
|
{
|
|
case RET_VAL_USB:
|
|
return PLUGIN_USB_CONNECTED;
|
|
break;
|
|
case RET_VAL_QUIT_ERR:
|
|
return PLUGIN_OK;
|
|
break;
|
|
}
|
|
/* computer movement */
|
|
computer_movement();
|
|
|
|
/* spoil food */
|
|
if(superdom_settings.spoil_enabled)
|
|
{
|
|
spoil_food();
|
|
}
|
|
|
|
/* feed men */
|
|
if(humanres.men)
|
|
{
|
|
if(humanres.food > humanres.men)
|
|
{
|
|
rb->snprintf(buf, sizeof(buf), "Your men ate %d units of food",
|
|
humanres.men);
|
|
humanres.food -= humanres.men;
|
|
}
|
|
else
|
|
{
|
|
rb->snprintf(buf, sizeof(buf), "There was not enough food"
|
|
" to feed all your men, %d men have died of starvation",
|
|
killmen(COLOUR_LIGHT));
|
|
}
|
|
rb->splash(HZ*2, buf);
|
|
}
|
|
if(compres.men)
|
|
{
|
|
if(compres.food > compres.men)
|
|
{
|
|
compres.food -= compres.men;
|
|
}
|
|
else
|
|
{
|
|
rb->snprintf(buf, sizeof(buf), "The computer does not have"
|
|
" enough food to feed its men. %d have died of starvation",
|
|
killmen(COLOUR_DARK));
|
|
rb->splash(HZ, buf);
|
|
}
|
|
}
|
|
/* war */
|
|
humanres.moves = superdom_settings.movesperturn;
|
|
startwar:
|
|
gamestate = GS_WAR;
|
|
switch(war_menu())
|
|
{
|
|
case RET_VAL_USB:
|
|
return PLUGIN_USB_CONNECTED;
|
|
break;
|
|
case RET_VAL_QUIT_ERR:
|
|
return PLUGIN_OK;
|
|
break;
|
|
}
|
|
compres.moves = superdom_settings.movesperturn;
|
|
computer_war();
|
|
gen_resources();
|
|
}
|
|
return PLUGIN_OK;
|
|
}
|