/*

 the 1986 Moria game ported to java, with everything the same or better !


 */

import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;

public class test6 extends Applet
{
	// buffer pre-display, to avoid any flickering
	Image back, offI;
	Graphics offG;
	
    Image imwall, imunknown, imunseen;
	Image implayer, imstairasc, imstairdes, imhit, imdanger;
	Image impitfall, implouf;
	String dirimages="images/";
	MediaTracker tracker;
	
	String FileToRead;
	String RemoteAddr;
	
	// get the component size of this applet
	Dimension dim = size();

	// sounds
    AudioClip sdmonsterhit, sdhitmonster, sdmissmonster, sddeath;
	String dirsounds="sounds/";
	
	// minimal graphical display, and graphical multiples
	int xbase= 5;
	int ybase= 10;
	int cx= 10;
	int cy= 10;
	int cy1=12;
	int imx=16;
	int imy=16;
	int iml=16;
	int mapc=7;
	int xun=3;
	int yun=3;
	double letterw=5.5;
	int startingtime=0;
	
	
	// number of dungeon levels, and levels size
	int lmax= 15;
	int xmax= 55;
	int ymax= 45;
	
	
	// GAME TEXTS
	// help mode text
	int helpnblines=12;
	String helptxt[]= { "", "Moria v0.2 2002/04/03", "", "* : inventaire", "/ : poser objet", "enter : utiliser objet ou escalier", "c : cheat mode",
			"t : test mode", "f : afficher faim", "d : afficher map", "h : afficher aide", "g : mode graphique ou texte", "m : afficher la carte" };
	// ingame texts
	String zapstring= "Vous êtes téléporté.";
	String strmonsterhit= " vous touche.";
	String stryourdeadmeat= "Vous êtes mort...";
	String strmonstermisses= " vous rate.";
	String strendofslowness= "Vous respirez à nouveau normalement.";
	String strendofacceleration= "Vous vous sentez plus calme.";
	String strendofsleep= "Vous pouvez bouger à nouveau.";
	String strfaimwarning= "Vous commencez à avoir faim...";
	String strfaimwarning2= "A manger !";
	String strfaimshortwarning= "faim ! ";
	String strfaimdeath= "Vous êtes mort de faim...";
	String strfaimwarninginv= "Vous avez trop mangé...";
	String strfaimdeathinv= "Vous êtes mort d'avoir trop manger !";
	String strsoifwarning= "Vous commencez à avoir soif...";
	String strsoifwarning2= "A boire !";
	String strsoifshortwarning= "soif ! ";
	String strsoifdeath= "Vous êtes mort de soif...";
	String strsoifwarninginv= "Vous avez trop bu...";
	String strsoifcreatebutthole= "Vous vous êtes soulagé...";
	String strsoifdeathinv= "Vous êtes mort d'avoir trop bu !";
	String strkillmonster= "Vous avez tué ";
	String strwelcomenewlevel= " bienvenue au niveau ";
	String strhitmonster= "Vous avez touché ";
	String strdropitem= "Vous posez l'objet à terre.";
	String strdropmustunreadyitem= "Rangez l'objet avant de pouvoir le poser.";
	String strcannotdropitem= "Impossible de poser un objet à cet endroit.";
	String strgrabitem= "Vous avez ramassé ";
	String strcannotgrabitem= "Vous êtes trop chargé pour ramasser ";
	String strmissmonster= "Vous ratez ";
	String strchooseidentify= "Choisissez un objet à identifier.";
	String strchooseuncurse= "Choisissez un objet maudit à enlever.";
	String strworn= " (en service)";
	String strready= " (prêt)";
	String strpotiontext[]= { "", "Vous vous sentez mieux.", "Vous vous sentez beaucoup mieux !", "Vous êtes beaucoup plus fort !",
			"Aie. Ca va vraiment très mal.", "Ca fait du bien !", "", "Vous vous sentez plus adroit.", "Vous vous sentez plus fort.",
			"Quelle force ! Quels muscles !", "Aie. Vous vous sentez plus faible.", "Vous avez du mal à respirer.",
			"Votre coeur bat comme un fou.", "Miam, en voilà une bonne potion !", "Hein ? Quoi ? Où vais-je ?", "Où suis-je ??" };
	String strscrolltext[]= { "", "Tiens mais il y a une carte sur ce parchemin !", "Vous savez où se trouve l'or dans ce niveau.",
			"Vous savez où se trouvent les rations dans ce niveau.", "Vous savez où se trouvent les objets dans ce niveau.",
			"Où suis-je ??", "", "", "Un voile doré englobe votre armure.",
			"Une musique tonitruante vous accompagne.", "Votre arme brille.", "Vous faites apparaitre un monstre !",
			"C'est un parchemin d'identification. Choisissez un objet à identifier.", "", "Une lumière aveuglante vous accompagne.",
			"Vous pouvez retirer un objet maudit !", "Vous voyez les monstres.", "", "", "BOUM !", "Votre armure se ternit...", "Votre arme se ternit..." };
	String strnoeffect= "Vous ne constatez pas d'effet.";
	String strfarceurscroll[]= { "", "Zut à celui qui me lira.", "Ce parchemin vous est offert par Moria Ltd.",
			"Il n'y a rien d'écrit sur ce parchemin.", "Le parchemin fou a encore frappé." };
	String strnofreefinger= "Vous ne pouvez plus enfiler d'anneau.";
	String strwearring= "Vous enfilez ";
	String strcutfinger= "Aaargh !!";
	String strwanddirection= "Quelle direction ?";
	String strcurseditem= "Impossible, vous êtes frappés par une malédiction.";
	String strendofblinding= "La lumière aveuglante s'est éteinte.";
	String strendofdeafening= "La musique s'est arrêtée.";
	String strnothingcarried= "Vous n'avez aucun objet !";
	String strwaitforenter= " (appuyez sur entrée)";
	String strlevel= "niveau ";
	String strgold= "or ";
	String strsoussol= "sous-sol ";
	String strdisplayssol= "Niv ";
	String strdisplaygold= "Or ";
	String strdisplaylife= "Vie ";
	String strdisplaydex= "Dex ";
	String strdisplaystr= "For ";
	String strdisplayca= "Armure ";
	String strdisplayxp= "Exp ";
	String strkilledby= "tué par ";
	String strmapmode= "Carte du niveau... appuyez sur m.";
	String strstart= "Jouer";
	String strend= "Score";
	String stritemdepleted= "(vide)";
	String strfellinpitfall= "Vous êtes tombé dans une trappe !";
	String strgloriousdeath= "Vous avez battu le jeu !!!";
	String strplouf= "Plouf !";
	String strrouille= "Votre armure rouille !";
	String strriskofdrowning= "Vous buvez la tasse !";
	String strdeathdrowning= "Vous vous noyez...";
	String strsamering= "Vous portez déjà un anneau de ce type.";
	String stridentificationring= "Humpf !";
	String strsleepwarning= "Vous êtes endormi !";
	String strcannotshoot= "Vous ne pouvez pas tirer !";
	String strblockedshoot= "Vous êtes gêné pour tirer !";
	String strreadyweapon= "Arme de poing prête.";
	String strreadybow= "Arc prêt.";
	
	
	// GAME PARAMETERS
	int startingturn=0; // the starting turn
	int startingssol=1; // the starting dungeon level
	int endingssol=6; // the temporary end of game
	int endingbonus=500; // the temporary end of game
	int strengthminimum=6; // absolute strength minimum
	String startinghp= "0D0+12"; // the starting hp
	String startingdex= "0D0+14"; // the starting dexterity
	String startingstr= "0D0+16"; // the starting strength
	int maxvib=7; // max distance of vision
	int disttohear=6; // starting distance at which monster will be trigerred
	int nbtourpv=8; // number of base turns to gain one hp back
	int nbtourpvbonus=0; // bonus to previous
	int nbdoigts=4; // number of rings you can use at the same time
	int faimdepart=1000; // starting hungry level
	int faimration=200; // what you get for each level of food
	int faimwarning=100; // time for the warning
	int faimwarninginv=3400; // time for the inverted warning
	int faimdeathinv=4000; // inverted hunger death
	int soifdepart=2005; // starting thirsty level
	int soifpotion=500; // what you get for each potion
	int soifwarning=100; // time for the warning
	int soifwarninginv=3400; // time for the inverted warning
	int soifdeathinv=4000; // inverted thirst death
	int nbfoodforonespace=4; // one inventory space is used by this many food rations
	int nbarrowforonespace=20; // one inventory space is used by this many arrows
	int xppaliers[]= { 0, 10, 30, 60, 120, 240, 500, 1000, 2000, 4000, 8000, 20000 }; // limits for gaining a new level
	int tacopaliers[]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; // taco for each level
	int dextacobonus[]= { 0, 0, 0, 0, 0, -3, -3, -2, -2, -1, -1,
			0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
			4, 4, 5, 5, 6, 6, 7, 7,	8, 8,
			9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
	int strdamagebonus[]= { 0, 0, 0, 0, 0, -4, -4, -3, -3, -2, -2,
			-1, -1, 0, 0, 0, 0, 1, 1, 2, 2,
			3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
			8, 8, 9, 9, 10, 10, 11, 11, 12, 12 };
	String hpgain[]= { "", "1D7+2", "1D7+2", "1D7+2", "1D7+2", "1D7+2", "1D7+2", "1D7+2", "1D7+2", "1D7+2", "1D7+2", "1D7+2"}; // hp gain for each new level
	String startingequip[]= { "", "F1", "Am+0", "Wa", "Wf20", "Wm+0" }; // the player starts with these objects
	String testingequipbonus[]= { "" }; // he also has these objects in testing mode
	int seearoundx=10; // limit zone of vision in graphical mode (x)
	int seearoundy=10; // limit zone of vision in graphical mode (y)
	double dangerlimit=0.25; // hp limit to display a warning
	double fallinginpitfall=0.20; // hp loss when falling in pitfall
	int startingdrowningchances=2; // you avoid this many times
	int drowningperc=50; // chances of drowning are % of encumbrance minus this
	int minimummalus=-5; // minimum malus on armor or weapon
	// levels parameters
	int[] roomsize= { 0, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 7, 7 }; // pattern of size of rooms in a level
	int chancesofbigroom=90; // percentage chance of having a large room in a level
	int chancesofremovepillar=80; // percentage chance of removing a lonely pillar in a level
	int minsizeofbigroom=5; // minimum size of the room
	int maxsizeofbigroom=15; // maximum size of the room
	String basenbitemsinlevel="1D20+16"; // n° of items in a level
	int testnbitemsinlevel=40; // n° of items in a level, test mode
	int nbmonstersinlevel=32; // initial n° of monsters in a level
	int monsterspawn=50; // one new monster is spawned every this number of turns
	int maxmonsterinalevel=50; // up to this maximum number in a level
	String nbpitfalls="1D5-2"; // nb of pitfalls in one level
	String nbbuttholes="1D5-2"; // nb of waterholes in one level
	Image imground[]=new Image[lmax+1];
	// starting cheat modes
	boolean graphical=true; // graphical mode
	boolean cheating=false; // you dont die
	boolean testing=false; // you get more items in a level, and one illimited identification scroll
	boolean displaymapbuild=false; // check level building
	boolean displayfaim=false; // display hunger and thirst
	boolean displaymap=false; // display complete map and all monsters and items (no fog of war or lines of vision)
	
	
	// MONSTERS VALUES
	String Tmonsterchar[]= 	{ "", "A", "C", "S", "R", "L", "g", "O", "U", "v", "W", "T" }; // character displayed
	int monsternbtotal=Tmonsterchar.length-1;
	Image immonster[]=new Image[Tmonsterchar.length];
	String Tmonsterlong[]= 	{ "", "l'Araignée", "la Chauve-souris", "le Serpent", "le Rat", "le Loup", "le Gobelin", "l'Orc", "l'Uruk-Haï", "le Variag", "le Wharg", "le Troll" }; // long name
	int Tmonsterxpvalue[]=	{  0,     1,       1,      2,      2,      3,      5,      10,       30,       50,      70,      150};
	String Tmonstertaco[]=	{ "","0D0+2","0D0+4","0D0+3","0D0+7","0D0+6","0D0+5", "0D0+6",   "0D0+7", "0D0+8", "0D0+9", "0D0+10"};
	String TmonsterCa[]=	{ "","0D0+3","0D0+5","0D0+6","0D0+8","1D2+3","1D3+3", "1D3+4",   "1D4+4", "1D4+6", "1D6+4",  "1D6+6"};
	String Tmonsterhp[]=	{ "","1D2+3","1D2+3","1D3+5","1D4+2","1D3+7","1D7+11","1D8+13","1D10+15","2D6+15","2D8+16","2D10+20"};
	String Tmonsterdegats[]={ "","1D2+0","1D3+0","1D3+0","1D2+0","1D4+1","1D4+3", "1D6+3",   "2D4+4", "2D6+6", "4D4+5", "3D6+10"};
	String Tmonsterbehave[]={ "","RAAAA","RSSSF","RAAAA","RAAFF","RAAFA","RAAFA","RAAFA","RAAAA","RAAAA","RAAAA","RAAAA" }; // monster behaviours
	// behaviours can be : IARSF = Immobile (static), Aggressive, Random, Semirandom (50% random 50% aggressive), Fleeing
	// behaviours ordered for these occasions : default, see player or at distance of detection, attacked, blinded, deafened
	int chancesofrandom=25; // percentage chance of a semirandom behaviour being random
	int monstercabonusinvisible=4; // ca bonus for invisible monster
	int Tmonstertable[][]= { 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  // relative chances of spawning a monster at any level
		{ 0, 30, 15, 20, 10, 10,  5,  0,  0,  0,  0,  0 },
		{ 0,  0, 20, 15, 20, 10, 10,  5,  0,  0,  0,  0 },
		{ 0,  0,  0, 20, 15, 20, 10, 10,  5,  0,  0,  0 },
		{ 0,  0,  0,  0, 20, 15, 20, 10, 10,  5,  0,  0 },
		{ 0,  0,  0,  0,  0, 20, 15, 20, 10, 10,  5,  0 },
		{ 0,  0,  0,  0,  0,  0, 20, 15, 20, 10, 10,  5 }
	};
	
	
	// ITEMS VALUES
	String Tliftstr= " >< #";
	String Titemsstr= " :!?o/^=$";
	String TitemsLCP= " FpsrwAWT";
	int itemsnbtotal=TitemsLCP.length()-1;
	Image imobject[]=new Image[TitemsLCP.length()];
	int Titemstable[]= { 0, 20, 20, 25, 5, 7, 5, 15, 30 }; // relative chances of creating one item of this sort
	// weapons
	String TWeapon= " depmhlcMsE2GVEfa"; // character used in LCP string
	String TWeaponlong[]= { "", "une dague", "une dague elfique", "un poignard", "une massue", "une hache", "une lance", "un cimeterre",
			"un Marteau de Thor", "un sabre", "une epée", "une épée à deux mains", "une épée de glace", "une épée vampirique", "une épée elfique",
			"une flèche", "un arc" };
	int weapontotal=TWeapon.length()-1; // n° of weapons
	int TWeaponcat[]= { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 4, 3 }; // weapon category : handweapon=1, both hands weapon=2, bow=3, bow ammo=4
	String Tdegats[]= { "1D2+0", "1D3+0", "1D3+1", "1D4+0", "1D6+0", "2D3+0", "1D6+1", "1D8+0", "1D10+0", "2D4+0",
			"2D5+0", "2D6+2", "2D5+0", "2D5+0", "2D8+2", "1D6+0", "0D0+0" };
	int TWeapontable[]= { 0, 5, 4, 5, 10, 10, 10, 10, 0, 8, 7, 5, 4, 3, 3, 50, 10 }; // relative chances of creating one item of this kind
	int vampiricdrain=50; // percentage of given hits that are restored to player's hp
	int chancesoffreezing=25; // percentage chance of freezing monster with ice sword
	String TWeaponbonus[]= { "", "+0", "+1", "+2", "+3", "-1", "-2", "-3" }; // weapon bonuses
	int TWeaponbonustable[]= { 0, 30, 8, 4, 1, 8, 4, 1 }; // and relative chances of getting them
	String arrowstacks= "1D6+4"; // number of arrows on the ground
	String arrowmany= "flèches";
	String arrowone= "flèche";
	// armors
	String TArmor= " crmfaMhe";
	String TArmorlong[]= { "", "une armure de cuir", "une armure de cuir renforcée", "une cotte de mailles", "une armure de fer", "une cuirasse d'acier",
			"une armure de Mithril", "un heaume", "une cape elfique"};
	int armortotal=TArmor.length()-1;
	int TArmorcat[]= { 0, 1, 1, 1, 1, 1, 1, 2, 3 }; // armor category : armor=1, helmet=2, cape=3, shield=4
	String Tca[]= { "", "0D0+4", "0D0+5", "0D0+6", "0D0+8", "0D0+10", "0D0+12", "0D0+1", "0D0+0" };
	int TArmortable[]= { 0, 20, 20, 16, 8, 4, 2, 10, 5 }; // relative chances of creating one item of this kind
	double elvishcapedisttohearmult=0.5; // modifier for distance at which monsters will hear
	String TArmorbonus[]= { "", "+0", "+1", "+2", "-1", "-2" }; // armor bonuses
	int TArmorbonustable[]= { 0, 15, 4, 2, 4, 2 }; // and relative chances of getting them
	// potions
	String Tpotion= " svedESDfpmraMcA";
	String Tpotionstring= "une potion ";
	String Tpotionlong[]= { "", "de soins.", "de pleine vie.", "d'expérience.", "de drain.", "d'eau.", "de sommeil.",
			"de dextérité.", "de force.", "de puissance.", "maudite.", "de ralentissement.", "d'accélération.",
			"alimentaire.", "de confusion.", "d'amnésie." };
	int potiontotal=Tpotion.length()-1;
	String Tpotionstuff[]= { "", "rouge", "bleue", "gludure", "brune", "multicolore", "jaune", "incolore", "cuivrée", "moirée", "blanche", "jaune", "rose", "beige", "chinée", "violette", "mauve", "dorée", "noire", "mordorée", "grise" };
	int potionstuffnb=20; // n° of colors of potions
	int potionwhatstuff[]= new int[potiontotal+1];
	boolean[] potionknown= new boolean[potiontotal+1]; // this type of potions is known or not
	String potionslowtiming= "0D0+16"; // number of rounds for player slowed
	String potionacceleratetiming= "0D0+20"; // number of rounds for player accelerated
	int Tpotiontable[]= { 0, 20, 5, 2, 2, 20, 20, 8, 8, 4, 4, 10, 10, 8, 5, 5 }; // relative chances of creating one item of this kind
	// scrolls
	String Tscroll= " conOAtsvMbiIzluVUDECW";
	String Tscrollstring= "un parchemin ";
	String Tscrollnamedstring= "intitulé ";
	String Tscrolllong[]= { "", "avec une carte.", "de localisation d'or.", "de localisation de nourriture.", "de localisation d'objets.", "d'amnésie.",
			"de téléportation.", "de sommeil.", "de voile doré.", "de cacophonie.", "d'arme brillante.",
			"d'invocation de monstre.",	"d'identification.", "de merde.", "de lumière aveuglante.", "de délivrance de la malédiction.",
			"de vision des monstres", "de téléportation au-dessus", "de téléportation en-dessous", "d'explosion", "de malédiction d'armure",
			"de malédiction d'arme" };
	int scrolltotal=Tscroll.length()-1;
	String Tscrollstuff[]= { "", "mer", "ash", "kri", "to", "azg", "al", "xi", "uk", "et", "zy", "dur", "ka", "ion", "os", "glu", "sog", "ky", "les", "tul" };
	int scrollstuffnb=19;
	String scrollwhatstuff[]= new String[scrolltotal+1];
	boolean[] scrollknown= new boolean[scrolltotal+1];
	String Tscrollblindingtiming= "2D30+50"; // timing for the effect of blinding scroll
	String Tscrolldeafeningtiming= "2D30+50"; // timing for the effect of deafening scroll
	String Tscrollidentifycost= "1D50+50"; // cost for using an identify scroll
	String Tscrollmonstervision= "1D20+10"; // timing for the effect of monster vision scroll
	int Tscrolltable[]= { 0, 15, 20, 20, 20, 0, 15, 10, 20, 15, 30, 15, 40, 20, 10, 10, 20, 10, 10, 20, 30 }; // relative chances of creating one item of this kind
	// rings
	String Tring= " crdtRpmfvIFA";
	String Tringstring= "un anneau ";
	String Tringlong[]= { "", "coupe-doigt.", "de régénération", "de ralentissement de digestion ", "de téléportation.", "de résurrection.",
			"de protection", "de monstres", "de chute de plume", "de vision des trappes", "d'identification", "pour conserver sa force",
			"pour protéger son armure" };
	int ringtotal=Tring.length()-1;
	String Tringstuff[]= { "", "d'or", "de jade", "d'argent", "de bronze", "de rubis", "de diamant", "d'os", "d'ivoire", "de jaspe", "d'opale", "d'émeraude", "de platine", "d'onyx", "de mithril" };
	int ringstuffnb=14;
	int ringwhatstuff[]=new int[ringtotal+1];
	boolean[] ringknown= new boolean[ringtotal+1];
	int ringdigestioneffet=5; // number of turns for when the digest ring will help (the slower the faster help)
	String ringresurrectnbofuse="1D10+2"; // number of times your resurrect ring will resurrect you
	int ringresurrectlastusebonus=20; // the hps you get for the last use of the ring
	double ringdisttohearmult=2; // modifier for distance at which monsters will hear
	String ringteleporttiming= "1D30+20"; // timing used each time for next teleport of player
	int ringidentstrloss=3; // one strength point is lost for each x objects identified
	String Tringregenerationbonus[]= { "", "+0", "+1", "+2", "-1", "-2" }; // this ring bonuses
	int Tringregenerationbonustable[]= { 0, 0, 10, 5, 10, 5 }; // and relative chances of getting them
	String Tringprotectionbonus[]= { "", "+0", "+1", "+2", "-1", "-2" }; // this ring bonuses
	int Tringprotectionbonustable[]= { 0, 0, 10, 5, 10, 5 }; // and relative chances of getting them
	int Tringtable[]= { 0, 15, 10, 20, 15, 2, 20, 15, 20, 15, 10, 15 }; // relative chances of creating one item of this kind
	int reduceringfactor=2; // chance of finding one type of ring is reduced whenever it is created
	// wands
	String Twand= " icfwaItTmMp";
	String Twandstring= "une baguette ";
	String Twandchargesmany= " charges";
	String Twandchargesone= " charge";
	String Twandlong[]= { "", "d'invocation de monstre", "de combat", "d'immobilisation", "d'affaiblissement", "d'accélération",
			"d'invisibilité", "de création de trappe", "de comblement de trappe", "de création de mur", "de suppression de mur",
			" de sourcier" };
	int wandtotal=Twand.length()-1;
	String Twandstuff[]= { "", "de platane", "de teck", "d'acajou", "de charme", "d'orme", "de chataigner", "de cerisier", "de noisetier", "de saule", "de chène", "d'érable" };
	int wandstuffnb=11;
	int wandwhatstuff[]= new int[wandtotal+1];
	String wandcharges="1D6+5"; // nb of charges for any wand
	String degatswandcombat="3D6+5"; // damages for combat wand
	String timefreezewand="0D0+20"; // time for frozen monster
	String timeacceleratewand="0D0+10"; // time for accelerated monster
	String timeinvisiblewand="0D0+20"; // time for invisible monster
	double weakenwand=0.5; // monster ca and taco are multiplied by this value with weakening wand
	boolean[] wandknown= new boolean[wandtotal+1];
	int Twandtable[]= { 0, 15, 10, 15, 20, 15, 15, 5, 5, 10, 10, 10 }; // relative chances of creating one item of this kind
	// treasure
	String Ttreasure[][]= { { "", "", "", "", "" }, // for each level, four possible amounts of gold per stack
		{ "", "1D6+5", "1D6+5", "1D8+25", "1D8+25" },
		{ "", "1D10+10", "1D10+10", "1D20+30", "1D20+30" },
		{ "", "1D15+20", "1D15+20", "2D20+40", "2D20+40" },
		{ "", "1D30+40", "1D30+40", "2D30+80", "2D30+100" },
		{ "", "1D50+50", "1D50+50", "3D50+100", "3D50+100" },
		{ "", "1D80+80", "1D80+80", "4D60+100", "4D60+100" }
	};
	String goldpieces= " pièces d'or";
	// food
	String faimtxt[]= { "", "Pouah, quelle horreur !", "Beuh, ca a vraiment un drôle de goût.", "Bof, c'est bizarre.", "Pas mal du tout !", "Mes compliments au chef !" };
	String foodmany= "rations";
	String foodone= "ration";
	
	
	// player values : position, caracteristics
	int xpos, ypos;
	int level, xp, taco, tacomagicbonus, ca, camagicbonus, hpmax, hp, dex, str, gold, faim, soif;
	int drowningchances, scrollmonstervision;
	int statshitstaken, statshitsgiven, statsnbrations, statsnbpotions, statsnbusescrolls, statsnbuserings, statsnbusewands, statsnbuseunknown;
	int statsnbtourwait, statsnbinvent, statsnbremontee, statsnboverloaded, statsnbdrop, statsnbhits, statsnbmiss, statsnbmap, statsnbfuite, statsnbpicked;
	int statsnbhitsmonster, statsnbmissmonster, statskillonehit, statsnbavanceonmonster, statslucky, statsunlucky, statsscars;
	int statshitsgivenbow, statsnbhitsbow, statsnbmissbow, statsnbdice20, statsnbdice1;
	String diestring="";
	int[] statskills= new int[monsternbtotal+1];
	double disttohearmult=1; // multiplier to the distance the player is heard by monsters
	// other player values
	int turn, ssol; // current turn and current dungeon level
	int[] doigts=new int[nbdoigts+1]; // what rings are being used
	int potionconfusion=0; // confusion in action
	int potionhallu=0; // hallucinations in action
	boolean ringteleport=false; // teleport ring in use
	boolean ringresurrect=false; // resurrect ring in use
	boolean ringdigestion=false; // digest ring in use
	boolean ringfeatherfall=false; // featherfall ring in use
	boolean ringtrapseeing=false; // trapseeing ring in use
	boolean ringkeepstrength=false; // keep strength ring in use
	boolean ringarmorprotect=false; // armor protecting ring in use
	int ringteleportturn; // teleport ring parameter
	boolean blindthem=false, deafenthem=false; // effects of blinding and deafening scrolls are on
	int scrollblind, scrolldeafen; // scrolls parameters
	int weaponworn, arcworn, armorworn, helmetworn, capeworn, shieldworn; // specific items currently active
	boolean weaponmode, arcmode;
	int itemcursor, itemcursorval; // inventory cursor and corresponding item
	
	
	
	// game modes
	boolean playing=false; // player is playing
	boolean savedone=false; // player is playing
	boolean mapmode=false; // display whole map
	boolean inventory=false; // display and manage inevntory
	boolean displayhelp=false; // display help - duh
	boolean waitforenter=false; // important message displayed, wait for enter key
	boolean waitforstart=true; // applet init
	
	String message=""; // main message on top of screen
	int wandering=0; // player is using a wand
	boolean bowing=false; // player is using a bow
	boolean identify=false; // player is using an identify scroll
	boolean uncurse=false; // player is using an uncurse scroll
	// out of normal sync actions (player slowed or accelerated)
	boolean playmonsters=true;
	boolean doanotherturn=false;
	int sleep, slowed, accelerated;
	
	// buttons
	Button buttstart, buttend;
	String stemp;
	
	// ITEMS
	public class Titem {
		String detail; // details : what sort, what kind, parameters
		boolean carried, worn, known, alive; // alive = still in game, worn = activated
		int val; // used for inventory display order
		
		Titem () {
			this.carried=false;
			this.worn=false;
			this.known=false;
			this.alive=true;
			this.val=0;
		}
		
		void create () {
			this.carried=false;
			this.worn=false;
			this.known=false;
			this.alive=true;
			this.val=0;
		}
	}
	int itemmax=1000; // max n° of items in game at the same time
	int itemsnb; // current n° of items in game
	int itemscarried; // n° of carried items
	Titem[] item=new Titem[itemmax]; // main item management
	int[] iteminv=new int[itemmax]; // reorder for inventory
	int[] iteminvval=new int[itemmax]; // temporary use for inventory reorder
	
	
	// MONSTERS
	public class Tmonster {
		int ssol, xpos, ypos; // dungeon level and position
		boolean alive;
		int type, taco, ca, hp, hpmax, xpvalue; // type of monster and rpg values
		String monsterlong, letter, degats; // name, char, damage
		int object, affected; // carried object, is monster affected by something
		char affect, behave; // what is the affect, current behaviour
		
		Tmonster(int ssol) {
			this.ssol=ssol;
			this.alive=true;
			this.type=tablelookup(Tmonstertable[ssol]);
			this.monsterlong=Tmonsterlong[this.type];
			this.letter=Tmonsterchar[this.type];
			this.taco=calc(Tmonstertaco[this.type]);
			this.ca=calc(TmonsterCa[this.type]);
			this.hp=calc(Tmonsterhp[this.type]);
			this.hpmax=this.hp;
			this.degats=Tmonsterdegats[this.type];
			this.xpvalue=Tmonsterxpvalue[this.type];
			this.object=0;
			this.affect=' ';
			this.behave=Tmonsterbehave[this.type].charAt(0);
			this.affected=0;
		}
		
		void create(int ssol) {
			this.ssol=ssol;
			this.alive=true;
			this.type=tablelookup(Tmonstertable[ssol]);
			this.monsterlong=Tmonsterlong[this.type];
			this.letter=Tmonsterchar[this.type];
			this.taco=calc(Tmonstertaco[this.type]);
			this.ca=calc(TmonsterCa[this.type]);
			this.hp=calc(Tmonsterhp[this.type]);
			this.hpmax=this.hp;
			this.degats=Tmonsterdegats[this.type];
			this.xpvalue=Tmonsterxpvalue[this.type];
			this.object=0;
			this.affect=' ';
			this.behave=Tmonsterbehave[this.type].charAt(0);
			this.affected=0;
		}
	}
	int monsternb=0; // current n° of monsters in game
	int monstermax=1000; // max n° of monsters in game
	Tmonster[] monster=new Tmonster[monstermax]; // main monsters management
	
	// basic variables
	int xposmem, yposmem;
	int x, y, a, b, xl, yl, i, j, k, l, m, n;
	String string;
	boolean enter, left, right, up, down, move, donextturn; // keyboard management
	
	// maps management
	boolean isleveldone[]= new boolean[lmax+1]; // has level been created
	int iswall[][][]=new int[lmax+1][xmax+1][ymax+1]; // walls
	int isoccupied[][][]=new int[lmax+1][xmax+1][ymax+1]; // monsters and player
	int isobject[][][]=new int[lmax+1][xmax+1][ymax+1]; // objects
	int islift[][][]=new int[lmax+1][xmax+1][ymax+1]; // exits (stairs, traps)
	int isflood[][][]=new int[lmax+1][xmax+1][ymax+1]; // used for levels building
	boolean isknown[][][]=new boolean[lmax+1][xmax+1][ymax+1]; // known for map display (fog of war)
	boolean isobjectknown[][][]=new boolean[lmax+1][xmax+1][ymax+1]; // place of object known for map display
	boolean isseen[][]=new boolean[xmax+1][ymax+1]; // currently seen (lines of vision)
	int nbmonstersoflevel[]= new int[lmax+1]; // current nb of monsters in a level
	int displayhits[][]= new int[xmax+1][ymax+1]; // write down and
	
	// lines of vision
	int ombrex[][]= { 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
		{ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
		{ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 },
		{ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 9 }, { 0, 0, 1, 2, 3, 4, 5, 6, 0, 7, 8, 9 },
		{ 0, 0, 1, 2, 3, 4, 5, 0, 6, 7, 8, 0 }, { 0, 0, 1, 2, 3, 4, 0, 5, 6, 0, 7, 8 },
		{ 0, 0, 1, 2, 0, 3, 4, 0, 5, 6, 7, 8 }, { 0, 0, 0, 1, 2, 3, 0, 4, 5, 0, 6, 7 },
		{ 0, 0, 0, 0, 1, 2, 3, 0, 4, 5, 6, 0 }, { 0, 0, 0, 0, 0, 1, 2, 3, 0, 4, 5, 6 },
		{ 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 5 }, { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
	int ombrey[][]= {	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4 }, { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0 },
		{ 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 5 }, { 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 5, 6 },
		{ 0, 0, 0, 0, 1, 2, 3, 0, 4 ,5, 6, 0 }, { 0, 0, 0, 1, 2, 3, 0, 4, 5, 0, 6, 7 },
		{ 0, 0, 1, 2, 0, 3, 4, 0, 5, 6, 7, 8 }, { 0, 0, 1, 2, 3, 4, 0, 5, 6, 0, 7, 8 },
		{ 0, 0, 1, 2, 3, 4, 5, 0, 6, 7, 8, 0 }, { 0, 0, 1, 2, 3, 4, 5, 6, 0, 7, 8, 9 },
		{ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 9 }, { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 },
		{ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
		{ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
		{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 } };
	int mx[]= { 0, 1, -1, -1, 1 };
	int my[]= { 0, 1, 1, -1, -1 };
	
	String[] strdata=new String[1001];
	void loaddata1() {
		int i=0;
		
		String line;
		String file = getCodeBase().getFile();
		try{
			BufferedReader s=new BufferedReader(new FileReader(file+getParameter("c:/moriadata.txt")));
			while((line=s.readLine())!=null) {
				i++;
				strdata[i]=line;
			}
		}
		catch (IOException ioe) {
			Toolkit.getDefaultToolkit().beep();
			ioe.printStackTrace();
		}
	}
	
	void decrunchdata() {
		int i=0, j;
		
		while (strdata[i+1]!=null) {
			i++;
			if (strdata[i].compareTo("zapstring")==0) { zapstring=strdata[i+1]; }
			if (strdata[i].compareTo("strmonsterhit")==0) { strmonsterhit=strdata[i+1]; }
			if (strdata[i].compareTo("stryourdeadmeat")==0) { stryourdeadmeat=strdata[i+1]; }
			if (strdata[i].compareTo("strmonstermisses")==0) { strmonstermisses=strdata[i+1]; }
			if (strdata[i].compareTo("strendofslowness")==0) { strendofslowness=strdata[i+1]; }
			if (strdata[i].compareTo("strendofacceleration")==0) { strendofacceleration=strdata[i+1]; }
			if (strdata[i].compareTo("strendofsleep")==0) { strendofsleep=strdata[i+1]; }
			if (strdata[i].compareTo("strfaimwarning")==0) { strfaimwarning=strdata[i+1]; }
			if (strdata[i].compareTo("strfaimwarning2")==0) { strfaimwarning2=strdata[i+1]; }
			if (strdata[i].compareTo("strfaimshortwarning")==0) { strfaimshortwarning=strdata[i+1]; }
			if (strdata[i].compareTo("strfaimdeath")==0) { strfaimdeath=strdata[i+1]; }
			if (strdata[i].compareTo("strfaimwarninginv")==0) { strfaimwarninginv=strdata[i+1]; }
			if (strdata[i].compareTo("strfaimdeathinv")==0) { strfaimdeathinv=strdata[i+1]; }
			if (strdata[i].compareTo("strsoifwarning")==0) { strsoifwarning=strdata[i+1]; }
			if (strdata[i].compareTo("strsoifwarning2")==0) { strsoifwarning2=strdata[i+1]; }
			if (strdata[i].compareTo("strsoifshortwarning")==0) { strsoifshortwarning=strdata[i+1]; }
			if (strdata[i].compareTo("strsoifdeath")==0) { strsoifdeath=strdata[i+1]; }
			if (strdata[i].compareTo("strsoifwarninginv")==0) { strsoifwarninginv=strdata[i+1]; }
			if (strdata[i].compareTo("strsoifdeathinv")==0) { strsoifdeathinv=strdata[i+1]; }
			if (strdata[i].compareTo("strsoifcreatebutthole")==0) { strsoifcreatebutthole=strdata[i+1]; }
			if (strdata[i].compareTo("strkillmonster")==0) { strkillmonster=strdata[i+1]; }
			if (strdata[i].compareTo("strwelcomenewlevel")==0) { strwelcomenewlevel=strdata[i+1]; }
			if (strdata[i].compareTo("strhitmonster")==0) { strhitmonster=strdata[i+1]; }
			if (strdata[i].compareTo("strdropitem")==0) { strdropitem=strdata[i+1]; }
			if (strdata[i].compareTo("strdropmustunreadyitem")==0) { strdropmustunreadyitem=strdata[i+1]; }
			if (strdata[i].compareTo("strcannotdropitem")==0) { strcannotdropitem=strdata[i+1]; }
			if (strdata[i].compareTo("strgrabitem")==0) { strgrabitem=strdata[i+1]; }
			if (strdata[i].compareTo("strcannotgrabitem")==0) { strcannotgrabitem=strdata[i+1]; }
			if (strdata[i].compareTo("strmissmonster")==0) { strmissmonster=strdata[i+1]; }
			if (strdata[i].compareTo("strchooseidentify")==0) { strchooseidentify=strdata[i+1]; }
			if (strdata[i].compareTo("strchooseuncurse")==0) { strchooseuncurse=strdata[i+1]; }
			if (strdata[i].compareTo("strworn")==0) { strworn=strdata[i+1]; }
			if (strdata[i].compareTo("strready")==0) { strready=strdata[i+1]; }
			if (strdata[i].compareTo("strfellinpitfall")==0) { strfellinpitfall=strdata[i+1]; }
			if (strdata[i].compareTo("strgloriousdeath")==0) { strgloriousdeath=strdata[i+1]; }
			if (strdata[i].compareTo("strplouf")==0) { strplouf=strdata[i+1]; }
			if (strdata[i].compareTo("strrouille")==0) { strrouille=strdata[i+1]; }
			if (strdata[i].compareTo("strriskofdrowning")==0) { strriskofdrowning=strdata[i+1]; }
			if (strdata[i].compareTo("strdeathdrowning")==0) { strdeathdrowning=strdata[i+1]; }
			if (strdata[i].compareTo("strsamering")==0) { strsamering=strdata[i+1]; }
			if (strdata[i].compareTo("stridentificationring")==0) { stridentificationring=strdata[i+1]; }
			if (strdata[i].compareTo("strsleepwarning")==0) { strsleepwarning=strdata[i+1]; }
			if (strdata[i].compareTo("strcannotshoot")==0) { strcannotshoot=strdata[i+1]; }
			if (strdata[i].compareTo("strblockedshoot")==0) { strblockedshoot=strdata[i+1]; }
			if (strdata[i].compareTo("strreadyweapon")==0) { strreadyweapon=strdata[i+1]; }
			if (strdata[i].compareTo("strreadybow")==0) { strreadybow=strdata[i+1]; }
			if (strdata[i].compareTo("strpotiontext")==0) {
				for (j=1; j<=potiontotal; j++) {
					i++;
					strpotiontext[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("strscrolltext")==0) {
				for (j=1; j<=scrolltotal; j++) {
					i++;
					strscrolltext[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("strnoeffect")==0) { strnoeffect=strdata[i+1]; }
			if (strdata[i].compareTo("strfarceurscroll")==0) {
				for (j=1; j<=4; j++) {
					i++;
					strfarceurscroll[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("strnofreefinger")==0) { strnofreefinger=strdata[i+1]; }
			if (strdata[i].compareTo("strwearring")==0) { strwearring=strdata[i+1]; }
			if (strdata[i].compareTo("strcutfinger")==0) { strcutfinger=strdata[i+1]; }
			if (strdata[i].compareTo("strwanddirection")==0) { strwanddirection=strdata[i+1]; }
			if (strdata[i].compareTo("strcurseditem")==0) { strcurseditem=strdata[i+1]; }
			if (strdata[i].compareTo("strendofblinding")==0) { strendofblinding=strdata[i+1]; }
			if (strdata[i].compareTo("strendofdeafening")==0) { strendofdeafening=strdata[i+1]; }
			if (strdata[i].compareTo("strnothingcarried")==0) { strnothingcarried=strdata[i+1]; }
			if (strdata[i].compareTo("strwaitforenter")==0) { strwaitforenter=strdata[i+1]; }
			if (strdata[i].compareTo("strlevel")==0) { strlevel=strdata[i+1]; }
			if (strdata[i].compareTo("strgold")==0) { strgold=strdata[i+1]; }
			if (strdata[i].compareTo("strsoussol")==0) { strsoussol=strdata[i+1]; }
			if (strdata[i].compareTo("strdisplayssol")==0) { strdisplayssol=strdata[i+1]; }
			if (strdata[i].compareTo("strdisplaygold")==0) { strdisplaygold=strdata[i+1]; }
			if (strdata[i].compareTo("strdisplaylife")==0) { strdisplaylife=strdata[i+1]; }
			if (strdata[i].compareTo("strdisplaydex")==0) { strdisplaydex=strdata[i+1]; }
			if (strdata[i].compareTo("strdisplaystr")==0) { strdisplaystr=strdata[i+1]; }
			if (strdata[i].compareTo("strdisplayca")==0) { strdisplayca=strdata[i+1]; }
			if (strdata[i].compareTo("strdisplayxp")==0) { strdisplayxp=strdata[i+1]; }
			if (strdata[i].compareTo("strkilledby")==0) { strkilledby=strdata[i+1]; }
			if (strdata[i].compareTo("strmapmode")==0) { strmapmode=strdata[i+1]; }
			if (strdata[i].compareTo("strstart")==0) { strstart=strdata[i+1]; }
			if (strdata[i].compareTo("strend")==0) { strend=strdata[i+1]; }
			if (strdata[i].compareTo("stritemdepleted")==0) { stritemdepleted=strdata[i+1]; }
			if (strdata[i].compareTo("Tmonsterlong")==0) {
				for (j=1; j<=monsternbtotal; j++) {
					i++;
					Tmonsterlong[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("TWeaponlong")==0) {
				for (j=1; j<=weapontotal; j++) {
					i++;
					TWeaponlong[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("TArmorlong")==0) {
				for (j=1; j<=armortotal; j++) {
					i++;
					TArmorlong[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("Tpotionstring")==0) { Tpotionstring=strdata[i+1]; }
			if (strdata[i].compareTo("Tpotionlong")==0) {
				for (j=1; j<=potiontotal; j++) {
					i++;
					Tpotionlong[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("Tpotionstuff")==0) {
				for (j=1; j<=potionstuffnb; j++) {
					i++;
					Tpotionstuff[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("Tscrollstring")==0) { Tscrollstring=strdata[i+1]; }
			if (strdata[i].compareTo("Tscrollnamedstring")==0) { Tscrollnamedstring=strdata[i+1]; }
			if (strdata[i].compareTo("Tscrolllong")==0) {
				for (j=1; j<=scrolltotal; j++) {
					i++;
					Tscrolllong[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("Tscrollstuff")==0) {
				for (j=1; j<=scrollstuffnb; j++) {
					i++;
					Tscrollstuff[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("Tringstring")==0) { Tringstring=strdata[i+1]; }
			if (strdata[i].compareTo("Tringlong")==0) {
				for (j=1; j<=ringtotal; j++) {
					i++;
					Tringlong[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("Tringstuff")==0) {
				for (j=1; j<=ringstuffnb; j++) {
					i++;
					Tringstuff[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("Twandstring")==0) { Twandstring=strdata[i+1]; }
			if (strdata[i].compareTo("Twandchargesmany")==0) { Twandchargesmany=strdata[i+1]; }
			if (strdata[i].compareTo("Twandchargesone")==0) { Twandchargesone=strdata[i+1]; }
			if (strdata[i].compareTo("Twandlong")==0) {
				for (j=1; j<=wandtotal; j++) {
					i++;
					Twandlong[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("Twandstuff")==0) {
				for (j=1; j<=wandstuffnb; j++) {
					i++;
					Twandstuff[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("goldpieces")==0) { goldpieces=strdata[i+1]; }
			if (strdata[i].compareTo("faimtxt")==0) {
				for (j=1; j<=5; j++) {
					i++;
					faimtxt[j]=strdata[i];
				}
			}
			if (strdata[i].compareTo("foodmany")==0) { foodmany=strdata[i+1]; }
			if (strdata[i].compareTo("foodone")==0) { foodone=strdata[i+1]; }
			if (strdata[i].compareTo("arrowmany")==0) { arrowmany=strdata[i+1]; }
			if (strdata[i].compareTo("arrowone")==0) { arrowone=strdata[i+1]; }
		}
	}
	
	void checkfile() {
		File f = new File("c:\\moriadata.txt");
		if (!(f.exists())) {
			System.out.println("File  " + f + " not found");
//			System.exit(0);
			cheating=true;
		}
	}
	
//	String FileToRead="file:///C:/moriadata.txt";
	void loaddata2() {
		int i=0;
		
		String param = getParameter("FileToRead");
		if ( param != null){
			FileToRead = new String(param);
		}
		
		String line;
		URL url=null;
		
		try {
			url = new URL (getCodeBase(), FileToRead );
		}
		catch (MalformedURLException  e ) {
			System.out.println("Malformed URL ");
			stop();
		}
		
		try {
			InputStream in=url.openStream();
			BufferedReader dis = new BufferedReader(new InputStreamReader(in));
			
			while ((line = dis.readLine()) != null){
				i++;
				strdata[i]=line;
			}
			
			in.close();
		}
		catch (IOException e ) {}
	}
	
	void loaddata3() {
		int i=0;
		
		try
		{
			// Open the file that is the first
			// command line parameter
			FileInputStream fstream = new FileInputStream("file://C:/moriadata.txt");
			
			// Convert our input stream to a
			// DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			
			// Continue to read lines while
			// there are still some left to read
			while (in.available() !=0) {
				i++;
				strdata[i]=in.readLine();
			}
			in.close();
		}
		catch (Exception e) {
			System.err.println("File input error");
		}
	}
	
	void loaddata4(String file) {
		int i=0;
		String line = new String();
		
		try {
			// create a URL (the URL for the text file on the server) then open a stream
			URL textURL = new URL( getCodeBase() , file );
//			URL textURL = new URL( "file://C:/moriadata.txt" );
			DataInputStream theStream = new DataInputStream( textURL.openStream() );
			
			// now read the lines one at a time
			while ( ( line = theStream.readLine() ) != null ) {
				i++;
				strdata[i]=line;
			} // close while loop
			
			theStream.close();
		} // close try
		catch ( Exception e ) {
			e.printStackTrace();
		}
	}
	/*
	 void writedata4() {
	 int i=0;
	 String line = new String();
	 
	 try {
	 // create a URL (the URL for the text file on the server) then open a stream
	 URL textURL = new URL( getCodeBase() , "moriadata2txt" );
//			URL textURL = new URL( "file://C:/moriadata.txt" );
	 DataOutputStream theStream;
	 
	 // now read the lines one at a time
	 while ( strdata[i+1]!= "" ) {
	 i++;
	 theStream.writeChars(strdata[i]);
	 } // close while loop
	 
	 theStream.close();
	 } // close try
	 catch ( Exception e ) {
	 e.printStackTrace();
	 }
	 }*/
	
	void loaddata5() {
		int i=0;
//		JFileChooser fileChooser = new JFileChooser();
//		int retVal = fileChooser.showOpenDialog(statusLabel);
		
		try{
//			BufferedReader br = new BufferedReader(new FileReader(fileChooser.getSelectedFile()));
			BufferedReader br = new BufferedReader(new FileReader("c:\\moriadata.txt"));
			String c;
			
			while((c = br.readLine()) != null){
				i++;
				strdata[i]=c;
			}
			
			br.close();
		}
		catch (IOException ioe) {
			Toolkit.getDefaultToolkit().beep();
			ioe.printStackTrace();
		}
	}
	
	void loaddata6() {
		int i=0;
		
		try{
			File fichier = new File ("moriadata.txt");
			BufferedReader in = new BufferedReader(new FileReader(fichier));
			String c;
			
			while((c = in.readLine()) != null){
				i++;
				strdata[i]=c;
			}
			in.close();
		}
		catch (IOException ioe) {
			Toolkit.getDefaultToolkit().beep();
			ioe.printStackTrace();
		}
	}
	/*
	 En écriture:
	
	 File fichier = new Fichier ("monFichier.txt");
	
	 BufferedWrite out = new BuffererWriter(new FileWriter(fichier));
	
	 out.write(texte, debut, fin) ;
	 */
	
	void loaddata7() {
		int i=0;
		
		//Code to read from file
		try {
			String inputFileName =
				System.getProperty("user.home",
								   "c:" ) +
//			File.separatorChar + "home" +
//			File.separatorChar + "monicap") +
				File.separatorChar + "moriadata.txt";
			File inputFile = new File(inputFileName);
			BufferedReader in = new BufferedReader(new FileReader(inputFile));
//		char c[] = new char[(char)inputFile.length()];
//		in.read(c);
//		s = new String(c);
			String c;
			
			while((c = in.readLine()) != null){
				i++;
				strdata[i]=c;
			}
			
			in.close();
		}
		catch (IOException ioe) {
			Toolkit.getDefaultToolkit().beep();
			ioe.printStackTrace();
		}
	}
	
	
	
	
	// extract a LCP "xDy+z" value and calculate it
	int calc(String form) {
		int i, res, l1, l2, d1, d2, d3, d3n;
		res=0;
		
		l1=form.indexOf('D'); // find positions
		l2=form.indexOf('+');
		d3n=1;
		if (l2==-1) {
			l2=form.indexOf('-');
			d3n=-1;
		}
		d1=Integer.parseInt(form.substring(0, l1), 10); // extract values
		d2=Integer.parseInt(form.substring(l1+1, l2), 10);
		if (l2==-1) {
			d3=0;
		} else {
			d3=Integer.parseInt(form.substring(l2+1), 10);
		}
		
		for (i=1; i<=d1; i++) { // now roll the dice
			res+=de(d2);
		}
		res+=d3*d3n; // and add any bonus
		
		return res;
	}
	
	// calculates what the player can see at this point
	void checkseen(){
		int x, y, d, p, l, cx, cy;
		boolean visib;
		
		// reset field of vision
		for (y=1; y<=ymax; y++) {
			for (x=1; x<=xmax; x++) {
				isseen[x][y]=false;
			}
		}
		/*
		 // the fast method, a square, doesnt take any walls or obstacles into account
		 
		 for (y=Math.max(1, ypos-distvision); y<=Math.min(ymax, ypos+maxvib); y++) {
		 for (x=Math.max(1, xpos-distvision); x<=Math.min(xmax, xpos+maxvib); x++) {
		 isseen[x][y]=true;
		 isknown[ssol][x][y]=true;
		 isobjectknown[ssol][x][y]=true;
		 }
		 }
		 */
		
		// the player's position is seen !
		isseen[xpos][ypos]=true;
		isknown[ssol][xpos][ypos]=true;
		isobjectknown[ssol][xpos][ypos]=true;
		// the long method, real lines of visions
		for (d=1; d<=4; d++) { // for each quarter of field of vision
			for (l=5; l<=17; l++) { // for each line of vision
				// normally should go fom 1 to 21 (go through all lines of vision
				// however distance of vision is only 7, and the first and last 4 lines are thus useless
				// should be able to calculate which lines to go through depending on dist of vision
				visib=true;
				for (p=1; p<=11; p++) { // go one square at a time on the line
					if (!((ombrex[l][p]==0) && (ombrey[l][p]==0))) {
						if (p>maxvib) { // gone too far, cant see anymore on this line
							visib=false;
						}
						x=ombrex[l][p]*mx[d]+xpos; // get relative position
						y=ombrey[l][p]*my[d]+ypos;
						if ((x>=1) && (y>=1) && (x<=xmax) && (y<=ymax)) {
							isseen[x][y]=visib;
							if (visib) { // if you can see, then you see objects and know the place
								isknown[ssol][x][y]=true;
								isobjectknown[ssol][x][y]=true;
							}
							if (iswall[ssol][x][y]!=0) { // obstacle, cant see anymore on this line
								visib=false;
							}
						}
					}
				}
			}
		}
	}
	
	// calculates distance between two points, used for pathfinding
	double dist(int x1, int y1, int x2, int y2) {
		return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
	}
	
	// all four directions
	int[] dirx= { 0, 0, 1, 0, -1};
	int[] diry= { 0, -1, 0, 1, 0};
	int godir;
	double min, d;
	
	// pathfinding, return choice of direction to be closest to player
	int findnext(int x1, int y1, int x2, int y2) {
		int i, j, k, xc, yc;
		double min;
		boolean search;
		int[] randomdir=new int[5];
		
		for (i=1; i<=4; i++) {
			do {
				search=false;
				randomdir[i]=de(4);
				for (j=0; j<=i-1; j++) {
					if (randomdir[j]==randomdir[i]) {
						search=true;
					}
				}
			} while (search);
		}

		k=0;
		min=xmax+ymax;
		for (i=1; i<=4; i++) { // checks all directions
			xc=x2+dirx[randomdir[i]];
			yc=y2+diry[randomdir[i]];
			if (iswall[ssol][xc][yc]==0) { // if possible
				if (isoccupied[ssol][xc][yc]==0) { // if empty
					if ((islift[ssol][xc][yc]!=3) && (islift[ssol][xc][yc]!=4)) { // if no trap
						d=dist(x1, y1, xc, yc);
						if (d<=min) { // get closest to player
							min=d;
							k=randomdir[i];
						}
					}
				}
			}
		}
		return k;
	}
	
	// pathfinding, return choice of direction to be farthest to player
	int findnextfar(int x1, int y1, int x2, int y2) {
		int i, k, xc, yc;
		double max;
		
		k=0;
		max=0;
		for (i=1; i<=4; i++) { // checks all directions
			xc=x2+dirx[i];
			yc=y2+diry[i];
			if (iswall[ssol][xc][yc]==0) { // if possible
				if (isoccupied[ssol][xc][yc]==0) { // if empty
					if ((islift[ssol][xc][yc]!=3) && (islift[ssol][xc][yc]!=4)) { // if no trap
						d=dist(x1, y1, xc, yc);
						if (d>=max) { // get farthest to player
							max=d;
							k=i;
						}
					}
				}
			}
		}
		return k;
	}
	
	// pathfinding, return choice of direction for random movement
	int findnextrandom(int x1, int y1) {
		int i, k, l, xc, yc;
		int choices[]=new int[5];
		
		k=0;
		l=0;
		min=xmax+ymax;
		for (i=1; i<=4; i++) { // checks all directions
			xc=x1+dirx[i];
			yc=y1+diry[i];
			if (iswall[ssol][xc][yc]==0) { // if possible
				if (isoccupied[ssol][xc][yc]==0) { // if empty
					if ((islift[ssol][xc][yc]!=3) && (islift[ssol][xc][yc]!=4)) { // if no trap
						l++;
						choices[l]=i;
					}
				}
			}
		}
		k=choices[de(l)];
		return k;
	}
	
	// is value odd or even
	boolean odd(int value) {
		return (value % 2 == 1);
	}
	
	// dice throwing, 1 to value
	int de(int value) {
		return (int)(Math.random()*value+1);
	}
	
	// return player level given the amount of xp
	int givelevel(int xp) {
		boolean search=true;
		int i;
		
		i=0;
		do {
			i++;
			if (xp<xppaliers[i]) { search=false; }
		} while (search);
		
		return i;
	}
	
	// recursive function to flood an array
	void doflood(int ssol, int x1, int y1, int v) {
		int i;
		
		isflood[ssol][x1][y1]=v;
		for (i=1; i<=4; i++) {
			if (isflood[ssol][x1+dirx[i]][y1+diry[i]]==0) {
				doflood(ssol, x1+dirx[i], y1+diry[i], v);
			}
		}
	}
	
	// return a table dice, given a table with proportional weights
	int tablelookup(int[] table) {
		int sum=0, d, i;
		
		for (i=1; i<=table.length-1; i++) {
			sum+=table[i];
		}
		d=de(sum);
		sum=0;
		i=0;
		do {
			i++;
			sum+=table[i];
		} while (d>sum);
		
		return i;
	}
	
	// return the value of the item, to reorder in the inventory
	int itemvalue(String theitem) {
		int res=0;
		
		res=TitemsLCP.indexOf(theitem.substring(0, 1))*1000;
		switch (theitem.charAt(0)) {
			case 'W' : { // weapon
					res+=TWeapon.indexOf(theitem.substring(1, 2));
					break;
				}
			case 'A' : { // armor
					res+=TArmor.indexOf(theitem.substring(1, 2));
					break;
				}
			case 'p' : { // potion
					res+=Tpotion.indexOf(theitem.substring(1, 2));
					break;
				}
			case 's' : { // scroll
					res+=Tscroll.indexOf(theitem.substring(1, 2));
					break;
				}
			case 'r' : { // ring
					res+=Tring.indexOf(theitem.substring(1, 2));
					break;
				}
			case 'w' : {
					res+=Twand.indexOf(theitem.substring(1, 2));
					break;
				}
		}
		
		return res;
	}
	
	// create a complete level, given the dungeon depth (to choose the monsters and items)
	void createlevel(int ssol) {
		int wall= 0, x1, y1, xl, yl;
		int nbitemsinlevel;
		boolean search;
		int stairx, stairy;
		
		if (testing) {
			nbitemsinlevel=testnbitemsinlevel;
		} else {
			nbitemsinlevel=calc(basenbitemsinlevel);
		}
		// these are used to clean the level
		int walls[]=new int[1000];
		int wallsx[][]=new int[1000][500];
		int wallsy[][]=new int[1000][500];
		int wallssame[]=new int[1000];
		int wallsnb=0;
		boolean isnewwall;
		
		// clean all arrays
		for (y=0; y<=ymax; y++) {
			for (x=0; x<=xmax; x++) {
				isoccupied[ssol][x][y]=0;
				isobject[ssol][x][y]=0;
				islift[ssol][x][y]=0;
				isknown[ssol][x][y]=false;
				isobjectknown[ssol][x][y]=false;
			}
		}
		
		// we need to create the map itself
		// first step : fill with basic walls
		for (y=0; y<=ymax; y++) {
			for (x=0; x<=xmax; x++) {
				wall=0;
				if (odd(x)) { wall=1; }
				if (odd(y)) { wall=1; }
				if (x==0) { wall=1; }
				if (y==0) { wall=1; }
				if (x==1) { wall=1; }
				if (y==1) { wall=1; }
				if (x==xmax) { wall=1; }
				if (y==ymax) { wall=1; }
				iswall[ssol][x][y]=wall;
			}
		}
		
		// second step : go through columns to add rooms randomly
		y=2;
		do {
			x=2;
			do {
				do {
					l= roomsize[de(roomsize.length-1)];
				} while ((xmax-x+1)<l);
				xl=l-1;
				yl=0;
				b=y;
				for (a=x; a<=x+xl; a++) {
					iswall[ssol][a][b]=0;
				}
				x+=l+1;
			} while (x<xmax-2);
			y+=2;
		} while (y<=ymax-1);
		
		// also go through lines to add rooms randomly
		x=2;
		do {
			y=2;
			do {
				do {
					l= roomsize[de(roomsize.length-1)];
				} while ((ymax-y+1)<l);
				yl=l-1;
				xl=0;
				a=x;
				for (b=y; b<=y+yl; b++) {
					iswall[ssol][a][b]=0;
				}
				y+=l+1;
			} while (y<ymax-2);
			x+=2;
		} while (x<=xmax-1);
		
		// put perimeter back just to be sure :p
		for (y=1; y<=ymax; y++) {
			for (x=1; x<=xmax; x++) {
				if (x==xmax) { iswall[ssol][x][y]=1; }
				if (y==ymax) { iswall[ssol][x][y]=1; }
			}
		}
		
		// third step : clean level, allowing the player to be able to reach any point
		// initialize the flood array map
		for (y=1; y<=ymax; y++) {
			for (x=1; x<=xmax; x++) {
				isflood[ssol][x][y]=iswall[ssol][x][y];
			}
		}
		
		// flood the map, each separated area getting a different value
		k=1;
		for (y=1; y<=ymax; y++) {
			for (x=1; x<=xmax; x++) {
				if (isflood[ssol][x][y]==0) {
					k++;
					doflood(ssol, x, y, k);
				}
			}
		}
		
		// find connection points between separated areas and tag them
		for (y=2; y<=ymax-1; y++) {
			for (x=2; x<=xmax-1; x++) {
				if (isflood[ssol][x][y]==1) { // we're looking for walls
					// that have different values on their left and right, or on their top and bottom
					if ((odd(x) && !odd(y) && (isflood[ssol][x-1][y]!=isflood[ssol][x+1][y])) || (!odd(x) && odd(y) && (isflood[ssol][x][y-1]!=isflood[ssol][x][y+1]))) {
						isflood[ssol][x][y]=Math.max(isflood[ssol][x-1][y], isflood[ssol][x+1][y])*1000+Math.min(isflood[ssol][x-1][y], isflood[ssol][x+1][y]);
						
						isnewwall=true; // do we have the same kind of connection tag already ?
						for (i=1; i<=wallsnb; i++) {
							if (walls[i]==isflood[ssol][x][y]) { // yes we do
								wallssame[i]++; // add one of this kind
								wallsx[i][wallssame[i]]=x; // mark its position
								wallsy[i][wallssame[i]]=y;
								isnewwall=false; // and dont create a new family
							}
						}
						if (isnewwall) { // create a new family of tag if this a new tag
							wallsnb++;
							walls[wallsnb]=isflood[ssol][x][y];
							wallssame[wallsnb]=1;
							wallsx[wallsnb][1]=x; // mark its position
							wallsy[wallsnb][1]=y;
						}
					}
				}
			}
		}
		
		// now, remove one tagged wall for each family of tag, thus connecting each separated areas that can be connected !
		for (i=1; i<=wallsnb; i++) {
			k=de(wallssame[i]);
			iswall[ssol][wallsx[i][k]][wallsy[i][k]]=0;
		}
		
		// the level may have a large room
		if (de(100)<=chancesofbigroom) {
			x1=de(xmax-8-minsizeofbigroom)+4;
			if (odd(x1)) { x1++; }
			y1=de(ymax-8-minsizeofbigroom)+4;
			if (odd(y1)) { y1++; }
			xl=de(maxsizeofbigroom-minsizeofbigroom+1)+minsizeofbigroom-1-1;
			if (odd(xl)) { xl++; }
			yl=de(maxsizeofbigroom-minsizeofbigroom+1)+minsizeofbigroom-1-1;
			if (odd(yl)) { yl++; }
			for (y=y1; y<=Math.min(ymax-4, y1+yl); y++) {
				for (x=x1; x<=Math.min(xmax-4, x1+xl); x++) {
					iswall[ssol][x][y]=0;
				}
			}
		}
		
		// test : removing all isolated pillars, to create rooms
		for (y=2; y<=ymax-1; y++) {
			for (x=2; x<=xmax-1; x++) {
				if (iswall[ssol][x][y]==1) {
					search=true;
					for (l=1; l<=4; l++) {
						if (iswall[ssol][x+dirx[l]][y+diry[l]]==1) {
							search=false;
						}
					}
					if ((search) && (de(100)<=chancesofremovepillar)) {
						iswall[ssol][x][y]=0;
					}
				}
			}
		}
		
		// place stairs
		if (ssol>1) {
			stairx=2;
			stairy=2;
			for (y=2; y<=ymax-1; y++) {
				for (x=2; x<=xmax-1; x++) {
					if (islift[ssol-1][x][y]==1) {
						stairx=x;
						stairy=y;
					}
				}
			}
			iswall[ssol][stairx][stairy]=0;
			islift[ssol][stairx][stairy]=2; // ascending stair
		}
		
		do { // search a possible, empty, and unoccupied place
			x=de(xmax);
			y=de(ymax);
		} while ((iswall[ssol][x][y]!=0) || (islift[ssol][x][y]!=0));
		islift[ssol][x][y]=1; // descending stair
		
		// place some pitfalls
		if (ssol!=endingssol) {
			k=calc(nbpitfalls);
			if (k>0) {
				for (i=1; i<=k; i++) {
					x=de((int)(Math.floor(xmax/2)))*2;
					y=de((int)(Math.floor(ymax/2)))*2;
					islift[ssol][x][y]=3; // unknown pitfall
				}
			}
		}
		
		// place some waterholes
		k=calc(nbbuttholes);
		if (k>0) {
			for (i=1; i<=k; i++) {
				do { // search a possible, empty, and unoccupied place
					x=de(xmax);
					y=de(ymax);
				} while ((iswall[ssol][x][y]!=0) || (islift[ssol][x][y]!=0));
				islift[ssol][x][y]=5; // waterhole
			}
		}
		
		// at this point the map has been created
		// now place items
		
		for (i=1; i<=nbitemsinlevel; i++) {
			do { // find an empty and useable place
				x=de(xmax);
				y=de(ymax);
			} while ((iswall[ssol][x][y]==1) || (isobject[ssol][x][y]!=0) || (islift[ssol][x][y]!=0));
			k=createitem(); // get a pointer
			j=tablelookup(Titemstable); // random category of items given the table of appearance
			isobject[ssol][x][y]=k; // place the object on the ground
			
			item[k].detail=TitemsLCP.substring(j,j+1); // start constructing the item LCP string
			switch (item[k].detail.charAt(0)) { // given the sort of item finish the description
				case 'W' : { // weapon
					j=tablelookup(TWeapontable); // random choice of this sort of item
					item[k].detail+=TWeapon.substring(j,j+1);
					if (TWeapon.substring(j,j+1).compareTo("f")==0) {
						j=calc(arrowstacks);
						item[k].detail+=j;
					} else {
						j=tablelookup(TWeaponbonustable); // add the magic bonus
						item[k].detail+=TWeaponbonus[j];
					}
					break;
				}
				case 'A' : { // armor
						j=tablelookup(TArmortable); // random choice of this sort of item
						item[k].detail+=TArmor.substring(j,j+1);
						j=tablelookup(TArmorbonustable); // add the magic bonus
						item[k].detail+=TArmorbonus[j];
						break;
					}
				case 'F' : { // food
						item[k].detail+="1";
						break;
					}
				case 'T' : { // treasure
						j=de(4);
						j=calc(Ttreasure[ssol][j]); // calculate an amount of gold
						item[k].detail+=j;
						break;
					}
				case 'p' : { // potion
						j=tablelookup(Tpotiontable); // random choice of this sort of item
						item[k].detail+=Tpotion.substring(j,j+1);
						break;
					}
				case 's' : { // scroll
						j=tablelookup(Tscrolltable); // random choice of this sort of item
						item[k].detail+=Tscroll.substring(j,j+1);
						break;
					}
				case 'r' : { // ring
					j=tablelookup(Tringtable); // random choice of this sort of item
					Tringtable[j]=(int)(Math.floor(Tringtable[j]/reduceringfactor)); // reduce chance of finding another such ring
						item[k].detail+=Tring.substring(j,j+1);
						switch (item[k].detail.charAt(1)) { // some rings need a parameter
							case 'r' : { // regeneration
									l=tablelookup(Tringregenerationbonustable);
									item[k].detail+=Tringregenerationbonus[l];
									break;
								}
							case 'R' : { // resurrect, in fact a counter for number of (automatic) uses
									item[k].detail+=calc(ringresurrectnbofuse);
									break;
								}
							case 'p' : { // protection
									l=tablelookup(Tringprotectionbonustable);
									item[k].detail+=Tringprotectionbonus[l];
									break;
								}
						}
						break;
					}
				case 'w' : {
						j=tablelookup(Twandtable); // random choice of this sort of item
						l=calc(wandcharges);
						item[k].detail+=Twand.substring(j,j+1)+l;
						break;
					}
			}
			item[k].val=itemvalue(item[k].detail); // note down the item value
		}
		
		// items have been placed, the level is ready for use
	}
	
	// get a pointer for a new item
	int createitem() {
		int i, k;
		
		k=0;
		i=0;
		if (itemsnb>0) {
			do {
				i++;
				if (!item[i].alive) { // an unused pointer is found, use this one
					k=i;
					item[k].create();
				}
			} while ((k==0) && (i<itemsnb));
		}
		
		if (k==0) { // create an entirely new one
			itemsnb++;
			k=itemsnb;
			item[k] = new Titem();
		}
		
		return k;
	}
	
	// get a pointer for a new monster
	int createmonster(int ssol) {
		int i, k;
		
		k=0;
		i=0;
		if (monsternb>0) { // an unused pointer is found, use this one
			do {
				i++;
				if (!monster[i].alive) {
					k=i;
					monster[k].create(ssol);
				}
			} while ((k==0) && (i<monsternb));
		}
		
		if (k==0) { // create an entirely new one
			monsternb++;
			k=monsternb;
			monster[k] = new Tmonster(ssol);
		}
		nbmonstersoflevel[ssol]++;
		placemonster(ssol, k);
		isoccupied[ssol][monster[k].xpos][monster[k].ypos]=k;
		
		return k;
	}
	
	
	// main applet
	public void init () {
		String s;
		
//		checkfile();
		s = getParameter("startingtime");
		if (s!=null) {
			startingtime = Integer.valueOf(s).intValue();
		}
		s = getParameter("remoteaddr");
		if ( s != null){
			RemoteAddr = new String(s);
		}

		loaddata4("languk.txt");
		decrunchdata();
		
		buttstart= new Button(strstart);
		buttend= new Button(strend);
		
		for (i=1; i<=endingssol; i++) {
			s=String.valueOf(i);
			if (i<=9) {
				s="0"+s;
			}
			imground[i] = getImage(getDocumentBase(), dirimages+"ground"+s+".gif");
		}
		impitfall = getImage(getDocumentBase(), dirimages+"pitfall.gif");
		implouf = getImage(getDocumentBase(), dirimages+"plouf.gif");
		imwall = getImage(getDocumentBase(), dirimages+"wall.gif");
		imunknown = getImage(getDocumentBase(), dirimages+"unknown.gif");
		imunseen = getImage(getDocumentBase(), dirimages+"unseen.gif");
		
		implayer = getImage(getDocumentBase(), dirimages+"player.gif");
		immonster[1] = getImage(getDocumentBase(), dirimages+"monspider.gif");
		immonster[2] = getImage(getDocumentBase(), dirimages+"monbat.gif");
		immonster[3] = getImage(getDocumentBase(), dirimages+"monsnake.gif");
		immonster[4] = getImage(getDocumentBase(), dirimages+"monrat.gif");
		immonster[5] = getImage(getDocumentBase(), dirimages+"monwolf.gif");
		immonster[6] = getImage(getDocumentBase(), dirimages+"mongoblin.gif");
		immonster[7] = getImage(getDocumentBase(), dirimages+"monorc.gif");
		immonster[8] = getImage(getDocumentBase(), dirimages+"monuruk.gif");
		immonster[9] = getImage(getDocumentBase(), dirimages+"monvariag.gif");
		immonster[10] = getImage(getDocumentBase(), dirimages+"monwharg.gif");
		immonster[11] = getImage(getDocumentBase(), dirimages+"montroll.gif");
		imobject[1] = getImage(getDocumentBase(), dirimages+"food.gif");
		imobject[2] = getImage(getDocumentBase(), dirimages+"potion.gif");
		imobject[3] = getImage(getDocumentBase(), dirimages+"scroll.gif");
		imobject[4] = getImage(getDocumentBase(), dirimages+"ring.gif");
		imobject[5] = getImage(getDocumentBase(), dirimages+"wand.gif");
		imobject[6] = getImage(getDocumentBase(), dirimages+"armor.gif");
		imobject[7] = getImage(getDocumentBase(), dirimages+"weapon.gif");
		imobject[8] = getImage(getDocumentBase(), dirimages+"treasure.gif");
		imstairasc = getImage(getDocumentBase(), dirimages+"stairasc.gif");
		imstairdes = getImage(getDocumentBase(), dirimages+"stairdes.gif");
		imhit = getImage(getDocumentBase(), dirimages+"hit.gif");
		imdanger = getImage(getDocumentBase(), dirimages+"danger.gif");
		
	    sdmonsterhit = getAudioClip(getDocumentBase(), dirsounds+"monsterhit.au");
		sdhitmonster = getAudioClip(getCodeBase(), dirsounds+"hitmonster.au");
		sdmissmonster = getAudioClip(getDocumentBase(), dirsounds+"missmonster.au");
		sddeath = getAudioClip(getDocumentBase(), dirsounds+"death.au");
		
		tracker = new MediaTracker(this);
		for (i=1; i<=11; i++) {
			tracker.addImage(immonster[i], 0);
		}
		for (i=1; i<=8; i++) {
			tracker.addImage(imobject[i], 0);
		}
		tracker.addImage(imhit, 0);
		tracker.addImage(impitfall, 0);
		tracker.addImage(implouf, 0);
		/*		tracker.addImage(imdanger, 0);
		tracker.addImage(imstairasc, 0);
		tracker.addImage(imstairdes, 0);
		tracker.addImage(imground, 0);
		tracker.addImage(imwall, 0);
		tracker.addImage(imunknown, 0);
		tracker.addImage(imunseen, 0);
		 tracker.addImage(implayer, 0);*/
		try {
			//Wait until all images are loaded before doing anything
			// animation or sound.
			tracker.waitForAll();
		} catch(InterruptedException e) {}

		sdhitmonster.play();

		setBackground(Color.white);
		add(buttstart);
		add(buttend);
		waitforstart=true;
		
		repaint();
	}
	
	// initialize objects and their unknown color
	void inititems() {
		boolean search, scrollspace;
		
		// potions are unknown, choose a color for each type
		for (i=1; i<=potiontotal; i++) {
			potionknown[i]= false;
			potionwhatstuff[i]=0;
			do {
				search=false;
				k=de(potionstuffnb);
				for (j=1; j<=i; j++) {
					if (potionwhatstuff[j]==k) {
						search=true;
					}
				}
			} while (search);
			potionwhatstuff[i]=k;
		}
		
		// scrolls are unknown, choose a name for each type
		for (i=1; i<=scrolltotal; i++) {
			scrollknown[i]= false;
			scrollwhatstuff[i]="";
			
			scrollspace=true;
			l=de(5)+7;
			for (j=1; j<=l; j++) {
				if ((!scrollspace) && (de(3)==1)) {
					scrollwhatstuff[i]+=" ";
					scrollspace=true;
				} else {
					scrollspace=false;
					scrollwhatstuff[i]+=Tscrollstuff[de(scrollstuffnb)];
				}
			}
		}
		
		// rings are unknown, choose a metal for each type
		for (i=1; i<=ringtotal; i++) {
			ringknown[i]= false;
			ringwhatstuff[i]=0;
			do {
				search=false;
				k=de(ringstuffnb);
				for (j=1; j<=i; j++) {
					if (ringwhatstuff[j]==k) {
						search=true;
					}
				}
			} while (search);
			ringwhatstuff[i]=k;
		}
		
		// wands are unknown, choose a wood for each type
		for (i=1; i<=wandtotal; i++) {
			wandknown[i]= false;
			wandwhatstuff[i]=0;
			do {
				search=false;
				k=de(wandstuffnb);
				for (j=1; j<=i; j++) {
					if (wandwhatstuff[j]==k) {
						search=true;
					}
				}
			} while (search);
			wandwhatstuff[i]=k;
		}
	}
	
	// start the game altogether
	void startgame() {
		boolean search;
		String equip[]= new String[itemmax];
		int equipnb;
		boolean wornweapon, wornbow;
		
		for (i=1; i<=lmax; i++) {
			isleveldone[i]=false;
			nbmonstersoflevel[i]=0;
		}
		message="";
		
		// player initialization
		level=1;
		xp=0;
		hpmax=calc(startinghp);
		hp=hpmax;
		dex=calc(startingdex);
		str=calc(startingstr);
		taco=tacopaliers[level];
		ca=0;
		gold=0;
		ringteleport=false;
		ringresurrect=false;
		camagicbonus=0;
		slowed=0; accelerated=0;
		faim=faimdepart;
		soif=soifdepart;
		for (i=1; i<=nbdoigts; i++) {
			doigts[i]=0;
		}
		potionconfusion=0;
		potionhallu=0;
		ringteleport=false;
		ringresurrect=false;
		ringdigestion=false;
		ringfeatherfall=false;
		ringtrapseeing=false;
		ringkeepstrength=false;
		ringarmorprotect=false;
		drowningchances=startingdrowningchances;
		scrollmonstervision=0;
		statshitstaken=0;
		statshitsgiven=0;
		statsnbrations=0;
		statsnbpotions=0;
		statsnbusescrolls=0;
		statsnbusewands=0;
		statsnbuserings=0;
		statsnbuseunknown=0;
		statsnbtourwait=0;
		statsnbinvent=0;
		statsnbremontee=0;
		statsnboverloaded=0;
		statsnbdrop=0;
		statsnbhits=0;
		statsnbmiss=0;
		statsnbmap=0;
		statsnbfuite=0;
		statsnbpicked=0;
		
		statsnbhitsmonster=0;
		statsnbmissmonster=0;
		statskillonehit=0;
		statsnbavanceonmonster=0;
		statslucky=0;
		statsunlucky=0;
		statsscars=0;
		statshitsgivenbow=0;
		statsnbhitsbow=0;
		statsnbmissbow=0;
		statsnbdice20=0;
		statsnbdice1=0;
		
		for (i=1; i<=monsternbtotal; i++) {
			statskills[i]=0;
		}
			
		// initialize objects and their unknown color
		inititems();
		
		// gather all starting equipment
		itemsnb=0;
		weaponworn=0; arcworn=0; armorworn=0; helmetworn=0; capeworn=0; shieldworn=0;
		weaponmode=false; arcmode=false;
		equipnb=startingequip.length-1;
		for (i=1; i<=startingequip.length-1; i++) {
			equip[i]=startingequip[i];
		}
		if (testing) {
			equipnb+=testingequipbonus.length-1;
			for (i=1; i<=testingequipbonus.length-1; i++) {
				equip[i+startingequip.length-1]=testingequipbonus[i];
			}
		}
		itemscarried=equipnb;
		
		// and create it
		wornweapon=false;
		wornbow=false;
		for (i=1; i<=equipnb; i++) {
			k=createitem();
			item[k].detail=equip[i]; // grab the item
			item[k].carried=true; // is carried by the player
			item[k].known=true; // item is known
			item[k].val=itemvalue(item[k].detail); // note down the item value
			// depending on the item, ready it, wear it, etc.
			switch (item[k].detail.charAt(0)) {
				case 'W' : { // weapon, ready it
					m=TWeaponcat[TWeapon.indexOf(item[i].detail.substring(1, 2))];
					if (m==3) {
						if (!wornbow) {
							weaponmode=false;
							arcmode=true;
							item[k].worn=true;
							arcworn=i;
							wornbow=true;
						}
					}
					if ((m==1) || (m==2)) {
						if (!wornweapon) {
							weaponmode=true;
							arcmode=false;
							item[k].worn=true;
							weaponworn=i;
							wornweapon=true;
						}
					}
					break;
				}
				case 'A' : { // armor, wear it
						item[k].worn=true;
						switch (TArmorcat[TArmor.indexOf(item[k].detail.substring(1, 2))]) {
							case 1 : {
									armorworn=k;
									break;
								}
							case 2 : {
									helmetworn=k;
									break;
								}
							case 3 : {
									capeworn=k;
									break;
								}
							case 4 : {
									shieldworn=k;
									break;
								}
						}
						break;
					}
				case 'T' : { // treasure, pocket it
						item[k].carried=false;
						item[k].alive=false; // item is also destroyed !
						gold+=getint(item[k].detail.substring(1));
						break;
					}
				case 's' : { // know the category of item
						scrollknown[Tscroll.indexOf(item[k].detail.substring(1, 2))]= true;
						break;
					}
				case 'w' : { // know the category of item
						wandknown[Twand.indexOf(item[k].detail.substring(1, 2))]= true;
						break;
					}
				case 'p' : { // know the category of item
						potionknown[Tpotion.indexOf(item[k].detail.substring(1, 2))]= true;
						break;
					}
				case 'r' : { // ring, wear it
						ringknown[Tring.indexOf(item[k].detail.substring(1, 2))]= true;
						l=0;
						search=true;
						do {
							l++;
							if (doigts[l]==0) {
								search=false;
							}
						} while ((l<nbdoigts) && (search));
						if (!search) {
							doigts[l]=itemcursorval;
							switch (item[itemcursorval].detail.charAt(1)) {
								case 'c' : {
										item[itemcursorval].worn=false;
										break;
									}
								case 'r' : {
										m=getint(item[k].detail.substring(2));
										nbtourpvbonus=m;
										break;
									}
								case 'd' : {
										ringdigestion=true;
										break;
									}
								case 't' : {
										ringteleport=true;
										ringteleportturn=turn+de(60)+20;
										break;
									}
								case 'R' : {
										ringresurrect=true;
										break;
									}
								case 'p' : {
										m=getint(item[k].detail.substring(2));
										camagicbonus+=m;
										break;
									}
							}
						}
					}
			}
		}
		// now that items are created and worn, calculate the ca
		calculateca();
		
		// initialize game state
		itemcursor=1; itemcursorval=1;
		playing=true;
		wandering=0;
		bowing=false;
		identify=false;
		uncurse=false;
		playmonsters=true;
		turn=startingturn;
		ssol=startingssol;
		
		// create current level
		createlevel(ssol);
		
		// place player
		search=true;
		do { // search a possible, empty, and unoccupied place
			x=de(xmax-1)+1;
			y=de(ymax-1)+1;
			if ((iswall[ssol][x][y]==0) && (islift[ssol][x][y]==0) && (isobject[ssol][x][y]==0) && (x>1) && (y>1)) {
				search=false;
			}
		} while (search);
		xpos=x;
		ypos=y;
		isoccupied[ssol][xpos][ypos]=-1;
		// do the first look around
		checkseen();
		
		// create and place monsters
		for (i=1; i<=nbmonstersinlevel; i++) {
			createmonster(ssol);
		}
		isleveldone[ssol]=true;
		
		// and show it all
		repaint();
	}
	
	// return an item description, used for inventory and when picking the item up
	String displayitem(int i) {
		String thestring, s; // return string
		int k, m;
		
		thestring="";
		switch(item[i].detail.charAt(0)) {
			case 'W' : {
				s=item[i].detail.substring(1, 2); // get the kind of item
				m=TWeaponcat[TWeapon.indexOf(item[i].detail.substring(1, 2))];
				if (m==4) {
					thestring+=item[i].detail.substring(2);
					if (getint(item[i].detail.substring(2))>1) {
						thestring+=" "+arrowmany; // possibly more than one
					} else {
						thestring+=" "+arrowone; // only one
					}
				} else {
					k=TWeapon.indexOf(s); // get the kind of item
					thestring+=TWeaponlong[k]; // add the item long name
					if (item[i].known) { // if known, add bonus
						if ((m==1) || (m==2)) {
							thestring+=" "+item[i].detail.substring(2);
						}
					}
				}
				break;
			}
			case 'A' : {
				k=TArmor.indexOf(item[i].detail.substring(1, 2));
				thestring+=TArmorlong[k];
				if (item[i].known) { // if known, add bonus
					thestring+=" "+item[i].detail.substring(2);
				}
				break;
			}
			case 'F' : {
					thestring+=item[i].detail.substring(1);
					if (getint(item[i].detail.substring(1))>1) {
						thestring+=" "+foodmany; // possibly more than one
					} else {
						thestring+=" "+foodone; // only one
					}
					break;
				}
			case 'T' : { // gold only appears in the main message, not in inventory
					thestring+=item[i].detail.substring(1)+" "+goldpieces;
					break;
				}
			case 'p' : {
					thestring+=	Tpotionstring+" ";
					k=Tpotion.indexOf(item[i].detail.substring(1, 2));
					if (potionknown[k]) { // if known, complete description
						thestring=Tpotionlong[k];
					} else { // otherwise, only the stuff seen
						thestring=Tpotionstuff[potionwhatstuff[k]];
					}
					break;
				}
			case 's' : {
					thestring+=Tscrollstring+" ";
					k=Tscroll.indexOf(item[i].detail.substring(1, 2));
					if (scrollknown[k]) { // if known, complete description
						thestring=Tscrolllong[k];
					} else { // otherwise, only the stuff seen
						thestring+=Tscrollnamedstring+" "+scrollwhatstuff[k];
					}
					break;
				}
			case 'r' : {
					thestring+=Tringstring+" ";
					k=Tring.indexOf(item[i].detail.substring(1, 2));
					if (ringknown[k]) { // if known, complete description
						thestring=Tringlong[k];
						if (item[i].known) {
							switch (item[i].detail.charAt(1)) { // complete with bonus if necessary
								case 'r' : {
										thestring+=" "+item[i].detail.substring(2);
										break;
									}
								case 'R' : {
										if (getint(item[i].detail.substring(2))==0) {
											thestring+=" "+stritemdepleted;
										}
										break;
									}
								case 'd' : {
										thestring+=" "+item[i].detail.substring(2);
										break;
									}
								case 'p' : {
										thestring+=" "+item[i].detail.substring(2);
										break;
								}
							}
						}
						
					} else { // otherwise, only the stuff seen
						thestring=Tringstuff[ringwhatstuff[k]];
					}
					break;
				}
			case 'w' : {
					thestring+=Twandstring+" ";
					k=Twand.indexOf(item[i].detail.substring(1, 2));
					if (wandknown[k]) { // if known, complete description
						thestring=Twandlong[k];
						m=getint(item[i].detail.substring(2));
						if (item[i].known) {
							if (m>1) {
								thestring+=" "+m+" "+Twandchargesmany;
							} else {
								thestring+=" "+m+" "+Twandchargesone;
								if (m==0) {
									thestring+=" "+stritemdepleted;
								}
							}
						} else {
							if (m==0) {
								thestring+=" "+stritemdepleted;
							}
						}
					} else { // otherwise, only the stuff seen
						thestring=Twandstuff[wandwhatstuff[k]];
						if (getint(item[i].detail.substring(2))==0) {
							thestring+=" "+stritemdepleted;
						}
					}
					break;
				}
		}
		return thestring;
	}
	
	// find a spot for a monster
	void placemonster(int ssol, int m) {
		boolean search;
		
		search=true;
		do { // search a possible, empty, and unoccupied place
			x=de(xmax);
			y=de(ymax);
			if ((!isseen[x][y]) && (iswall[ssol][x][y]==0) && (isoccupied[ssol][x][y]==0) && (isobject[ssol][x][y]==0)) {
				search=false;
			}
		} while (search);
		monster[m].xpos=x;
		monster[m].ypos=y;
	}
	
	// check if the player could be dead
	void checkfordeath(int t, int k) {
		boolean search;
		
		if (hp<=0) { // player is dead
			if (ringresurrect) { // resurrect ring to the rescue
				j=0;
				search=true;
				do { // search any active resurrect ring (can have more than one active)
					j++;
					if (doigts[j]>0) {
						if (item[doigts[j]].detail.charAt(1)=='R') {
							if (getint(item[doigts[j]].detail.substring(2))>0) { // found one still active
								search=false;
							}
						}
					}
				} while ((j<nbdoigts) && (search));
				if (!search) {
					m=getint(item[doigts[j]].detail.substring(2))-1; // one use less
					item[doigts[j]].detail="rR"+m; // recrunch ring values
					hp=hpmax; // player back to life
					if (m==0) { // last use of the ring
						hp+=ringresurrectlastusebonus; // special hp bonus
					}
				}
			}
			if (hp<=0) { // player is really dead now...
				dodeath(t, k);
			}
		}
	}
	
	// teleport player, to be updated to teleport him one level up or down
	void doteleport(int goingssol) {
		boolean search;
		
		search=true;
		do { // search a possible, empty, and unoccupied place
			x=de(xmax);
			y=de(ymax);
			if ((iswall[goingssol][x][y]==0) && (isoccupied[goingssol][x][y]==0) && (isobject[goingssol][x][y]==0)) {
				search=false;
			}
		} while (search);
		isoccupied[ssol][xpos][ypos]=0;
		xpos=x;
		ypos=y;
		isoccupied[goingssol][xpos][ypos]=-1;
		ssol=goingssol;
		message=zapstring;
	}
	
	// game over
	void dodeath(int t, int k) {
		int i, p;
		
		if (!cheating) { // he wasnt cheating !
			playing=false; // game over
			playmonsters=false;
			switch (t) {
				case 1 : { // killed by monster
					message=stryourdeadmeat+" "; // say so
					message+=strkilledby+" ";
					message+=Tmonsterlong[monster[k].type];
					break;
				}
				case 2 : { // hunger death
					message=strfaimdeath; // say so
					break;
				}
				case 3 : { // thirst death
					message=strsoifdeath; // say so
					break;
				}
				case 4 : { // overfood death
					message=strfaimdeathinv; // say so
					break;
				}
				case 5 : { // overwater death
					message=strsoifdeathinv; // say so
					break;
				}
				case 6 : { // glorious death
					message=strgloriousdeath; // say so
					gold+=endingbonus;
					break;
				}
				case 7 : { // drowning
					message=strdeathdrowning; // say so
					break;
				}
			}
			
			if (k!=0) {
				diestring=t+";"+monster[k].type+";";
			} else {
				diestring=t+";0;";
			}
			level=givelevel(xp);
			diestring+=level+";"+ssol+";"+gold;
			diestring+=";"+hpmax+";"+dex+";"+str;
			diestring+=";"+turn+";"+statshitstaken+";"+statshitsgiven;
			diestring+=";"+statsnbrations+";"+statsnbpotions+";"+statsnbusescrolls+";"+statsnbuserings+";"+statsnbusewands+";"+statsnbuseunknown;
			diestring+=";"+statsnbtourwait+";"+statsnbinvent+";"+statsnbmap+";"+statsnbremontee+";"+statsnboverloaded+";"+statsnbdrop;
			diestring+=";"+statsnbhits+";"+statsnbmiss+";"+statsnbfuite+";"+statsnbpicked;
			p=0;
			if (ssol>1) {
				for (i=1; i<=ssol-1; i++) {
					for (y=1; y<=ymax; y++) {
						for (x=1; x<=xmax; x++) {
							if (isknown[i][x][y]) {
								p++;
							}
						}
					}
				}
				p=(int)(Math.floor((p*100/(xmax*ymax*(ssol-1)))));
			}
			diestring+=";"+p;
			p=0;
			for (y=1; y<=ymax; y++) {
				for (x=1; x<=xmax; x++) {
					if (isknown[ssol][x][y]) {
						p++;
					}
				}
			}
			p=(int)(Math.floor((p*100/(xmax*ymax))));
			diestring+=";"+p;
			
			diestring+=";"+RemoteAddr;
			diestring+=";"+statsnbhitsmonster+";"+statsnbmissmonster+";"+statskillonehit+";"+statsnbavanceonmonster;
			diestring+=";"+statslucky+";"+statsunlucky+";"+statsscars+";"+statshitsgivenbow+";"+statsnbhitsbow;
			diestring+=";"+statsnbmissbow+";"+statsnbdice20+";"+statsnbdice1+";"+"endingtime";
			p=0;
			for (i=1; i<=monsternbtotal; i++) {
				if (statskills[i]>0) {
					p=i;
				}
			}
			diestring+=";"+p;
			for (i=1; i<=p; i++) {
				diestring+=";"+statskills[i];
			}
			diestring+=";";

//			butt.move((int)(Math.floor(dim.width/2-(strstart.length()/2)*10-10)), 200); // start button
//			butt.requestFocus();
			for (i=1; i<=monsternb; i++) { // kill all monsters
				monster[i].alive=false;
			}
			for (i=1; i<=itemsnb; i++) { // kill all objects
				item[i].alive=false;
			}
			
			repaint();
		}
	}
	
	// move monster from A to B
	void movemonster(int i, int l) {
		isoccupied[ssol][monster[i].xpos][monster[i].ypos]=0; // leave place
		monster[i].xpos+=dirx[l]; // move
		monster[i].ypos+=diry[l];
		isoccupied[ssol][monster[i].xpos][monster[i].ypos]=i;
	}
	
	// do the complete aggressive move for a monster
	void doaggressivemove(int i) {
		int l, m;
		boolean indanger=false;
		
		// if monster can hit the player, it bloody well will
		if (dist(xpos, ypos, monster[i].xpos, monster[i].ypos)==1) {
			l=de(20); // roll dice
			if (testing) {
				message+=" ("+l+"+"+monster[i].taco+"<>"+ca*2+") "; // display values for test
			}
			if (l+monster[i].taco>=(ca+camagicbonus)*2) { // if dice + monster taco > player ca*2, its a hit
				message+=" "+monster[i].monsterlong+" "+strmonsterhit; // say so
				m=calc(monster[i].degats); // damages
				if (hp<=Math.floor(hpmax*dangerlimit)) {
					indanger=true;
				}
				hp-=m; // damages
				statshitstaken+=m;
				statsnbhitsmonster+=1;
				displayhits[xpos][ypos]+=m;
				checkfordeath(1, i);
				if ((playing) && (indanger)) {
					statsscars+=1;
				}
			} else {
				message+=" "+monster[i].monsterlong+" "+strmonstermisses;
				statsnbmissmonster+=1;
			}
		} else {
			// monster too far to hit, will move towards the player
			l=findnext(xpos, ypos, monster[i].xpos, monster[i].ypos); // find best place (theres always one !)
			movemonster(i, l);
		}
	}
	
	// do all turn calculations
	void doturn() {
		boolean search, dothismonster;
		int nbplay;
		
		turn++; // one more turn
		
		if (turn % (nbtourpv-nbtourpvbonus) == 0) { // the player gains one hp back
			if (hp<hpmax) {
				hp++;
			}
		}
		if ((turn % monsterspawn == 1) && (nbmonstersoflevel[ssol]<maxmonsterinalevel)) { // spawn a new monster
			createmonster(ssol);
		}
		
		if (accelerated>0) { // player is accelerated
			if (accelerated % 2 ==0) {
				playmonsters=false; // every two turns, monsters dont get to move !
			} else {
				playmonsters=true;
			}
		}
		
		// now, play the monsters
		if (playmonsters) {
			for (i=1; i<=monsternb; i++) {
				if ((monster[i].ssol==ssol) && (monster[i].alive)) { // for all alive monsters in this dungeon level
					dothismonster=true;
					nbplay=1;
					if (monster[i].affect!=' ') { // check if monster is affected by anything
						switch (monster[i].affect) {
							case 'f' : {
								dothismonster=false; // monster frozen : dont play it
								break;
							}
							case 'a' : {
								nbplay=2; // monster accelerated : will move twice !
								break;
							}
						}
					}
					
					if (dothismonster) { // if the monster still has to play
						for (j=1; j<=nbplay; j++) { // move it as many times as necessary
							if (isseen[monster[i].xpos][monster[i].ypos]) { // trigger behaviour if seen
								if (blindthem) {
									monster[i].behave=Tmonsterbehave[monster[i].type].charAt(3);
								} else {
									monster[i].behave=Tmonsterbehave[monster[i].type].charAt(1);
								}
							}
							if (dist(xpos, ypos, monster[i].xpos, monster[i].ypos)<=disttohear*disttohearmult) { // trigger behaviour if heard
								if (deafenthem) {
									monster[i].behave=Tmonsterbehave[monster[i].type].charAt(4);
								} else {
									monster[i].behave=Tmonsterbehave[monster[i].type].charAt(1);
								}
							}
							
							switch (monster[i].behave) { // move the monster depending on its current behaviour
								case 'A' : { // aggressive monster
										doaggressivemove(i);
										break;
									}
								case 'F' : { // fleeing monster
										l=findnextfar(xpos, ypos, monster[i].xpos, monster[i].ypos); // find best place (there should be one !)
										movemonster(i, l);
										break;
									}
								case 'R' : { // random moving monster
										l=findnextrandom(monster[i].xpos, monster[i].ypos); // find best place (theres always one !)
										movemonster(i, l);
										break;
									}
								case 'S' : { // semi-random moving monster
										if (de(100)<=chancesofrandom) {
											l=findnextrandom(monster[i].xpos, monster[i].ypos); // find best place (if theres one !)
											movemonster(i, l);
										} else {
											doaggressivemove(i);
										}
										break;
									}
							}
						}
					}
					if (monster[i].affect!=' ') { // check if monster is affected by anything
						monster[i].affected--; // decrease the number of turns he will still be affected by that
						if (monster[i].affected==0) {
							monster[i].affect=' '; // delete affect if time has come
						}
					}
				}
			}
		}
		
		// do all special effects calculations
		if (scrollmonstervision>0) {
			scrollmonstervision--;
		}
		if (scrollblind>0) { // blinding light
			scrollblind=Math.max(0, scrollblind-1);
			if (scrollblind==0){ // end of effect
				blindthem=false;
				message=strendofblinding; // say so
			}
		}
		if (scrolldeafen>0) { // deafening music
			scrolldeafen=Math.max(0, scrolldeafen-1);
			if (scrolldeafen==0){ // end of effect
				deafenthem=false;
				message=strendofdeafening; // say so
			}
		}
		if (slowed>0) { // player slowed
			slowed=Math.max(0, slowed-1);
			doanotherturn=!doanotherturn; // the player wont play one turn out of two !
			if (doanotherturn) { // do the non-played turn
				doturn();
			}
			if (slowed==0){ // end of effect
				message=strendofslowness; // say so
				doanotherturn=false;
			}
		}
		if (accelerated>0) { // player accelerated
			accelerated=Math.max(0, accelerated-1);
			if (accelerated==0){ // end of effect
				playmonsters=true;
				message=strendofacceleration;
			}
		}
		if (sleep>0) { // player is sleeping (magic sleep !)
			sleep=Math.max(0, sleep-1);
			playmonsters=true;
			if (sleep>0) { // while sleeping, monsters roam free !
				message=strsleepwarning;
				waitforenter=true;
				message+=" "+strwaitforenter;
			} else { // end of effect
				message=strendofsleep;
			}
		}
		if ((ringteleport) && (turn==ringteleportturn)) { // teleport ring effect
			doteleport(ssol);
			ringteleportturn=turn+calc(ringteleporttiming);
		}
		
		// do all hunger and thirst effects
		if ((ringdigestion) && (turn % ringdigestioneffet == 0)) { // the digest ring comes to help
		} else {
			faim--;
			soif--;
		}
		// display necessary texts
		if (faim==faimwarning) {
			message=strfaimwarning;
			waitforenter=true;
			message+=" "+strwaitforenter;
		}
		if ((faim==Math.floor(faimwarning/2)) || (faim==Math.floor(faimwarning/4)) || (faim==Math.floor(faimwarning/8)) || (faim==Math.floor(faimwarning/16))) {
			message=strfaimwarning2;
			waitforenter=true;
			message+=" "+strwaitforenter;
		}
		if (faim==0) {
			message=strfaimdeath;
			dodeath(2, 0);
		}
		if (soif==soifwarning) {
			message=strsoifwarning;
			waitforenter=true;
			message+=" "+strwaitforenter;
		}
		if ((soif==Math.floor(soifwarning/2)) || (soif==Math.floor(soifwarning/4)) || (soif==Math.floor(soifwarning/8)) || (soif==Math.floor(soifwarning/16))) {
			message=strsoifwarning2;
			waitforenter=true;
			message+=" "+strwaitforenter;
		}
		if (soif==0) {
			message=strsoifdeath;
			dodeath(3, 0);
		}
	}
	
	// save a tree, kill a monster
	void checkkillmonster(int k) {
		if (monster[k].hp<=0) {
			message+=strkillmonster+" "+monster[k].monsterlong+". ";
			l=givelevel(xp); // check current level
			xp+=monster[k].xpvalue; // get xp
			level=givelevel(xp); // new level
			taco=tacopaliers[level]; // new taco if changed
			monster[k].alive=false; // dead monster
			statskills[monster[k].type]+=1;
			nbmonstersoflevel[ssol]--; // one less monsters here
			isoccupied[ssol][monster[k].xpos][monster[k].ypos]=0; // empty the place
			if (level>l) { // if new level
				message+=" "+strwelcomenewlevel+" "+level+" !";
				waitforenter=true;
				message+=" "+strwaitforenter;
				l=calc(hpgain[level-1]);
				hpmax+=l; // gain some hp
				hp+=l; // also realtime (leave it that way ?)
			}
		} else { // monster is only harmed
			message+=strhitmonster+" "+monster[k].monsterlong+", ";
		}
	}
	
	// transtype any 'x' or '+x' or '-x' string into an integer
	int getint(String LCPstr) {
		int res;
		
		if (LCPstr.charAt(0)=='+') {
			res=Integer.parseInt(LCPstr.substring(1));
		} else {
			if (LCPstr.charAt(0)=='-') {
				res=Integer.parseInt(LCPstr.substring(1))*-1;
			} else {
				res=Integer.parseInt(LCPstr);
			}
		}
		
		return res;
	}
	
	// calculates the ca by adding all worn items armor classes, also adding magic bonuses
	void calculateca() {
		int m;
		
		ca=0;
		if (armorworn!=0) {
			item[armorworn].known=true;
			k=TArmor.indexOf(item[armorworn].detail.substring(1, 2));
			ca+=calc(Tca[k])+getint(item[armorworn].detail.substring(2));
		}
		if (helmetworn!=0) {
			item[helmetworn].known=true;
			k=TArmor.indexOf(item[helmetworn].detail.substring(1, 2));
			ca+=calc(Tca[k])+getint(item[helmetworn].detail.substring(2));
		}
		if (capeworn!=0) {
			item[capeworn].known=true;
			k=TArmor.indexOf(item[capeworn].detail.substring(1, 2));
			ca+=calc(Tca[k])+getint(item[capeworn].detail.substring(2));
		}
		if (shieldworn!=0) {
			item[shieldworn].known=true;
			k=TArmor.indexOf(item[shieldworn].detail.substring(1, 2));
			ca+=calc(Tca[k])+getint(item[shieldworn].detail.substring(2));
		}
	}
	
	int givemonstercabonus(int k) {
		int t=0;
		if (monster[k].affect=='I') { // check if monster is affected by anything
			t+=monstercabonusinvisible;
		}
		
		return t;
	}
	
	// do wand effects
	void dowandering() {
		int m, l, w, o, d;
		m=isoccupied[ssol][xpos+dirx[godir]][ypos+diry[godir]]; // get whatever monster could be there
		l=islift[ssol][xpos+dirx[godir]][ypos+diry[godir]];
		w=iswall[ssol][xpos+dirx[godir]][ypos+diry[godir]];
		o=isobject[ssol][xpos+dirx[godir]][ypos+diry[godir]];
		switch (item[wandering].detail.charAt(1)) {
			case 'i' : { // invoke monster
					if (m==0) { // if none here !
						k=createmonster(ssol);
						isoccupied[ssol][monster[k].xpos][monster[k].ypos]=0; // remove monster created
						isoccupied[ssol][xpos+dirx[godir]][ypos+diry[godir]]=k; // place monster where we want him
						monster[k].xpos=xpos+dirx[godir];
						monster[k].ypos=ypos+diry[godir];
					}
					break;
				}
			case 'c' : {
					if (m!=0) {
						wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
						d=calc(degatswandcombat);
						monster[m].hp-=d; // damage monster
						if (d==monster[m].hpmax) {
							statskillonehit+=1;
						}
						checkkillmonster(m); // might be killed
					}
					break;
				}
			case 'f' : {
					if (m!=0) {
						wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
						monster[m].affect='f'; // freeze monster
						monster[m].affected=calc(timefreezewand); // for that many rounds
					}
					break;
				}
			case 'w' : {
					if (m!=0) {
						wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
						monster[m].taco=(int)(Math.floor(monster[m].taco*weakenwand)); // weaken monster
						monster[m].ca=(int)(Math.floor(monster[m].ca*weakenwand));
					}
					break;
				}
			case 'a' : {
					if (m!=0) {
						wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
						monster[m].affect='a'; // accelerate monster
						monster[m].affected=calc(timeacceleratewand); // for that many rounds
					}
					break;
				}
			case 'I' : {
					if (m!=0) {
						wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
						monster[m].affect='I'; // make monster invisible
						monster[m].affected=calc(timeinvisiblewand); // for that many rounds
					}
					break;
				}
			case 't' : {
				if ((m==0) && (w==0) && (l==0) && (o==0) && (ssol!=endingssol)) {
					wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
					islift[ssol][xpos+dirx[godir]][ypos+diry[godir]]=4; // pitfall created
				}
				break;
			}
			case 'T' : {
				if ((l==3) || (l==4)) {
					wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
					islift[ssol][xpos+dirx[godir]][ypos+diry[godir]]=0; // pitfall suppressed
				}
				break;
			}
			case 'm' : {
				if ((m==0) && (l==0) && (o==0)) {
					wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
					iswall[ssol][xpos+dirx[godir]][ypos+diry[godir]]=1; // wall created
				}
				break;
			}
			case 'M' : {
				if ((w==1) && (xpos+dirx[godir]>1) && (ypos+diry[godir]>1) && (xpos+dirx[godir]<xmax) && (ypos+diry[godir]<ymax)) {
					wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
					iswall[ssol][xpos+dirx[godir]][ypos+diry[godir]]=0; // wall suppressed
				}
				break;
			}
			case 'p' : {
				if ((m==0) && (w==0) && (l==0) && (o==0)) {
					wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // effect of wand known
					islift[ssol][xpos+dirx[godir]][ypos+diry[godir]]=5; // butthole created
				}
				break;
			}
		}
		
		wandering=0; // wand use has been done, back to normal
		doturn(); // this was the player's action, end of turn
	}
	
	// check for ammunition, and consume one if asked so
	boolean checkammo(boolean consume) {
		int i, n;
		boolean result=false;
		
		for (i=1; i<=itemsnb; i++) {
			if ((item[i].carried) && (!result)) {
				if (item[i].detail.substring(0,2).compareTo("Wf")==0) {
					result=true;
					n=getint(item[i].detail.substring(2));
					if (consume) {
						if (n>1) {
							item[i].detail="Wf"+String.valueOf(n-1); // recrunch string
						} else {
							item[i].carried=false;
							item[i].alive=false;
						}
					}
				}
			}
		}
		
		return result;
	}
	
	// bow attack
	void dobowing() {
		int i, ix, iy;
		int k, l, m, w;
		
		checkammo(true);
		i=0;
		ix=xpos;
		iy=ypos;
		do {
			i++;
			ix+=dirx[godir];
			iy+=diry[godir];
			k=isoccupied[ssol][ix][iy]; // get whatever monster could be there
			w=iswall[ssol][ix][iy];
		} while ((k==0) && (w==0));
		m=calc("1D6+0");
		if (k!=0) {
			if (i==1) {
				message=strblockedshoot;
			} else {
				l=de(20);
				if (l==1) {
					statsnbdice1+=1;
				}
				if (l==20) {
					statsnbdice20+=1;
				}
				if (l+taco+dextacobonus[dex]>=(monster[k].ca+givemonstercabonus(k))*2) {
					m=Math.max(1, Math.min(m, monster[k].hp));
					statshitsgivenbow+=m;
					statsnbhitsbow+=1;
					displayhits[ix][iy]=m;
					monster[k].hp-=m; // ouch ! me monster !
					if (m==monster[k].hpmax) {
						statskillonehit+=1;
					}
					checkkillmonster(k); // monster might bloody well killed !
				} else {
					statsnbmissbow+=1;
				}
			}
		} else {
			displayhits[ix][iy+1]=m;
		}
	}
	
	// forget everything
	void doamnesia() {
		for (y=1; y<=ymax; y++) { // forget map (current map only)
			for (x=1; x<=xmax; x++) {
				isknown[ssol][x][y]=false;
				isobjectknown[ssol][x][y]=false;
			}
		}
		// and everything else !
		for (l=1; l<=potiontotal; l++) {
			potionknown[l]= false;
		}
		for (l=1; l<=scrolltotal; l++) {
			scrollknown[l]= false;
		}
		for (l=1; l<=ringtotal; l++) {
			ringknown[l]= false;
		}
		for (l=1; l<=wandtotal; l++) {
			wandknown[l]= false;
		}
		for (l=1; l<=itemsnb; l++) {
			item[l].known=false;
		}
	}
	
	// check if the item's bonus is negative, and thus, cursed
	boolean cursed(String theitem) {
		boolean curse=false;
		
		if (getint(theitem.substring(2))<0) {
			message=strcurseditem;
			curse=true;
		}
		
		return curse;
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	// main keyboard management
	public boolean keyDown(Event e, int key) {
		boolean search, wanderingdone, bowingdone, ringunworn, trytodrop, cangrab, grabtreated, donotgrab;
		int nl, ni;
		
		sdhitmonster.play();
		sdmonsterhit.play();
		sddeath.play();
		
		// reset them
		left=false; right=false; up=false; down=false; enter=false;
		move=false; donextturn=false; wanderingdone=false; bowingdone=false; trytodrop=false;
		/*		if ((!waitforenter) && (playing)) { // message is not changed if waiting for enter
			message="";
		 }*/
		
		if (playing) { // manage the keyboard only when playing
			switch (key) {
				// movement, choosing item, choosing direction...
				case Event.UP : { godir=1; up = true; break; }
				case Event.RIGHT : { godir=2; right = true;  break; }
				case Event.DOWN : { godir=3; down = true; break; }
				case Event.LEFT : { godir=4; left = true; break; }
				case Event.ENTER : { enter=true; break; } // un/ready item, use item
				case '.' : { enter=true; break; } // wait without moving
				case '*' : { if (!mapmode) { inventory=!inventory; if (inventory) { statsnbinvent+=1; } identify=false; uncurse=false; repaint(); } break; } // in and out of inventory
				case 'm' : { if (!inventory) { mapmode=!mapmode; if (mapmode) { statsnbmap+=1; } repaint(); } break; } // display map
				case '/' : { trytodrop=true; break;	} // drop item
				case 'f' : { bowing=!bowing; break; } // bow attack
				case 'w' : {
					if ((arcmode) && (weaponworn!=0)) {
						arcmode=false;
						weaponmode=true;
						message=strreadyweapon;
						donextturn=true;
					} else {
						if ((weaponmode) && (arcworn!=0)) {
							arcmode=true;
							weaponmode=false;
							message=strreadybow;
							donextturn=true;
						}
					}
					break;
				} // in and out of display build map
//				case 'c' : { cheating=!cheating; repaint(); break; } // in and out of cheat mode
//				case 't' : { testing=!testing; repaint(); break; } // in and out of testing mode
//				case 'w' : { displaymapbuild=!displaymapbuild; repaint(); break; } // in and out of display build map
//				case 'f' : { displayfaim=!displayfaim; repaint(); break; } // in and out of show hunger/thirst
//				case 'h' : { displayhelp=!displayhelp; repaint(); break; } // in and out of help display
//				case 'd' : { displaymap=!displaymap; repaint(); break; } // in and out of whole map display
//				case 'g' : { graphical=!graphical; repaint(); break; } // in and out of graphical mode
			}
		}
		
		if (up || down || left || right) {
			if ((wandering==0) && (!bowing)) {
				move=true;
			}
			if (wandering!=0) {
				wanderingdone=true;
			}
			if (bowing) {
				bowing=false;
				bowingdone=true;
			}
		}
		
		if ((potionconfusion>0) && (!inventory) && (up || down || left || right)) {
			potionconfusion-=1;
			left=false; right=false; up=false; down=false;
			switch (de(4)) {
				case 1 : { up=true; break; }
				case 2 : { down=true; break; }
				case 3 : { left=true; break; }
				case 4 : { right=true; break; }
			}
		}
		
		if (mapmode) {
			left=false; right=false; up=false; down=false;
			move=false; donextturn=false; wanderingdone=false; trytodrop=false;
			enter=false; // cancel any action
			message=strmapmode;
		}
		
		// player has pressed enter for a waiting message
		if (waitforenter) { // still waiting, everything else is out
			left=false; right=false; up=false; down=false;
			move=false; donextturn=false; wanderingdone=false; trytodrop=false;
			if (enter) {
				waitforenter=false;
				message="";
			}
			enter=false; // cancel any action
			
			if (sleep>0) {
				doturn();
			}
		}
		
		// has just chosen a direction for wand effect !
		if (wanderingdone) {
			dowandering(); // go and do it
		}
		
		// bow attack
		if (bowing) {
			if ((!arcmode) || (arcworn==0) || (!checkammo(false))) {
				message=strcannotshoot;
				bowing=false;
				donextturn=true;
			} else {
				message=strwanddirection;
			}
		}
		
		// has just chosen a direction for bow attack
		if (bowingdone) {
			message="";
			dobowing(); // go and do it
			bowingdone=false;
			donextturn=true;
		}
		
		// find how many of the objects are actually carried
		itemscarried=0;
		for (i=1; i<=itemsnb; i++) {
			if (item[i].carried) {
				itemscarried++;
			}
		}
		if ((itemscarried==0) && (inventory)) {
			inventory=false;
			message=strnothingcarried;
		}
		
		if ((!inventory) && (enter) && (islift[ssol][xpos][ypos]==0)) {
			statsnbtourwait+=1;
			donextturn=true;
			message="";
			for (y=1; y<=ymax; y++) {
				for (x=1; x<=xmax; x++) {
					displayhits[x][y]=0;
				}
			}
		}
			
			
		// change dungeon level by using a stair
		if ((!inventory) && (enter) && (islift[ssol][xpos][ypos]!=0)){
			if ((islift[ssol][xpos][ypos]==1) && (ssol==endingssol)) {
				dodeath(6,0); // glorious ending
			} else {
			if (islift[ssol][xpos][ypos]==1) { ni=2; nl=1; } else { ni=1; nl=-1; statsnbremontee+=1; }
			isoccupied[ssol][xpos][ypos]=0;
			// create current level
			if (!isleveldone[ssol+nl]) {
				createlevel(ssol+nl);
			}
			
			// replace player
			for (y=1; y<=ymax; y++) {
				for (x=1; x<=xmax; x++) {
					if (islift[ssol+nl][x][y]==ni) {
						xpos=x;
						ypos=y;
					}
				}
			}
			// change level
			ssol=ssol+nl;
			isoccupied[ssol][xpos][ypos]=-1;
			
			// do the look around
			checkseen();
			
			// create and place monsters
			if (!isleveldone[ssol]) {
				for (i=1; i<=nbmonstersinlevel; i++) {
					createmonster(ssol);
				}
			}
			isleveldone[ssol]=true;
			}
		}
		
		// inventory mode
		if (inventory) {
			move=false; // dont move the player
			donextturn=false; // dont do the next turn
			
			// change selection
			if (down) { // move cursor down
				itemcursor++;
				repaint();
			}
			if (up) { // move cursor up
				itemcursor--;
				repaint();
			}
			if (itemcursor>itemscarried) {
				itemcursor=1;
			}
			if (itemcursor<1) {
				itemcursor=itemscarried;
			}
			itemcursorval=iteminv[itemcursor]; // get the n° of the item being selected
			
			//try to drop some item
			if (trytodrop) {
				if ((isobject[ssol][xpos][ypos]==0) && (islift[ssol][xpos][ypos]==0)) { // only if no object and no stair here
					if (!item[itemcursorval].worn) {
						isobject[ssol][xpos][ypos]=itemcursorval; // actually put it down
						item[itemcursorval].carried=false; // not carried anymore
						inventory=false; // exit inventory
						message=strdropitem; // say so
						statsnbdrop+=1;
					} else {
						message=strdropmustunreadyitem; // item worn, must unready it first (just to avoid too much code right here)
					}
				} else { // cannot drop here
					message=strcannotdropitem;
				}
			}
			
			// use selection
			if (enter) {
				donextturn=true; // this time it is an action, so the next turn will be calculated
				switch(item[itemcursorval].detail.charAt(0)) {
					case 'W' : { // weapon
						m=TWeaponcat[TWeapon.indexOf(item[itemcursorval].detail.substring(1, 2))];
						if (m!=4) {
							if ((weaponworn==0) || (!cursed(item[weaponworn].detail))) {
							if ((m==1) || (m==2)) {
								// hand weapons management
								if ((!identify) && (!uncurse)) {
									arcmode=false;
									if (weaponworn==0) { // ready or unready weapon
										weaponworn=itemcursorval;
										item[weaponworn].worn=true;
										if (!item[weaponworn].known) {
											if (getint(item[weaponworn].detail.substring(2))>0) {
												statslucky+=1;
											}
											if (getint(item[weaponworn].detail.substring(2))<0) {
												statsunlucky+=1;
											}
										}
										item[weaponworn].known=true;
										weaponmode=true;
									} else {
										if (!cursed(item[weaponworn].detail)) {
											if (weaponworn!=itemcursorval) {
												item[weaponworn].worn=false;
												weaponworn=itemcursorval;
												item[weaponworn].worn=true;
												if (!item[weaponworn].known) {
													if (getint(item[weaponworn].detail.substring(2))>0) {
														statslucky+=1;
													}
													if (getint(item[weaponworn].detail.substring(2))<0) {
														statsunlucky+=1;
													}
												}
												item[weaponworn].known=true;
												weaponmode=true;
											} else {
												if (weaponmode) {
													item[weaponworn].worn=false;
													weaponworn=0;
													weaponmode=false;
													if (arcworn!=0) {
														arcmode=true;
													}
												} else {
													weaponmode=true;
												}
											}
										}
									}
								}
								if (identify) { // only identify item
									item[itemcursorval].known=true;
									identify=false;
								}
								if ((uncurse) && (item[itemcursorval].worn)) { // remove cursed item
									if (cursed(item[itemcursorval].detail)) {
										item[itemcursorval].worn=false;
										weaponmode=false;
										uncurse=false;
									}
								}
							} else {
								// bows management
								weaponmode=false;
								if (arcworn==0) { // ready or unready bow
									arcworn=itemcursorval;
									item[arcworn].worn=true;
									arcmode=true;
								} else {
									if (arcworn!=itemcursorval) {
										item[arcworn].worn=false;
										arcworn=itemcursorval;
										item[arcworn].worn=true;
										item[arcworn].known=true;
										arcmode=true;
									} else {
										if (arcmode) {
											item[arcworn].worn=false;
											arcworn=0;
											arcmode=false;
											if (weaponworn!=0) {
												weaponmode=true;
											}
										} else {
											arcmode=true;
										}
									}
								}
							}
							}
						}
						break;
					}
					case 'A' : { // armor
						if ((!identify) && (!uncurse)) {
								m=TArmorcat[TArmor.indexOf(item[itemcursorval].detail.substring(1, 2))];
								switch (m) { // depending on the category of armor, ready or unready it
									case 1: {
											if (armorworn==0) {
												armorworn=itemcursorval;
												item[armorworn].worn=true;
												if (!item[armorworn].known) {
													if (getint(item[armorworn].detail.substring(2))>0) {
														statslucky+=1;
													}
													if (getint(item[armorworn].detail.substring(2))<0) {
														statsunlucky+=1;
													}
												}
											} else {
												if (!cursed(item[armorworn].detail)) {
													if (armorworn!=itemcursorval) {
														item[armorworn].worn=false;
														armorworn=itemcursorval;
														item[armorworn].worn=true;
														if (!item[armorworn].known) {
															if (getint(item[armorworn].detail.substring(2))>0) {
																statslucky+=1;
															}
															if (getint(item[armorworn].detail.substring(2))<0) {
																statsunlucky+=1;
															}
														}
													} else {
														item[armorworn].worn=false;
														armorworn=0;
													}
												}
											}
											break;
										}
									case 2: {
											if (helmetworn==0) {
												helmetworn=itemcursorval;
												item[helmetworn].worn=true;
											} else {
												if (!cursed(item[helmetworn].detail)) {
													if (helmetworn!=itemcursorval) {
														item[helmetworn].worn=false;
														helmetworn=itemcursorval;
														item[helmetworn].worn=true;
													} else {
														item[helmetworn].worn=false;
														helmetworn=0;
													}
												}
											}
											break;
										}
									case 3: {
											if (capeworn!=0) {
												if (item[itemcursorval].detail.charAt(1)=='e') {
													disttohearmult/=elvishcapedisttohearmult;
												}
											}
											if (capeworn==0) {
												capeworn=itemcursorval;
												item[capeworn].worn=true;
											} else {
												if (!cursed(item[capeworn].detail)) {
													if (capeworn!=itemcursorval) {
														item[capeworn].worn=false;
														capeworn=itemcursorval;
														item[capeworn].worn=true;
													} else {
														item[capeworn].worn=false;
														capeworn=0;
													}
												}
											}
											if (capeworn!=0) {
												if (item[itemcursorval].detail.charAt(1)=='e') {
													disttohearmult*=elvishcapedisttohearmult;
												}
											}
											break;
										}
									case 4: {
											if (shieldworn==0) {
												shieldworn=itemcursorval;
												item[shieldworn].worn=true;
											} else {
												if (!cursed(item[shieldworn].detail)) {
													if (shieldworn!=itemcursorval) {
														item[shieldworn].worn=false;
														shieldworn=itemcursorval;
														item[shieldworn].worn=true;
													} else {
														item[shieldworn].worn=false;
														shieldworn=0;
													}
												}
											}
											break;
										}
								}
								calculateca(); // re-calculate player ca in any case
							}
						if (identify) { // only identify item
							item[itemcursorval].known=true;
							identify=false;
						}
						if ((uncurse) && (item[itemcursorval].worn)) { // remove cursed item
							if (cursed(item[itemcursorval].detail)) {
								item[itemcursorval].worn=false;
								uncurse=false;
								m=TArmorcat[TArmor.indexOf(item[itemcursorval].detail.substring(1, 2))];
								switch (m) { // depending on the category of armor, ready or unready it
									case 1: {
										armorworn=0;
									}
									break;
									case 2: {
										helmetworn=0;
									}
									break;
									case 3: {
										capeworn=0;
									}
									break;
									case 4: {
										shieldworn=0;
									}
									break;
								}
							}
						}
						break;
					}
					case 'F' : { // food
							if (identify) { // only identify item, well thats food mate
								identify=false;
							} else { // eat
								l=getint(item[itemcursorval].detail.substring(1))-1; // one less ration
								statsnbrations+=1;
								item[itemcursorval].detail="F"+String.valueOf(l);
								if (l==0) { // destroy food item if stack finished
									item[itemcursorval].alive=false;
									item[itemcursorval].carried=false;
								}
								l=de(5); // quality is random...
								message=faimtxt[l]; // message depends on quality
								faim+=l*faimration; // but mostly food effect
								if (faim>faimdeathinv) {
									message=" "+strfaimdeathinv;
									dodeath(4, 0);
								} else {
									if (faim>faimwarninginv) {
										message=" "+strfaimwarninginv;
									}
								}
							}
							break;
						}
					case 'p' : { // potion
							if (identify) { // only identify item
								potionknown[Tpotion.indexOf(item[itemcursorval].detail.substring(1, 2))]= true;
								identify=false;
							} else {
								soif+=soifpotion; // whatever the potion drunk, it helps thirst
								if (!potionknown[Tpotion.indexOf(item[itemcursorval].detail.substring(1, 2))]==false) {
									statsnbuseunknown+=1;
								}
								statsnbpotions+=1;
								potionknown[Tpotion.indexOf(item[itemcursorval].detail.substring(1, 2))]= true;
								inventory=false;
								item[itemcursorval].carried=false;
								item[itemcursorval].alive=false; // item is destroyed by consumption
								message=strpotiontext[Tpotion.indexOf(item[itemcursorval].detail.substring(1, 2))];
								switch(item[itemcursorval].detail.charAt(1)) {
									case 's' : { // cure
											hp+=de(6)+17;
											hp=Math.min(hp, hpmax);
											break;
										}
									case 'v' : { // hp gain
											hpmax+=de(4)+6;
											hp=hpmax;
											break;
										}
									case 'e' : { // experience gain
										level=givelevel(xp);
										xp=xppaliers[level];
										level=givelevel(xp);
										taco=tacopaliers[level];
										message+=" "+strwelcomenewlevel+" "+level+" !";
										waitforenter=true;
										message+=" "+strwaitforenter;
										l=calc(hpgain[level-1]);
										hpmax+=l; // gain some hp
										hp+=l; // also realtime (leave it that way ?)
										break;
									}
									case 'd' : { // experience drain
										if (level>1) {
											level=givelevel(xp);
											xp=xppaliers[level-1];
											level=givelevel(xp);
											taco=tacopaliers[level];
											l=calc(hpgain[level-1]);
											hpmax-=l; // lose some hp !
											hp=Math.min(hp, hpmax);
										}
										break;
									}
									case 'E' : { // simple water, no side effect
											break;
										}
									case 'S' : { // sleep potion !
											sleep=10;
											break;
										}
									case 'D' : { // dex gain
											dex++;
											break;
										}
									case 'f' : { // strength gain
											str++;
											break;
										}
									case 'p' : { // power, strength gain
											str+=de(2)+1;
											break;
										}
									case 'm' : { // cursed potion, lose strength
											if (!ringkeepstrength) {
												str-=de(2)+1;
												str=Math.max(str, strengthminimum);
											} else {
												message=strnoeffect;
											}
											break;
										}
									case 'r' : { // slowed player
											accelerated=0;
											slowed=calc(potionslowtiming);
											break;
										}
									case 'a' : { // acceleration
											slowed=0;
											accelerated=calc(potionacceleratetiming);
											break;
										}
									case 'M' : { // "food" potion
											faim+=5*faimration;
											break;
										}
									case 'c' : { // confusion
											potionconfusion=30;
											break;
										}
									case 'A' : { // amnesia potion !
											doamnesia();
											potionknown[Tpotion.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // this potion is still recognized...
											break;
										}
								}
								if (soif>soifdeathinv) {
									if (islift[ssol][xpos][ypos]==0) {
										message+=" "+strsoifcreatebutthole;
										islift[ssol][xpos][ypos]=5; // waterhole
										soif-=soifpotion*2;
									}
								} else {
									if (soif>soifwarninginv) {
										message+=" "+strsoifwarninginv;
									}
								}
							}
							break;
						}
					case 's' : { // scroll
							if (identify) { // only identify item
								scrollknown[Tscroll.indexOf(item[itemcursorval].detail.substring(1, 2))]= true;
								identify=false;
							} else {
								item[itemcursorval].carried=false;
								item[itemcursorval].alive=false; // item get destroyed by reading
								statsnbusescrolls+=1;
								if (!scrollknown[Tscroll.indexOf(item[itemcursorval].detail.substring(1, 2))]) {
									statsnbuseunknown+=1;
								}
								scrollknown[Tscroll.indexOf(item[itemcursorval].detail.substring(1, 2))]= true;
								inventory=false;
								message=strscrolltext[Tscroll.indexOf(item[itemcursorval].detail.substring(1, 2))];
								switch(item[itemcursorval].detail.charAt(1)) {
									case 'c' : { // map is known
											for (y=1; y<=ymax; y++) {
												for (x=1; x<=xmax; x++) {
													if (iswall[ssol][x][y]==1) {
														isknown[ssol][x][y]=true;
													}
													if (islift[ssol][x][y]!=0) {
														isknown[ssol][x][y]=true;
													}
												}
											}
											break;
										}
									case 'o' : { // gold places are known
											for (y=1; y<=ymax; y++) {
												for (x=1; x<=xmax; x++) {
													if (isobject[ssol][x][y]!=0) {
														if (item[isobject[ssol][x][y]].detail.charAt(0)=='T') { // search for gold objects
															isobjectknown[ssol][x][y]=true;
														}
													}
												}
											}
											break;
										}
									case 'n' : { // food places are known
											for (y=1; y<=ymax; y++) {
												for (x=1; x<=xmax; x++) {
													if (isobject[ssol][x][y]!=0) {
														if (item[isobject[ssol][x][y]].detail.charAt(0)=='F') { // search for food objects
															isobjectknown[ssol][x][y]=true;
														}
													}
												}
											}
											break;
										}
									case 'O' : { // objects are known (all but food and gold)
											for (y=1; y<=ymax; y++) {
												for (x=1; x<=xmax; x++) {
													k=isobject[ssol][x][y];
													if (k!=0) {
														if ((item[k].detail.charAt(0)!='T') && (item[k].detail.charAt(0)!='F')) {
															if ((item[k].detail.charAt(0)=='W') || (item[k].detail.charAt(0)=='A')) {
																if (getint(item[k].detail.substring(2))!=0) {
																	isobjectknown[ssol][x][y]=true;
																}
															} else {
																isobjectknown[ssol][x][y]=true;
															}
														}
													}
												}
											}
											break;
										}
									case 'A' : { // amnesia
											doamnesia();
											scrollknown[Tscroll.indexOf(item[itemcursorval].detail.substring(1, 2))]= true; // the scroll is still recognized...
											break;
										}
									case 't' : { // teleport player
											doteleport(ssol);
											break;
										}
									case 's' : { // scroll of sleep !
											sleep=10;
											break;
										}
									case 'v' : { // bonus protection on armor (if any worn)
											if (armorworn!=0) {
												k=getint(item[armorworn].detail.substring(2))+1; // one more ca point
												item[armorworn].detail="A"+item[armorworn].detail.substring(1, 2);
												if (k>=0) {
													item[armorworn].detail+="+";
												}
												item[armorworn].detail+=k;
												calculateca(); // re-calculate player ca
											} else {
												message=strnoeffect;
											}
											break;
										}
									case 'M' : { // deafening music
											deafenthem=true;
											scrolldeafen=calc(Tscrolldeafeningtiming);
											break;
										}
									case 'b' : { // bonus attack on weapon (if any readied)
											if (weaponworn!=0) {
												k=getint(item[weaponworn].detail.substring(2))+1;
												item[weaponworn].detail="W"+item[weaponworn].detail.substring(1, 2);
												if (k>=0) {
													item[weaponworn].detail+="+";
												}
												item[weaponworn].detail+=k;
												item[weaponworn].known=true;
											} else {
												message=strnoeffect;
											}
											break;
										}
									case 'i' : { // invok monster
											l=0;
											search=true;
											do { // search a possible place around the player
												l++;
												if ((iswall[ssol][xpos+dirx[l]][ypos+diry[l]]==0) && (isoccupied[ssol][xpos+dirx[l]][ypos+diry[l]]==0)) {
													search=false;
												}
											} while ((l<4) && (search));
											if (!search) { // if any, spawn a monster... right next to the player !
												k=createmonster(ssol);
												isoccupied[ssol][monster[k].xpos][monster[k].ypos]=0; // replace monster where we want him
												monster[k].xpos=xpos+dirx[l];
												monster[k].ypos=ypos+diry[l];
												isoccupied[ssol][monster[k].xpos][monster[k].ypos]=k;
											}
											break;
										}
									case 'I' : { // identification
										inventory=true; // stay in inventory
										identify=true; // switch to identify mode
										m=calc(Tscrollidentifycost);
										if (m>gold) {
											hpmax=Math.max(1, (int)(hpmax-Math.floor((m-gold)/10)));
											hp=Math.min(hp, hpmax);
										}
										gold=Math.max(0, gold-m);
									
										if (testing) { // in testing mode, this scroll is not destroyed
											item[itemcursorval].carried=true;
											item[itemcursorval].alive=true;
										}
										break;
									}
									case 'z' : { // ze farceur scroll !
											scrollknown[Tscroll.indexOf(item[itemcursorval].detail.substring(1, 2))]= false; // category is not recognized
											message=strfarceurscroll[de(strfarceurscroll.length-1)];
											break;
										}
									case 'l' : { // blinding light
										blindthem=true;
										scrollblind=calc(Tscrollblindingtiming);
										break;
									}
									case 'u' : { // uncurse
										inventory=true; // stay in inventory
										uncurse=true; // switch to uncurse mode
										
										if (testing) { // in testing mode, this scroll is not destroyed
											item[itemcursorval].carried=true;
											item[itemcursorval].alive=true;
										}
										break;
									}
									case 'V' : { // monster vision
										scrollmonstervision=calc(Tscrollmonstervision);
										break;
									}
									case 'U' : { // teleport up
										if (ssol>1) {
											doteleport(ssol-1);
										}
										break;
									}
									case 'D' : { // teleport down
										if (ssol<endingssol) {
											// attention, niveau à créer !
											// create current level
											if (!isleveldone[ssol+1]) {
												createlevel(ssol+1);
											}
			
											// change level
											doteleport(ssol+1);
						
											// do the look around
											checkseen();
			
											// create and place monsters
											if (!isleveldone[ssol]) {
												for (i=1; i<=nbmonstersinlevel; i++) {
													createmonster(ssol);
												}
											}
											isleveldone[ssol]=true;
										}
										break;
									}
									case 'E' : { // explosion
										for (y=2; y<=ymax-1; y++) {
											iswall[ssol][xpos][y]=0;
										}
										for (x=2; x<=xmax-1; x++) {
											iswall[ssol][x][ypos]=0;
										}
										break;
									}
									case 'C' : { // malus on armor (if any worn)
										if (armorworn!=0) {
											k=getint(item[armorworn].detail.substring(2))-1; // one less ca point !
											if (k>=minimummalus) {
												item[armorworn].detail="A"+item[armorworn].detail.substring(1, 2);
												if (k>=0) {
													item[armorworn].detail+="+";
												}
												item[armorworn].detail+=k;
											}
											calculateca(); // re-calculate player ca
										} else {
											message=strnoeffect;
										}
										break;
									}
									case 'W' : { // malus on weapon (if any readied)
										if (weaponworn!=0) {
											k=getint(item[weaponworn].detail.substring(2))-1;
											if (k>=minimummalus) {
												item[weaponworn].detail="W"+item[weaponworn].detail.substring(1, 2);
												if (k>=0) {
													item[weaponworn].detail+="+";
												}
												item[weaponworn].detail+=k;
												item[weaponworn].known=true;
											}
										} else {
											message=strnoeffect;
										}
										break;
									}
								}
							}
							break;
						}
					case 'r' : { // ring
						if ((!identify) && (!uncurse)) { // rings can be worn and unworn
							if (item[itemcursorval].worn) { // if this one is worn, then it has to be unworn
								ringunworn=false;
								switch (item[itemcursorval].detail.charAt(1)) { // cancel ring effects, to be modified if another same ring is still worn
									case 'r' : { // regenerate
										if (!cursed(item[itemcursorval].detail)) {
											nbtourpvbonus=0;
											ringunworn=true;
										}
										break;
									}
									case 'd' : { // digest
										ringdigestion=false;
										ringunworn=true;
										break;
									}
									case 't' : { // teleport
										ringteleport=false;
										ringunworn=true;
										break;
									}
									case 'R' : { // resurrect
										ringresurrect=false;
										ringunworn=true;
										break;
									}
									case 'p' : { // protection
										if (!cursed(item[itemcursorval].detail)) {
											m=getint(item[itemcursorval].detail.substring(2));
											camagicbonus-=m;
											ringunworn=true;
										}
										break;
									}
									case 'm' : { // monster
										message=strcurseditem;
//										disttohearmult/=ringdisttohearmult;
//										ringunworn=true;
										break;
									}
									case 'f' : { // featherfall
										ringfeatherfall=false;
										ringunworn=true;
										break;
									}
									case 'v' : { // trapseeing
										ringtrapseeing=false;
										ringunworn=true;
										break;
									}
									case 'I' : { // identification
										ringunworn=true;
										break;
									}
									case 'F' : { // keep strength
										ringkeepstrength=false;
										ringunworn=true;
										break;
									}
									case 'A' : { // armor protect
										ringarmorprotect=false;
										ringunworn=true;
										break;
									}
								}
									if (ringunworn) {
										item[itemcursorval].worn=false;
										for (i=1; i<=nbdoigts; i++) { // liberate the finger that was used by the ring
											if (doigts[i]==itemcursorval) {
												doigts[i]=0;
											}
										}
									}
							} else { // wear the ring
								// check no same ring is worn
								search=false;
								for (i=1; i<=nbdoigts; i++) {
									if (doigts[i]>0) {
										if (item[itemcursorval].detail.charAt(1)==item[doigts[i]].detail.charAt(1)) {
											search=true; // a same ring has been found
										}
									}
								}
								if (search)  {
									message=strsamering;
								} else {
									l=0;
									search=true;
									do { // search for a free finger
										l++;
										if (doigts[l]==0) {
											search=false;
										}
									} while ((l<nbdoigts) && (search));
									if (search) {
										message=strnofreefinger;
									} else {
										message=strwearring+" "+displayitem(itemcursorval);
										item[itemcursorval].worn=true;
										statsnbuserings+=1;
										if (!ringknown[Tring.indexOf(item[itemcursorval].detail.substring(1, 2))]) {
											statsnbuseunknown+=1;
										}
										doigts[l]=itemcursorval;
										switch (item[itemcursorval].detail.charAt(1)) {
											case 'c' : { // finger is cut !
												ringknown[Tring.indexOf(item[itemcursorval].detail.substring(1, 2))]= true;
												doigts[l]=-1; // one less free finger !
												item[itemcursorval].worn=false; // ring is unworn at once, of course
												message=strcutfinger;
												break;
											}
											case 'r' : { // regenerate
												m=getint(item[itemcursorval].detail.substring(2));
												nbtourpvbonus=m; // change time for regeneration
												break;
											}
											case 'd' : { // digest
												ringdigestion=true;
												break;
											}
											case 't' : { // teleport
												ringteleport=true;
												ringteleportturn=turn+de(60)+20;
												break;
											}
											case 'R' : { // resurrect
												ringresurrect=true;
												break;
											}
											case 'p' : { // protection
												m=getint(item[itemcursorval].detail.substring(2));
												camagicbonus+=m; // change bonus
												break;
											}
											case 'm' : { // monster
												disttohearmult*=ringdisttohearmult;
												break;
											}
											case 'f' : { // featherfall
												ringfeatherfall=true;
												break;
											}
											case 'v' : { // trapseeing
												ringtrapseeing=true;
												break;
											}
											case 'I' : { // identification
												m=0;
												n=0;
												for (i=1; i<=itemsnb; i++) {
													if (item[i].carried) {
														switch(item[i].detail.charAt(0)) {
															case 'W' : {
																if (!item[i].known) {
																	item[i].known=true;
																	m++;
																}
																break;
															}
															case 'A' : {
																if (!item[i].known) {
																	item[i].known=true;
																	m++;
																}
																break;
															}
															case 'p' : {
																if (!potionknown[Tpotion.indexOf(item[i].detail.substring(1, 2))]) {
																	potionknown[Tpotion.indexOf(item[i].detail.substring(1, 2))]= true;
																	m++;
																}
																break;
															}
															case 's' : {
																if (!scrollknown[Tscroll.indexOf(item[i].detail.substring(1, 2))]) {
																	scrollknown[Tscroll.indexOf(item[i].detail.substring(1, 2))]= true;
																	m++;
																}
																break;
															}
															case 'r' : {
																if (!ringknown[Tring.indexOf(item[i].detail.substring(1, 2))]) {
																	ringknown[Tring.indexOf(item[i].detail.substring(1, 2))]= true;
																	m++;
																}
																break;
															}
															case 'w' : {
																if (!wandknown[Twand.indexOf(item[i].detail.substring(1, 2))]) {
																	wandknown[Twand.indexOf(item[i].detail.substring(1, 2))]= true;
																	m++;
																}
																if (!item[i].known) {
																	item[i].known=true;
																}
																break;
															}
														}
														if (m==ringidentstrloss) {
															n++;
															m=0;
														}
													}
												}
												str-=n;
												str=Math.max(str, strengthminimum);
												message=stridentificationring;
												break;
											}
											case 'F' : { // keep strength
												ringkeepstrength=true;
												break;
											}
											case 'A' : { // armor protect
												ringarmorprotect=true;
												break;
											}
										}
									}
								}
							}
						}
						if (identify) { // only identify item
							ringknown[Tring.indexOf(item[itemcursorval].detail.substring(1, 2))]= true;
							item[itemcursorval].known=true;
							identify=false;
						}
						if ((uncurse) && (item[itemcursorval].worn))  { // remove cursed item
							ringunworn=false;
							switch (item[itemcursorval].detail.charAt(1)) {
								case 'r' : { // regenerate
									if (cursed(item[itemcursorval].detail)) {
										ringunworn=true;
										uncurse=false;
									}
									break;
								}
								case 'p' : { // protection
									if (cursed(item[itemcursorval].detail)) {
										ringunworn=true;
										uncurse=false;
									}
									break;
								}
							}
							if (ringunworn) {
								item[itemcursorval].worn=false;
								for (i=1; i<=nbdoigts; i++) { // liberate the finger that was used by the ring
									if (doigts[i]==itemcursorval) {
										doigts[i]=0;
									}
								}
							}
						}
						break;
					}
					case 'w' : { // wand
							if (identify) { // only identify item
								wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]= true;
								item[itemcursorval].known=true;
								identify=false;
							} else {
								m=getint(item[itemcursorval].detail.substring(2)); // check wand charges
								donextturn=false; // dont play next turn
								if (m>0) { // if any left
									m--;
									statsnbusewands+=1;
									if (!wandknown[Twand.indexOf(item[itemcursorval].detail.substring(1, 2))]) {
										statsnbuseunknown+=1;
									}
									item[itemcursorval].detail=item[itemcursorval].detail.substring(0,2)+m; // decrease it
									wandering=itemcursorval; // mark down the number of the wand
									inventory=false; // exit inventory
									message=strwanddirection; // ask for direction
								}
							}
							break;
						}
				}
			}
		}
		
		// player tries to move
		if (move) {
			xposmem=xpos+dirx[godir]; // targeted position
			yposmem=ypos+diry[godir];
			
			if (iswall[ssol][xposmem][yposmem]==0) {
				for (y=1; y<=ymax; y++) {
					for (x=1; x<=xmax; x++) {
						displayhits[x][y]=0;
					}
				}

				if (isoccupied[ssol][xposmem][yposmem]==0) { // objective is empty, move there
					for (i=1; i<=4; i++) {
						if (isoccupied[ssol][xpos+dirx[i]][ypos+diry[i]]!=0) {
							statsnbfuite+=1;
						}
					}
					
					isoccupied[ssol][xpos][ypos]=0;
					xpos=xposmem;
					ypos=yposmem;
					isoccupied[ssol][xpos][ypos]=-1;
					
					for (i=1; i<=4; i++) {
						if (isoccupied[ssol][xpos+dirx[i]][ypos+diry[i]]!=0) {
							statsnbavanceonmonster+=1;
						}
					}
					
					if ((islift[ssol][xpos][ypos]==3) || (islift[ssol][xpos][ypos]==4)) { // falling in a pitfall
						islift[ssol][xpos][ypos]=4;
						// change dungeon level by falling into a pitfall
						ni=2; nl=1;
						isoccupied[ssol][xpos][ypos]=0;
						// create current level
						if (!isleveldone[ssol+nl]) {
							createlevel(ssol+nl);
						}
			
						// change level
						ssol=ssol+nl;
						isoccupied[ssol][xpos][ypos]=-1;
						if (!ringfeatherfall) { // may be saved by ring
							m=(int)(Math.round(hp*fallinginpitfall)); // hp loss !
							hp-=m;
							displayhits[xpos][ypos]=m;
						}
						
						// do the look around
						checkseen();
			
						// create and place monsters
						if (!isleveldone[ssol]) {
							for (i=1; i<=nbmonstersinlevel; i++) {
								createmonster(ssol);
							}
						}
						isleveldone[ssol]=true;
						message=strfellinpitfall;
						waitforenter=true;
						message+=" "+strwaitforenter;
					}
					
					if (islift[ssol][xpos][ypos]==5) { // falling in a waterhole
						if (soif<soifwarninginv) {
							soif+=soifpotion; // helps thirst
						}
						
						// display message
						message=strplouf;
						
						// decreases worn armor CA
						if ((armorworn!=0) && (!ringarmorprotect)) { // ring may protect
							if ((item[armorworn].detail.charAt(1)=='m') || (item[armorworn].detail.charAt(1)=='f')) {
								k=getint(item[armorworn].detail.substring(2))-1; // one less ca point
								if (k>=minimummalus) {
									item[armorworn].detail="A"+item[armorworn].detail.substring(1, 2);
									if (k>=0) {
										item[armorworn].detail+="+";
									}
									item[armorworn].detail+=k;
									calculateca(); // re-calculate player ca
									message+=" "+strrouille;
								}
							}
						}
						
						// risk of drowning
						if (de(100)<=(int)((itemscarried*100/str)-drowningperc)) {
							drowningchances-=1;
							if (drowningchances==0){
								message+=" "+strdeathdrowning;
								dodeath(7,0);
							} else {
								message+=" "+strriskofdrowning;
							}
						}
					}

					if (isobject[ssol][xpos][ypos]!=0) { // grab any item there
						cangrab=false;
						grabtreated=false;
						donotgrab=false;
						message="";
						k=isobject[ssol][xpos][ypos]; // get the item
						if ((itemscarried<str) || (item[k].detail.charAt(0)=='T')
						    || (item[k].detail.charAt(0)=='F')
						    || (item[k].detail.substring(0,2).compareTo("Wf")==0)) { // if the player can ! or if its gold or stacking
							
								switch(item[k].detail.charAt(0)) { // special auto effect
									case 'F' : { // food : stack in already carried food, if possible
										for (i=1; i<=itemsnb; i++) {
											if ((item[i].carried==true) && (item[i].detail.charAt(0)=='F')) {
												if (getint(item[i].detail.substring(1))<nbfoodforonespace) { // maximum food stack
													cangrab=true;
													item[k].carried=false;
													item[k].alive=false; // grabbed item gets destroyed !
													l=getint(item[i].detail.substring(1))+1; // one more in this stack
													item[i].detail="F"+String.valueOf(l); // recrunch string
													grabtreated=true;
												}
											}
										}
										break;
									}
									case 'T' : { // gold : pocket it
										cangrab=true;
										item[k].carried=false;
										item[k].alive=false;
										gold+=getint(item[k].detail.substring(1));
										grabtreated=true;
										break;
									}
								}
								if (item[k].detail.substring(0,2).compareTo("Wf")==0) {
									for (i=1; i<=itemsnb; i++) {
										if ((!grabtreated) && (item[i].carried==true) && (item[i].detail.substring(0,2).compareTo("Wf")==0)) {
											if (getint(item[i].detail.substring(2))<nbarrowforonespace) { // maximum arrow stack
												m=nbarrowforonespace-getint(item[i].detail.substring(2)); // room left in stack
												n=getint(item[k].detail.substring(2)); // room needed
												grabtreated=true;
												cangrab=true;
												if (m>=n) {
													item[k].carried=false;
													item[k].alive=false; // grabbed item gets destroyed !
													l=getint(item[i].detail.substring(2))+n; // stack
													item[i].detail="Wf"+String.valueOf(l); // recrunch string
												} else {
													item[i].detail="Wf"+String.valueOf(nbarrowforonespace); // recrunch string
													a=createitem(); // get a pointer
													item[k].detail="Wf"+String.valueOf(m); // recrunch string
													item[a].detail="Wf"+String.valueOf(n-m); // recrunch string
													item[a].val=itemvalue(item[a].detail); // note down the item value
													isobject[ssol][xpos][ypos]=a;
													donotgrab=true;
												}
											}
										}
									}
								}
								if ((!cangrab) && (!grabtreated) && (itemscarried<str)) { // for all items, or stacking which found no room, need empty slot
									cangrab=true;
									item[k].carried=true; // carry it
								}
								if (cangrab) {
									statsnbpicked+=1;
									if (!donotgrab) {
										isobject[ssol][xpos][ypos]=0; // empty place
									}
									message= strgrabitem+" ";
									message+= displayitem(k)+"."; // says what has been found
									if (testing) { // testing mode, show item details
										message+="   ( "+item[k].detail+" )  ";
									}
								}
						}
						if (!cangrab) {
							message=strcannotgrabitem+" "+displayitem(isobject[ssol][xpos][ypos])+"."; // say player is overloaded
							statsnboverloaded+=1;
						}
					}
				} else { // objective is occupied by monster, attack it !
					k=isoccupied[ssol][xposmem][yposmem]; // get monster
					monster[k].behave=Tmonsterbehave[monster[k].type].charAt(2); // behaviour triggered
					message="";
					l=de(20); // throw dice
					if (l==1) {
						statsnbdice1+=1;
					}
					if (l==20) {
						statsnbdice20+=1;
					}
					tacomagicbonus=0;
					if ((weaponworn!=0) && (weaponmode)) { // get current weapon magic bonus
						tacomagicbonus=getint(item[weaponworn].detail.substring(2));
					}
					if (testing) { // test mode : say values
						message+=" ("+l+"+"+taco+"+"+tacomagicbonus+"<>"+monster[k].ca*2+") ";
					}
					if (l+tacomagicbonus+taco+dextacobonus[dex]>=(monster[k].ca+givemonstercabonus(k))*2) { // if de(20)+taco +weapon bonus > monster ca *2, hit monster !
						if ((weaponworn!=0) && (weaponmode)) { // calculate weapon damages
							m=calc(Tdegats[TWeapon.indexOf(item[weaponworn].detail.substring(1, 2))])+tacomagicbonus;
						} else {
							m=calc(Tdegats[0]);
						}
						m+=strdamagebonus[str];
						m=Math.max(1, Math.min(m, monster[k].hp));
						if (m==monster[k].hpmax) {
							statskillonehit+=1;
						}
						statshitsgiven+=m;
						statsnbhits+=1;
						monster[k].hp-=m; // ouch ! me monster !
						displayhits[monster[k].xpos][monster[k].ypos]=m;
			            sdhitmonster.play();
						if ((weaponworn!=0) && (weaponmode)) {
							if (item[weaponworn].detail.substring(1, 2).compareTo("V")==0) { // special vampiric sword bonus
								hp+=Math.round(m*vampiricdrain/100); // get a part of the damage back
								hp=Math.min(hp, hpmax); // but not more than the maximum hp
							}
							if (item[weaponworn].detail.substring(1, 2).compareTo("G")==0) { // special ice sword bonus
								if (de(100)<=chancesoffreezing) { // roll dice
									monster[k].affected=1; //  for just one turn
									monster[k].affect='f'; // the monster is frozen
								}
							}
						}
						
						checkkillmonster(k); // monster might bloody well get killed !
					} else { // miss monster... (woo !)
						message+=strmissmonster+" "+monster[k].monsterlong+",";
						statsnbmiss+=1;
					}
					if (testing) { // testing mode, show values
						message+=" (hp="+String.valueOf(monster[k].hp)+") ";
					}
				}
				donextturn=true; // player has played, will calculate next turn
			}
		}
		
		if (donextturn) { // if ordered to do so, do the next turn
			doturn();
		}
		checkseen(); // recalculate vision
		repaint(); // and draw world
		
		return true;
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	// all graphical display
    public void paint(Graphics g) {
		int xd, yd, xdp, ydp, idp, xm, mapm, imapx, imapy, xfg, yfg;
		
		dim = size();
		
		if (!tracker.checkAll()) {
			g.drawString("Please wait...", 0, dim.height/2);
		}
		
		xm=(int)(Math.floor((dim.width-(seearoundx*2+1)*imx)/2));
		mapm=(int)(Math.floor((dim.width-xmax*mapc)/2));
		
		g.setColor(Color.black);
		
		// identify mode, say so
		if ((identify) && (message=="")) {
			message=strchooseidentify;
		}
		// warns constantly for hunger and thirst
		if ((playing) && (!waitforenter)) {
			if (faim<Math.floor(faimwarning/2)) {
				message=strfaimshortwarning+message;
			}
			if (soif<Math.floor(soifwarning/2)) {
				message=strsoifshortwarning+message;
			}
		}
		// display message, whatever it is
		g.drawString(message, xbase+(int)(Math.floor(((dim.width-message.length()*letterw)/2))), ybase);
			
		// if ingame, display player stats
		if (playing) {
			level=givelevel(xp);
			string=strdisplayssol+" "+ssol+"   "+strdisplaygold+" "+gold+"   "+strdisplaylife+" "+hp+"/"+hpmax;
			string+="   "+strdisplaydex+" "+dex+"   "+strdisplaystr+" "+str;
			string+="   "+strdisplayca+" "+(ca+camagicbonus)+"   "+strdisplayxp+" "+level+"/"+xp;
			if (testing) { // if in test mode, add turn number
				string="T "+turn+" "+string+"  ";
			}
			if (displayfaim) { // add hunger / thirst if asked for
				string+=" hunger:"+faim+" thirst:"+soif;
			}
			if (cheating) { // tell if cheat mode
				string+=" cheating!";
				string+=" x="+xpos+" y="+ypos;
			}
			if (testing) { // tell if test mode
				string+=" testing!";
			}
//				g.drawString(string, xbase, (ymax+3)*cy+ybase); // text mode display
//				g.drawString(string, xbase, (ymax+3)*imy+ybase); // graphical mode display
			g.drawString(string, xbase+(int)(Math.floor(((dim.width-string.length()*letterw)/2))), (seearoundy*2+1+3)*imy+ybase); // graphical mode display
		}
			
		// inventory mode, display inventory
		if ((playing) && (inventory) && (!mapmode)) {
			// find how many of the objects are actually carried
			itemscarried=0;
			for (i=1; i<=itemsnb; i++) {
				if (item[i].carried) {
					itemscarried++;
				}
			}
					
			// first reorder it correctly please
			for (i=1; i<=itemsnb; i++) { // write down all items value
				iteminvval[i]=item[i].val;
			}
			for (j=1; j<=itemscarried; j++) { // and order them
				l=0;
				m=10*1000;
				for (i=1; i<=itemsnb; i++) {
					if ((item[i].carried) && (iteminvval[i]<m)) {
						m=iteminvval[i];
						l=i;
					}
				}
				iteminv[j]=l;
				iteminvval[l]=10*1000;
			}
					
			// then do the display in order
			for (i=1; i<=itemscarried; i++) {
				string="   ";
				if (i==itemcursor) {
					string="*  "; // selected item
				}
				if (testing) { // testing mode, show item details
					string+="( "+item[iteminv[i]].detail+" )  ";
				}
				string+=i+". ";
				string+=displayitem(iteminv[i]); // get the item description
				if (item[iteminv[i]].worn) {
					if (item[iteminv[i]].detail.charAt(0)=='W') {
						m=TWeaponcat[TWeapon.indexOf(item[iteminv[i]].detail.substring(1, 2))];
						if (((m==3) && (arcmode)) || (((m==1) || (m==2)) && (weaponmode)))  {
							string+=" "+strworn;
						} else {
							string+=" "+strready;
						}
					} else {
						string+=" "+strworn;
					}
				}
				g.drawString(string, cx+xbase, i*cy1+ybase); // display it
			}
		}
					
		if ((playing) && (mapmode) && (!inventory)) {
			for (y=1 ; y <= ymax ; y++) {
				for (x=1 ; x <= xmax ; x++) {
					imapx= (x-1)*mapc+mapm;
					imapy= y*mapc+ybase;
					g.drawImage(imunknown, imapx, imapy, mapc, mapc, this);
					if (isknown[ssol][x][y]) {
						if (iswall[ssol][x][y]==1) {
							g.drawImage(imwall, imapx, imapy, mapc, mapc, this);
						} else {
							g.drawImage(imground[ssol], imapx, imapy, mapc, mapc, this);
						}
						if (islift[ssol][x][y]!=0) {
							if (islift[ssol][x][y]==1) {
								g.drawImage(imstairdes, imapx, imapy, mapc, mapc, this); // display stair descending
							}
							if (islift[ssol][x][y]==2) {
								g.drawImage(imstairasc, imapx, imapy, mapc, mapc, this); // display stair ascending
							}
							if ((islift[ssol][x][y]==3) && (ringtrapseeing)) { // trapseeing ring in use !
							}
							if (islift[ssol][x][y]==4) {
								g.drawImage(impitfall, imapx, imapy, mapc, mapc, this); // display pitfall
							}
							if (islift[ssol][x][y]==5) {
								g.drawImage(implouf, imapx, imapy, mapc, mapc, this); // display water hole
							}
						}
					}
					if ((isobjectknown[ssol][x][y]) || (displaymap)) { // only if place of object known
						if (((isobject[ssol][x][y]!=0) && (isoccupied[ssol][x][y]==0) && (isseen[x][y])) ||
								((isobject[ssol][x][y]!=0) && (!isseen[x][y]))) {
									k=TitemsLCP.indexOf(item[isobject[ssol][x][y]].detail.substring(0, 1)); // get type of object
									g.drawImage(imobject[k], imapx, imapy, mapc, mapc, this);
								}
					}
					if (isoccupied[ssol][x][y]!=0) {
						if (isoccupied[ssol][x][y]==-1) {
							g.drawImage(implayer, imapx, imapy, mapc, mapc, this);
						} else {
							if ((isseen[x][y]) || (displaymap)) { // and seen
								idp=isoccupied[ssol][x][y];
								if (monster[idp].affect!='I') { // and not invisible
									//graphical display
									g.drawImage(immonster[monster[idp].type], imapx, imapy, mapc, mapc, this);
								}
							}
						}
					}
				}
			}
		}
						
		if ((playing) && (!mapmode) && (!inventory)) {
			xd=xpos-seearoundx-1;
			yd=ypos-seearoundy-1;
			for (y=1; y<=seearoundy*2+1; y++) {
				for (x=1; x<=seearoundx*2+1; x++) {
					xdp=xd+x;
					ydp=yd+y;
					xfg=(x-1)*imx+xbase+xm;
					yfg=y*imy+ybase;
					g.drawImage(imunknown, xfg, yfg, this);
					if ((xdp>=1) && (xdp<=xmax) && (ydp>=1) && (ydp<=ymax)) {
						if (isknown[ssol][xdp][ydp]) {
							if (iswall[ssol][xdp][ydp]==1) {
								g.drawImage(imwall, xfg, yfg, this); // display walls
							} else {
								g.drawImage(imground[ssol], xfg, yfg, this); // display ground
							}
							if (islift[ssol][xdp][ydp]!=0) {
								if (islift[ssol][xdp][ydp]==1) {
									g.drawImage(imstairdes, xfg, yfg, this); // display stair descending
								}
								if (islift[ssol][xdp][ydp]==2) {
									g.drawImage(imstairasc, xfg, yfg, this); // display stair ascending
								}
								if ((islift[ssol][xdp][ydp]==3) && (ringtrapseeing)) { // trapseeing ring in use !
									islift[ssol][xdp][ydp]=4; // pit fall known !
								}
								if (islift[ssol][xdp][ydp]==4) {
									g.drawImage(impitfall, xfg, yfg, this); // display pitfall
								}
								if (islift[ssol][xdp][ydp]==5) {
									g.drawImage(implouf, xfg, yfg, this); // display water hole
								}
							}
						}
						
						if ((isobjectknown[ssol][xdp][ydp]) || (displaymap)) { // only if place of object known
							if (((isobject[ssol][xdp][ydp]!=0) && (isoccupied[ssol][xdp][ydp]==0) && (isseen[xdp][ydp])) ||
									((isobject[ssol][xdp][ydp]!=0) && (!isseen[xdp][ydp]))) {
										k=TitemsLCP.indexOf(item[isobject[ssol][xdp][ydp]].detail.substring(0, 1)); // get type of object
										g.drawImage(imobject[k], xfg, yfg-iml, this);
									}
						}
								
						if (isoccupied[ssol][xdp][ydp]!=0) {
							if (isoccupied[ssol][xdp][ydp]==-1) {
								g.drawImage(implayer, xfg, yfg-iml, this);
								if (hp<=Math.floor(hpmax*dangerlimit)) {
									g.drawImage(imdanger, xfg, yfg, this);
								}
							} else {
								if ((isseen[xdp][ydp]) || (displaymap) || (scrollmonstervision>0)) { // and seen
									idp=isoccupied[ssol][xdp][ydp];
									if (monster[idp].affect!='I') { // and not invisible
										//graphical display
										g.drawImage(immonster[monster[idp].type], xfg, yfg-iml, this);
									}
								}
							}
						}
							
						if (displayhits[xdp][ydp]!=0) {
							g.drawImage(imhit, (x-1)*imx+xbase+xm, y*imy+ybase-iml, this);
							g.setColor(Color.white);
							if (displayhits[xdp][ydp]>9) {
								g.drawString(String.valueOf(displayhits[xdp][ydp]), xfg, yfg-2); // display it
							} else {
								g.drawString(String.valueOf(displayhits[xdp][ydp]), xfg+5, yfg-2); // display it
							}
						}
							
						if (!isseen[xdp][ydp]) { // place unseen
							g.drawImage(imunseen, xfg, yfg, this);
						}
					}
				}
			}
		}
		
		if (waitforstart) {
			buttstart.move((int)(Math.floor(dim.width/2-(strstart.length()/2)*10-10)), 200);
			buttend.move(70, -100);
			buttstart.requestFocus();
		} else {
			if ((!playing) && (!savedone)) {
				try {
					SendData2(diestring);
				} catch (Exception e1) {}
			}
		}
	}
	
	// buffer drawing, avoid flickers
	public void update(Graphics g)
	{
		Dimension d = this.size();
		if(offI == null)
		{
			offI = createImage(d.width, d.height);
			offG = offI.getGraphics();
		}
		offG.clearRect(0, 0, d.width, d.height);
		paint(offG);
		g.drawImage(offI, 0, 0, null);
	}
	
	void enterscore() throws Exception {
		URL url2 = new URL (getCodeBase(), "newentry.php?t="+startingtime);
		getAppletContext().showDocument(url2,"_self");
	}
	
	public void SendData(String data) throws Exception {
		String senddata="";
		
		URL url = new URL("http","www.muad.net","/cgi-bin/wdwrite3.cgi");
		URLConnection con = url.openConnection();
		
		senddata=">../moria/scores/"+startingtime+".txt #"+data;
		con.setDoOutput(true);
		con.setDoInput(true);
		con.setUseCaches(false);
		con.setRequestProperty("Content-type", "text/plain");
		con.setRequestProperty("Content-length", senddata.length()+"");

		PrintStream out = new PrintStream(con.getOutputStream());
		out.print(senddata);
		out.flush();
		out.close();
		
		InputStream is = con.getInputStream();
		is.close();
		savedone=true;

		try {
			enterscore();
		} catch (Exception e1) {}
	}

	public void SendData2(String data) {
		String senddata="";
		
		URL url;
		HttpURLConnection urlc;
		try {
			url = new URL("http","www.muad.net","/cgi-bin/wdwrite3.cgi");
		} catch(MalformedURLException e) { data="MalformedURLException"; return; }
		try {
			urlc = (HttpURLConnection) url.openConnection();
		} catch(IOException e) { data="IOException connection"; return; }

		senddata=">../moria/scores/"+startingtime+".txt#"+data;

		urlc.setAllowUserInteraction(false);
		urlc.setDoInput(true);
		urlc.setDoOutput(true);
		urlc.setUseCaches(false);
		urlc.setRequestProperty("Content-type", "text/plain");
		urlc.setRequestProperty("Content-length", senddata.length()+"");
		try {
			urlc.setRequestMethod("POST");
		} catch(IOException e) { data="IOException protocol"; return; }

		try {
			urlc.connect();
		} catch(IOException e) { data="IOException connect"; return; }

		OutputStream os;
		OutputStreamWriter osw;
		try {
			os = urlc.getOutputStream();
			osw = new OutputStreamWriter(os);
			osw.write(senddata);
			osw.flush();
			osw.close();
		} catch(IOException e) { data="IOException write"; return; }

		try {
			InputStream is = urlc.getInputStream();
			is.close();
			urlc.disconnect();
		} catch(IOException e) { data="IOException input"; return; }
		savedone=true;

		try {
			enterscore();
		} catch (Exception e1) {}
	}

	// clicks on the button, so start the game
	public boolean action(Event event, Object obj) {
		String stemp;
		stemp = (String) obj;
		
		if (stemp.equals(strstart)) {
			buttstart.move(70, -100);
			waitforstart=false;
			startgame();
		}
		if (stemp.equals(strend)) {
			try {
				enterscore();
			} catch (Exception e1) {}
		}
		return false;
	}
}

