Description
The arrogance of wizards. They're all half mad if you ask me. Now they're squabbling again. No one knows why and I doubt they remember. Once again they're leaving a trail of scorched ruins and charred corpses as they try to kill each other.
Objective
Take all four castles by killing the defenders and entering the keep.
Instructions
Controls
Movement: WASD or Cursor Keys
Targeting: Mouse
Firing: Left Mouse Button
Players: 1-4
Players must be on the same LAN segment. Thus you can play on multiple computers at home, but not against an opponent elsewhere on the internet.
User Comments
No comments have been made.
You need be logged in to write comments. If you need to register click here .
Source code
Note: We recommend you copy+paste this to a better editor. Some special character encoding may have been lost.
=time) x1 = i; // Existing record Found if (timestamp[i]+TIMEOUT
<<24) + ((rxMsg[k+2]&0xff)<<16) + ((rxMsg[k+1]&0xff)<<8) + (rxMsg[k]&0xff); } LANbuffer[x1][i] = LANdata[x1][i]; LANdata[x1][i] = LANvars; // Atomic operation } // Update timestamp last, as if this is the first // time this record is written, we want to ensure // it is valid before being processed. timestamp[x1] = time; } } catch (Exception e) {} } while (isActive()); return; } // // Game Thread // // // Initialise LAN Play // try { // Open socket socket = new MulticastSocket(PORT); group = InetAddress.getByName(GROUP); socket.joinGroup(group); // Initialise Transmit packet txMsg[0] = (byte)(processID & 0xff); txMsg[1] = (byte)((processID>>8) & 0xff); txMsg[2] = (byte)((processID>>16) & 0xff); txMsg[3] = (byte)((processID>>24) & 0xff); } catch (Exception e) {} txPacket = new DatagramPacket(txMsg, txMsg.length, group, PORT); rxPacket = new DatagramPacket(rxMsg, rxMsg.length, group, PORT); // Start receive thread receiveThread = true; new Thread(this).start(); long sendTime = 0; // // Initialise Graphics Engine // // Object culling variables float[][] drawList1 = new float[PLAYERS*(ENEMIES+1)][]; float[][] drawList2 = new float[PLAYERS*(ENEMIES+1)][]; int drawCount1, drawCount2; // Object pointers int[] LANentity; // LANdata pointers float[] entity1 = null, entity2; // LocalData pointers // Entity Colours int[] cMap = new int[8]; // Entity type to colour map int colour; // Entity colour RRGGBB // Renderer Variables int tileType; // Terrain type of current tile int dst; // Pointer into screen image int sx, sy; // Screen x and y pointers float rx, rz; // x & z rotated into player frame float radius, depth; // Object radius & distance float rayAngle=0, cosRayAngle, sinRayAngle; // Raycasting variables int ray, angle; // Map Creation float m1, m2; // Mountain heights float p1, p2; // Plains heights // Create screen image (double buffer) BufferedImage screen = new BufferedImage( SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_RGB); int[] screenData = ((DataBufferInt)screen.getRaster().getDataBuffer()).getData(); float[] zBuffer = new float[SCREENHEIGHT*SCREENWIDTH]; Graphics gs = getGraphics(); // Precalculate trigonometry for (i=0;i<36000;i++) { // Sin & Cos lookup cos[i] = (float)Math.cos(i/DEGREES); sin[(i+9000)%36000] = cos[i]; } for (i=0; i<8;i++) { cMap[i] = 0x3fc000*(i&4) + 0x7f80*(i&2) + 0xff*(i&1); } // Define World Map // 0 = plain // 1 = grass effect on plain (This is not used directly on map) // 2 = mountain // 3 = castle for (i=0;i<=2||j>=4)) || ((i<=2||i>=4) && j==3)) map[i][j] = MOUNTAIN; // Some mountains if (((i==1 || i==MAPSIZE-2)) && (j==1 || j==MAPSIZE-2)) map[i][j] = CASTLE; // 4 Castles } } // Define Map Tiles for (i=0;i<0) m1=-m1; m1 = 3f*(1f-m1); for (j=0;j<0) m2=-m2; m2 = 3f*(1f-m2); // Rolling Plain y = 2f; if (p1z)?y:z; // Allow grass at edges // Castle z = 3f; // Walls if (y<-1.75f/2f) z = -1f; // Entrance if (p1<1.9f/2f && p2<1.9f/2f) z = -1f; // Courtyard if (y>1f/2f) z = 10f; // Towers tiles[CASTLE][i][j] = z; } } // Define Wizard Object int[][] entityHeight = new int[ENTITYSIZE][ENTITYSIZE]; int[][] entityColour = new int[ENTITYSIZE][ENTITYSIZE]; // Define as a base64 number with 4 bits height and 2 bits colour // 0000000011111111222222223333333344444444555555556666666677777777 // Colour 0000000000000000000000000001100000022000000000000020020000000030 // Height 0000000000666600067777600679976006799760056776500454454004444470"; String wizard = "000000000066660006777760067II760067YY7600567765004U44U40044444g0"; for (i=0;i<4;i++) castle[i] = false; // Surrender all castles } // // LAN Transmit data // if (time > sendTime) { sendTime = time+RATE; // Send Entity data for (i=0;i>24) & 0xff); txMsg[k+2] = (byte)((m>>16) & 0xff); txMsg[k+1] = (byte)((m>>8) & 0xff); txMsg[k] = (byte)(m & 0xff); } } try { socket.send(txPacket); } catch (Exception e) {} } // // Game // // // Estimate all entity positions // for(i=0;itime) { // Valid player for (j=0;j<=Z;k++) { // Estimate position based on last known position and velocity entity1[k] = (LANentity[k] + LANdeltaTime*TRANSLATERATE*LANentity[VX+k])/SCALEXYZ; } entity1[A] = LANentity[A]; } } } } // // Collision Detect: Local Player Fireballs against Local and Remote Enemies and Remote Players // Local Enemy Fireballs against Local and Remote Players // for (i=0;itime) { // Valid player for (j=0;j<=ENEMIES;j++) { entity1 = localData[i][j]; if (entity1[T]!=0) { // Valid Entity // Entity1 = Local & Remote Enemies & Players for (k=ENEMIES+1;k<=ENEMIES+PLAYERFIREBALLS) continue; // Local Enemy fireballs do not damage enemies if (j!=0 && k>ENEMIES+PLAYERFIREBALLS) continue; entity2 = localData[0][k]; // Local Fireball if (entity2[T]!=0) { // Valid Local Fireball // Entity2 = Local Fireball dx= entity1[X]-entity2[X]; dz= entity1[Z]-entity2[Z]; // Check end points if (dx*dx+dz*dz0) health[j]--; if (health[j]==0 && j>0) LANdata[0][j][T]=0; } } } } } } } } // // Collision Detect: Remote Fireballs against Local Player & Entities // for (i=1;itime) { // Valid player for (j=ENEMIES+1;j<=ENEMIES;k++) { // Remote Enemy fireballs do not damage Local Enemies if (j>ENEMIES+PLAYERFIREBALLS && k!=0) continue; entity1 = localData[0][k]; // Local Entity if (entity1[T]!=0) { // Valid Local Entity // Entity2 = Local Player and Local Entities dx= entity1[X]-entity2[X]; dz= entity1[Z]-entity2[Z]; // Check end points if (dx*dx+dz*dz0) health[k]--; if (health[k]==0 && k>0) LANdata[0][k][T]=0; } } } } } } } // // Collision detect local fireballs against terrain // for(i=ENEMIES+1;i=0 && mapX=0 && mapZentity1[Y]) { // Delete fireball LANdata[0][i][T] = 0; } } // // Player Movement: Set Player Velocity Vector and Angle // // Rotate player if (keyboard[Event.LEFT] || keyboard['a']) // Rotate Left angleY +=ROTATERATE*deltaTime; if (keyboard[Event.RIGHT] || keyboard['d']) // Rotate Right angleY -=ROTATERATE*deltaTime; angleY = (angleY + 2f*(float)Math.PI)%(2f*(float)Math.PI); sinAngleY = sin[(int)(DEGREES*angleY)]; cosAngleY = cos[(int)(DEGREES*angleY)]; // Translate player rx = 0; rz = 0; if (keyboard[Event.UP] || keyboard ['w']) { // Move Forward rx = -sinAngleY; rz = -cosAngleY; } if (keyboard[Event.DOWN] || keyboard['s']) { // Move Backward rx = sinAngleY; rz = cosAngleY; } // Determine player map square and location in that square x = playerX+TRANSLATERATE*deltaTime*rx; z = playerZ+TRANSLATERATE*deltaTime*rz; mapX = (int)(x+1000)-1000; mapZ = (int)(z+1000)-1000; tileX = (int)(TILESIZE*(x-mapX)); tileZ = (int)(TILESIZE*(z-mapZ)); // Determine which height map to use tileType = MOUNTAIN; if (mapX>=0 && mapX=0 && mapZ= 2) { rx = rz = 0; } else { playerX = x; playerY = y; playerZ = z; } LANentity = LANdata[0][0]; LANentity[X] = (int)(SCALEXYZ*playerX); LANentity[Y] = (int)(SCALEXYZ*playerY); LANentity[Z] = (int)(SCALEXYZ*playerZ); LANentity[A] = (int)(DEGREES*angleY); LANentity[VX]= (int)(SCALEXYZ*rx); LANentity[VZ]= (int)(SCALEXYZ*rz); // // Entity Movement AI: Set Entity Velocity Vector and Angle // // Local Entities attack local player. // Remote entities no not attack local player but the player // is not immune from their fireballs. entity2 = localData[0][0]; // Use time corrected player coords for (j=1;j<=ENEMIES;j++) { entity1 = localData[0][j]; if (entity1[T]!=0) { // Valid Enemy dx= entity1[X]-entity2[X]; dy= entity1[Y]-entity2[Y]; dz= entity1[Z]-entity2[Z]; depth = dx*dx+dz*dz; // Adjust enemy destination coordinates to prevent huddling k = ((entity1[X]-3.5f)*(entity1[Z]-3.5f)>0)?-1:1; x = dx + (j-(ENEMIES+1)/2f)*0.1f*k; z = dz + (j-(ENEMIES+1)/2f)*0.1f; // Determine velocity vector to destination // This requires an ARCTAN which is a slow operation angle = (int)(Math.atan2(x,z)*DEGREES); angle = (angle+36000)%36000; x = sin[angle]; // Unit Vector toward destination z = cos[angle]; // This is used for enemy movement // Determine angle to player // This requires an ARCTAN which is a slow operation dy = (dy-1f)/dx; angle = (int)(Math.atan2(dx,dz)*DEGREES); angle = (angle+36000)%36000; dx = sin[angle]; // Unit Vector toward player dy *= dx; dz = cos[angle]; // This is used for enemy fireballs // Determine relative angle to player angle = (int)entity1[A]-angle; // This is used to control angle = (angle+36000)%36000; // enemy rotation // Enemy movement and firing AI LANdata[0][j][VX] = 0; // Default action is to LANdata[0][j][VZ] = 0; // stay in the same place // If we are too close to the player, turn and run if (depth19000) { LANdata[0][j][A]-=(int)(ROTATERATE*10f*deltaTime*DEGREES); } else if(angle < 17000) { LANdata[0][j][A]+=(int)(ROTATERATE*10f*deltaTime*DEGREES); } LANdata[0][j][A] = (LANdata[0][j][A]+36000)%36000; // If we are too far from the player, face and close in } else if (depth>TOOFAR) { // Move towards the player LANdata[0][j][VX] = -(int)(x*SCALEXYZ); LANdata[0][j][VZ] = -(int)(z*SCALEXYZ); // Rotate enemy to face player if (angle>1000 && angle<18000) { LANdata[0][j][A]-=(int)(ROTATERATE*10f*deltaTime*DEGREES); } else if(angle<35000 && angle>18000) { LANdata[0][j][A]+=(int)(ROTATERATE*10f*deltaTime*DEGREES); } LANdata[0][j][A] = (LANdata[0][j][A]+36000)%36000; // Otherwise stand our ground } else { // Rotate enemy to face player if (angle>1000 && angle<18000) { LANdata[0][j][A]-=(int)(ROTATERATE*10f*deltaTime*DEGREES); } else if(angle<35000 && angle>18000) { LANdata[0][j][A]+=(int)(ROTATERATE*10f*deltaTime*DEGREES); } LANdata[0][j][A] = (LANdata[0][j][A]+36000)%36000; // Fire at player if a fireball is available and // the enemy is facing the player if (localData[0][j+ENEMIES+PLAYERFIREBALLS][T]==0 && (angle<1000 || angle>35000)){ // Set the fireball start location about halfway up the enemy LANdata[0][j+ENEMIES+PLAYERFIREBALLS][X]=LANdata[0][j][X]; LANdata[0][j+ENEMIES+PLAYERFIREBALLS][Y]=LANdata[0][j][Y]+(int)SCALEXYZ; LANdata[0][j+ENEMIES+PLAYERFIREBALLS][Z]=LANdata[0][j][Z]; // Set the fireball velocity equal to unit vector towards the player LANdata[0][j+ENEMIES+PLAYERFIREBALLS][VX] = -(int)(dx*SCALEXYZ); LANdata[0][j+ENEMIES+PLAYERFIREBALLS][VY] = -(int)(dy*SCALEXYZ); LANdata[0][j+ENEMIES+PLAYERFIREBALLS][VZ] = -(int)(dz*SCALEXYZ); // Set Fireball timeout LANdata[0][j+ENEMIES+PLAYERFIREBALLS][T] = LANdata[0][j][T]; fireballTimeout[j+PLAYERFIREBALLS-1] = time + FIREBALLTIMEOUT1; } } } } // // Player Fire // if (mouse) { for (i=ENEMIES+1;i<=ENEMIES+PLAYERFIREBALLS;i++) { if (localData[0][i][T]==0) { // Found an inactive fireball mouse=false; if (mouseX>=00 && mouseX<=Z;j++) { LANentity[j] += deltaTime*TRANSLATERATE*LANentity[j+VX]; } } } // // Adjust Enemy heights to match terrain // for (i=1;i<=ENEMIES;i++) { x = localData[0][i][X]; z = localData[0][i][Z]; mapX = (int)(x+1000)-1000; mapZ = (int)(z+1000)-1000; tileX = (int)(TILESIZE*(x-mapX)); tileZ = (int)(TILESIZE*(z-mapZ)); // Determine which height map to use tileType = MOUNTAIN; if (mapX>=0 && mapX=0 && mapZtime) { // Valid player for (j=1;j<=ENEMIES;j++) { entity1 = localData[i][j]; if (entity1[T]>0) { if ((playerX>3.5f)==(entity1[X]>3.5f) && (playerZ>3.5f)==(entity1[Z]>3.5f)) found = true; break; } } } } if (!found) { // If there are no enemies allow change of castle ownership for (i=0;itime) { // Valid player j = (int)localData[i][0][X]; k = (int)localData[i][0][Z]; if ((j==1 || j==5) && (k==1 || k==5)) { castle[(j-1)/4 +2*((k-1)/4)] = (i==0); } } } } // // Spawn enemies // // If player is in a quarter they don't own, then spawn if ((int)playerX==3 && (int)playerZ==3) { for (i=1;i<=ENEMIES;i++) LANdata[0][i][T] = 0; allowSpawn = true; // Player Retreated so allow respawn } else { j = playerX<3.5f?1:5; k = playerZ<3.5f?1:5; if (allowSpawn && !castle[(j-1)/4 +2*((k-1)/4)]) { // Spawn all enemies for (i=1;i<=ENEMIES;i++) { LANdata[0][i][X] = (int)((j+0.5f)*SCALEXYZ); LANdata[0][i][Z] = (int)((k+0.5f)*SCALEXYZ); LANdata[0][i][T] = (i%6)+1; health[i]=ENEMYHEALTH; } } allowSpawn = false; // No respawning once battle commenced } // // Draw 3D View // // Pre-calculate which entities are likely to be on screen drawCount1 = 0; for (i=0;itime) { // Valid player for (j=(i==0)?1:0;j<=ENEMIES;j++) { entity1 = localData[i][j]; if (entity1[T]!=0f) { // Valid entity // Don't draw entities more than 3 tiles away // Don't draw entities behind the camera dx = entity1[X] - playerX; dz = entity1[Z] - playerZ; if (dx*dx+dz*dz<0) drawList1[drawCount1++] = entity1; } } } } for (ray=0; ray=0 && mapX=0 && mapZ<0 || x1>=ENTITYSIZE) continue; z1 = (int)(dx*sin[angle] + dz*cos[angle]+CENTRE); if (z1<0 || z1>=ENTITYSIZE) continue; // Check for non-zero height if (entityHeight[x1][z1]>0) { drawObject = true; break; // Quit on first object } } } colour = 0x000200; // Default to green if (drawObject) { // Define object colour and height y += (float)entityHeight[x1][z1]/ENTITYSCALE; switch (entityColour[x1][z1]) { case 0: colour = (cMap[(int)entity1[T]] & 0x007f7f7f)+0x080808*(x1+z1); break; case 1: colour = 0; break; case 2: colour = 0x800000+0x080808*(x1+z1); break; default: colour = 0x801010; } } else { // Define Ground Colour switch (tileType) { case PLAIN: //case GRASS: y = tiles[GRASS][tileX][tileZ]; break; case MOUNTAIN: if (tiles[PLAIN][tileX][tileZ]<2f?y/1f:2f))/(1f+depth)))&255; } // Draw ground // Calculate screen position sy = SCREENHEIGHT/2 - (int)(20f*(y-playerY-PLAYERHEIGHT)/depth); if (sy<0) sy = 0; if (sy>SCREENHEIGHT-1) sy = SCREENHEIGHT-1; if (sysy; j--) { screenData[dst] = screenData[dst+1] = colour; zBuffer[dst] = zBuffer[dst+1] = -depth; dst-=SCREENWIDTH; } lsy = sy; } } // Draw sky dst = ray+lsy*SCREENWIDTH; for (j=lsy; j>=0; j--) { screenData[dst] = screenData[dst+1] = ((health[0]<10)?0x400040:0x200080) + 0x010101*(j/3); zBuffer[dst] = zBuffer[dst+1] = -Float.MAX_VALUE; dst-=SCREENWIDTH; } } // Draw fireballs for (i=0;itime) { // Valid player for (j=ENEMIES+1;j<0) { sx = SCREENWIDTH/2 - (int)(SCREENDEPTH*rx/rz); sy = SCREENHEIGHT/2 + (int)(20f*(entity1[Y]-playerY-PLAYERHEIGHT)/rz); // Limit number of points drawn to 10,000 maximum drawCount1 = (int)(50/(rz*rz)); if (drawCount1>50000) drawCount1=50000; colour = cMap[(int)entity1[T]]; for (k=0;k0 && x1=0 && y1<20;i++) { // Draw 10 rows for (j=0;j<4;k++) { // Draw castles owned colour = 0xff<<(8*k); if (k==3) colour = 0xffff00; if (!castle[k]) colour = 0; for (j=0;j<10;j++) { screenData[120+k*20+j+SCREENWIDTH*i] = colour; } } } // // Draw Screen // if (gs!=null) { gs.drawImage(screen, 0, 0, null); } Thread.yield(); } while (isActive()); // Tidy up //gs.dispose(); socket.close(); } /** Process Keyboard and Mouse Events */ @Override public boolean handleEvent(Event e) { switch((e.id-1)|1) { case java.awt.Event.KEY_PRESS: case java.awt.Event.KEY_ACTION: keyboard[e.key] = (e.id&1)==1; return true; case java.awt.Event.MOUSE_DOWN: mouse = (e.id&1)==1; mouseX = e.x; mouseY = e.y; return true; default: } return false; } }