diff --git a/html/main.html b/html/main.html
index 59463a4..1a104b6 100644
--- a/html/main.html
+++ b/html/main.html
@@ -9,7 +9,7 @@
COMBAT !
diff --git a/js/script.js b/js/script.js
index 62e5287..f9743a6 100644
--- a/js/script.js
+++ b/js/script.js
@@ -1,154 +1,23 @@
document.addEventListener('DOMContentLoaded', () => {
-
- // --- Sprite Drawing Functions ---
-
- function drawGrasslandFrame(ctx, frame, width, height) {
- const PIXEL_SCALE = 2;
- const colors = ['#6A994E', '#588142', '#A5A450'];
-
- ctx.fillStyle = colors[1];
- ctx.beginPath();
- ctx.moveTo(width / 2, 0); ctx.lineTo(width, height / 2); ctx.lineTo(width / 2, height); ctx.lineTo(0, height / 2);
- ctx.closePath();
- ctx.fill();
-
- for (let i = 0; i < 150; i++) {
- const x = Math.random() * width;
- const y = (Math.random() * height / 2) + height / 4;
- const dx = Math.abs(x - width / 2);
- const dy = Math.abs(y - height / 2);
- if (dx / (width / 2) + dy / (height / 2) > 1) continue;
-
- const wave = Math.sin(x / 5 + frame * Math.PI / 2) * PIXEL_SCALE;
- ctx.fillStyle = colors[i % 2];
- ctx.fillRect(x + wave, y - PIXEL_SCALE, PIXEL_SCALE, PIXEL_SCALE);
- }
- }
-
- function drawWaterFrame(ctx, frame, width, height) {
- const colors = ['#5A8AB8', '#4A7AA8', '#97aabdff'];
- ctx.fillStyle = colors[0];
- ctx.beginPath();
- ctx.moveTo(width / 2, 0); ctx.lineTo(width, height / 2); ctx.lineTo(width / 2, height); ctx.lineTo(0, height / 2);
- ctx.closePath();
- ctx.fill();
- for (let i = 0; i < 3; i++) {
- ctx.strokeStyle = colors[1];
- ctx.lineWidth = 1;
- ctx.beginPath();
- const startY = (height / 4) * (i + 1);
- const offset = (frame * 0.2) + i * 2;
- ctx.moveTo(0, startY + Math.sin(offset) * 2);
- for (let x = 0; x < width; x++) {
- const y = startY + Math.sin(x * 0.15 + offset) * 2;
- if (Math.abs(x - width / 2) / (width / 2) + Math.abs(y - height / 2) / (height / 2) < 1) {
- ctx.lineTo(x, y);
- }
- }
- ctx.stroke();
- }
- }
-
- function drawDeepWaterFrame(ctx, frame, width, height) {
- const colors = ['#3D5A80', '#2A4A6D', '#1A202C'];
- ctx.fillStyle = colors[0];
- ctx.beginPath();
- ctx.moveTo(width / 2, 0); ctx.lineTo(width, height / 2); ctx.lineTo(width / 2, height); ctx.lineTo(0, height / 2);
- ctx.closePath();
- ctx.fill();
- for (let i = 0; i < 2; i++) {
- ctx.strokeStyle = colors[1];
- ctx.lineWidth = 2;
- ctx.beginPath();
- const startY = (height / 3) * (i + 1);
- const offset = (frame * 0.1) + i * 3;
- ctx.moveTo(0, startY + Math.sin(offset) * 1.5);
- for (let x = 0; x < width; x++) {
- const y = startY + Math.sin(x * 0.1 + offset) * 1.5;
- if (Math.abs(x - width / 2) / (width / 2) + Math.abs(y - height / 2) / (height / 2) < 1) {
- ctx.lineTo(x, y);
- }
- }
- ctx.stroke();
- }
- }
-
- function drawMountainFrame(ctx, frame, width, height) {
- const colors = ['#A9A9A9', '#808080', '#696969'];
- ctx.fillStyle = colors[0];
- ctx.beginPath();
- ctx.moveTo(width / 2, 0); ctx.lineTo(width, height / 2); ctx.lineTo(width / 2, height); ctx.lineTo(0, height / 2);
- ctx.closePath();
- ctx.fill();
- for (let i = 0; i < 70; i++) {
- const x = Math.random() * width;
- const y = Math.random() * height;
- if (Math.abs(x - width / 2) / (width / 2) + Math.abs(y - height / 2) / (height / 2) > 1) continue;
- const w = 2 + Math.random() * 4;
- const h = 2 + Math.random() * 4;
- ctx.fillStyle = colors[1 + Math.floor(Math.random() * 2)];
- ctx.fillRect(x - w / 2, y - h / 2, w, h);
- }
- }
-
- function drawSwampFrame(ctx, frame, width, height) {
- const colors = ['#526044', '#36402D', '#6A994E'];
- ctx.fillStyle = colors[1];
- ctx.beginPath();
- ctx.moveTo(width / 2, 0); ctx.lineTo(width, height / 2); ctx.lineTo(width / 2, height); ctx.lineTo(0, height / 2);
- ctx.closePath();
- ctx.fill();
-
- for (let i = 0; i < 3; i++) {
- ctx.strokeStyle = colors[0];
- ctx.lineWidth = 1;
- ctx.beginPath();
- const startY = (height / 4) * (i + 1);
- const offset = (frame * 0.05) + i * 2.5;
- ctx.moveTo(0, startY + Math.sin(offset) * 1);
- for (let x = 0; x < width; x++) {
- const y = startY + Math.sin(x * 0.1 + offset) * 1;
- if (Math.abs(x - width / 2) / (width / 2) + Math.abs(y - height / 2) / (height / 2) < 1) {
- ctx.lineTo(x, y);
- }
- }
- ctx.stroke();
- }
- }
-
- function drawBeachFrame(ctx, frame, width, height) {
- const colors = ['#E9D9A1', '#D4C092', '#FFFFFF'];
- ctx.fillStyle = colors[0];
- ctx.beginPath();
- ctx.moveTo(width / 2, 0); ctx.lineTo(width, height / 2); ctx.lineTo(width / 2, height); ctx.lineTo(0, height / 2);
- ctx.closePath();
- ctx.fill();
-
- for (let i = 0; i < 10; i++) {
- const x = Math.random() * width;
- const y = Math.random() * height;
- if (Math.abs(x - width / 2) / (width / 2) + Math.abs(y - height / 2) / (height / 2) > 0.9) continue;
- ctx.fillStyle = colors[2];
- ctx.fillRect(x, y, 1, 1);
- }
- }
-
- // --- Biome Configuration ---
const Biome = {
- WATER_DEEP: { sprite: null, design: { frames: 8, duration: 250, drawer: drawDeepWaterFrame }, acceptStructure:false, movements:['swim'], affinities:[('water',0.8),('dark',0.2)], name: 'Eau Profonde', winterColor: '#3D5A80', fallColor: '#3D5A80', summerColor: '#3D5A80', autumnColor: '#3D5A80',maxElevation:0,minElevation:0 },
- WATER_SHALLOW: { sprite: null, design: { frames: 6, duration: 150, drawer: drawWaterFrame }, acceptStructure:false,movements:['swim','ride','navigate','fly'], affinities:[('water',0.8),('life',0.2)],name: 'Eau Peu Profonde', winterColor: '#97aabdff', fallColor: '#5A8AB8', summerColor: '#5A8AB8', autumnColor: '#5A8AB8',maxElevation:1,minElevation:1 },
- BEACH: { sprite: null, design: { frames: 8, duration: 200, drawer: drawBeachFrame }, acceptStructure:true, movements:['walk','ride','fly'],affinities:[('sand',0.8),('water',0.2)],name: 'Sable', winterColor: '#E9D9A1', fallColor: '#E9D9A1', summerColor: '#E9D9A1', autumnColor: '#E9D9A1',maxElevation:2 ,minElevation:2 },
- GRASSLAND: { sprite: null, design: { frames: 4, duration: 200, drawer: drawGrasslandFrame }, acceptStructure:true, movements:['walk','ride','fly'],affinities:[('life',0.6),('earth',0.2)],name: 'Plaine', winterColor: '#ecf1e3ff', fallColor: '#98C159', summerColor: '#a5a450ff', autumnColor: '#455e21ff',maxElevation:3,minElevation:2 },
- FOREST: { sprite: null, acceptStructure:true,movements:['walk','ride','fly'],affinities:[('wood',0.6),('earth',0.4)], name: 'Forêt', winterColor: '#92ac83ff', fallColor: '#21a32cff', summerColor: '#6A994E', autumnColor: '#b88a28ff',maxElevation:3,minElevation:2 },
- ENCHANTED_FOREST: { sprite: null, acceptStructure:true,movements:['walk','ride','fly'],affinities:[('wood',0.8),('dark',0.2),('life',0.2)], name: 'Forêt Enchantée', winterColor: '#7B6094', fallColor: '#7B6094', summerColor: '#7B6094', autumnColor: '#7B6094',maxElevation:3,minElevation:2 },
- MOUNTAIN: { sprite: null, design: { frames: 1, duration: 99999, drawer: drawMountainFrame }, acceptStructure:false, movements:['climb','fly'],affinities:[('rock',0.6),('wind',0.4)],name: 'Montagne', winterColor: '#F7F7F7', fallColor: '#A9A9A9', summerColor: '#A9A9A9', autumnColor: '#A9A9A9',maxElevation:5,minElevation:3 },
- SNOWLAND: { sprite: null, acceptStructure:true,movements:['walk','ride','fly'], affinities:[('ice',0.8),('earth',0.2)],name: 'Toundra', winterColor: '#F7F7F7', fallColor: '#F7F7F7', summerColor: '#F7F7F7', autumnColor: '#F7F7F7',maxElevation:2,minElevation:2 },
- SNOWMOUNTAIN: { sprite: null, acceptStructure:false, movements:['climb','fly'],affinities:[('ice',0.4),('rock',0.4),('wind',0.2)],name: 'Mont enneigé', winterColor: '#F7F7F7', fallColor: '#F7F7F7', summerColor: '#F7F7F7', autumnColor: '#F7F7F7',maxElevation:8,minElevation:4 },
- DESERT: { sprite: null, acceptStructure:true,movements:['walk','ride','fly'],affinities:[('sand',0.8),('life',0.1),('fire',0.1)], name: 'Désert', winterColor: '#D4A373', fallColor: '#D4A373', summerColor: '#D4A373', autumnColor: '#D4A373',maxElevation:2,minElevation:2 },
- RIVER: { sprite: null, acceptStructure:false,movements:['navigate','swim','fly'],affinities:[('water',0.6),('earth',0.2),('life',0.2)],name: 'Rivière', winterColor: '#97aabdff', fallColor: '#5A8AB8', summerColor: '#5A8AB8', autumnColor: '#5A8AB8',maxElevation:2,minElevation:2 },
- SWAMP: { sprite: null, design: { frames: 10, duration: 300, drawer: drawSwampFrame }, acceptStructure:false, movements:['fly'],affinities:[('water',0.6),('earth',0.2),('dark',0.2)],name: 'Marais', winterColor: '#0b2e10ff', fallColor: '#0b2e10ff', summerColor: '#0b2e10ff', autumnColor: '#0b2e10ff',maxElevation:1,minElevation:1 }
+ WATER_DEEP: { acceptStructure:false, movements:['swim'], affinities:[('water',0.8),('dark',0.2)], name: 'Eau Profonde', winterColor: '#3D5A80', fallColor: '#3D5A80', summerColor: '#3D5A80', autumnColor: '#3D5A80',maxElevation:0,minElevation:0 },
+ WATER_SHALLOW: { acceptStructure:false,movements:['swim','ride','navigate','fly'], affinities:[('water',0.8),('life',0.2)],name: 'Eau Peu Profonde', winterColor: '#97aabdff', fallColor: '#5A8AB8', summerColor: '#5A8AB8', autumnColor: '#5A8AB8',maxElevation:1,minElevation:1 },
+ BEACH: { acceptStructure:true, movements:['walk','ride','fly'],affinities:[('sand',0.8),('water',0.2)],name: 'Sable', winterColor: '#E9D9A1', fallColor: '#E9D9A1', summerColor: '#E9D9A1', autumnColor: '#E9D9A1',maxElevation:2 ,minElevation:2 },
+ GRASSLAND: { sprite:{frame:4,animationSpeed:200 }, acceptStructure:true, movements:['walk','ride','fly'],affinities:[('life',0.6),('earth',0.2)],name: 'Plaine', winterColor: '#ecf1e3ff', fallColor: '#98C159', summerColor: '#a5a450ff', autumnColor: '#455e21ff',maxElevation:3,minElevation:2 },
+ FOREST: { acceptStructure:true,movements:['walk','ride','fly'],affinities:[('wood',0.6),('earth',0.4)], name: 'Forêt', winterColor: '#92ac83ff', fallColor: '#21a32cff', summerColor: '#6A994E', autumnColor: '#b88a28ff',maxElevation:3,minElevation:2 },
+ ENCHANTED_FOREST: { acceptStructure:true,movements:['walk','ride','fly'],affinities:[('wood',0.8),('dark',0.2),('life',0.2)], name: 'Forêt Enchantée', winterColor: '#7B6094', fallColor: '#7B6094', summerColor: '#7B6094', autumnColor: '#7B6094',maxElevation:3,minElevation:2 },
+ MOUNTAIN: { acceptStructure:false, movements:['climb','fly'],affinities:[('rock',0.6),('wind',0.4)],name: 'Montagne', winterColor: '#F7F7F7', fallColor: '#A9A9A9', summerColor: '#A9A9A9', autumnColor: '#A9A9A9',maxElevation:5,minElevation:3 },
+ SNOWLAND: { acceptStructure:true,movements:['walk','ride','fly'], affinities:[('ice',0.8),('earth',0.2)],name: 'Toundra', winterColor: '#F7F7F7', fallColor: '#F7F7F7', summerColor: '#F7F7F7', autumnColor: '#F7F7F7',maxElevation:2,minElevation:2 },
+ SNOWMOUNTAIN: { acceptStructure:false, movements:['climb','fly'],affinities:[('ice',0.4),('rock',0.4),('wind',0.2)],name: 'Mont enneigé', winterColor: '#F7F7F7', fallColor: '#F7F7F7', summerColor: '#F7F7F7', autumnColor: '#F7F7F7',maxElevation:8,minElevation:4 },
+ DESERT: { acceptStructure:true,movements:['walk','ride','fly'],affinities:[('sand',0.8),('life',0.1),('fire',0.1)], name: 'Désert', winterColor: '#D4A373', fallColor: '#D4A373', summerColor: '#D4A373', autumnColor: '#D4A373',maxElevation:2,minElevation:2 },
+ RIVER: { acceptStructure:false,movements:['navigate','swim','fly'],affinities:[('water',0.6),('earth',0.2),('life',0.2)],name: 'Rivière', winterColor: '#97aabdff', fallColor: '#5A8AB8', summerColor: '#5A8AB8', autumnColor: '#5A8AB8',maxElevation:2,minElevation:2 },
+ SWAMP: { acceptStructure:false, movements:['fly'],affinities:[('water',0.6),('earth',0.2),('dark',0.2)],name: 'Marais', winterColor: '#15521eff', fallColor: '#15521eff', summerColor: '#15521eff', autumnColor: '#15521eff',maxElevation:1,minElevation:1 }
}
+ let grasslandSheet;
+ let grasslandFrame = 0;
+ let lastGrassFrameTime = 0;
+
const NAME_PREFIXES = ["Chêne", "Sombre", "Pierre", "Haut", "Val", "Mur", "Guet", "Clair"];
const NAME_SUFFIXES = ["bourg", "fort", "ville", "mont", "port", "bois", "rivage", "gard"];
@@ -157,7 +26,9 @@ document.addEventListener('DOMContentLoaded', () => {
const RACE = {
HUMAN: {strength:1,vitality:1,dexterity:1,intelligence:1,wisdom:1,luck:1}
};
-
+
+
+
const Slot = ['head','body','leg','foot','right-hand','left-hand','two-hands','finger1','finger2','neck','purse','backpack','belt1','belt2'];
const ItemType = {
@@ -193,32 +64,11 @@ document.addEventListener('DOMContentLoaded', () => {
};
- const STRUCTURE_TYPE = {
- CITY: { name: 'Ville', population: 15, spawnChance: 0.005, svg: () => citySVG },
- VILLAGE: { name: 'Village', population: 5, spawnChance: 0.02, svg: () => villageSVG},
- FARM: { name: 'Ferme', population: 3, spawnChance: 0.01, svg: () => farmSVG },
- CAMP: { name: 'Campement', population: 2, spawnChance: 0.002, svg: () => campSVG },
- };
-
- const NPC_TYPES = {
- VILLAGER: {
- name: "Habitant",
- dialogues: [
- "Bien le bonjour, étranger.", "J'espère que la récolte sera bonne cette année.", "Faites attention aux loups dans la forêt.", "Le forgeron a de nouvelles marchandises, je crois."
- ]
- },
- FARMER: {
- name: "Fermier",
- dialogues: [
- "Le temps est parfait pour les cultures.", "Ces sangliers n'arrêtent pas de saccager mes champs !", "Une bonne terre, c'est tout ce qui compte."
- ]
- },
- BANDIT: {
- name: "Bandit",
- dialogues: [
- "Qu'est-ce que tu regardes ?", "Dégage d'ici avant que je me fâche.", "Ta bourse ou la vie !"
- ]
- }
+ const StructureType = {
+ TOWN: { name: 'Ville', population:15, icon: '🗡️', occurence:0.001,design: `
`},
+ VILLAGE: { name: 'Village', population:5, icon: '🗡️',occurence:0.005, design: `
`},
+ FARM: { name: 'Ferme', population:5, icon: '🗡️',occurence:0.010 },
+ CAMP:{ name: 'Campement', population:2, icon: '⛺',occurence:0.002 },
};
const MONSTER_TYPES = {
@@ -227,150 +77,36 @@ document.addEventListener('DOMContentLoaded', () => {
};
const ANIMAL_TYPES = {
- WOLF: { name: 'Loup', movementClass: 'ground', svgAsset: () => wolfPackSVG, hp: 20, strength: 4, xp: 15, loot: { 'Cuir': 1, 'Os': 1 }, biomes: [Biome.FOREST, Biome.MOUNTAIN, Biome.SNOWLAND], spawnChance: 0.01, size: { w: 40, h: 40 }, offset: { x: -20, y: -35 } },
- BOAR: { name: 'Sanglier', movementClass: 'ground', svgAsset: () => boarSVG, hp: 25, strength: 5, xp: 20, loot: { 'Cuir': 2 }, biomes: [Biome.FOREST, Biome.GRASSLAND], spawnChance: 0.02, size: { w: 30, h: 30 }, offset: { x: -15, y: -28 } },
- BIRD: { name: 'Aigle', movementClass: 'fly', svgAsset: () => birdSVG, hp: 10, strength: 1, xp: 5, loot: { 'Plume': 1 }, biomes: [Biome.FOREST, Biome.GRASSLAND, Biome.MOUNTAIN, Biome.BEACH], spawnChance: 0.03, size: { w: 45, h: 40 }, offset: { x: -22, y: -45 }, flightHeight: 40 },
+ WOLF: { name: 'Loup', svgAsset: () => wolfsSVG, hp: 20, strength: 4, xp: 15, loot: { 'Cuir': 1, 'Os': 1 }, biomes: [Biome.FOREST, Biome.MOUNTAIN, Biome.SNOWLAND], spawnChance: 0.01 },
+ BOAR: { name: 'Sanglier', svgAsset: () => boarSVG, hp: 25, strength: 5, xp: 20, loot: { 'Cuir': 2 }, biomes: [Biome.FOREST, Biome.GRASSLAND], spawnChance: 0.02 },
};
- // --- Global Asset Variables ---
- let forestSVG, villageSVG, citySVG, playerSVG, enchantedForestSVG, swampSVG, wolfPackSVG, boarSVG, birdSVG, farmSVG, campSVG, npcSVG;
- let geminiApiKey = "";
-
- // --- QUEST DATABASE ---
- const QUEST_DATABASE = {
- 'WOLF_MENACE': {
- title: "La menace des loups",
- stages: {
- 0: {
- text: "Les loups sont devenus bien trop agressifs ces derniers temps. Ils s'approchent du village et effraient les enfants. Pourriez-vous nous aider à chasser quelques-uns d'entre eux ?",
- choices: [
- { text: "J'accepte de vous aider.", nextStage: 1 },
- { text: "Désolé, je n'ai pas le temps.", nextStage: null }
- ]
- },
- 1: {
- text: "Merci ! S'il vous plaît, éliminez 3 meutes de loups dans les environs et revenez me voir. Soyez prudent.",
- objective: { type: 'kill', target: 'WOLF', count: 3 },
- choices: [
- { text: "Je suis dessus.", nextStage: null }
- ]
- },
- 2: {
- text: "Vous l'avez fait ! Merci, vous avez rendu un grand service au village.",
- choices: [
- { text: "Ce n'était rien.", nextStage: 3 }
- ]
- },
- 3: {
- text: "Voici une petite récompense pour vos efforts. Revenez nous voir quand vous voulez !",
- reward: { xp: 100, items: [] },
- isEnd: true
- }
- }
- }
- };
-
-
- class Sprite {
- constructor(sheet, frameWidth, frameHeight, frameCount, animationSpeed) {
- this.sheet = sheet;
- this.frameWidth = frameWidth;
- this.frameHeight = frameHeight;
- this.frameCount = frameCount;
- this.animationSpeed = animationSpeed;
- this.currentFrame = 0;
- this.lastFrameTime = 0;
- }
-
- update(currentTime) {
- if (this.frameCount <= 1) return;
- if (currentTime - this.lastFrameTime > this.animationSpeed) {
- this.currentFrame = (this.currentFrame + 1) % this.frameCount;
- this.lastFrameTime = currentTime;
- }
- }
-
- draw(ctx, dx, dy) {
- if (!this.sheet) return;
- const sx = 0;
- const sy = this.currentFrame * this.frameHeight;
- ctx.drawImage(this.sheet,
- sx, sy, this.frameWidth, this.frameHeight,
- dx, dy, this.frameWidth, this.frameHeight
- );
- }
- }
-
+
+
+
class Position{
constructor(x,y,h=0) {
this.x = x;
this.y = y;
this.h = h;
}
+ setPosition(x, y, h=0) {
+ this.x = x;
+ this.y = y;
+ this.h = h;
+ }
+
+ getPosition() {
+ return { x: this.x, y: this.y, h: this.h };
+ }
cartToIso() {
return {
- x: (this.x - this.y) * (TILE_WIDTH / 2),
- y: (this.x + this.y) * (TILE_HEIGHT / 2)
+ x: (this.x - this.y) * 32,
+ y: (this.x + this.y) * 16
}
}
- }
-
- class Animal {
- constructor(type, tile) {
- this.type = type;
- this.tile = tile;
- this.lastMoveTime = 0;
- this.moveCooldown = 2000 + Math.random() * 3000;
- }
-
- update(currentTime, gameMap) {
- if (currentTime - this.lastMoveTime > this.moveCooldown) {
- this.move(gameMap);
- this.lastMoveTime = currentTime;
- this.moveCooldown = 2000 + Math.random() * 3000;
- }
- }
-
- move(gameMap) {
- const possibleMoves = [ { dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
- const move = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
- const newX = this.tile.position.x + move.dx;
- const newY = this.tile.position.y + move.dy;
-
- if (newX >= 0 && newX < gameMap.size && newY >= 0 && newY < gameMap.size) {
- const targetTile = gameMap.tiles[newY][newX];
- const canMoveGround = this.type.movementClass === 'ground' && targetTile.biome && (targetTile.biome.movements.includes('walk') || targetTile.biome.movements.includes('climb'));
- const canFly = this.type.movementClass === 'fly' && targetTile.biome;
-
- if ((canMoveGround || canFly) && this.type.biomes.includes(targetTile.biome)) {
- this.tile = targetTile;
- }
- }
- }
-
- setDesign(ctx) {
- if (!this.tile || this.tile.visibility !== 2) return;
- const svgImage = this.type.svgAsset();
- if (!svgImage) return;
-
- const screenPos = this.tile.position.cartToIso();
- const elevationHeight = this.tile.position.h * ELEVATION_STEP;
- const flightHeight = this.type.flightHeight || 0;
- const size = this.type.size || { w: 40, h: 40 };
- const offset = this.type.offset || { x: -20, y: -35 };
-
- ctx.save();
- ctx.translate(screenPos.x, screenPos.y - elevationHeight - flightHeight);
- if (flightHeight > 0) {
- ctx.fillStyle = 'rgba(0,0,0,0.2)';
- ctx.beginPath();
- ctx.ellipse(0, flightHeight, 8, 4, 0, 0, 2 * Math.PI);
- ctx.fill();
- }
- ctx.drawImage(svgImage, offset.x, offset.y, size.w, size.h);
- ctx.restore();
- }
+ // Méthode de l'objet
}
class Tile {
@@ -379,12 +115,15 @@ document.addEventListener('DOMContentLoaded', () => {
this.position = new Position(x, y);
this.biome = null;
this.structure = null;
- this.visibility = 0; // 0: Unseen, 1: Seen, 2: Visible
this.setBiome();
this.setElevation();
this.setStructure();
+ //this.setDesign(ctx);
+ //this.forestSvg=null;
+ //this.setSvg();
}
+ // Méthode de l'objet
setBiome() {
let scale = 0.05;
let eRaw = (simplex.noise2D(this.position.x * scale, this.position.y * scale) + 1) / 2;
@@ -402,96 +141,88 @@ document.addEventListener('DOMContentLoaded', () => {
else this.biome = Biome.GRASSLAND;
}
}
-
- setDesign(ctx){
- if (!this.biome || this.visibility === 0) return;
-
+ setDesign(ctx,currentTime){
+ if (!this.biome) return;
const screenPos = this.position.cartToIso();
- const elevationHeight = this.position.h * ELEVATION_STEP;
-
+ const elevationHeight = this.position.h * 8;
+ let baseColor=null;
ctx.save();
ctx.translate(screenPos.x, screenPos.y - elevationHeight);
-
- const baseColor = this.biome.summerColor;
+ if (currentTime.season == 'winter') { baseColor = this.biome.winterColor;}
+ if (currentTime.season == 'summer') { baseColor = this.biome.summerColor;}
+ if (currentTime.season == 'fall') { baseColor = this.biome.fallColor;}
+ if (currentTime.season == 'autumn') { baseColor = this.biome.autumnColor;}
const shadowColor = shadeColor(baseColor, -30);
+ // Right Face
ctx.fillStyle = shadowColor;
ctx.beginPath();
- ctx.moveTo(0, TILE_HEIGHT); ctx.lineTo(TILE_WIDTH / 2, TILE_HEIGHT / 2); ctx.lineTo(TILE_WIDTH / 2, TILE_HEIGHT / 2 + elevationHeight); ctx.lineTo(0, TILE_HEIGHT + elevationHeight);
+ ctx.moveTo(0, 32); ctx.lineTo(32, 16); ctx.lineTo(32, 16 + elevationHeight); ctx.lineTo(0, 32 + elevationHeight);
ctx.closePath();
ctx.fill();
+ // Left Face
ctx.fillStyle = shadeColor(shadowColor, -10);
ctx.beginPath();
- ctx.moveTo(0, TILE_HEIGHT); ctx.lineTo(-TILE_WIDTH / 2, TILE_HEIGHT / 2); ctx.lineTo(-TILE_WIDTH / 2, TILE_HEIGHT / 2 + elevationHeight); ctx.lineTo(0, TILE_HEIGHT + elevationHeight);
+ ctx.moveTo(0, 32); ctx.lineTo(-32, 16); ctx.lineTo(-32, 16 + elevationHeight); ctx.lineTo(0, 32 + elevationHeight);
ctx.closePath();
ctx.fill();
- if (this.biome.sprite) {
- this.biome.sprite.draw(ctx, -TILE_WIDTH / 2, 0);
- } else {
- ctx.fillStyle = baseColor;
- ctx.beginPath();
- ctx.moveTo(0, 0); ctx.lineTo(TILE_WIDTH / 2, TILE_HEIGHT / 2); ctx.lineTo(0, TILE_HEIGHT); ctx.lineTo(-TILE_WIDTH / 2, TILE_HEIGHT / 2);
- ctx.closePath();
- ctx.fill();
+ // Top Face
+ ctx.fillStyle = baseColor;
+ ctx.beginPath();
+ ctx.moveTo(0, 0); ctx.lineTo(32, 16); ctx.lineTo(0, 32); ctx.lineTo(-32, 16);
+ ctx.closePath();
+ ctx.fill();
+ if (this.biome === Biome.FOREST && forestSVG) {
+ ctx.drawImage(forestSVG, -25, -25, 50, 50);
+ } else if (this.biome === Biome.ENCHANTED_FOREST && mforestSVG) {
+ ctx.drawImage(mforestSVG, -25, -25, 50, 50);
+ } else if (this.biome === Biome.SWAMP && swampSVG) {
+ ctx.drawImage(swampSVG, -25, -5, 50, 30);
}
-
- if(this.visibility === 2) {
- if (this.biome === Biome.FOREST && forestSVG) {
- ctx.drawImage(forestSVG, -25, -25, 50, 50);
- } else if (this.biome === Biome.ENCHANTED_FOREST && enchantedForestSVG) {
- ctx.drawImage(enchantedForestSVG, -25, -25, 50, 50);
- } else if (this.biome === Biome.SWAMP && swampSVG) {
- ctx.drawImage(swampSVG, -25, -5, 50, 30);
- }
-
- if (this.structure) {
- const svg = this.structure.type.svg();
- ctx.drawImage(svg, -20, -20, 40, 40);
- }
+ if (this.structure) {
+ if (this.structure.type === 'village' && villageSVG) {
+ ctx.drawImage(villageSVG, -20, -16, 40, 40);
+ } else if (this.structure.type === 'city' && citySVG) {
+ ctx.drawImage(citySVG, -24, -28, 48, 48);
+ }
}
-
- if (this.visibility === 1) {
- ctx.fillStyle = 'rgba(0,0,0,0.5)';
- ctx.beginPath();
- ctx.moveTo(0, 0); ctx.lineTo(TILE_WIDTH / 2, TILE_HEIGHT / 2); ctx.lineTo(0, TILE_HEIGHT); ctx.lineTo(-TILE_WIDTH / 2, TILE_HEIGHT / 2);
- ctx.closePath();
- ctx.fill();
- }
-
ctx.restore();
}
+ setExplored(explored) {
+ this.explored=explored;
+ }
setElevation(){
- if (!this.biome) return;
+ if (!this.biome) {
+ console.error("Impossible de définir l'élévation car le biome n'est pas défini pour la tuile", this.position);
+ return;
+ }
this.position.h = Math.floor(Math.random() * (this.biome.maxElevation - this.biome.minElevation+ 1) + this.biome.minElevation);
}
-
setStructure(){
- if (!this.biome || !this.biome.acceptStructure) return;
- var rand = seededRandom(this.position.x * 13 + this.position.y * 59);
-
- let chance = rand();
- let cumulativeChance = 0;
-
- for(const key in STRUCTURE_TYPE) {
- const type = STRUCTURE_TYPE[key];
- cumulativeChance += type.spawnChance;
- if (chance < cumulativeChance) {
- this.structure = {
- type: type,
- name: type.name === 'Campement' ? 'Campement de bandits' : (NAME_PREFIXES[Math.floor(rand() * NAME_PREFIXES.length)] + " " + NAME_SUFFIXES[Math.floor(rand() * NAME_SUFFIXES.length)])
- };
- return;
- }
+ if (!this.biome) {
+ console.error("Impossible de définir une structure car le biome n'est pas défini pour la tuile", this.position);
+ return;
}
+ var rand = seededRandom(this.position.x * 13 + this.position.y * 59);
+ var structureChance = rand();
+ if (structureChance < 0.005 && this.biome.acceptStructure) {
+ this.structure = { type: 'city', name: NAME_PREFIXES[Math.floor(rand() * NAME_PREFIXES.length)] + NAME_SUFFIXES[Math.floor(rand() * NAME_SUFFIXES.length)], population: StructureType.TOWN.population + Math.floor(rand() * 150), buildings: ['Remparts', 'Place Forte (Château)', 'Grand Marché', 'Forge', 'Alchimiste', 'Enchanteur', 'Écurie', 'Ferme', 'Ferme', 'Ferme', 'Ferme', 'Ferme', 'Lieu de Culte', 'Nombreuses Maisons'] };
+ } else if (structureChance < 0.02 && this.biome.acceptStructure) {
+ this.structure = { type: 'village', name: NAME_PREFIXES[Math.floor(rand() * NAME_PREFIXES.length)] + NAME_SUFFIXES[Math.floor(rand() * NAME_SUFFIXES.length)], population: StructureType.VILLAGE.population + Math.floor(rand() * 20), buildings: ['Maison du Chef', 'Marchand', 'Lieu de Culte', 'Ferme', 'Ferme', 'Plusieurs Maisons'] };
+ }
+ if ( this.biome.acceptStructure && Math.random() < 0.2) {
+ this.structure={type:'farm', name: 'farm', population: 2, buildings: []}
+ // npcs.push({ x: nx, y: ny, homeX: loc.x, homeY: loc.y, workX: nx, workY: ny, type: 'farmer', icon: '🧑🌾' });
+ }
}
}
class Map {
constructor(size) {
this.size = size;
- this.tiles = [];
+ this.tiles = null
this.initializeMap();
}
@@ -514,35 +245,43 @@ document.addEventListener('DOMContentLoaded', () => {
tick(){
this.minute=(this.minute+1) % 60
- if (this.minute==0) this.hour=(this.hour+1) % 24
- if (this.hour==0 && this.minute == 0) this.day=(this.day+1) % 31;
- if (this.day==1 && this.hour == 0 && this.minute == 0) this.month=(this.month+1) % 13;
- if (this.month==1 && this.day == 1 && this.hour == 0 && this.minute == 0) this.year++;
-
- const timeStr = `${this.hour.toString().padStart(2, '0')}:${this.minute.toString().padStart(2, '0')}`;
- document.getElementById('time-display').textContent = `Jour ${this.day}, ${timeStr}`;
- document.getElementById('season-display').textContent = this.season;
+ if (this.minute==1) this.hour=(this.hour+1) % 24
+ if (this.hour==1) this.day=(this.day+1) % 30
+ if (this.day==1) {this.month=(this.month+1) % 12;}
+ if (this.month==1) {this.year=(this.year+1); }
+ if (this.month==6 && this.day==21) {this.season='summer'; }
+ if (this.month==9 && this.day==21) {this.season='autumn'; }
+ if (this.month==12 && this.day==21) {this.season='winter'; }
+ if (this.month==3 && this.day==21) {this.season='fall'; }
}
-
- setDesign(ctx){
+ wait(nbrHour){
+ for (let i = 0; i < nbrHour; i++) {
+ this.tick();
+ }
+ }
+ setDesign(){
+ let overlayColor = 'rgba(0,0,0,0)';
let opacity = 0;
- if (this.hour >= 20 || this.hour < 6) {
+ if (this.hour > 20 || this.hour < 6) { // Nuit (20h -> 6h)
+ overlayColor = '#000033';
opacity = 0.5;
- } else if (this.hour >= 18) {
- opacity = 0.3 * ( (this.hour - 18) / 2 );
- } else if (this.hour < 8) {
- opacity = 0.3 * ( (8 - this.hour) / 2 );
+ } else if (this.hour > 18) { // Soir (18h -> 20h)
+ overlayColor = '#FF8C00';
+ opacity = 0.3 * ( ((this.hour/24) - 0.75) / 0.08 );
+ } else if (this.hour < 8) { // Matin (6h -> 8h)
+ overlayColor = '#FFD700';
+ opacity = 0.3 * ( (0.33 - (this.hour/24) ) / 0.08 );
}
- if (opacity > 0) {
- ctx.fillStyle = '#000033';
- ctx.globalAlpha = opacity;
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.globalAlpha = 1.0;
- }
+ ctx.fillStyle = overlayColor;
+ ctx.globalAlpha = opacity;
+ document.getElementById('time-display').textContent = `Jour ${this.day}, ${this.hour}:${this.minute}`;
+ document.getElementById('season-display').textContent = this.season;
+
}
+ // Méthode de l'objet
}
class Attributes {
@@ -554,8 +293,58 @@ document.addEventListener('DOMContentLoaded', () => {
this.wisdom = race.wisdom;
this.luck = race.luck;
}
+
+ // Méthode de l'objet
+
+ }
+ class Animal {
+ constructor(type, tile) {
+ this.type = type;
+ this.tile = tile; // The tile the animal is currently on
+ this.lastMoveTime = 0;
+ this.moveCooldown = 2000 + Math.random() * 3000; // Move every 2-5 seconds
+ }
+
+ update(currentTime, gameMap) {
+ if (currentTime - this.lastMoveTime > this.moveCooldown) {
+ this.move(gameMap);
+ this.lastMoveTime = currentTime;
+ this.moveCooldown = 2000 + Math.random() * 3000;
+ }
+ }
+
+ move(gameMap) {
+ const possibleMoves = [
+ { dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 },
+ ];
+
+ const move = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
+ const newX = this.tile.position.x + move.dx;
+ const newY = this.tile.position.y + move.dy;
+
+ if (newX >= 0 && newX < gameMap.size && newY >= 0 && newY < gameMap.size) {
+ const targetTile = gameMap.tiles[newY][newX];
+ if (targetTile.biome && (targetTile.biome.movements.includes('walk') || targetTile.biome.movements.includes('climb')) && this.type.biomes.includes(targetTile.biome)) {
+ this.tile = targetTile;
+ }
+ }
+ }
+ setDesign(ctx) {
+ const svgImage = this.type.svgAsset();
+ if (!this.tile || !svgImage) return;
+
+ const screenPos = this.tile.position.cartToIso();
+ const elevationHeight = this.tile.position.h * 8;
+
+ const size = this.type.size || { w: 40, h: 40 };
+ const offset = this.type.offset || { x: -20, y: -35 };
+
+ ctx.save();
+ ctx.translate(screenPos.x, screenPos.y - elevationHeight);
+ ctx.drawImage(svgImage, offset.x, offset.y, size.w, size.h);
+ ctx.restore();
+ }
}
-
class Creature {
constructor(name,attributes,level,affinities,alignments,tile,species,race,hp,items) {
this.name = name;
@@ -570,96 +359,61 @@ document.addEventListener('DOMContentLoaded', () => {
this.items=items;
this.hp=hp;
}
- }
-
- class Npc {
- constructor(type, homeTile) {
- this.type = type;
- this.tile = homeTile;
- this.homeTile = homeTile;
- this.lastMoveTime = 0;
- this.moveCooldown = 3000 + Math.random() * 4000;
- this.questId = null;
- }
-
- assignQuest(questId) {
- this.questId = questId;
- }
-
- update(currentTime, gameMap) {
- if (currentTime - this.lastMoveTime > this.moveCooldown) {
- this.move(gameMap);
- this.lastMoveTime = currentTime;
- }
- }
-
- move(gameMap) {
- const wanderRadius = 3;
- const possibleMoves = [];
- for (let y = -wanderRadius; y <= wanderRadius; y++) {
- for (let x = -wanderRadius; x <= wanderRadius; x++) {
- const newX = this.homeTile.position.x + x;
- const newY = this.homeTile.position.y + y;
- if (newX >= 0 && newX < gameMap.size && newY >= 0 && newY < gameMap.size) {
- const targetTile = gameMap.tiles[newY][newX];
- if(targetTile.biome.movements.includes('walk') && !targetTile.structure) {
- possibleMoves.push(targetTile);
- }
- }
- }
- }
- if(possibleMoves.length > 0) {
- this.tile = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
- }
- }
-
- setDesign(ctx) {
- if (!this.tile || this.tile.visibility !== 2) return;
-
- const screenPos = this.tile.position.cartToIso();
- const elevationHeight = this.tile.position.h * ELEVATION_STEP;
+
+ fight(){
- ctx.save();
- ctx.translate(screenPos.x, screenPos.y - elevationHeight);
- ctx.drawImage(npcSVG, -25, -50, 50, 60);
- ctx.restore();
+ }
+ // Méthode de l'objet
+
+ }
+ class Npc {
+ constructor(name, creature, settlement, equipments) {
+ this.creature = creature;
+ this.settlement = settlement;
+ this.equipments = equipments;
}
- getDialogue(questManager) {
- if(this.questId) {
- let quest = questManager.getQuest(this.questId);
- if(quest && quest.status !== 'completed') {
- return { name: this.type.name, ...quest.getCurrentStage() };
- }
- }
- const randomDialogue = this.type.dialogues[Math.floor(Math.random() * this.type.dialogues.length)];
- return { name: this.type.name, text: randomDialogue, choices: [] };
+ talk() {
+
}
+
+ steal() {
+
+ }
+
+ trade() {
+
+ }
+ // Méthode de l'objet
+
}
class Player {
- constructor(creature,equipments) {
+ constructor(creature,equipments,playerImage) {
this.creature=creature;
this.equipments=equipments;
+ this.color = '#E53E3E';
+ //this.playerImage = playerImage ;
+ //this.setSvg();
}
get position() {
return this.creature.tile.position;
}
-
+
setDesign(ctx) {
const tile = this.creature.tile
if(!tile) return;
const screenPos = tile.position.cartToIso();
- const elevationHeight = tile.position.h * ELEVATION_STEP;
+ const elevationHeight = tile.position.h * 8;
ctx.save();
ctx.translate(screenPos.x, screenPos.y - elevationHeight);
- if (playerSVG) {
+ if (playerSVG && playerSVG.complete) {
ctx.drawImage(playerSVG, -25, -50, 50, 60);
- } else {
- ctx.fillStyle = '#E53E3E'; ctx.beginPath(); ctx.arc(0, TILE_HEIGHT / 2 - 6, 8, 0, Math.PI * 2); ctx.fill();
+ } else { // Fallback to circle
+ ctx.fillStyle = this.color; ctx.beginPath(); ctx.arc(0, TILE_HEIGHT / 2 - 6, 8, 0, Math.PI * 2); ctx.fill();
}
ctx.restore();
}
@@ -673,278 +427,180 @@ document.addEventListener('DOMContentLoaded', () => {
}
}
}
- }
-
- class Quest {
- constructor(id, definition) {
- this.id = id;
- this.definition = definition;
- this.currentStage = 0;
- this.status = 'inactive'; // inactive, active, completed
- }
-
- getCurrentStage() {
- return this.definition.stages[this.currentStage];
- }
-
- advance(choiceIndex) {
- const stage = this.getCurrentStage();
- if(stage.choices && stage.choices[choiceIndex]) {
- const nextStageIndex = stage.choices[choiceIndex].nextStage;
- if (nextStageIndex !== null) {
- this.currentStage = nextStageIndex;
- if(this.status === 'inactive') this.status = 'active';
- if(this.getCurrentStage().isEnd) {
- this.status = 'completed';
- }
- }
- }
- }
- }
-
- class QuestManager {
- constructor(questGenerator) {
- this.quests = {};
- this.generator = questGenerator;
- }
-
- getQuest(questId) {
- return this.quests[questId];
- }
-
- startQuest(questId, questDefinition) {
- if(!this.quests[questId]) {
- this.quests[questId] = new Quest(questId, questDefinition);
- }
- return this.quests[questId];
- }
-
- async getQuestForNpc(npc) {
- if(npc.questId && this.quests[npc.questId]) {
- return this.quests[npc.questId];
- }
-
- try {
- const questDefinition = await this.generator.generateQuestForNpc(npc);
- if(questDefinition) {
- const questId = `GEMINI_${Date.now()}`;
- npc.assignQuest(questId);
- return this.startQuest(questId, questDefinition);
- }
- } catch(e) {
- console.error("Gemini quest generation failed, using fallback.", e);
- const fallbackId = 'WOLF_MENACE';
- npc.assignQuest(fallbackId);
- return this.startQuest(fallbackId, QUEST_DATABASE[fallbackId]);
- }
- return null;
- }
- }
-
- class GeminiQuestGenerator {
- async generateQuestForNpc(npc) {
- if (!geminiApiKey) {
- throw new Error("API key is missing.");
- }
-
- const prompt = `Génère une quête simple pour un jeu RPG en 2D isométrique.
- Le PNJ est un "${npc.type.name}".
- Il se trouve près d'une structure de type "${npc.homeTile.structure.type.name}" dans un biome de type "${npc.homeTile.biome.name}".
-
- Crée une quête avec un titre, et 3 à 4 étapes.
- La première étape (index 0) doit proposer d'accepter ou de refuser la quête.
- L'étape suivante (index 1) doit décrire l'objectif. L'objectif doit être simple, comme tuer un certain nombre de monstres ou collecter des objets.
- La dernière étape doit être la récompense (isEnd: true).
-
- Réponds UNIQUEMENT avec un objet JSON valide qui suit le schéma ci-dessous. N'ajoute aucun texte avant ou après le JSON.
- `;
-
- const payload = {
- contents: [{ parts: [{ text: prompt }] }],
- generationConfig: {
- responseMimeType: "application/json",
- responseSchema: {
- type: "OBJECT",
- properties: {
- "title": { "type": "STRING" },
- "stages": {
- "type": "ARRAY",
- "items": {
- "type": "OBJECT",
- "properties": {
- "text": { "type": "STRING" },
- "choices": {
- "type": "ARRAY",
- "items": {
- "type": "OBJECT",
- "properties": {
- "text": { "type": "STRING" },
- "nextStage": { "type": ["NUMBER", "NULL"] }
- },
- "required": ["text"]
- }
- },
- "objective": { "type": ["OBJECT", "NULL"], "properties": {"type": {"type": "STRING"}, "target": {"type": "STRING"}, "count": {"type": "NUMBER"}} },
- "reward": { "type": ["OBJECT", "NULL"], "properties": {"xp": {"type": "NUMBER"}, "items": {"type": "ARRAY", "items": { "type": "STRING" }}} },
- "isEnd": { "type": ["BOOLEAN", "NULL"] }
- },
- "required": ["text", "choices"]
- }
- }
- },
- "required": ["title", "stages"]
- }
- }
- };
-
- const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${geminiApiKey}`;
-
- const response = await fetch(apiUrl, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload)
- });
-
- if (!response.ok) {
- const errorBody = await response.text();
- console.error("API Error Body:", errorBody);
- throw new Error(`API call failed with status: ${response.status}`);
- }
-
- const result = await response.json();
- const jsonText = result.candidates?.[0]?.content?.parts?.[0]?.text;
- if(jsonText) {
- return JSON.parse(jsonText);
- }
- throw new Error("Invalid response from API.");
- }
- }
-
- class World {
- constructor(name) {
+
+ rename(name) {
this.name = name;
- this.map = new Map(200);
- this.currentTime=new Time();
- this.animals = [];
- this.npcs = [];
- this.questManager = new QuestManager(new GeminiQuestGenerator());
- this.spawnEntities();
}
- spawnEntities() {
- console.time('Entity Spawning');
- for (let y = 0; y < this.map.size; y++) {
- for (let x = 0; x < this.map.size; x++) {
- const tile = this.map.tiles[y][x];
-
- if(tile.structure) {
- const structureType = tile.structure.type;
- let npcType = NPC_TYPES.VILLAGER;
- if(structureType === STRUCTURE_TYPE.FARM) npcType = NPC_TYPES.FARMER;
- if(structureType === STRUCTURE_TYPE.CAMP) npcType = NPC_TYPES.BANDIT;
-
- for(let i = 0; i < structureType.population; i++) {
- this.npcs.push(new Npc(npcType, tile));
- }
- }
-
- if (tile.biome) {
- for (const key in ANIMAL_TYPES) {
- const animalType = ANIMAL_TYPES[key];
- if (animalType.biomes.includes(tile.biome) && Math.random() < animalType.spawnChance) {
- if(animalType.movementClass === 'ground' && tile.biome.movements.includes('walk')) {
- this.animals.push(new Animal(animalType, tile));
- } else if (animalType.movementClass === 'fly') {
- this.animals.push(new Animal(animalType, tile));
- }
- }
- }
- }
- }
- }
- console.log(`Spawned ${this.animals.length} animals and ${this.npcs.length} NPCs.`);
- console.timeEnd('Entity Spawning');
+ equipItem(item,slot) {
+ equipment.equip(item,slot);
+ updateAttribute();
}
}
+
+ class Settlement {
+ constructor(level, affinities, name, alignments, position, structures) {
+ this.level = level;
+ this.affinities = affinities;
+ this.name = name;
+ this.alignments = alignments;
+ this.structures = structures;
+ }
+
+ // Méthode de l'objet
+
+ }
+ class Resource {
+ constructor(resourceType, position) {
+ this.resourceType = resourceType;
+ this.position = position;
+ }
+ collect(tile){
+
+ }
+ collect(creature){
+ // check if creature can collect
+ // transfer resulting items creature.items[]=this.resourceType.item
+ //end object
+ }
+ // Méthode de l'objet
+
+ }
+ class Equipment {
+
+ constructor(item,slot) {
+ this.item = item;
+ this.slot = slot;
+ }
+ // Méthode de l'objet
+ equip(slot) {
+ //replace current equipment
+ //check if compatible
+ //check if equipable
+ this.slot = slot;
+ }
+ unequip() {
+ this.slot = null;
+
+ }
+ }
+ class Soul {
+
+ constructor(name,description) {
+ this.name = name;
+ this.description = description;
+ }
+ // Méthode de l'objet
+ invoke() {
+ //
+
+ }
+ }
+ class Item {
+
+ constructor(name,duration,itemType) {
+ this.name = name;
+ if (!Object.values(ItemType).includes(itemType)) {
+ throw new Error('Item type invalide !');
+ }
+ this.itemType = itemType;
+ if (duration){
+ this.duration = itemType.duration
+ } else {
+ this.duration = duration
+ }
+
+ }
+ // Méthode de l'objet
+ throw() {
+ // delete after x Tick
+ }
+ destroy() {
+
+ }
+ use(){
+ if (this.duration >1) {
+ this.duration = this.duration-1
+ }
+ else {
+ this.destroy();
+ }
+ }
+ }
+
+
+ class World {
+
+ constructor(name) {
+ this.name = name;
+ this.map = new Map(200)
+ this.currentTime=new Time();
+ this.animals = [];
+ this.setAnimals();
+ }
+ // Méthode de l'objet
+ save() {
+
+ }
+ setAnimals() {
+ console.time('Animal Spawning');
+ for (let y = 0; y < this.map.size; y++) {
+ for (let x = 0; x < this.map.size; x++) {
+ const tile = this.map.tiles[y][x];
+ if (tile.biome && tile.biome.movements.includes('walk')) {
+ for (const key in ANIMAL_TYPES) {
+ const animalType = ANIMAL_TYPES[key];
+ if (animalType.biomes.includes(tile.biome) && Math.random() < animalType.spawnChance) {
+ this.animals.push(new Animal(animalType, tile));
+ }
+ }
+ }
+ }
+ }
+ console.log(`Spawned ${this.animals.length} animals.`);
+ console.timeEnd('Animal Spawning');
+ }
+
+ }
class Camera {
+
constructor(x=0,y=0,canvas) {
this.x=x;
this.y=y;
this.canvas = canvas;
}
+ // Méthode de l'objet
setCamera(position) {
var target = position.cartToIso();
var perspective = position.h * ELEVATION_STEP;
this.x = this.canvas.width / 2 - target.x;
this.y = this.canvas.height / 2 - (target.y - perspective);
}
+
}
class Session {
constructor() {
this.world = new World('Defiance');
this.camera = new Camera(0,0, canvas);
- this.isDialogueActive = false;
this.setControls();
this.loop = this.loop.bind(this);
this.player=null;
this.setInitial();
}
+ // Méthode de l'objet
loop(currentTime) {
- this.update(currentTime);
+ this.updateAnimals(currentTime);
+ this.handleMovement(currentTime);
+ this.camera.setCamera(this.player.position);
this.setDesign();
requestAnimationFrame(this.loop);
}
-
- update(currentTime) {
- if(this.isDialogueActive) return;
-
- this.handleMovement(currentTime);
- this.updateVisibility();
-
- this.world.animals.forEach(animal => animal.update(currentTime, this.world.map));
- this.world.npcs.forEach(npc => npc.update(currentTime, this.world.map));
-
- this.camera.setCamera(this.player.position);
-
- for (const key in Biome) {
- if (Biome[key].sprite) {
- Biome[key].sprite.update(currentTime);
- }
- }
-
- this.world.currentTime.tick();
+ updateAnimals(currentTime) {
+ this.world.animals.forEach(animal => {
+ animal.update(currentTime, this.world.map);
+ });
}
-
- updateVisibility() {
- const map = this.world.map;
- const px = this.player.position.x;
- const py = this.player.position.y;
- const radius = VISION_RADIUS;
- const radiusSq = radius * radius;
-
- const viewBox = {
- minX: Math.max(0, px - radius - 2),
- maxX: Math.min(map.size - 1, px + radius + 2),
- minY: Math.max(0, py - radius - 2),
- maxY: Math.min(map.size - 1, py + radius + 2)
- };
-
- for(let y = viewBox.minY; y <= viewBox.maxY; y++) {
- for(let x = viewBox.minX; x <= viewBox.maxX; x++) {
- const tile = map.tiles[y][x];
- if(tile.visibility === 2) tile.visibility = 1;
-
- const dx = px - x;
- const dy = py - y;
- if (dx * dx + dy * dy <= radiusSq) {
- tile.visibility = 2;
- }
- }
- }
- }
-
-
setInitial(){
let spawnX, spawnY;
do {
@@ -952,99 +608,48 @@ document.addEventListener('DOMContentLoaded', () => {
spawnY = Math.floor(Math.random() * this.world.map.size);
} while (!this.world.map.tiles[spawnY][spawnX].biome || !this.world.map.tiles[spawnY][spawnX].biome.movements.includes('walk') );
this.player = new Player(new Creature('player',new Attributes(RACE.HUMAN),1,null,null,this.world.map.tiles[spawnY][spawnX],'human',RACE.HUMAN,10,null),);
- this.updateVisibility();
}
setControls() {
- document.addEventListener('keydown', e => {
- const key = e.key.toLowerCase();
- if (key.startsWith('arrow')) { controls[key.replace('arrow', '')] = true; }
- else if (['w', 'z'].includes(key)) { controls.up = true; }
- else if (['s'].includes(key)) { controls.down = true; }
- else if (['a', 'q'].includes(key)) { controls.left = true; }
- else if (['d'].includes(key)) { controls.right = true; }
- else if (key === 'e') {
- if(!this.isDialogueActive) this.handleInteraction();
- }
- });
- document.addEventListener('keyup', e => {
- const key = e.key.toLowerCase();
- if (key.startsWith('arrow')) { controls[key.replace('arrow', '')] = false; }
- else if (['w', 'z'].includes(key)) { controls.up = false; }
- else if (['s'].includes(key)) { controls.down = false; }
- else if (['a', 'q'].includes(key)) { controls.left = false; }
- else if (['d'].includes(key)) { controls.right = false; }
- });
- const controlMap = { 'btn-up': 'up', 'btn-down': 'down', 'btn-left': 'left', 'btn-right': 'right' };
- for (const [id, dir] of Object.entries(controlMap)) {
- const btn = document.getElementById(id);
- btn.addEventListener('touchstart', e => { e.preventDefault(); controls[dir] = true; }, { passive: false });
- btn.addEventListener('touchend', e => { e.preventDefault(); controls[dir] = false; });
- }
- document.getElementById('btn-action').addEventListener('click', () => { if(!this.isDialogueActive) this.handleInteraction(); });
- document.getElementById('dialogue-close').addEventListener('click', () => this.hideDialogue());
-
- const apiKeyInputDesktop = document.getElementById('apiKeyInputDesktop');
- const apiKeyInputMobile = document.getElementById('apiKeyInputMobile');
-
- apiKeyInputDesktop.addEventListener('input', (e) => {
- geminiApiKey = e.target.value;
- apiKeyInputMobile.value = geminiApiKey;
- });
- apiKeyInputMobile.addEventListener('input', (e) => {
- geminiApiKey = e.target.value;
- apiKeyInputDesktop.value = geminiApiKey;
- });
+ document.addEventListener('keydown', e => {
+ const key = e.key.toLowerCase();
+ if (key.startsWith('arrow')) { controls[key.replace('arrow', '')] = true; }
+ else if (['w', 'z'].includes(key)) { controls.up = true; }
+ else if (['s'].includes(key)) { controls.down = true; }
+ else if (['a', 'q'].includes(key)) { controls.left = true; }
+ else if (['d'].includes(key)) { controls.right = true; }
+ });
+ document.addEventListener('keyup', e => {
+ const key = e.key.toLowerCase();
+ if (key.startsWith('arrow')) { controls[key.replace('arrow', '')] = false; }
+ else if (['w', 'z'].includes(key)) { controls.up = false; }
+ else if (['s'].includes(key)) { controls.down = false; }
+ else if (['a', 'q'].includes(key)) { controls.left = false; }
+ else if (['d'].includes(key)) { controls.right = false; }
+ });
+ const controlMap = { 'btn-up': 'up', 'btn-down': 'down', 'btn-left': 'left', 'btn-right': 'right' };
+ for (const [id, dir] of Object.entries(controlMap)) {
+ const btn = document.getElementById(id);
+ btn.addEventListener('touchstart', e => { e.preventDefault(); controls[dir] = true; }, { passive: false });
+ btn.addEventListener('touchend', e => { e.preventDefault(); controls[dir] = false; });
+ }
}
setDesign(){
- ctx.fillStyle = '#000000';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(this.camera.x, this.camera.y);
- const [startX, endX, startY, endY] = this.getVisibleTileBounds();
-
- for (let y = startY; y < endY; y++) {
- for (let x = startX; x < endX; x++) {
- if (this.world.map.tiles[y] && this.world.map.tiles[y][x]) {
- this.world.map.tiles[y][x].setDesign(ctx);
- }
- }
- }
-
+ for (let y = 0; y < this.world.map.size; y++) for (let x = 0; x < this.world.map.size; x++) this.world.map.tiles[y][x].setDesign(ctx,this.world.currentTime);
this.world.animals.forEach(animal => animal.setDesign(ctx));
- this.world.npcs.forEach(npc => npc.setDesign(ctx));
this.player.setDesign(ctx);
-
- ctx.restore();
- ctx.save();
this.world.currentTime.setDesign(ctx);
- ctx.restore();
-
+ ctx.fillRect(this.camera.x * -1, this.camera.y * -1, canvas.width, canvas.height);
+ ctx.globalAlpha = 1.0;
+ //drawFloatingTexts();
+
+ ctx.restore();
+ this.world.currentTime.tick();
}
-
- getVisibleTileBounds() {
- const margin = 5;
- const viewWidth = canvas.width;
- const viewHeight = canvas.height;
-
- const isoToCart = (isoX, isoY) => {
- const cartX = (isoX / (TILE_WIDTH / 2) + isoY / (TILE_HEIGHT / 2)) / 2;
- const cartY = (isoY / (TILE_HEIGHT / 2) - isoX / (TILE_WIDTH / 2)) / 2;
- return { x: Math.floor(cartX), y: Math.floor(cartY) };
- };
-
- const topLeft = isoToCart(-this.camera.x, -this.camera.y);
- const bottomRight = isoToCart(-this.camera.x + viewWidth, -this.camera.y + viewHeight);
-
- const startX = Math.max(0, topLeft.x - margin);
- const endX = Math.min(this.world.map.size - 1, bottomRight.x + margin);
- const startY = Math.max(0, topLeft.y - margin);
- const endY = Math.min(this.world.map.size - 1, bottomRight.y + margin);
-
- return [startX, endX, startY, endY];
- }
-
handleMovement(currentTime) {
if (currentTime - lastMoveTime < 150) return;
let moved = false;
@@ -1054,186 +659,104 @@ document.addEventListener('DOMContentLoaded', () => {
if (controls.right) { this.player.move(1, 0,this.world.map); moved = true; }
if(moved) lastMoveTime = currentTime;
}
-
- async handleInteraction() {
- const px = this.player.position.x;
- const py = this.player.position.y;
-
- let targetNpc = null;
- for (const npc of this.world.npcs) {
- const dx = Math.abs(npc.tile.position.x - px);
- const dy = Math.abs(npc.tile.position.y - py);
- if (dx <= 1 && dy <= 1) {
- targetNpc = npc;
- break;
- }
- }
-
- if(targetNpc) {
- this.showDialogue(targetNpc, {name: targetNpc.type.name, text: "Laissez-moi réfléchir..."});
- const quest = await this.world.questManager.getQuestForNpc(targetNpc);
- const dialogueData = targetNpc.getDialogue(this.world.questManager);
- this.showDialogue(targetNpc, dialogueData);
- }
- }
-
- showDialogue(npc, dialogueData) {
- this.isDialogueActive = true;
- const dialogueBox = document.getElementById('dialogue-box');
- document.getElementById('dialogue-name').textContent = dialogueData.name;
- document.getElementById('dialogue-text').textContent = dialogueData.text;
-
- const choicesContainer = document.getElementById('dialogue-choices');
- choicesContainer.innerHTML = '';
-
- if(dialogueData.choices && dialogueData.choices.length > 0) {
- document.getElementById('dialogue-close').classList.add('hidden');
- dialogueData.choices.forEach((choice, index) => {
- const button = document.createElement('button');
- button.textContent = choice.text;
- button.className = 'quest-choice-btn w-full';
- button.onclick = () => {
- const quest = this.world.questManager.getQuest(npc.questId);
- if (quest) {
- quest.advance(index);
- this.updateQuestLogUI();
- }
- if(choice.nextStage !== null) {
- const nextDialogue = npc.getDialogue(this.world.questManager);
- this.showDialogue(npc, nextDialogue);
- } else {
- this.hideDialogue();
- }
- };
- choicesContainer.appendChild(button);
- });
- } else {
- document.getElementById('dialogue-close').classList.remove('hidden');
- }
-
- dialogueBox.classList.remove('hidden');
- }
-
- hideDialogue() {
- document.getElementById('dialogue-box').classList.add('hidden');
- this.isDialogueActive = false;
- }
-
- updateQuestLogUI() {
- const questLogDesktop = document.getElementById('quest-log-desktop');
- const questLogMobile = document.getElementById('quest-log-mobile');
- questLogDesktop.innerHTML = '';
- questLogMobile.innerHTML = '';
-
- const activeQuests = Object.values(this.world.questManager.quests).filter(q => q.status === 'active');
-
- if (activeQuests.length === 0) {
- const noQuestText = '
Aucune quête active.
';
- questLogDesktop.innerHTML = noQuestText;
- questLogMobile.innerHTML = noQuestText;
- return;
- }
-
- activeQuests.forEach(quest => {
- const stage = quest.getCurrentStage();
- const questElement = document.createElement('div');
- questElement.innerHTML = `
${quest.definition.title}
${stage.text}
`;
- questLogDesktop.appendChild(questElement);
- questLogMobile.appendChild(questElement.cloneNode(true));
- });
- }
}
- const controls = { up: false, down: false, left: false, right: false, interact: false };
+
+ const controls = { up: false, down: false, left: false, right: false };
const simplex = new SimplexNoise();
let lastMoveTime = 0;
+ let isGameReady = false;
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const loadingScreen = document.getElementById('loading');
const TILE_WIDTH = 64;
const TILE_HEIGHT = 32;
const ELEVATION_STEP = TILE_HEIGHT / 4;
- const VISION_RADIUS = 8;
-
+ let forestSVG, villageSVG, citySVG, boarSVG,wolfsSVG,swampSVG, playerSVG,mforestSVG;
function seededRandom(seed) {
let s = seed;
return () => { s = (s * 9301 + 49297) % 233280; return s / 233280.0; };
}
+ function loadSvgAsImage(gElement) {
+ return new Promise((resolve, reject) => {
+ if (!gElement) {
+ return reject(new Error("SVG
element not found."));
+ }
+
+ const viewBox = gElement.getAttribute('viewBox') || '0 0 100 100';
+
+ // Create a new, standalone