/* * Set up the required initial conditions for a match, and switch to playing state */ void start_game() { /* Seed the RNG */ srand(TCNT1); /* Load in the sprites */ reticule_SPR = reticule(0); /* Generate and draw a level */ clear_screen(); free((void*) level_map); level_map = generate_flat(WIDTH, 129); if(level_map != NULL) draw_level(level_map, SILVER, 0, WIDTH - 1); /* Set up the players in the field */ playersX = malloc(players * sizeof(int16_t)); playersY = malloc(players * sizeof(int16_t)); players_HP = malloc(players * sizeof(uint8_t)); if(playersX == NULL || playersY == NULL || players_HP == NULL) { display_string("Out of memory error!"); return; } uint8_t i; for(i = 0; i < players; i++) { playersX[i] = (i + 1) * WIDTH / (players + 1); playersY[i] = 120; players_HP[i] = MAX_PLAYER_HP; free_sprite(player_SPR); player_SPR = botleft(i); fill_sprite(player_SPR, playersX[i], playersY[i], HEIGHT_NO_UI, WIDTH); } /* Pick a random player to ... go last * First call to start_turn will cycle to next player. Poor sod. */ current_player = rand() % players; reticuleX = (playersX[current_player] + (RETICULE_DISTANCE * ml_cos(position))/100); reticuleY = (playersY[current_player] + (RETICULE_DISTANCE * ml_sin(position))/100); /* Initialise movement variables */ direction = 0; launch_speed = 0; /* Draw the UI */ display_string_xy("POWER:", POWER_LABEL_X, POWER_LABEL_Y); fill_rectangle(power_outline, WHITE); fill_rectangle(power_empty, BLACK); display_string_xy("WIND:", WIND_LABEL_X, WIND_LABEL_Y); fill_rectangle(wind_outline, WHITE); fill_rectangle(wind_empty, BLACK); /* Start the first player's turn */ start_turn(); }
Report* Fight::start(ls::VM& vm, ls::VM& vm_v1) { Simulator::fight = this; for (auto& team : teams) { for (auto& entity : team->entities) { entities.insert({entity->id, entity}); order.addEntity(entity); entity->ai->compile(vm, vm_v1); } } actions.add(new ActionStartFight()); while (order.getTurn() <= MAX_TURNS) { auto entity = order.current(); Simulator::entity = entity; vm.output = entity->debug_output; vm_v1.output = entity->debug_output; LOG << "Turn of " << entity->name << " (" << entity->id << "), AI " << entity->ai->name << "..." << std::endl; actions.add(new ActionEntityTurn(entity)); entity->start_turn(); try { entity->ai->execute(vm, vm_v1); } catch (ls::vm::ExceptionObj* ex) { LOG << ex->to_string(true); // vm.last_exception = nullptr; // vm_v1.last_exception = nullptr; if (ex->type == ls::vm::Exception::OPERATION_LIMIT_EXCEEDED) { actions.add(new ActionAIError(entity)); // TODO Add Breaker Trophy } // TODO delete ex } vm.operations = 0; entity->end_turn(); actions.add(new ActionEndTurn(entity)); if (is_finished()) { break; } if (order.next()) { actions.add(new ActionNewTurn(order.getTurn())); LOG << "Turn " << order.getTurn() << std::endl; } } Simulator::entity = nullptr; Report* report = new Report(this); report->actions = &actions; return report; }
/* * Perform the given action. */ static void perform_act(game *g, action a) { player *p, *opp; int old_phase; /* Get player pointers */ p = &g->p[g->turn]; opp = &g->p[!g->turn]; /* Remember current phase */ old_phase = p->phase; /* Switch on action */ switch (a.act) { /* No action, advance phase counter */ case ACT_NONE: { /* Advance phase counter */ p->phase++; /* Take care of bookkeeping */ switch (old_phase) { /* Start of turn */ case PHASE_START: /* Start turn */ start_turn(g); /* Done */ break; /* After support/booster */ case PHASE_AFTER_SB: /* End support phase */ end_support(g); /* Done */ break; /* Refresh hand */ case PHASE_REFRESH: /* Refresh */ refresh_phase(g); /* Done */ break; /* End of turn */ case PHASE_END: /* End current turn */ end_turn(g); /* Done */ break; /* Move to next player */ case PHASE_OVER: /* Player is done */ p->phase = PHASE_NONE; /* Next player */ g->turn = !g->turn; /* Start opponent's turn */ opp->phase = PHASE_START; /* Done (completely) */ return; } /* Clear last played card */ p->last_played = 0; /* Done */ break; } /* Retreat */ case ACT_RETREAT: { /* Retreat */ retreat(g); /* Done */ break; } /* Retrieve a card */ case ACT_RETRIEVE: { /* Set last played */ p->last_played = a.index; /* Retrieve card */ retrieve_card(g, a.arg); /* Done */ break; } /* Play a card */ case ACT_PLAY: { /* Set last played */ p->last_played = a.index; /* Play card */ play_card(g, a.arg, 0, 0); /* Done */ break; } /* Play a card without special effect */ case ACT_PLAY_NO: { /* Set last played */ p->last_played = a.index; /* Play card */ play_card(g, a.arg, 1, 0); /* Done */ break; } /* Announce power */ case ACT_ANN_FIRE: case ACT_ANN_EARTH: { /* Increment phase counter */ p->phase++; /* Announce power */ announce_power(g, a.act - ACT_ANN_FIRE); /* Done */ break; } /* Use card special power */ case ACT_USE: { /* Use card */ use_special(g, a.arg); /* Done */ break; } /* Satisfy opponent's "discard" card */ case ACT_SATISFY: { /* Satisfy requirement */ satisfy_discard(g, a.arg); /* Done */ break; } /* Land a ship */ case ACT_LAND: { /* Set last played */ p->last_played = a.index; /* Land ship */ land_ship(g, a.arg); /* Done */ break; } /* Load a card onto a ship */ case ACT_LOAD: { /* Set last played */ p->last_played = a.index; /* Load card */ load_card(g, a.arg, a.target); /* Done */ break; } /* Play a bluff card */ case ACT_BLUFF: { /* Set last played */ p->last_played = a.index; /* Play bluff */ play_bluff(g, a.arg); /* Done */ break; } /* Reveal a bluff card */ case ACT_REVEAL: { /* Set last played */ p->last_played = a.index; /* Reveal bluff */ reveal_bluff(g, g->turn, a.arg); /* Done */ break; } } }
/* * Main function to run one tick of the game */ int run_game() { cli(); /* Movement phase */ if(game_state == 1) { /* Move the reticule based on readings from the rotary encoder */ int16_t newRX = (playersX[current_player] + (RETICULE_DISTANCE * ml_cos(position))/100); int16_t newRY = (playersY[current_player] + (RETICULE_DISTANCE * ml_sin(position))/100); rectangle reticuleOld = {reticuleX - reticule_SPR->width / 2, reticuleX + reticule_SPR->width / 2 - 1, reticuleY - reticule_SPR->height / 2, reticuleY + reticule_SPR->height / 2 - 1}; draw_background(level_map, SILVER, reticuleOld, HEIGHT_NO_UI, WIDTH); //fill_rectangle(reticuleOld, BLACK); if(newRX >= 0 && newRX < WIDTH && newRY >= 0 && newRY < HEIGHT) fill_sprite(reticule_SPR, newRX, newRY, HEIGHT_NO_UI, WIDTH); reticuleX = newRX; reticuleY = newRY; /* Read firing input */ if (get_switch_long(_BV(SWC))) { game_state = 3; return 0; } /* Read directional input.*/ if (get_switch_rpt(_BV(SWE))) { direction = 4; free_sprite(player_SPR); player_SPR = botright(current_player); } else if (get_switch_rpt(_BV(SWW))) { direction = -4; free_sprite(player_SPR); player_SPR = botleft(current_player); } else { direction = 0; return 0; } /* Move the player */ int16_t newX = playersX[current_player] + direction; int16_t newY = ml_min(level_map[newX - PLAYER_WIDTH], level_map[newX + PLAYER_WIDTH - 1]) - PLAYER_HEIGHT; int8_t i; int8_t player_collision = 0; for(i = 0; i < players; i++) { if(players_HP[i] != 0 && i != current_player && newX >= playersX[i] - PLAYER_WIDTH && newX <= playersX[i] + PLAYER_WIDTH - 1) { player_collision = 1; break; } } /* Cancel movement if trying to climb to high or off the sides of the screen */ if(player_collision || playersY[current_player] - newY > MAX_CLIMB_HEIGHT || newX <= PLAYER_WIDTH || newX + PLAYER_WIDTH > WIDTH) { /* DEBUGGING prints: */ //display_string_xy("Error\n", 10, 10); //ml_printf("Cannot move there... (%u,%u) to (%u,%u)", playersX[0], playersY[0], newX, newY); return 0; } rectangle playerOld = {playersX[current_player] - PLAYER_WIDTH, playersX[current_player] + PLAYER_WIDTH - 1, playersY[current_player] - PLAYER_HEIGHT, playersY[current_player] + PLAYER_HEIGHT - 1}; fill_rectangle(playerOld, BLACK); fill_sprite(player_SPR, newX, newY, HEIGHT_NO_UI, WIDTH); playersX[current_player] = newX; playersY[current_player] = newY; } /* Projectile phase (missile in the air) */ else if(game_state == 2) { /* Find the new position of the projectile */ int16_t newX = projectileX + proVelX/1000; int16_t newY = projectileY + proVelY/1000; rectangle projectileOld = {projectileX, projectileX, projectileY, projectileY}; fill_rectangle(projectileOld, BLACK); /* End turn if projectile goes off the sides of the level */ if(newX < 0 || newX > WIDTH) { start_turn(); return 0; } /* If the new position is in the ground, EXPLODE! * (Also, if off the bottom of the level) */ if(newY >= level_map[newX] || newY > HEIGHT_NO_UI) { int i; for(i = -EXPLOSION_RADIUS; i <= EXPLOSION_RADIUS; i++) { /* Make sure that terrain is on screen */ if(newX + i >= 0 && newX + i < WIDTH) { uint16_t new_ground_level = newY + ml_sqrt(EXPLOSION_RADIUS * EXPLOSION_RADIUS - i * i); if(new_ground_level >= HEIGHT_NO_UI) level_map[newX + i] = HEIGHT_NO_UI - 1; //Clamp to bottom of level else if(new_ground_level > level_map[newX + i]) //Don't raise the ground level! level_map[newX + i] = new_ground_level; } } draw_level(level_map, SILVER, newX - EXPLOSION_RADIUS, newX + EXPLOSION_RADIUS); /* Check players for damage + redraw them */ for(i = 0; i < players; i++) { int16_t deltaX = playersX[i] - newX; int16_t deltaY = playersY[i] - newY; int16_t distance = ml_sqrt(deltaX * deltaX + deltaY * deltaY); if(distance < BLAST_RADIUS) { int16_t damage = ml_clamp(0, players_HP[i], MAX_EXPLOSION_DAMAGE - (distance * MAX_EXPLOSION_DAMAGE / BLAST_RADIUS)); players_HP[i] -= damage; } rectangle playerOld = {playersX[i] - PLAYER_WIDTH, playersX[i] + PLAYER_WIDTH - 1, playersY[i] - PLAYER_HEIGHT, playersY[i] + PLAYER_HEIGHT - 1}; fill_rectangle(playerOld, BLACK); /* If the player is still alive, redraw them */ if(players_HP[i] != 0) { free_sprite(player_SPR); player_SPR = botleft(i); playersY[i] = ml_min(level_map[playersX[i] - PLAYER_WIDTH], level_map[playersX[i] + PLAYER_WIDTH - 1]) - PLAYER_HEIGHT; fill_sprite(player_SPR, playersX[i], playersY[i], HEIGHT_NO_UI, WIDTH); } } start_turn(); return 0; } rectangle projectileNew = {newX, newX, newY, newY}; /* Continue turn if off top of level, but only draw if projectile onscreen */ if(newY >= 0) { fill_rectangle(projectileNew, RED); } projectileX = newX; projectileY = newY; /* Apply gravity and wind */ proVelY += 500; proVelX += (wind_velocity - 10) * 50; } /* Charging phase */ else if(game_state == 3) { /* Increase the launch speed while the fire button is held */ if (get_switch_state(_BV(SWC))) { launch_speed++; rectangle bar = {POWER_BAR_START, POWER_BAR_START + launch_speed, 231, 236}; fill_rectangle(bar, WHITE); /* Fire once max power is reached */ if(launch_speed == 100) fire_projectile(); } else { fire_projectile(); } } /* Menu phase */ else if(game_state == 4) { if (get_switch_press(_BV(SWE))) { if(players == 4) players = 2; else players++; } else if (get_switch_press(_BV(SWW))) { if(players == 2) players = 4; else players--; } ml_printf_at("< %u Players >", 5, 115, players); if (get_switch_long(_BV(SWC))) { start_game(); } } /* End game phase */ else if(game_state == 5) { if (get_switch_long(_BV(SWC))) { start_menu(); } } sei(); return 0; }