From 9b86f52bc54048fac38f94353382ae2cb53c9f7b Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 9 Jul 2017 00:59:32 +1000 Subject: [PATCH] Rough semi-functional implementation of Tetris minigame --- gfx/hud/default/minigames/tetris/board.png | Bin 0 -> 1554 bytes gfx/hud/default/minigames/tetris/icon.jpg | Bin 0 -> 10439 bytes .../default/minigames/tetris/icon_notif.jpg | Bin 0 -> 18950 bytes .../minigames/tetris/icon_notif_alpha.jpg | Bin 0 -> 606 bytes gfx/hud/default/minigames/tetris/piece.png | Bin 0 -> 16753 bytes qcsrc/common/minigames/minigame/_mod.inc | 1 + qcsrc/common/minigames/minigame/_mod.qh | 1 + qcsrc/common/minigames/minigame/all.qh | 1 + qcsrc/common/minigames/minigame/tetris.qc | 737 ++++++++++++++++++ qcsrc/common/minigames/minigame/tetris.qh | 1 + 10 files changed, 741 insertions(+) create mode 100644 gfx/hud/default/minigames/tetris/board.png create mode 100644 gfx/hud/default/minigames/tetris/icon.jpg create mode 100644 gfx/hud/default/minigames/tetris/icon_notif.jpg create mode 100644 gfx/hud/default/minigames/tetris/icon_notif_alpha.jpg create mode 100644 gfx/hud/default/minigames/tetris/piece.png create mode 100644 qcsrc/common/minigames/minigame/tetris.qc create mode 100644 qcsrc/common/minigames/minigame/tetris.qh diff --git a/gfx/hud/default/minigames/tetris/board.png b/gfx/hud/default/minigames/tetris/board.png new file mode 100644 index 0000000000000000000000000000000000000000..ed116d06bf59b5b1aa2acdadb1e0b240540e4be0 GIT binary patch literal 1554 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 I7!$7hKiMC~n*aa+ literal 0 HcmV?d00001 diff --git a/gfx/hud/default/minigames/tetris/icon_notif_alpha.jpg b/gfx/hud/default/minigames/tetris/icon_notif_alpha.jpg new file mode 100644 index 0000000000000000000000000000000000000000..040990f6d5ec0dd7a9d8d435991b2e158a88d7f8 GIT binary patch literal 606 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<G5bS>*OeQt(?=H)>VR?bDOiRxKi6wom00~E#j{^sETK( zxMpahW>T#ZU4j)>^<Uq?*bZA-pOu2bh{?wjyux3@egF&B}BtxN4PAE5r_b(=O6h)C*J*n4g zpob<d^SM)IbV?Dy($#fwTQ0@TGeWlDDm{n%i~KV@&xga~?&4yX(iRr^?S78~HJ{4_h|ML3o0fo31%%6k{5PbAbc=-AmXH;4@N z#AV`Eh_8?Z&eiPjv#ucTFXH)1TYQ%l-a@EyIK*A0dZL)~GJGg7-a_`5^saYrG&bKA zWNmOinlxJYlUJmp%sQ=EoBJgCtHpxEJ^RIQ@(fpAJ#Y+z0gr)s_KyM25912DzEu?5 z7mLHt=5pTeaRqIh$F-Oxecr_uvbPcQOzbMNgRkWMa9FG@*Fp}|jH$$(W-jmsf-W!3 z7UbJtLk`C=X4*uVs5}#`(HrzGjm~JGHH#)_4H}b{GV1lYxjJ1QEkblUarteb^Le@o zotB=^%w2}Y*3ZG4&Q*!|pwZuLiUGphmTEc|fcvu=+qZD{ig(#;c1hpXTOF00 zcqjQ>{s4k6;Xmp#|XmlW0#zOXK?HyJ^*(2kIx`6Tcn0)XY zAvf#2(}9YI61@fw65z{&A(x+>ycq6@Z*$h;QTxjJhqsXE_OImnE=0Vw@A}-@vA3_M z1zl=pxEJ8KT;dw&EaYu%y_w_wC1_16t3-d3UJD;mhyM0R@$UZ8d3ToL-TkG#$~j&x zXw&y(>(`8_(9Ija7G}ej1lBgeXv{TQ$o|d!6@^@wtt`ps;Q0-pU1`+8RHCSV&w$E# zgDSC}9%X#7uz%>{TE<)9>jHdJArr4EeU~|L6z;RpJEi*^7!M~W!D&d^GA>C(xPy!f zry*&}xFiwb4l*vBhNLayl0<|%$hdGClD3RX5)tkoD3#TDz%eW*F z;SMq`oQ9+=kBY0J1I5#bIpE}Vv> zE#s0zggeN%a2k@fj7t&`?jYmBX-L{KE=feVgNzHOA!*CFBoW~bGA^8kq%Gr;M1(uY zxNsVhwv0;>5$+)4!f8m_GA>C(xPy!fry*&}xFiwb4l*vBhNLayl0<|%$hdGClJ*^O zrT2dP$9mxxe+gBOdmQ*DNbodhvLL^Ig5_ z%FHiI_XI;B(}DjPoD+e*v2*8BKeGzIZ`!gN{JT-u+0=ETv~8G*iG|)@5Q~iiD@uh@ z;vk4@?h>*=7O4G0_f|y%0e;jeOa%s@a>Y(&L{h-W=5ApmEZ2P4EsQ|*54tZV+ZABy z0^urhcQgqrlVEu!DojK55_lSGNI0)>md!n_izfwEOdHnF9I+L~S# zGocx8*RH6$)(TE{HnWh!d)>z}$|%C#akz2xjek z>LGhaV;xN}8P8FV*gMwMwZPUAYOK9uX_lTl2Kvv)9a$W-g28K0gx)U0V>KRxrc zFYofDv4_u}-L`L=8;guQ-suz}4D){j2Y~ zBl6#_S0SfJ*Ru(cXPM^TOWY3oTab@e?O@Dn?#7efqBtCZAGv z?mW@fwgc9?yH7m0@SBGphyb91n;d;-I}~#$8Z1==FOUAG!D_SZRIG{7yPCF4Oxs;?pmCBr0*`!WW|j&Z9(xcF?{!B~(jr}{ z20QF)Ad<>rgz^E&BPa%S_t2+`sjqQBO-xUJ%7An2hW%L zx4bcpIQ3e~e2pa;tlxC9^~xFI=nKE7*piu2b}Z%d_DSE1d_Svj{Y@=s #include #include +#include #include diff --git a/qcsrc/common/minigames/minigame/_mod.qh b/qcsrc/common/minigames/minigame/_mod.qh index fe8378bf1..040bdc98c 100644 --- a/qcsrc/common/minigames/minigame/_mod.qh +++ b/qcsrc/common/minigames/minigame/_mod.qh @@ -5,4 +5,5 @@ #include #include #include +#include #include diff --git a/qcsrc/common/minigames/minigame/all.qh b/qcsrc/common/minigames/minigame/all.qh index 5bbb7ebbf..bcf330820 100644 --- a/qcsrc/common/minigames/minigame/all.qh +++ b/qcsrc/common/minigames/minigame/all.qh @@ -70,6 +70,7 @@ that .owner is set to the minigame session entity and .minigame_autoclean is tru #include "ps.qc" #include "pp.qc" #include "bd.qc" +#include "tetris.qc" /** * Set up automatic entity read/write functionality diff --git a/qcsrc/common/minigames/minigame/tetris.qc b/qcsrc/common/minigames/minigame/tetris.qc new file mode 100644 index 000000000..c5b9af57e --- /dev/null +++ b/qcsrc/common/minigames/minigame/tetris.qc @@ -0,0 +1,737 @@ +#include "tetris.qh" +REGISTER_MINIGAME(tetris, "Tetris"); + +const float TETRIS_TURN_MOVE = 0x0100; // player has to place a piece on the board +const float TETRIS_TURN_WIN = 0x0200; // player has won +const float TETRIS_TURN_LOSS = 0x0400; // no moves are possible + +const int TETRIS_LET_CNT = 10; +const int TETRIS_NUM_CNT = 16; + +const int TETRIS_TILE_SIZE = 16; + +const int TETRIS_SHAPE_CNT = 7; + +const int TET_DIR_UP = 0; +const int TET_DIR_DN = 1; +const int TET_DIR_LF = 2; +const int TET_DIR_RT = 3; + +.bool tetris_inair; // flag to indicate whether the piece has landed and is no longer part of the moving pieces +.int tetris_shape; // flag to indicate which shape the piece is part of +.vector tetris_pos; // position in the shape the piece is +.int tetris_rotation; // player-chosen rotation of the controller + +// bit diagram +// 0 1 2 3 +// 0 1 2 3 +vector tetris_shapes[TETRIS_SHAPE_CNT]; + +STATIC_INIT(tetris_set_shapes) +{ + tetris_shapes[0] = vec2(BIT(0) | BIT(1) | BIT(2) | BIT(3), 0); // I block + tetris_shapes[1] = vec2(BIT(0), BIT(0) | BIT(1) | BIT(2)); // J block + tetris_shapes[2] = vec2(BIT(2), BIT(0) | BIT(1) | BIT(2)); // L block + tetris_shapes[3] = vec2(BIT(0) | BIT(1), BIT(0) | BIT(1)); // O block + tetris_shapes[4] = vec2(BIT(1) | BIT(2), BIT(0) | BIT(1)); // S block + tetris_shapes[5] = vec2(BIT(1), BIT(0) | BIT(1) | BIT(2)); // T block + tetris_shapes[6] = vec2(BIT(0) | BIT(1), BIT(1) | BIT(2)); // Z block +} + +// find connect 4 piece given its tile name +entity tetris_find_piece(entity minig, string tile) +{ + entity e = NULL; + while ( ( e = findentity(e,owner,minig) ) ) + if ( e.classname == "minigame_board_piece" && e.netname == tile ) + return e; + return NULL; +} + +entity tetris_find_static_piece(entity minig, string tile) +{ + entity e = NULL; + while ( ( e = findentity(e,owner,minig) ) ) + if ( e.classname == "minigame_board_piece" && e.netname == tile && !e.tetris_inair ) + return e; + return NULL; +} + +entity tetris_find_controller(entity minig) +{ + entity e = NULL; + while ( ( e = findentity(e,owner,minig) ) ) + if ( e.classname == "tetris_controller" ) + return e; + return NULL; +} + +// Checks if the given piece completes a row +bool tetris_winning_piece(entity piece) +{ + return false; +} + +// check if the tile name is valid (6x7 grid) +bool tetris_valid_tile(string tile) +{ + if ( !tile ) + return false; + float number = minigame_tile_number(tile); + float letter = minigame_tile_letter(tile); + return 0 <= number && number < TETRIS_NUM_CNT && 0 <= letter && letter < TETRIS_LET_CNT; +} + +int tetris_dir_fromname(string bdir) +{ + if(bdir == "up" || bdir == "u") + return TET_DIR_UP; // up + if(bdir == "down" || bdir == "dn" || bdir == "d") + return TET_DIR_DN; /// down + if(bdir == "left" || bdir == "lt" || bdir == "l") + return TET_DIR_LF; // left + if(bdir == "right" || bdir == "rt" || bdir == "r") + return TET_DIR_RT; // right + + return TET_DIR_RT; // invalid, just go right? +} + +int tetris_pick_shape(int oldshape) +{ + RandomSelection_Init(); + + for(int j = 0; j < TETRIS_SHAPE_CNT; ++j) + { + RandomSelection_AddFloat(j, ((oldshape == j) ? 0.5 : 1), 1); + } + + return RandomSelection_chosen_float; +} + +// same as spawn function, but checks spaces instead of spawning at them +bool tetris_checkspace(entity minigame, int shape, int rotation, int letter, int number) +{ + vector chosen = tetris_shapes[shape]; + + for(int j = 0; j < 4; ++j) + { + if(chosen.x & BIT(j)) + { + int newlet = letter; + int newnum = number; + if(rotation == 1) + newnum += j; + else if(rotation == 2) + newlet -= j; + else if(rotation == 3) + newnum -= j; + else + newlet += j; + string tile = minigame_tile_buildname(newlet, newnum); + if(!tetris_valid_tile(tile) || tetris_find_static_piece(minigame, tile)) + return false; + } + } + + for(int j = 0; j < 4; ++j) + { + if(chosen.y & BIT(j)) + { + int newlet = letter; + int newnum = number; + if(rotation == 1) + { + newnum += j; + newlet -= 1; + } + else if(rotation == 2) + { + newlet -= j; + newnum += 1; + } + else if(rotation == 3) + { + newnum -= j; + newlet += 1; + } + else + { + newlet += j; + newnum -= 1; + } + string tile = minigame_tile_buildname(newlet, newnum); + if(!tetris_valid_tile(tile) || tetris_find_static_piece(minigame, tile)) + return false; + } + } + + return true; +} + +entity tetris_spawn_piece(entity minigame, vector pos, int shape, int letter, int number) +{ + entity piece = msle_spawn(minigame,"minigame_board_piece"); + piece.tetris_inair = true; + piece.tetris_shape = shape; + piece.tetris_pos = pos; + piece.team = 1; // TODO? + piece.netname = strzone(minigame_tile_buildname(letter, number)); + minigame_server_sendflags(piece,MINIG_SF_ALL); + + return piece; +} + +void tetris_spawn_shape(entity minigame, entity controller, int letter, int number, int shape, int rotation) +{ + vector chosen = tetris_shapes[shape]; + controller.tetris_shape = shape; // so we know which one is currently in play + if(controller.netname) { strunzone(controller.netname); } + controller.netname = strzone(minigame_tile_buildname(letter, number)); // keep tabs on the position of the clump + + if(!tetris_checkspace(minigame, shape, rotation, letter, number)) + { + minigame.minigame_flags = TETRIS_TURN_LOSS; + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + return; + } + + for(int j = 0; j < 4; ++j) + { + if(chosen.x & BIT(j)) + { + int newlet = letter; + int newnum = number; + if(rotation == 1) + newnum += j; + else if(rotation == 2) + newlet -= j; + else if(rotation == 3) + newnum -= j; + else + newlet += j; + tetris_spawn_piece(minigame, vec2(j, 0), shape, newlet, newnum); + } + } + + for(int j = 0; j < 4; ++j) + { + if(chosen.y & BIT(j)) + { + int newlet = letter; + int newnum = number; + if(rotation == 1) + { + newnum += j; + newlet -= 1; + } + else if(rotation == 2) + { + newlet -= j; + newnum += 1; + } + else if(rotation == 3) + { + newnum -= j; + newlet += 1; + } + else + { + newlet += j; + newnum -= 1; + } + tetris_spawn_piece(minigame, vec2(0, j), shape, newlet, newnum); + } + } + + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); +} + +void tetris_move_above(entity minigame, int startpos) +{ + for(int k = startpos; k < TETRIS_NUM_CNT; ++k) + { + for(int j = 0; j < TETRIS_LET_CNT; ++j) + { + string pos = minigame_tile_buildname(j, k - 1); // let's just assume this is a valid new position + entity piece = tetris_find_static_piece(minigame, minigame_tile_buildname(j, k)); + if(piece && !wasfreed(piece)) + { + if(piece.netname) { strunzone(piece.netname); } + piece.netname = strzone(pos); + minigame_server_sendflags(piece,MINIG_SF_UPDATE); + } + } + } +} + +void tetris_checkrows(entity minigame) +{ + for(int k = 0; k < TETRIS_NUM_CNT; ++k) + { + int found = 0; + for(int j = 0; j < TETRIS_LET_CNT; ++j) + { + string pos = minigame_tile_buildname(j, k); + entity piece = tetris_find_static_piece(minigame, pos); + if(piece && !wasfreed(piece)) // due to the nature of this function, we'd better make sure it isn't already removed + ++found; + } + + if(found >= TETRIS_LET_CNT) + { + // this is where things get messy: we need to remove the row, then loop through every row again! + for(int j = 0; j < TETRIS_LET_CNT; ++j) + { + string pos = minigame_tile_buildname(j, k); + entity piece = tetris_find_static_piece(minigame, pos); + if(piece && !wasfreed(piece)) // just to make extra sure + { + if(piece.netname) { strunzone(piece.netname); } + delete(piece); + } + } + if(k < TETRIS_NUM_CNT - 1) // if it wasn't the top row, move everything above it down one + tetris_move_above(minigame, k + 1); + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + tetris_checkrows(minigame); + return; // no need to continue, the new loop will find new ones + } + } +} + +void tetris_controller_think(entity this) +{ + entity minigame = this.owner; + + if(!(minigame.minigame_flags & TETRIS_TURN_MOVE)) + return; + + int letter = minigame_tile_letter(this.netname); + int number = minigame_tile_number(this.netname) - 1; + + if(!tetris_checkspace(minigame, this.tetris_shape, this.tetris_rotation, letter, number)) + { + entity e2 = NULL; + while( (e2 = findentity(e2, owner, minigame)) ) + if(e2.classname == "minigame_board_piece" && e2.tetris_inair) + e2.tetris_inair = false; + + int newshape = tetris_pick_shape(this.tetris_shape); + this.tetris_rotation = 0; // reset rotation for the new shape + tetris_spawn_shape(minigame, this, 4, TETRIS_NUM_CNT - 1, newshape, 0); + + tetris_checkrows(minigame); + + this.nextthink = time + 1; + return; + } + + string cpos = minigame_tile_buildname(letter, number); + if(this.netname) { strunzone(this.netname); } + this.netname = strzone(cpos); + + entity e = NULL; + while ( ( e = findentity(e,owner,minigame) ) ) + if ( e.classname == "minigame_board_piece" && e.tetris_inair ) + { + int pletter = minigame_tile_letter(e.netname); + int pnumber = minigame_tile_number(e.netname); + string newpos = minigame_tile_buildname(pletter, pnumber - 1); + if(e.netname) { strunzone(e.netname); } + e.netname = strzone(newpos); + minigame_server_sendflags(e,MINIG_SF_UPDATE); + } + + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + + this.nextthink = time + 0.75; +} + +void tetris_begin(entity minigame) +{ + entity e = NULL; + while( (e = findentity(e, owner, minigame)) ) + if(e.classname == "tetris_controller") + { + delete(e); + } + + entity controller = new_pure(tetris_controller); + controller.owner = minigame; + setthink(controller, tetris_controller_think); + controller.nextthink = time + 1; // begin moving down in a second + + int theshape = tetris_pick_shape(-1); + tetris_spawn_shape(minigame, controller, 4, TETRIS_NUM_CNT - 1, theshape, 0); +} + +// make a move +void tetris_move(entity minigame, entity player, string dir ) +{ + if ( minigame.minigame_flags & TETRIS_TURN_MOVE ) + if ( dir ) + { + string thedir = strtolower(dir); + int bdir = tetris_dir_fromname(thedir); + entity controller = tetris_find_controller(minigame); + + if(bdir == TET_DIR_RT || bdir == TET_DIR_LF) + { + int realdir = ((bdir == TET_DIR_LF) ? -1 : 1); + int cletter = minigame_tile_letter(controller.netname) + realdir; + int cnumber = minigame_tile_number(controller.netname); + string cpos = minigame_tile_buildname(cletter, cnumber); + if(!tetris_checkspace(minigame, controller.tetris_shape, controller.tetris_rotation, cletter, cnumber)) + return; + + if(controller.netname) { strunzone(controller.netname); } + controller.netname = strzone(cpos); + + entity e = NULL; + while ( ( e = findentity(e,owner,minigame) ) ) + if ( e.classname == "minigame_board_piece" && e.tetris_inair ) + { + int pletter = minigame_tile_letter(e.netname); + int pnumber = minigame_tile_number(e.netname); + string newpos = minigame_tile_buildname(pletter + realdir, pnumber); + if(e.netname) { strunzone(e.netname); } + e.netname = strzone(newpos); + minigame_server_sendflags(e,MINIG_SF_UPDATE); + } + + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + } + else if(bdir == TET_DIR_DN) + { + controller.nextthink = time + 0.1; // quicker fall + } + else if(bdir == TET_DIR_UP) + { + // this is where things get complicated + // we need to somehow rotate the pieces... + int oldrotation = controller.tetris_rotation; + controller.tetris_rotation += 1; + if(controller.tetris_rotation >= 4) + controller.tetris_rotation = 0; + + int myletter = minigame_tile_letter(controller.netname); + int mynumber = minigame_tile_number(controller.netname); + if(!tetris_checkspace(minigame, controller.tetris_shape, controller.tetris_rotation, myletter, mynumber)) + { + controller.tetris_rotation = oldrotation; // restore it so we aren't performing weird tests above + return; + } + + entity e3 = NULL; + while ( ( e3 = findentity(e3,owner,minigame) ) ) + if ( e3.classname == "minigame_board_piece" && e3.tetris_inair ) + { + if(e3.netname) { strunzone(e3.netname); } + delete(e3); + } + + tetris_spawn_shape(minigame, controller, myletter, mynumber, controller.tetris_shape, controller.tetris_rotation); + } + } +} + +#ifdef SVQC + + +// required function, handle server side events +int tetris_server_event(entity minigame, string event, ...) +{ + switch(event) + { + case "start": + { + tetris_begin(minigame); + minigame.minigame_flags = TETRIS_TURN_MOVE; + return true; + } + case "end": + { + entity e = NULL; + while( (e = findentity(e, owner, minigame)) ) + if(e.classname == "minigame_board_piece") + { + if(e.netname) { strunzone(e.netname); } + delete(e); + } + e = NULL; + while( (e = findentity(e, owner, minigame)) ) + if(e.classname == "tetris_controller") + { + delete(e); + } + return false; + } + case "join": + { + int pl_num = minigame_count_players(minigame); + + // Don't allow more than 1 player + if(pl_num >= 1) { return false; } + + // Team 1 by default + return 1; + } + case "cmd": + { + switch(argv(0)) + { + case "move": + tetris_move(minigame, ...(0,entity), ...(1,int) == 2 ? argv(1) : string_null ); + return true; + } + + return false; + } + case "network_send": + { + entity sent = ...(0,entity); + int sf = ...(1,int); + if ( sent.classname == "minigame_board_piece" && (sf & MINIG_SF_UPDATE) ) + { + int letter = minigame_tile_letter(sent.netname); + int number = minigame_tile_number(sent.netname); + + WriteByte(MSG_ENTITY,letter); + WriteByte(MSG_ENTITY,number); + + WriteByte(MSG_ENTITY,sent.tetris_shape); + } + return false; + } + } + + return false; +} + + +#elif defined(CSQC) + +vector tetris_boardpos; // HUD board position +vector tetris_boardsize;// HUD board size + +vector tetris_shape_color(int shape) +{ + switch(shape) + { + case 0: return '0 1 1'; + case 1: return '0 0 1'; + case 2: return '1 0.5 0'; + case 3: return '1 1 0'; + case 4: return '0 0.5 0'; + case 5: return '0.5 0 0.5'; + case 6: return '1 0 0'; + } + + return '1 1 1'; +} + +#define tetris_hud_fitsquare(pos, mySize, xx) \ + if ( mySize##_x > mySize##_y ) \ + { \ + pos##_x += (mySize##_x-(mySize##_y / xx))/2; \ + mySize##_x = mySize##_y / xx; \ + } \ + if(panel_bg_padding) \ + { \ + pos += '1 1 0' * panel_bg_padding; \ + mySize -= '2 2 0' * panel_bg_padding; \ + } + +// Required function, draw the game board +void tetris_hud_board(vector pos, vector mySize) +{ + float xsize = (TETRIS_NUM_CNT / TETRIS_LET_CNT); + + tetris_hud_fitsquare(pos, mySize, xsize); + tetris_boardpos = pos; + tetris_boardsize = mySize; + + minigame_hud_simpleboard(pos,mySize,minigame_texture("tetris/board")); + + vector tile_size = minigame_hud_denormalize_size(vec2(1 / TETRIS_LET_CNT, 1 / TETRIS_NUM_CNT),pos,mySize); + vector tile_pos; + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_board_piece" ) + { + tile_pos = minigame_tile_pos(e.netname,TETRIS_NUM_CNT,TETRIS_LET_CNT); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + + minigame_drawpic_centered( tile_pos, + minigame_texture("tetris/piece"), + tile_size, tetris_shape_color(e.tetris_shape), panel_fg_alpha, DRAWFLAG_NORMAL ); + } + } + + if ( active_minigame.minigame_flags & TETRIS_TURN_WIN ) + { + vector winfs = hud_fontsize*2; + string pname = ""; + FOREACH_MINIGAME_ENTITY(e) + if ( e.classname == "minigame_player" ) + { + pname = entcs_GetName(e.minigame_playerslot-1); + break; + } + + vector win_pos = pos+eY*(mySize_y-winfs_y)/2; + vector win_sz; + win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos, + sprintf("%s^7 won the game!",pname), + winfs, 0, DRAWFLAG_NORMAL, 0.5); + + drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + + minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos, + sprintf("%s^7 won the game!",pname), + winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5); + } +} + + +// Required function, draw the game status panel +void tetris_hud_status(vector pos, vector mySize) +{ + HUD_Panel_DrawBg(); + vector ts; + ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, + hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); + + pos_y += ts_y; + mySize_y -= ts_y; + + vector player_fontsize = hud_fontsize * 1.75; + ts_y = ( mySize_y - 2*player_fontsize_y ) / 2; + ts_x = mySize_x; + vector mypos; + vector tile_size = '48 48 0'; + + mypos = pos; + //if ( (active_minigame.minigame_flags&TETRIS_TURN_TEAM) == 2 ) + //mypos_y += player_fontsize_y + ts_y; + drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + mypos_y += player_fontsize_y; + drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE); + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_player" ) + { + mypos = pos; + if ( e.team == 2 ) + mypos_y += player_fontsize_y + ts_y; + minigame_drawcolorcodedstring_trunc(mySize_x,mypos, + entcs_GetName(e.minigame_playerslot-1), + player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + + mypos_y += player_fontsize_y; + drawpic( mypos, + minigame_texture("tetris/piece"), + tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL ); + + mypos_x += tile_size_x; + } + } +} + +// Turn a set of flags into a help message +string tetris_turn_to_string(int turnflags) +{ + if ( turnflags & TETRIS_TURN_LOSS ) + return _("Game over!"); + + if ( turnflags & TETRIS_TURN_WIN ) + return _("You win!"); + + if ( turnflags & TETRIS_TURN_MOVE ) + return _("Use the left and right arrows to move, up arrow to change orientation and down arrow to place the piece"); + + return ""; +} + +// Make the correct move +void tetris_make_move(entity minigame, string dir) +{ + if ( minigame.minigame_flags & TETRIS_TURN_MOVE ) + { + minigame_cmd("move ", dir); + } +} + +// Required function, handle client events +int tetris_client_event(entity minigame, string event, ...) +{ + switch(event) + { + case "activate": + { + minigame.message = tetris_turn_to_string(minigame.minigame_flags); + return false; + } + case "key_pressed": + { + if(minigame.minigame_flags & TETRIS_TURN_MOVE) + { + switch(...(0,int)) + { + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + tetris_make_move(minigame, "r"); + return true; + case K_LEFTARROW: + case K_KP_LEFTARROW: + tetris_make_move(minigame, "l"); + return true; + case K_UPARROW: + case K_KP_UPARROW: + tetris_make_move(minigame, "u"); + return true; + case K_DOWNARROW: + case K_KP_DOWNARROW: + tetris_make_move(minigame, "d"); + return true; + } + } + + return false; + } + case "network_receive": + { + entity sent = ...(0,entity); + int sf = ...(1,int); + if ( sent.classname == "minigame" ) + { + if ( sf & MINIG_SF_UPDATE ) + { + sent.message = tetris_turn_to_string(sent.minigame_flags); + } + } + else if(sent.classname == "minigame_board_piece") + { + if(sf & MINIG_SF_UPDATE) + { + int letter = ReadByte(); + int number = ReadByte(); + if(sent.netname) { strunzone(sent.netname); } + sent.netname = strzone(minigame_tile_buildname(letter, number)); + + sent.tetris_shape = ReadByte(); + } + } + + return false; + } + } + + return false; +} + +#endif diff --git a/qcsrc/common/minigames/minigame/tetris.qh b/qcsrc/common/minigames/minigame/tetris.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/minigames/minigame/tetris.qh @@ -0,0 +1 @@ +#pragma once -- 2.39.2