diff --git a/assets/lucker/A_Button.png b/assets/lucker/A_Button.png new file mode 100644 index 00000000..2ec354c2 Binary files /dev/null and b/assets/lucker/A_Button.png differ diff --git a/assets/lucker/Bite.wav b/assets/lucker/Bite.wav new file mode 100644 index 00000000..933aa2df Binary files /dev/null and b/assets/lucker/Bite.wav differ diff --git a/assets/lucker/Cheer.wav b/assets/lucker/Cheer.wav new file mode 100644 index 00000000..587b2e10 Binary files /dev/null and b/assets/lucker/Cheer.wav differ diff --git a/assets/lucker/L_Button.png b/assets/lucker/L_Button.png new file mode 100644 index 00000000..39e8702f Binary files /dev/null and b/assets/lucker/L_Button.png differ diff --git a/assets/lucker/Periander.wav b/assets/lucker/Periander.wav new file mode 100644 index 00000000..05f1720d Binary files /dev/null and b/assets/lucker/Periander.wav differ diff --git a/assets/lucker/R_Button.png b/assets/lucker/R_Button.png new file mode 100644 index 00000000..1e640e04 Binary files /dev/null and b/assets/lucker/R_Button.png differ diff --git a/assets/lucker/Slot_Bomb.png b/assets/lucker/Slot_Bomb.png new file mode 100644 index 00000000..e871d377 Binary files /dev/null and b/assets/lucker/Slot_Bomb.png differ diff --git a/assets/lucker/Slot_Burst.png b/assets/lucker/Slot_Burst.png new file mode 100644 index 00000000..fe9bdcbe Binary files /dev/null and b/assets/lucker/Slot_Burst.png differ diff --git a/assets/lucker/Slot_Crit.png b/assets/lucker/Slot_Crit.png new file mode 100644 index 00000000..86f4495c Binary files /dev/null and b/assets/lucker/Slot_Crit.png differ diff --git a/assets/lucker/Slot_Evade.png b/assets/lucker/Slot_Evade.png new file mode 100644 index 00000000..43b11db6 Binary files /dev/null and b/assets/lucker/Slot_Evade.png differ diff --git a/assets/lucker/Slot_EvadeRemove.png b/assets/lucker/Slot_EvadeRemove.png new file mode 100644 index 00000000..a5f0518c Binary files /dev/null and b/assets/lucker/Slot_EvadeRemove.png differ diff --git a/assets/lucker/Slot_Heart.png b/assets/lucker/Slot_Heart.png new file mode 100644 index 00000000..d00f4234 Binary files /dev/null and b/assets/lucker/Slot_Heart.png differ diff --git a/assets/lucker/Slot_Lightning.png b/assets/lucker/Slot_Lightning.png new file mode 100644 index 00000000..ccf12b5f Binary files /dev/null and b/assets/lucker/Slot_Lightning.png differ diff --git a/assets/lucker/Slot_Run.wav b/assets/lucker/Slot_Run.wav new file mode 100644 index 00000000..206973bf Binary files /dev/null and b/assets/lucker/Slot_Run.wav differ diff --git a/assets/lucker/Slot_Start.wav b/assets/lucker/Slot_Start.wav new file mode 100644 index 00000000..62bcb071 Binary files /dev/null and b/assets/lucker/Slot_Start.wav differ diff --git a/assets/lucker/Slot_Swap.png b/assets/lucker/Slot_Swap.png new file mode 100644 index 00000000..d4285cbc Binary files /dev/null and b/assets/lucker/Slot_Swap.png differ diff --git a/assets/lucker/Slot_Sword.png b/assets/lucker/Slot_Sword.png new file mode 100644 index 00000000..7856ffb9 Binary files /dev/null and b/assets/lucker/Slot_Sword.png differ diff --git a/assets/lucker/Slot_Vampirism.png b/assets/lucker/Slot_Vampirism.png new file mode 100644 index 00000000..ecd0a526 Binary files /dev/null and b/assets/lucker/Slot_Vampirism.png differ diff --git a/assets/lucker/Slot_Win.wav b/assets/lucker/Slot_Win.wav new file mode 100644 index 00000000..bbe0ad0f Binary files /dev/null and b/assets/lucker/Slot_Win.wav differ diff --git a/assets/lucker/m6x11plus.ttf b/assets/lucker/m6x11plus.ttf new file mode 100644 index 00000000..17deaf66 Binary files /dev/null and b/assets/lucker/m6x11plus.ttf differ diff --git a/assets/lucker/snake.glb b/assets/lucker/snake.glb new file mode 100644 index 00000000..6b67cae2 Binary files /dev/null and b/assets/lucker/snake.glb differ diff --git a/assets/lucker/wheel.glb b/assets/lucker/wheel.glb new file mode 100644 index 00000000..ae7ce117 Binary files /dev/null and b/assets/lucker/wheel.glb differ diff --git a/code/lucker/.vs/ProjectSettings.json b/code/lucker/.vs/ProjectSettings.json new file mode 100644 index 00000000..0cf5ea50 --- /dev/null +++ b/code/lucker/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "No Configurations" +} \ No newline at end of file diff --git a/code/lucker/.vs/VSWorkspaceState.json b/code/lucker/.vs/VSWorkspaceState.json new file mode 100644 index 00000000..6b611411 --- /dev/null +++ b/code/lucker/.vs/VSWorkspaceState.json @@ -0,0 +1,6 @@ +{ + "ExpandedNodes": [ + "" + ], + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/code/lucker/.vs/lucker/v16/.suo b/code/lucker/.vs/lucker/v16/.suo new file mode 100644 index 00000000..fe7c9dc3 Binary files /dev/null and b/code/lucker/.vs/lucker/v16/.suo differ diff --git a/code/lucker/.vs/lucker/v16/Browse.VC.db b/code/lucker/.vs/lucker/v16/Browse.VC.db new file mode 100644 index 00000000..b189e191 Binary files /dev/null and b/code/lucker/.vs/lucker/v16/Browse.VC.db differ diff --git a/code/lucker/.vs/slnx.sqlite b/code/lucker/.vs/slnx.sqlite new file mode 100644 index 00000000..25ea9e8a Binary files /dev/null and b/code/lucker/.vs/slnx.sqlite differ diff --git a/code/lucker/abilities.c b/code/lucker/abilities.c new file mode 100644 index 00000000..26a3f703 --- /dev/null +++ b/code/lucker/abilities.c @@ -0,0 +1,79 @@ +#include +#include "../../core.h" +#include "../../minigame.h" +#include "lucker.h" +#include "abilities.h" +#include +#include +#include +#include +#include +#include + +//this will be called first so that +void ability_init() +{ + //!!!! bomb model? +} + +void sword(player* p) +{ + p->fighter.abilityActive[SWORD] = true; + p->fighter.abilityTimers[SWORD] = 0; +} + +void heart(player* p) +{ + p->fighter.hp += 50; +} + +void bomb(player* src, player* dest) +{ + //!!!! do this later, need to spawn bomb on src + dest->fighter.hp = 0; + dest->fighter.color = RGBA32(0,0,0,1); + dest->fighter.boom = true; +} + +void swap_hp(player* src, player* dest) +{ + float tempHP = src->fighter.hp; + src->fighter.hp = dest->fighter.hp; + dest->fighter.hp = tempHP; +} + +void evade_remove(player* p) +{ + p->fighter.abilityActive[EVADE_REMOVE] = true; + p->fighter.abilityTimers[EVADE_REMOVE] = 0; +} + +void burst(player* p) +{ + p->fighter.abilityActive[BURST] = true; + p->fighter.abilityTimers[BURST] = 0; +} + +void crit(player* p) +{ + p->fighter.abilityActive[CRIT] = true; + p->fighter.abilityTimers[CRIT] = 0; +} + +void vampirism(player* p) +{ + p->fighter.abilityActive[VAMPIRISM] = true; + p->fighter.abilityTimers[VAMPIRISM] = 0; +} + +void lightning(player* p) +{ + p->fighter.abilityActive[LIGHTNING] = true; + p->fighter.abilityTimers[LIGHTNING] = 0; +} + +void evade(player* p) +{ + p->fighter.abilityActive[EVADE] = true; + p->fighter.abilityTimers[EVADE] = 0; +} \ No newline at end of file diff --git a/code/lucker/abilities.h b/code/lucker/abilities.h new file mode 100644 index 00000000..91eeab7a --- /dev/null +++ b/code/lucker/abilities.h @@ -0,0 +1,63 @@ +#include +#include "../../core.h" +#include "../../minigame.h" +#include "lucker.h" + +void sword(player* p); + +void heart(player* p); + +void bomb(player* src, player* dest); + +void swap_hp(player* src, player* dest); + +void evade_remove(player* p); + +void burst(player* p); + +void crit(player* p); + +void vampirism(player* p); + +void lightning(player* p); + +void evade(player* p); + +static inline void activate_ability(wheel selection, player* targ, player* other) +{ + switch (selection) + { + case SWORD: + sword(targ); + break; + case HEART: + heart(targ); + break; + case BOMB: + bomb(targ, other); + break; + case SWAP_HP: + swap_hp(targ, other); + break; + case EVADE_REMOVE: + evade_remove(targ); + break; + case BURST: + burst(targ); + break; + case CRIT: + crit(targ); + break; + case VAMPIRISM: + vampirism(targ); + break; + case LIGHTNING: + lightning(targ); + break; + case EVADE: + evade(targ); + break; + default: + return; + } +} \ No newline at end of file diff --git a/code/lucker/battle.c b/code/lucker/battle.c new file mode 100644 index 00000000..e9e2a1ba --- /dev/null +++ b/code/lucker/battle.c @@ -0,0 +1,485 @@ +#include +#include "../../core.h" +#include "../../minigame.h" +//#include "lucker.h" +#include "battle.h" +#include +#include +#include +#include +#include +#include + +//all chances are out of 100, so that we don't have to deal with floats +//when we gen random numbers we don't want to convert floats to values +#define DEFAULT_STUN_DURATION 1.5f +#define DEFAULT_ATTACK_CD .35f +#define DEFAULT_HP 425 +#define DEFAULT_CRIT_CHANCE 15 +#define DEFAULT_CRIT_MULT 2 +#define DEFAULT_EVADE_CHANCE 15 +#define DEFAULT_BASH_CHANCE 12 +#define DEFAULT_BASH_DAMAGE 20 +#define DEFAULT_DAMAGE 7 + +#define COMBAT_DEATH_DELAY 5 + +#define POWERBAR_WIDTH 50 +#define POWERBAR_HEIGHT 6 +#define POWERBAR_YOFFSET 110 + +player* currentBattlers[2]; + +float abilityCoolDowns[] = + { + 2, + 0, + 0, + 0, + 3, + 2, + 3, + 1.5f, + 3, + 3 + }; +const char messages[4][5] = +{ + " ", + "Crit!", + "Miss!", + "Stun!" +}; +bool isBattle; +bool isDead; +player* battleVictor; +float deathTimer; + +T3DVec3 fighter_start_positions[2] = +{ + (T3DVec3){{-100,-25,25}}, + (T3DVec3){{100,-25,25}}, +}; +T3DVec3 fighter_start_rotations[2] = +{ + (T3DVec3){{0,-1.5f,0}}, + (T3DVec3){{0,1.5f,0}}, +}; + +wav64_t sfx_bite; +wav64_t sfx_cheer; +wav64_t sfx_boom; + +void battle_init() +{ + wav64_open(&sfx_bite, "rom:/lucker/Bite.wav64"); + wav64_open(&sfx_cheer, "rom:/lucker/Cheer.wav64"); + wav64_open(&sfx_boom, "rom:/lucker/Periander.wav64"); +} +void battle_player_init (player *player, color_t color, T3DModel* model) +{ + player->fighter.fighterMatFP = malloc_uncached(sizeof(T3DMat4FP)); + + player->fighter.skel = t3d_skeleton_create(model); + player->fighter.color = color; + player->fighter.model = model; + //player->fighter.skelBlend = t3d_skeleton_clone(&player->fighter.skel, false); // optimized for blending, has no matrices + + // Now create animation instances (by name), the data in 'model' is fixed, + // whereas 'anim' contains all the runtime data. + // Note that tiny3d internally keeps no track of animations, it's up to the user to manage and play them. + player->fighter.animIdle = t3d_anim_create(model, "Snake_Idle"); + t3d_anim_attach(&player->fighter.animIdle, &player->fighter.skel); // tells the animation which skeleton to modify + + player->fighter.animWalk = t3d_anim_create(model, "Snake_Walk"); + t3d_anim_attach(&player->fighter.animWalk, &player->fighter.skel); + + player->fighter.animDeath = t3d_anim_create(model, "Snake_Death"); + t3d_anim_set_looping(&player->fighter.animDeath, false); // don't loop this animation + t3d_anim_set_playing(&player->fighter.animDeath, false); // start in a paused state + t3d_anim_set_speed(&player->fighter.animDeath, .65f); + t3d_anim_attach(&player->fighter.animDeath, &player->fighter.skel); + + player->fighter.animJump = t3d_anim_create(model, "Snake_Jump"); + t3d_anim_set_looping(&player->fighter.animJump, true); // don't loop this animation + t3d_anim_set_playing(&player->fighter.animJump, false); // start in a paused state + t3d_anim_attach(&player->fighter.animJump, &player->fighter.skel); + + // multiple animations can attach to the same skeleton, this will NOT perform any blending + // rather the last animation that updates "wins", this can be useful if multiple animations touch different bones + player->fighter.animAttack = t3d_anim_create(model, "Snake_Attack"); + t3d_anim_set_speed(&player->fighter.animAttack, 2.0f); + t3d_anim_set_looping(&player->fighter.animAttack, false); // don't loop this animation + t3d_anim_set_playing(&player->fighter.animAttack, false); // start in a paused state + t3d_anim_attach(&player->fighter.animAttack, &player->fighter.skel); + + //not using rspq block cuz we can't change color during block + /*rspq_block_begin(); + t3d_matrix_push(player->fighter.fighterMatFP); + rdpq_set_prim_color(b); + t3d_model_draw_skinned(model, &player->fighter.skel); // as in the last example, draw skinned with the main skeleton + + t3d_matrix_pop(1); + player->fighter.dplFighter = rspq_block_end();*/ + +} +void fighter_start (player* player, T3DVec3 pos, T3DVec3 rot) +{ + //hate this but needs to be done + const color_t colors[] = { + PLAYERCOLOR_1, + PLAYERCOLOR_2, + PLAYERCOLOR_3, + PLAYERCOLOR_4, + }; + player->fighter.pos = pos; + player->fighter.rot = rot; + + player->fighter.isAttack = false; + player->fighter.isStunned = false; + player->fighter.boom = false; + player->fighter.color = colors[player->playerNumber]; + player->fighter.hp = DEFAULT_HP; + +} + +void battle_start (player *playerOne, player *playerTwo) +{ + mixer_ch_set_vol(30, 0.15f, 0.15f); + isBattle = true; + isDead = false; + deathTimer = 0; + currentBattlers[0] = playerOne; + currentBattlers[1] = playerTwo; + + for (int i = 0; i < 2; i++) + { + currentBattlers[i]->fighter.onLeftSide = i == 0 ? true : false; + fighter_start(currentBattlers[i], fighter_start_positions[i], fighter_start_rotations[i]); + } +} +void ability_finish(player *source, int abilityIndex) +{ + source->fighter.abilityTimers[abilityIndex] = 0; + source->fighter.abilityActive[abilityIndex] = false; +} + +void add_message_to_board(player *p, int index) +{ + for (int i = 2; i >= 0; i--) + { + p->fighter.messageboard[i + 1] = p->fighter.messageboard[i]; + } + p->fighter.messageboard[0] = index; +} +void deal_damage(player *src, player* dst) +{ + int evasionRand = rand()%100; + if (!dst->fighter.abilityActive[EVADE_REMOVE]) + { + int evchnce = DEFAULT_EVADE_CHANCE + (dst->fighter.abilityActive[EVADE] * DEFAULT_EVADE_CHANCE); + //basically if the random number is below the evade chance (which could be boosted by buff) + if (evasionRand < evchnce) + { + add_message_to_board(dst,2); + return; + } + } + + //wouldn't be a lucker game if default damage didn't have some randomness to it + int damage = (DEFAULT_DAMAGE + (src->fighter.abilityActive[SWORD] * DEFAULT_DAMAGE)) + (rand()%4); + + int critRand = rand()%100; + if (critRand < (DEFAULT_CRIT_CHANCE + (dst->fighter.abilityActive[CRIT] *2* DEFAULT_CRIT_CHANCE))) + { + //CRITICAL HIT!!! + damage *= DEFAULT_CRIT_MULT; + dst->fighter.lastDamageCrit = damage; + add_message_to_board(dst,1); + } + + int bashRand = rand()%100; + + if (bashRand < (DEFAULT_BASH_CHANCE)) + { + damage += 20; + dst->fighter.isStunned = true; + add_message_to_board(dst,3); + } + + if (src->fighter.abilityActive[VAMPIRISM]) + { + //SUCK THE BLOOD + src->fighter.hp += damage; + } + + dst->fighter.hp -= damage; + wav64_play(&sfx_bite, 28); + + +} +void ability_fixedLoop(player *p, float dt) +{ + for (int i = 0; i < 10; i++) + { + if (abilityCoolDowns[i] == 0) + { + continue; + } + if (p->fighter.abilityActive[i]) + { + p->fighter.abilityTimers[i] += dt; + if (p->fighter.abilityTimers[i] >= abilityCoolDowns[i]) + { + ability_finish(p,i); + } + } + } +} +void battle_fixedLoop(float dt) +{ + if (isDead) + { + deathTimer += dt; + if (deathTimer >= COMBAT_DEATH_DELAY) + { + battle_end(battleVictor); + isDead = false; + } + return; + } + //happens every tick + //responsible for fighters attacking eachother! + //and for calculating ability timers and cd + for (int i = 0; i < 2; i++) { + if (currentBattlers[i]->fighter.hp <= 0) + { + isDead = true; + mixer_ch_set_vol(30, 0.0f, 0.0f); + + + if (currentBattlers[i]->fighter.boom) + { + wav64_play(&sfx_boom, 26); + } else + { + wav64_play(&sfx_cheer, 27); + } + //do these to make death anim play + currentBattlers[i]->fighter.isAttack = false; + currentBattlers[i]->fighter.isStunned = false; + currentBattlers[i]->fighter.abilityActive[LIGHTNING] = false; + player* p = currentBattlers[(i+1)%2]; + p->fighter.isAttack = false; + p->fighter.isStunned = false; + p->fighter.abilityActive[LIGHTNING] = false; + t3d_anim_set_playing(¤tBattlers[i]->fighter.animDeath, true); + t3d_anim_set_time(¤tBattlers[i]->fighter.animDeath, 0.0f); + t3d_anim_set_playing(&p->fighter.animJump, true); + t3d_anim_set_time(&p->fighter.animJump, 0.0f); + + battleVictor = p; + } + currentBattlers[i]->fighter.attackTimer += dt; + + ability_fixedLoop(currentBattlers[i], dt); + + //BASHED + if (currentBattlers[i]->fighter.isStunned) + { + currentBattlers[i]->fighter.stunTimer += dt; + if (currentBattlers[i]->fighter.stunTimer >= DEFAULT_STUN_DURATION) + { + currentBattlers[i]->fighter.stunTimer = 0; + currentBattlers[i]->fighter.isStunned = false; + } + } + + //STUNNED + if (currentBattlers[i]->fighter.abilityActive[LIGHTNING] || currentBattlers[i]->fighter.isStunned) + { + continue; + } + float cd = currentBattlers[i]->fighter.abilityActive[BURST] ? DEFAULT_ATTACK_CD / 2 : DEFAULT_ATTACK_CD; + if (currentBattlers[i]->fighter.attackTimer >= cd) + { + currentBattlers[i]->fighter.isAttack = true; + t3d_anim_set_playing(¤tBattlers[i]->fighter.animAttack, true); + t3d_anim_set_time(¤tBattlers[i]->fighter.animAttack, 0.0f); + deal_damage(currentBattlers[i], currentBattlers[(i+1)%2]); + currentBattlers[i]->fighter.attackTimer = 0; + } + } +} +void battle_draw() { + for (int i = 0; i < 2; i++) + { + //not using rspq block because we can't change color + //rspq_block_run(currentBattlers[i]->fighter.dplFighter); + t3d_matrix_push(currentBattlers[i]->fighter.fighterMatFP); + rdpq_set_prim_color(currentBattlers[i]->fighter.color); + t3d_model_draw_skinned(currentBattlers[i]->fighter.model, + ¤tBattlers[i]->fighter.skel); + t3d_matrix_pop(1); + } +} +void battle_ui(int fontIndex, T3DViewport viewport) +{ + //320x240 + rdpq_sync_tile(); + rdpq_sync_pipe(); + const int hp[2] = {15, 250}; + const int fullhp[2] = {15, 250}; + const int y = 15; + for (int i = 0; i < 2; i++) + { + + rdpq_textparms_t textparms = {.style_id = currentBattlers[i]->playerNumber}; + rdpq_text_printf(&textparms, fontIndex, hp[i], y, "P%d HP W:%d", + currentBattlers[i]->playerNumber+1, currentBattlers[i]->wins); + + + int curhp = (int)currentBattlers[i]->fighter.hp; + + //rdpq_textparms_t textparms = {.style_id = currentBattlers[i]->playerNumber}; + + rdpq_text_printf(&textparms, fontIndex, fullhp[i], y*2, "%d / %d",curhp, DEFAULT_HP); + + + /*T3DVec3 FiPos = (T3DVec3) + {{ + players[i].sl.pos[j].v[0], + players[i].sl.pos[1].v[1] + BILLBOARD_YOFFSET, + players[i].sl.pos[1].v[2] + }};*/ + T3DVec3 barPos = currentBattlers[i]->fighter.pos; + barPos.v[1] += POWERBAR_YOFFSET; + + T3DVec3 billboardScreenPos; + t3d_viewport_calc_viewspace_pos(&viewport, &billboardScreenPos, &barPos); + + int x = floorf(billboardScreenPos.v[0]) - 8; + int y = floorf(billboardScreenPos.v[1]); + + if (i == 1) + { + x -= 30; + } + + rdpq_mode_combiner(RDPQ_COMBINER_FLAT); + float t = (float)curhp/DEFAULT_HP; + + //r is the final value and g is the starting value + int r = floorf(t*(-255) + 255); + int g = floorf((float)(t*255)); + + rdpq_set_prim_color(RGBA32(r,g,0,255)); + int width = (curhp*POWERBAR_WIDTH)/DEFAULT_HP; + if (width > POWERBAR_WIDTH) width = POWERBAR_WIDTH; + rdpq_fill_rectangle(x, y, x+width, (y)+POWERBAR_HEIGHT); + + y -= 10; + x += 5; + for (int j = 0; j < 4; j++) + { + //rdpq_text_printf(&textparms, fontIndex, x, y - (j * 15), "test"); + + //rdpq_text_printf(&textparms, fontIndex, x, y - (j * 15), + //messages[currentBattlers[i]->fighter.messageboard[0]]); + + /*if (currentBattlers[i]->fighter.messageboard[j] == 1) + { + //rdpq_text_printf(&textparms, fontIndex, x, y - (j * 15), "%d", + //currentBattlers[i]->fighter.lastDamageCrit); + } else + { + + }*/ + if (currentBattlers[i]->fighter.messageboard[j] == 1) + { + rdpq_text_printf(&textparms, fontIndex, x, y - (j * 15), + "%d!",currentBattlers[i]->fighter.lastDamageCrit); + } else + { + rdpq_text_printn(&textparms, fontIndex, x, y - (j * 15), + messages[currentBattlers[i]->fighter.messageboard[j]], sizeof(char)*5); + } + } + } + +} +void battle_loop(float dt, rspq_syncpoint_t syncPoint) +{ + //happens every frame + //draw the mayhem! + //respoinsible for drawing bots and animating slots + if (isBattle) + { + for (int i = 0; i < 2; i++) + { + + if (currentBattlers[i]->fighter.abilityActive[LIGHTNING] || currentBattlers[i]->fighter.isStunned) + { + t3d_anim_update(¤tBattlers[i]->fighter.animWalk, dt); + } else if(currentBattlers[i]->fighter.isAttack) + { + t3d_anim_update(¤tBattlers[i]->fighter.animAttack, dt); // attack animation now overrides the idle one + if(!currentBattlers[i]->fighter.animAttack.isPlaying) + { + currentBattlers[i]->fighter.isAttack = false; + } + } else if (currentBattlers[i]->fighter.hp <= 0) + { + t3d_anim_update(¤tBattlers[i]->fighter.animDeath, dt); + } else if (isDead && currentBattlers[i]->fighter.hp > 0) + { + t3d_anim_update(¤tBattlers[i]->fighter.animJump, dt); + } else + { + t3d_anim_update(¤tBattlers[i]->fighter.animIdle, dt); + } + if(syncPoint)rspq_syncpoint_wait(syncPoint); // wait for the RSP to process the previous frame + t3d_skeleton_update(¤tBattlers[i]->fighter.skel); + + t3d_mat4fp_from_srt_euler(currentBattlers[i]->fighter.fighterMatFP, + (float[3]){.5f, .5f, .5f}, + currentBattlers[i]->fighter.rot.v, + currentBattlers[i]->fighter.pos.v + ); + } + } + +} +//return the left palyer if true, otherwise return the right player +player* get_current_player(bool left) +{ + return left ? currentBattlers[0] : currentBattlers[1]; +} + +void fighter_cleanup(player *player) +{ + //rspq_block_free(player->fighter.dplFighter); + + t3d_skeleton_destroy(&player->fighter.skel); + t3d_skeleton_destroy(&player->fighter.skelBlend); + + t3d_anim_destroy(&player->fighter.animIdle); + t3d_anim_destroy(&player->fighter.animWalk); + t3d_anim_destroy(&player->fighter.animDeath); + t3d_anim_destroy(&player->fighter.animJump); + + free_uncached(player->fighter.fighterMatFP); +} + +void battle_cleanup() +{ + wav64_close(&sfx_bite); + wav64_close(&sfx_cheer); + wav64_close(&sfx_boom); +} + +void battle_end(player *victor) +{ + victor->wins++; + isBattle = false; +} \ No newline at end of file diff --git a/code/lucker/battle.h b/code/lucker/battle.h new file mode 100644 index 00000000..4977ded8 --- /dev/null +++ b/code/lucker/battle.h @@ -0,0 +1,45 @@ +#ifndef BATTLE_H +#define BATTLE_H +#include +#include "../../core.h" +#include "../../minigame.h" + + +#include "lucker.h" // Include the header for the function declaration + + +#include +#include +#include +#include +#include +#include + +extern bool isBattle; + +extern bool isDead; + +void battle_init(); + +void battle_cleanup(); + +void battle_end(player *victor); + +void battle_fixedLoop(float dt); + +void battle_loop(float dt, rspq_syncpoint_t syncPoint); + +void battle_draw(); + +void fighter_cleanup(player *player); + +void battle_start(player *playerOne, player *playerTwo); + +void battle_player_init (player *player, color_t color, T3DModel *model); + +void battle_ui(int fontIndex, T3DViewport viewport); + +player* get_current_player(bool left); + +#endif // BATTLE_H +//#include "lucker.h" \ No newline at end of file diff --git a/code/lucker/lucker.c b/code/lucker/lucker.c new file mode 100644 index 00000000..553093e1 --- /dev/null +++ b/code/lucker/lucker.c @@ -0,0 +1,709 @@ +//#include +#include +#include "../../core.h" +#include "../../minigame.h" +#include "lucker.h" +#include "battle.h" +#include "abilities.h" +#include +#include +#include +#include +#include +#include + +#define GAME_BACKGROUND 0x000000FF + +#define SLOT_ROTATION_SPEED 500 +#define SLOT_TIMER 3 +#define SLOT_START_ROT_DEGREES 36 +//!!!! change this back to an appropriate number like 5 +#define GAME_START_TIMER .25f + +//3 +#define COMBAT_COUNTDOWN_DELAY .25f + +#define GAME_OVER_DELAY 3 + +#define FONT_TEXT 1 +#define FONT_BILLBOARD 2 +#define TEXT_COLOR 0x6CBB3CFF +#define TEXT_OUTLINE 0x30521AFF +#define BILLBOARD_YOFFSET 20 + +const MinigameDef minigame_def = { + .gamename = "Lucker's Arena", + .developername = "Tanner P (Ritter7124)", + .description = "This game is a homage to a mini game of a custom game in Warcraft3 Frozen Throne", + .instructions = "Spin to win!" +}; + +surface_t* depthBuffer; +T3DViewport viewport; +rdpq_font_t *font; +rdpq_font_t *fontBillboard; +sprite_t *AButton; +sprite_t *LButton; +sprite_t *RButton; +T3DModel* wheelModel; +T3DModel* fighterModel; +T3DMat4* scaleMat; +T3DMat4* xRMat; +T3DMat4* yRMat; +T3DMat4* zRMat; +//T3DMat4FP* modelMatFP; +T3DVec3 lightDirVec; + +T3DVec3 camPos = { {0, 125.0f, 200.0f} }; +T3DVec3 camTarget = { {0, 0, 0} }; +float gameTimer; +float combatTimer; + +rspq_syncpoint_t syncPoint; + +player players[4]; +PlyNum winner; + +wav64_t sfx_slot_start; +wav64_t sfx_slot_run; +wav64_t sfx_slot_win; + + +const int battleSequence[6][2] = { + {0,1}, + {2,3}, + {0,2}, + {1,3}, + {0,3}, + {1,2} +}; +int tieFighterindex[3]; +uint8_t sequenceIndex; +int sequenceLimit; +bool gameOver; + +void slot_init (player *player, color_t color, T3DVec3 pos[], T3DVec3 rot[]) { + player->sl.firstMatFP = malloc_uncached(sizeof(T3DMat4FP)); + player->sl.secondMatFP = malloc_uncached(sizeof(T3DMat4FP)); + player->sl.thirdMatFP = malloc_uncached(sizeof(T3DMat4FP)); + scaleMat = malloc_uncached(sizeof(T3DMat4)); + xRMat = malloc_uncached(sizeof(T3DMat4)); + yRMat = malloc_uncached(sizeof(T3DMat4)); + zRMat = malloc_uncached(sizeof(T3DMat4)); + + for (int i = 0; i < 3; i++) { + player->sl.pos[i] = pos[player->playerNumber * 3 + i]; + player->sl.rot[i] = rot[player->playerNumber * 3 + i]; + } + + player->sl.isSpinning = false; + player->sl.slotTimer = 0; + + player->fighter.lastDamageCrit = 0; + + rspq_block_begin(); + + t3d_matrix_push(player->sl.firstMatFP); + rdpq_set_prim_color(color); + t3d_model_draw(wheelModel); + t3d_matrix_pop(1); + + t3d_matrix_push(player->sl.secondMatFP); + rdpq_set_prim_color(color); + t3d_model_draw(wheelModel); + t3d_matrix_pop(1); + + t3d_matrix_push(player->sl.thirdMatFP); + rdpq_set_prim_color(color); + t3d_model_draw(wheelModel); + t3d_matrix_pop(1); + + player->sl.dplWheel = rspq_block_end(); + +} + +void player_init(player *player, bool human) +{ + player->isHuman = human; + player->wins = 0; +} + + +void minigame_init() +{ + const color_t colors[] = { + PLAYERCOLOR_1, + PLAYERCOLOR_2, + PLAYERCOLOR_3, + PLAYERCOLOR_4, + }; + T3DVec3 start_positions[] = { + (T3DVec3){{-98,30,159}}, //first + (T3DVec3){{-84,30,157}}, + (T3DVec3){{-71,30,154}}, + (T3DVec3){{-38,31,152}}, //second + (T3DVec3){{-26,31,151}}, + (T3DVec3){{-14,31,150}}, + (T3DVec3){{18,31,150}}, //third + (T3DVec3){{29,31,151}}, + (T3DVec3){{41,31,152}}, + (T3DVec3){{73,30,153}}, //last + (T3DVec3){{85,30,156}}, + (T3DVec3){{98,30,158}}, + }; + float x = T3D_DEG_TO_RAD(SLOT_START_ROT_DEGREES); + T3DVec3 wheel_rot_sets[] = { + (T3DVec3){{x, -.56f, .4f}}, + (T3DVec3){{x, -.555f, .4f}}, + (T3DVec3){{x, -.55f, .4f}}, + (T3DVec3){{x,-.2f,.2f}}, + (T3DVec3){{x,-.2f,.2f}}, + (T3DVec3){{x,-.2f,.2f}}, + (T3DVec3){{x,.2f,-.2f}}, + (T3DVec3){{x,.2f,-.2f}}, + (T3DVec3){{x,.2f,-.175f}}, + (T3DVec3){{x, .5f, -.4f}}, + (T3DVec3){{x, .5f, -.4f}}, + (T3DVec3){{x, .5f, -.4f}}, + }; + + display_init(RESOLUTION_320x240, DEPTH_16_BPP, 3, GAMMA_NONE, FILTERS_RESAMPLE); + t3d_init((T3DInitParams) {}); + + //rdpq_debug_start(); + + //replace font stuff if I had more time + font = rdpq_font_load("rom:/lucker/m6x11plus.font64"); + rdpq_text_register_font(FONT_TEXT, font); + rdpq_font_style(font, 0, &(rdpq_fontstyle_t){.color = color_from_packed32(TEXT_COLOR) }); + + fontBillboard = rdpq_font_load("rom:/squarewave.font64"); + rdpq_text_register_font(FONT_BILLBOARD, fontBillboard); + for (size_t i = 0; i < MAXPLAYERS; i++) + { + rdpq_font_style(fontBillboard, i, &(rdpq_fontstyle_t){ .color = colors[i] }); + } + + lightDirVec = (T3DVec3){ {1.0f, 1.0f, 1.0f} }; + t3d_vec3_norm(&lightDirVec); + + depthBuffer = display_get_zbuf(); + viewport = t3d_viewport_create(); + wheelModel = t3d_model_load("rom:/lucker/wheel.t3dm"); + fighterModel = t3d_model_load("rom:/lucker/snake.t3dm"); + AButton = sprite_load("rom:/lucker/A_Button.sprite"); + LButton = sprite_load("rom:/lucker/L_Button.sprite"); + RButton = sprite_load("rom:/lucker/R_Button.sprite"); + + + sequenceIndex = 0; + sequenceLimit = 6; + + winner = -1; + gameTimer = 0; + combatTimer = 0; + gameOver = false; + + uint32_t playercount = core_get_playercount(); + for (int i = 0; i < 4; i++) + { + player_init(&players[i], i < playercount); + players[i].playerNumber = i; + slot_init(&players[i], colors[i], start_positions, wheel_rot_sets); + battle_player_init(&players[i], colors[i], fighterModel); + } + + battle_init(); + + syncPoint = 0; + wav64_open(&sfx_slot_start, "rom:/lucker/Slot_Start.wav64"); + wav64_open(&sfx_slot_run, "rom:/lucker/Slot_Run.wav64"); + wav64_open(&sfx_slot_win, "rom:/lucker/Slot_Win.wav64"); + + mixer_ch_set_vol(31, 0.5f, 0.5f); //slot start + mixer_ch_set_vol(30, 0.15f, 0.15f); //slot run + mixer_ch_set_vol(29, 0.3f, 0.3f); //slot win + mixer_ch_set_vol(28, 0.3f, 0.3f); //bites + mixer_ch_set_vol(27, 0.3f, 0.3f); //crits and cheers + mixer_ch_set_vol(26, 0.3f, 0.3f); +} +void wheel_matrix(player *plyr, T3DVec3 pos, T3DVec3 rot, T3DMat4FP *out) +{ + float rads0 = rot.v[0]; + float rads1 = rot.v[1]; + float rads2 = rot.v[2]; + float s = (plyr->playerNumber == 1 || plyr->playerNumber == 2) ? .175f : .25f; + *scaleMat = (T3DMat4){{ + {.175f, 0, 0, 0}, + {0, s, 0, 0}, + {0, 0, s, 0}, + {0, 0, 0, 1} + }}; + *xRMat = (T3DMat4){{ + {1, 0, 0, 0}, + {0, cos(rads0), -sin(rads0), 0}, + {0, sin(rads0), cos(rads0), 0}, + {0, 0, 0, 1} + }}; + *yRMat = (T3DMat4){{ + {cos(rads1), 0, sin(rads1), 0}, + {0, 1, 0, 0}, + {-sin(rads1), 0, cos(rads1), 0}, + {0, 0, 0, 1} + }}; + *zRMat = (T3DMat4){{ + {cos(rads2), -sin(rads2), 0, 0}, + {sin(rads2), cos(rads2), 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1} + }}; + //this funciton seems to multiply B*A meaning, that since our rots are non commutative + //that we should have xRMat be the first 'b' matrix + t3d_mat4_mul(xRMat, xRMat, scaleMat); + t3d_mat4_mul(xRMat, yRMat, xRMat); + t3d_mat4_mul(xRMat, zRMat, xRMat); + xRMat->m[3][0] = pos.v[0]; + xRMat->m[3][1] = pos.v[1]; + xRMat->m[3][2] = pos.v[2]; + t3d_mat4_to_fixed_3x4(out, xRMat); +} +void spin_slot(player *plyr, float dt) +{ + plyr->sl.slotTimer += dt; + //slotTimer[plyr->playerNumber] += dt; + + for (int i = 0; i < 3; i++) + { + if (plyr->sl.slotTimer >= (SLOT_TIMER - (1 + (.5f * i))) && !plyr->sl.finished[i]) + { + //float rad = T3D_DEG_TO_RAD(deca(plyr->sl.currentSelection[i])); + int deg = rad_to_deg(plyr->sl.rot[i].v[0]); + int final = deca(plyr->sl.currentSelection[i])%360; + if (deg >= (final - 16) && deg <= (final + 16)) + { + plyr->sl.rot[i].v[0] = T3D_DEG_TO_RAD(final); + plyr->sl.finished[i] = true; + continue; + } + } + if (!plyr->sl.finished[i]) + { + plyr->sl.rot[i].v[0] += T3D_DEG_TO_RAD(SLOT_ROTATION_SPEED * dt); + } + + } + if (plyr->sl.finished[0] && plyr->sl.finished[1] && plyr->sl.finished[2]) + { + plyr->sl.isSpinning = false; + //if all slots are the same + if (plyr->sl.currentSelection[0] == plyr->sl.currentSelection[1] && plyr->sl.currentSelection[1] == plyr->sl.currentSelection[2]) + { + player* target = get_current_player(plyr->sl.left); + player* other = get_current_player(!plyr->sl.left); + activate_ability(plyr->sl.currentSelection[0], target, other); + if (plyr->sl.currentSelection[0] != BOMB) + { + wav64_play(&sfx_slot_win, 29); + } + //activate_ability(HEART, target, other); + } + } +} +void slot_settle(player *plyr, int value) +{ + wav64_play(&sfx_slot_start, 31); + wav64_play(&sfx_slot_run, 30); + for(int i = 0; i < 3; i++) + { + plyr->sl.finished[i] = false; + if (value == -1) + { + if (i == 1) + { + plyr->sl.currentSelection[1] = (plyr->sl.currentSelection[0] + 2)%10; + } else + { + plyr->sl.currentSelection[i] = rand()%10; + } + } else + { + plyr->sl.currentSelection[i] = value; + } + } +} +void player_loop(player *plyr, float deltaTime, joypad_port_t port) +{ + if (isBattle && !isDead) + { + if (plyr->isHuman) { + joypad_buttons_t btn = joypad_get_buttons_pressed(port); + if (!plyr->sl.isSpinning) + { + if (btn.a) + { + //start spinning + plyr->sl.isSpinning = true; + plyr->sl.slotTimer = 0; + int value = randomSelection(); + //value = LIGHTNING; //for testing purposes + //value = HEART; + slot_settle(plyr, value); + } + } else + { + spin_slot(plyr, deltaTime); + } + if (btn.l && !plyr->sl.left) + { + plyr->sl.left = true; + } + if (btn.r && plyr->sl.left) + { + plyr->sl.left = false; + } + } else + { + if (!plyr->sl.isSpinning) + { + int r = rand()%100; + if (r < 2) //low odds but runs every frame + { + plyr->sl.isSpinning = true; + plyr->sl.slotTimer = 0; + plyr->sl.left = rand()%2; + int value = randomSelection(); + slot_settle(plyr, value); + } + } else + { + spin_slot(plyr, deltaTime); + } + } + } + + wheel_matrix(plyr, plyr->sl.pos[0], plyr->sl.rot[0], plyr->sl.firstMatFP); + wheel_matrix(plyr, plyr->sl.pos[1], plyr->sl.rot[1], plyr->sl.secondMatFP); + wheel_matrix(plyr, plyr->sl.pos[2], plyr->sl.rot[2], plyr->sl.thirdMatFP); +} + + +void player_fixedloop(player *player, float deltaTime, joypad_port_t port) +{ + +} +bool check_game_end() +{ + //first round robin + if (sequenceLimit == 6 && sequenceIndex == 6) + { + int zeroWinIndex = -1; + for (int i = 0; i < 4; i++) + { + //if a player has more than 3 wins the game is over, + if (players[i].wins == 3) + { + core_set_winner(players[i].playerNumber); + winner = players[i].playerNumber; + gameOver = true; + return true; + } + if (players[i].wins == 0) + { + zeroWinIndex = i; + } + } + if (zeroWinIndex != -1) + { + sequenceLimit = 3; + sequenceIndex = 0; + //for(int i = zeroWinIndex; ) + //we have a three way tie + tieFighterindex[0] = (zeroWinIndex + 1 )%4; + tieFighterindex[1] = (zeroWinIndex + 2 )%4; + tieFighterindex[2] = (zeroWinIndex + 3 )%4; + + battle_start(&players[tieFighterindex[0]], + &players[tieFighterindex[1]]); + return true; + } else + { + int firstIndex = -1; + int secondIndex = 0; + for (int i = 0; i < 4; i++) + { + if (players[i].wins == 2) + { + if (firstIndex == -1) + { + firstIndex = i; + continue; + } + secondIndex = i; + } + } + battle_start(&players[firstIndex], &players[secondIndex]); + //after this final battle, it will roll back around and find a player with 3 wins + return true; + //we have a two way tie + } + } + if (sequenceLimit == 3) + { + if (sequenceIndex < 3) + { + + int secondIndex = (sequenceIndex == 2) ? 2 : sequenceIndex + 1; + battle_start(&players[tieFighterindex[sequenceIndex>>1]], + &players[tieFighterindex[secondIndex]]); + return true; + } else + { + //another tie + if (players[tieFighterindex[0]].wins == players[tieFighterindex[1]].wins + && players[tieFighterindex[1]].wins == players[tieFighterindex[2]].wins) + { + sequenceIndex = 0; + battle_start(&players[tieFighterindex[0]], &players[tieFighterindex[1]]); + return true; + } else //we have a winner thank god + { + player lastWinner = players[tieFighterindex[0]]; + for (int i = 1; i < 3; i++) + { + if (players[tieFighterindex[i]].wins > lastWinner.wins) + { + lastWinner = players[tieFighterindex[i]]; + } + } + core_set_winner(lastWinner.playerNumber); + winner = lastWinner.playerNumber; + gameOver = true; + return true; + } + } + } + return false; +} +void minigame_fixedloop(float deltaTime) +{ + gameTimer += deltaTime; + if (gameTimer < GAME_START_TIMER) + { + //!!!! display splash screen + //do I have to display in minigame loop? + //I think so + //make it look like wc3 load screen + return; + } + + if (!isBattle) + { + combatTimer += deltaTime; + if (combatTimer >= COMBAT_COUNTDOWN_DELAY && !gameOver) + { + if (check_game_end()) + { + //if game over we set flags in function + //if tie we started a batte_start function inside check_game_end + } else + { + battle_start(&players[battleSequence[sequenceIndex][0]], + &players[battleSequence[sequenceIndex][1]]); + } + + combatTimer = 0; + if (sequenceIndex < 6) + { + sequenceIndex++; + } + } + } else + { + for (int i = 0; i < 4; i++) + { + player_fixedloop(&players[i], deltaTime, core_get_playercontroller(i)); + } + battle_fixedLoop(deltaTime); + } +} + +void draw_slot_ui() +{ + PlyNum fighterNumbers[2] = + { + get_current_player(true)->playerNumber, + get_current_player(false)->playerNumber + }; + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 3; j++) + { + T3DVec3 billboardPos = (T3DVec3){{ + players[i].sl.pos[j].v[0], + players[i].sl.pos[1].v[1] + BILLBOARD_YOFFSET, + players[i].sl.pos[1].v[2] + }}; + + T3DVec3 billboardScreenPos; + t3d_viewport_calc_viewspace_pos(&viewport, &billboardScreenPos, &billboardPos); + + int x = floorf(billboardScreenPos.v[0]); + int y = floorf(billboardScreenPos.v[1]); + + x -= (1 - i)*-10; + y -= 20; + + //the outer and middle slots need a few adjustments + if (i%3 == 0) + { + y -= 5; + } + + rdpq_sync_pipe(); // Hardware crashes otherwise + rdpq_sync_tile(); // Hardware crashes otherwise + + rdpq_set_mode_standard(); + rdpq_mode_alphacompare(1); + + switch (j) + { + case 0: + if (players[i].sl.left) + { + rdpq_text_printf(&(rdpq_textparms_t){ .style_id = fighterNumbers[j] }, + FONT_BILLBOARD, x, y, "P%d", fighterNumbers[j]+1); + } else + { + rdpq_sprite_blit(LButton,x-5,y-10,NULL); + } + + break; + case 1: + if (!players[i].sl.left) + { + rdpq_text_printf(&(rdpq_textparms_t){ .style_id = fighterNumbers[j] }, + FONT_BILLBOARD, x, y, "P%d", fighterNumbers[j]+1); + } else + { + rdpq_sprite_blit(RButton,x-5,y-10,NULL); + } + break; + case 2: + if (!players[i].sl.isSpinning) + { + rdpq_sprite_blit(AButton,x,y-10,NULL); + } + break; + } + } + } +} + +void minigame_loop(float deltaTime) +{ + uint8_t colorAmbient[4] = { 0xAA, 0xAA, 0xAA, 0xFF }; + uint8_t colorDir[4] = { 0xFF, 0xAA, 0xAA, 0xFF }; + + //viewport far plane was 160, but our camera is 235 units away, we want to capture origin + t3d_viewport_set_projection(&viewport, T3D_DEG_TO_RAD(90.0f), 20.0f, 250); + t3d_viewport_look_at(&viewport, &camPos, &camTarget, &(T3DVec3){ {0, 1, 0}}); + + + for (int i = 0; i < 4; i++) + { + player_loop(&players[i], deltaTime, core_get_playercontroller(i)); + } + battle_loop(deltaTime, syncPoint); + + // ======== Draw (3D) ======== // + rdpq_attach(display_get(), depthBuffer); + t3d_frame_start(); + t3d_viewport_attach(&viewport); + + t3d_screen_clear_color(RGBA32(224, 180, 96, 0xFF)); + t3d_screen_clear_depth(); + + t3d_light_set_ambient(colorAmbient); + t3d_light_set_directional(0, colorDir, &lightDirVec); + t3d_light_set_count(1); + + if (syncPoint)rspq_syncpoint_wait(syncPoint); + + if (gameTimer < GAME_START_TIMER) + { + //!!!! add splash screen here!!!! + } + if (isBattle) { + for (int i = 0; i < 4; i++) + { + rspq_block_run(players[i].sl.dplWheel); + //rspq_block_run(players[i]) + } + } + + if (isBattle || isDead) + { + battle_draw(); + } + syncPoint = rspq_syncpoint_new(); + + if (isBattle) + { + for (int i = 0; i < 4; i++) + { + draw_slot_ui(); + } + battle_ui(FONT_BILLBOARD, viewport); + } + if (gameOver) + { + rdpq_textparms_t textparms = { .align = ALIGN_CENTER, .width = 320, }; + rdpq_text_printf(&textparms, FONT_TEXT, 0, 100, "Player %d wins!", winner+1); + if (combatTimer >= GAME_OVER_DELAY) + { + minigame_end(); + } + } + + rdpq_sync_tile(); + rdpq_sync_pipe(); + + rdpq_detach_show(); +} +void player_cleanup(player *player) { + rspq_block_free(player->sl.dplWheel); + + free_uncached(player->sl.firstMatFP); + free_uncached(player->sl.secondMatFP); + free_uncached(player->sl.thirdMatFP); +} + +void minigame_cleanup() +{ + for (int i = 0; i < 4; i++) { + player_cleanup(&players[i]); + fighter_cleanup(&players[i]); + } + battle_cleanup(); + wav64_close(&sfx_slot_start); + wav64_close(&sfx_slot_run); + wav64_close(&sfx_slot_win); + + //free_uncached(modelMatFP); + free_uncached(scaleMat); + free_uncached(xRMat); + free_uncached(yRMat); + free_uncached(zRMat); + t3d_model_free(wheelModel); + t3d_model_free(fighterModel); + sprite_free(AButton); + sprite_free(LButton); + sprite_free(RButton); + rdpq_text_unregister_font(FONT_BILLBOARD); + rdpq_font_free(fontBillboard); + rdpq_text_unregister_font(FONT_TEXT); + rdpq_font_free(font); + t3d_destroy(); + display_close(); +} \ No newline at end of file diff --git a/code/lucker/lucker.h b/code/lucker/lucker.h new file mode 100644 index 00000000..8c5c41b9 --- /dev/null +++ b/code/lucker/lucker.h @@ -0,0 +1,144 @@ +#ifndef LUCKER_H +#define LUCKER_H +#include +#include "../../core.h" +#include "../../minigame.h" +#include +#include +#include +#include +#include +#include + + +typedef struct +{ + bool isSpinning; + float slotTimer; + int currentSelection[3]; + bool finished[3]; + + bool left; + + T3DVec3 pos[3]; + T3DVec3 rot[3]; + T3DMat4FP* firstMatFP; + T3DMat4FP* secondMatFP; + T3DMat4FP* thirdMatFP; + rspq_block_t *dplWheel; + +} slot; + +typedef struct +{ + T3DVec3 pos; + T3DVec3 rot; + T3DAnim animAttack; + T3DAnim animWalk; + T3DAnim animIdle; + T3DAnim animDeath; + T3DAnim animJump; + T3DSkeleton skelBlend; + T3DSkeleton skel; + T3DModel* model; + color_t color; + T3DMat4FP* fighterMatFP; + float animBlend; + bool onLeftSide; + bool boom; + + bool isAttack; + bool isStunned; + float stunTimer; + float attackTimer; + float abilityTimers[10]; + int messageboard[4]; + bool abilityActive[10]; + int lastDamageCrit; + float hp; + +} fighterData; + +typedef struct +{ + PlyNum playerNumber; + bool isHuman; + int wins; + slot sl; + fighterData fighter; + +} player; + +typedef enum { + SWORD = 0, + HEART = 1, + BOMB = 2, + SWAP_HP = 3, + EVADE_REMOVE = 4, + BURST = 5, + CRIT = 6, + VAMPIRISM = 7, + LIGHTNING = 8, + EVADE = 9, +} wheel; + +static inline int deca(wheel icon) { + return (icon + 1) * 36; +} +static inline int rad_to_deg(float radian) +{ + int x = (int)floorf(radian * (180/3.14f)); + return x%360; +} +static inline int randomSelection() { + int x = rand()%800; + + //do nothing! + if (x >= 0 && x < 480) + { + return -1; + } else if (x >= 480 && x < 680) + { + //200 common + if (x >= 480 && x < 530) + { + return LIGHTNING; + } else if (x >= 530 && x < 580) + { + return HEART; + } else if (x >= 580 && x < 630) + { + return SWORD; + } else // x >= 630 && x < 680 + { + return EVADE; + } + + } else if (x >= 680 && x < 780) + { + //100 uncommon + if (x >= 680 && x < 705) + { + return VAMPIRISM; + } else if (x >= 705 && x < 730) + { + return CRIT; + } else if (x >= 730 && x < 755) + { + return BURST; + } else // x >= 755 && x < 780 + { + return EVADE_REMOVE; + } + } else { + //20 rare + if (x >= 780 && x < 790) + { + return SWAP_HP; + } else + { + return BOMB; + } + } +} +#endif // BATTLE_H \ No newline at end of file diff --git a/code/lucker/lucker.mk b/code/lucker/lucker.mk new file mode 100644 index 00000000..042103b9 --- /dev/null +++ b/code/lucker/lucker.mk @@ -0,0 +1,27 @@ + +ASSETS_LIST += \ + filesystem/lucker/wheel.t3dm \ + filesystem/lucker/snake.t3dm \ + filesystem/lucker/Slot_Bomb.sprite \ + filesystem/lucker/Slot_Burst.sprite \ + filesystem/lucker/Slot_Crit.sprite \ + filesystem/lucker/Slot_Evade.sprite \ + filesystem/lucker/Slot_EvadeRemove.sprite \ + filesystem/lucker/Slot_Heart.sprite \ + filesystem/lucker/Slot_Lightning.sprite \ + filesystem/lucker/Slot_Swap.sprite \ + filesystem/lucker/Slot_Sword.sprite \ + filesystem/lucker/Slot_Vampirism.sprite \ + filesystem/lucker/A_Button.sprite \ + filesystem/lucker/L_Button.sprite \ + filesystem/lucker/R_Button.sprite \ + filesystem/lucker/Bite.wav64 \ + filesystem/lucker/Cheer.wav64 \ + filesystem/lucker/Slot_Start.wav64 \ + filesystem/lucker/Slot_Run.wav64 \ + filesystem/lucker/Slot_Win.wav64 \ + filesystem/lucker/Periander.wav64 \ + filesystem/lucker/m6x11plus.font64 + + +filesystem/snake3d/m6x11plus.font64: MKFONT_FLAGS += --outline 1 --size 36 diff --git a/code/snake3d/snake3d.c b/code/snake3d/snake3d.c index 73994b75..982d6220 100644 --- a/code/snake3d/snake3d.c +++ b/code/snake3d/snake3d.c @@ -67,7 +67,7 @@ typedef struct T3DSkeleton skel; T3DVec3 moveDir; T3DVec3 playerPos; - float rotY; + float rotY; float currSpeed; float animBlend; bool isAttack;