Skip to content

Instantly share code, notes, and snippets.

@lovroselic
Created October 17, 2019 13:35
Show Gist options
  • Save lovroselic/ff7f6f7a0be96c106c562f7d54bcae02 to your computer and use it in GitHub Desktop.
Save lovroselic/ff7f6f7a0be96c106c562f7d54bcae02 to your computer and use it in GitHub Desktop.
Deep Down Into Darkness
<div id="preload" class="hidden"></div>
<div class="win" id="setup">
<div id="load">
</div>
<!--<a href="/Games/" title="Return to games' index"><img alt="Anxys" class="fr logo" src="/Images/Eyes.png" width="20" height="20"></a>-->
<div id="SC"></div>
<h1 id="title"></h1>
<p>text</p>
Hero's name: <input type = "text" id = "HeroName" value = "HERO" maxlength="10"/>
<p id="buttons">
<input type='button' id='toggleHelp' value='Show/Hide Instructions'>
<input type='button' id='toggleAbout' value='About'>
</p>
<div id="help" class="section">
<fieldset>
<legend>
Instructions:
</legend>
<p><strong>KEYS:</strong></p>
<p>Use cursor keys to move.</p>
<p>CTRL ... cast magic.</p>
<p>H ... use healing potion</p>
<p>M ... use mana potion</p>
<p>A, D ... move scroll selection cursor</p>
<p>TAB ... level up</p>
<p>ENTER ... cast selected scroll</p>
<p><strong>SCROLLS:</strong></p>
<div>
<image src="https://www.c00lsch00l.eu/Games/AA/Light.png" alt="Light" class="fl pic" title="Light">
<p style="position: relative; top: 24px">Magic lamp</p>
<p class="cb"></p>
</div>
<div>
<image src="https://www.c00lsch00l.eu/Games/AA/DrainMana.png" alt="DrainMana" class="fl pic" title="DrainMana">
<p style="position: relative; top: 24px">Drain Mana: drains mana from all creatures in the area. Also yours!</p>
<p class="cb"></p>
</div>
<div>
<image src="https://www.c00lsch00l.eu/Games/AA/Map.png" alt="Map" class="fl pic" title="Map">
<p style="position: relative; top: 24px">Map: reveals the map of the exit area</p>
<p class="cb"></p>
</div>
<div>
<image src="https://www.c00lsch00l.eu/Games/AA/BoostWeapon.png" alt="BoostWeapon" class="fl pic" title="BoostWeapon">
<p style="position: relative; top: 24px">Increase the damage of your sword for the duration of the fight.</p>
<p class="cb"></p>
</div>
<div>
<image src="https://www.c00lsch00l.eu/Games/AA/BoostArmor.png" alt="BoostArmor" class="fl pic" title="BoostArmor">
<p style="position: relative; top: 24px">Increase your armor for the duration of the fight.</p>
<p class="cb"></p>
</div>
<div>
<image src="https://www.c00lsch00l.eu/Games/AA/DestroyArmor.png" alt="DestroyArmor" class="fl pic" title="DestroyArmor">
<p style="position: relative; top: 24px">Decrease your opponent's armor for the duration of the fight.</p>
<p class="cb"></p>
</div>
<div>
<image src="https://www.c00lsch00l.eu/Games/AA/DestroyWeapon.png" alt="DestroyWeapon" class="fl pic" title="DestroyWeapon">
<p style="position: relative; top: 24px">Decrease the damage of your opponent's sword for the duration of the fight.</p>
<p class="cb"></p>
</div>
<div>
<image src="https://www.c00lsch00l.eu/Games/AA/Invisibility.png" alt="Invisibility" class="fl pic" title="Invisibility">
<p style="position: relative; top: 24px">Invisibility: I will let you figure this one by yourself..</p>
<p class="cb"></p>
</div>
</fieldset>
</div>
<div id="about" class="section">
<fieldset>
<legend>
About:
</legend>
<image src="https://www.c00lsch00l.eu/Images/SoF.png" alt="Sword of Fargoal" class="fl pic" title="Sword of Fargoal">
<p> 'Deep Down Into the Darkness' was inspired by C64 classic <a href="https://www.c64-wiki.com/wiki/Sword_of_Fargoal" target="_blank">Sword of Fargoal</a> from 1983, which was itself influenced by 1980 Unix game <a href="https://en.wikipedia.org/wiki/Rogue_(video_game)"
target="_blank">Rogue</a>.</p>
</fieldset>
</div>
<p class="version cb" id="version"></p>
</div>
<div id="game" class="winTrans"></div>
<div id="bottom" class="cb" style="margin-top: 720px"></div>
<div id="temp" class="hidden"></div>
<div id="temp2" class="hidden"></div>
//console.clear();
/////////////////////////////////////////////////
/*
to do:
MONSTER:Don't melee attack if mana available
Statistics on death
TEST. BALANCE
more scrolls,
new scroll ideas:
-
monster strategies
known bugs:
-can heal dead hero on fight form
*/
////////////////////////////////////////////////////
var DEBUG = {
frameCount: 0,
fog: false,
coord: false,
level: false,
viewMonsters: true,
addScrolls: function() {
let scrolls = [
//{ type: "Light", use: "explore" },
//{ type: "Invisibility", use: "explore" },
//{ type: "Map", use: "explore" },
//{ type: "DrainMana", use: "explore" },
//{ type: "Cripple", use: "explore" },
//{ type: "BoostWeapon", use: "fight" },
//{ type: "BoostArmor", use: "fight" },
//{ type: "DestroyArmor", use: "fight" },
//{ type: "DestroyWeapon", use: "fight" }
];
for (let q = 0, QL = scrolls.length; q < QL; q++) {
let selected = scrolls[q];
let scroll = new Scroll(new Grid(0, 0), selected.type, selected.use);
HERO.scrolls.add(scroll);
//HERO.scrolls.add(scroll);
//HERO.scrolls.add(scroll);
}
//add potion2
//let temp = new Potion("health", null);
//temp.exe();
}
};
var INI = {
LAST_LEVEL: 2,
GBpLVL: 10,
CpLVL: 10,
GBpDE: 2,
CpCRD: 5,
HPpLVL: 10,
MPpCRD: 9,
WpLVL: 2,
CHpLVL: 6,
LMPpLVL: 3,
SCRpLVL: 6,
Health_INC: 0.4,
EXP_KEY: 50,
MINI_PIX: 4,
TEMPLE_TIMEOUT: 10000,
EXP: 400,
MAGIC_EXP: 50,
EXP_FACTOR: 1.2,
MAGIC_EXP_FACTOR: 1.4,
PTS_LVL: 3,
LVL_HEALTH: 5,
LVL_MANA: 7,
MAGIC_FAIL: 1.5,
MAGIC_POWER_COST: 2,
ORB_MAX_RANGE: 10,
ENEMY_COMMON: 3,
ENEMY_START: 1,
ENEMY_KEY_ADD: 2,
ENEMY_END_ADD: 2,
ENEMY_TEMPLE: 1,
ENEMY_CORRIDOR: 25,
TRIGGER_WAKE: 11,
TRIGGER_VISION: 9,
SHOOT_TIMEOUT: 3000,
INVISIBILITY_TIME: 20,
MANA_DRAIN_RANGE: 10,
MAP_RADIUS: 5,
INITIATIVE_BONUS: 5,
ATTACk_OFFSET: -1,
AGILITY_TURN: 20,
FIGHT_PANEL_WIDTH: 200,
FLEE_AGILITY_DELTA: 5,
POINTS_ON_START: 4,
NEMESIS_RESPAWN: 900,
//NEMESIS_RESPAWN: 30,
STALK_DISTANCE: 3
};
var PRG = {
VERSION: "0.27.13.dev",
CSS: "color: #80f709",
NAME: "Deep Down Into the Darkness",
YEAR: 2019,
INIT: function() {
console.log("%c****************************", PRG.CSS);
console.log(
`%c${PRG.NAME} ${PRG.VERSION} by Lovro Selic, (c) C00lSch00l ${
PRG.YEAR
} on ˘${navigator.userAgent}`,
PRG.CSS
);
$("#title").html(PRG.NAME);
$("#version").html(
PRG.NAME +
" V" +
PRG.VERSION +
" <span style='font-size:14px'>&copy</span> C00lSch00l 2019"
);
$("input#toggleAbout").val("About " + PRG.NAME);
$("#about fieldset legend").append(" " + PRG.NAME + " ");
ENGINE.readyCall = GAME.setup;
ENGINE.init();
},
setup: function() {
$("#toggleHelp").click(function() {
$("#help").toggle(400);
});
$("#toggleAbout").click(function() {
$("#about").toggle(400);
});
},
start: function() {
console.log(`%c${PRG.NAME} started.`, PRG.CSS);
$("#startGame").addClass("hidden");
var disableKeys = ["enter", "space", "tab", "back"];
for (let key in disableKeys) ENGINE.disableKey(key);
//add boxes
if (!GAME.restarted) {
console.log("%cAdding boxes, setting ENGINE ....", PRG.CSS);
ENGINE.gameWIDTH = 768;
ENGINE.sideWIDTH = 1024 - ENGINE.gameWIDTH;
ENGINE.gameHEIGHT = 768;
ENGINE.titleHEIGHT = 80;
ENGINE.titleWIDTH = 1024;
ENGINE.bottomHEIGHT = 40;
ENGINE.bottomWIDTH = 1024;
ENGINE.checkProximity = false;
ENGINE.checkIntersection = false;
ENGINE.setCollisionsafe(49);
$("#bottom").css(
"margin-top",
ENGINE.gameHEIGHT + ENGINE.titleHEIGHT + ENGINE.bottomHEIGHT
);
$(ENGINE.gameWindowId).width(ENGINE.gameWIDTH + ENGINE.sideWIDTH + 4);
ENGINE.addBOX(
"TITLE",
ENGINE.titleWIDTH,
ENGINE.titleHEIGHT,
["title"],
null
);
ENGINE.addBOX(
"ROOM",
ENGINE.gameWIDTH,
ENGINE.gameHEIGHT,
[
"background",
"coordview",
"grave",
"animation",
"actors",
"orbs",
"explosion",
"fogview",
"text"
],
"side"
);
ENGINE.addBOX(
"SIDE",
ENGINE.sideWIDTH,
ENGINE.gameHEIGHT,
["sideback", "status", "map", "time"],
"fside"
);
ENGINE.addBOX(
"DOWN",
ENGINE.bottomWIDTH,
ENGINE.bottomHEIGHT,
["bottom"],
null
);
ENGINE.addBOX(
"LEVEL",
ENGINE.gameWIDTH,
ENGINE.gameHEIGHT,
["floor", "wall", "config", "fog", "coord"],
null
);
if (!DEBUG.level) $("#LEVEL").addClass("hidden");
}
GAME.start();
}
};
class Gold {
constructor(value, grid) {
this.name = "Gold";
this.value = value;
this.grid = grid;
this.static = false;
if (value === 100) {
this.sprite = SPRITE.Gold;
} else this.sprite = SPRITE.Coin;
}
exe() {
HERO.gold += this.value;
TEXTPOOL.pool.push(
new TextSprite(this.value, GRID.gridToCoord(this.grid), "#DAA520")
);
TITLE.change();
}
}
class Potion {
constructor(type, grid) {
this.name = "Potion";
this.type = type;
this.grid = grid;
this.static = false;
switch (type) {
case "health":
this.sprite = SPRITE.RedPotion;
this.exe = () => {
HERO.redPotion++;
TITLE.change();
};
break;
case "magic":
this.sprite = SPRITE.BluePotion;
this.exe = () => {
HERO.bluePotion++;
TITLE.change();
};
break;
}
}
}
class Chest {
constructor(grid) {
this.name = "Chest";
this.grid = grid;
this.sprite = SPRITE.Chest;
this.static = true;
let option = RND(1, 5);
switch (option) {
case 1:
case 2:
this.contains = new Gold(100, this.grid);
break;
case 3:
case 4:
let type = ["health", "magic"];
this.contains = new Potion(type.chooseRandom(), this.grid);
break;
case 5:
let boosts = ["health", "mana", "weapon", "armor", "magic"];
this.contains = new Boost(boosts.chooseRandom(), this.grid);
break;
}
}
exe() {
MAP[GAME.level].DUNGEON.chests.push(this.contains);
}
}
class Boost {
constructor(type, grid) {
this.name = "Boost";
this.type = type;
this.grid = grid;
this.static = false;
switch (type) {
case "health":
this.sprite = SPRITE.Heart;
this.exe = () => {
HERO.maxHealth += INI.LVL_HEALTH;
HERO.health = HERO.maxHealth;
TITLE.change();
};
break;
case "mana":
this.sprite = SPRITE.Mana;
this.exe = () => {
HERO.maxMana += INI.LVL_MANA;
HERO.mana = HERO.maxMana;
TITLE.change();
};
break;
case "weapon":
this.sprite = SPRITE.Sword;
this.exe = () => {
HERO.weapon++;
TITLE.change();
};
break;
case "armor":
this.sprite = SPRITE.Shield;
this.exe = () => {
HERO.armor++;
TITLE.change();
};
break;
case "magic":
this.sprite = SPRITE.Magic;
this.exe = () => {
HERO.magic++;
//HERO.magic += 2;
TITLE.change();
};
break;
case "agility":
this.sprite = SPRITE.Agility;
this.exe = () => {
HERO.agility++;
TITLE.change();
};
break;
}
}
}
class Lamp {
constructor(grid) {
this.grid = grid;
this.sprite = SPRITE.Lamp;
this.value = 99;
this.start = null;
this.now = null;
this.delta = null;
}
exe() {
if (!HERO.lamp) {
HERO.visibility = 2;
HERO.lamp = this;
HERO.inventory.add(SPRITE.Lamp);
TITLE.change();
this.switchOn();
} else {
HERO.lamp.value += this.value;
}
}
update() {
this.now = performance.now();
this.delta = Math.round((this.now - this.start) / 1000);
if (this.delta >= this.value) this.switchOff();
}
switchOn() {
this.start = performance.now();
}
switchOff() {
HERO.visibility = 1;
HERO.lamp = false;
HERO.inventory.delete(SPRITE.Lamp);
TITLE.change();
}
}
class Scroll {
constructor(grid, type, use) {
this.name = "Scroll";
this.type = type;
this.id = this.name + this.type;
this.sprite = SPRITE["SCR_" + type] || SPRITE.Scroll;
this.grid = grid;
this.static = false;
this.use = use;
}
exe() {
HERO.scrolls.add(this);
}
action() {
let POOL;
switch (this.type) {
case "Cripple":
console.log("action cripple");
POOL = MAP[GAME.level].DUNGEON.ENEMY;
for (let q = 0, PL = POOL.length; q < PL; q++) {
let enemy = POOL[q];
let distance = HERO.MoveState.endGrid.distanceDiagonal(
enemy.MoveState.endGrid
);
if (distance <= INI.MANA_DRAIN_RANGE) {
enemy.speed = 1;
enemy.agility = drain(enemy.agility);
}
}
break;
case "Map":
let grid;
if (MAP[GAME.level].DUNGEON.mapAnchors.length) {
grid = MAP[GAME.level].DUNGEON.mapAnchors.shift();
} else grid = MAP[GAME.level].DUNGEON.getAnyGrid();
MINIMAP.unveil(grid);
TITLE.change();
break;
case "Light":
let lamp = new Lamp(new Grid(0, 0));
lamp.exe();
break;
case "Invisibility":
HERO.invisible();
break;
case "DrainMana":
HERO.mana = 0;
TITLE.change();
POOL = MAP[GAME.level].DUNGEON.ENEMY;
for (let q = 0, PL = POOL.length; q < PL; q++) {
let enemy = POOL[q];
let distance = HERO.MoveState.endGrid.distanceDiagonal(
enemy.MoveState.endGrid
);
if (distance <= INI.MANA_DRAIN_RANGE) {
enemy.mana = 0;
}
}
break;
case "BoostWeapon":
$("#hero_sword").css({ color: "blue" });
HERO.weapon = inflate(HERO.weapon);
CONSOLE.print(
`<span class="blue">${
HERO.name
}</span> applied magical sharpening oil to the sword.`
);
break;
case "BoostArmor":
$("#hero_shield").css({ color: "blue" });
HERO.armor = inflate(HERO.armor);
CONSOLE.print(
`<span class="blue">${
HERO.name
}</span> applied magic protection oil to the armor.`
);
break;
case "DestroyArmor":
$("#enemy_armor").css({ color: "red" });
GAME.TURN.enemy.armor = drain(GAME.TURN.enemy.armor);
CONSOLE.print(
`<span class="blue">${
HERO.name
}</span> magically lowered <span class="red">${
GAME.TURN.enemy.type.title
}'s</span> defense.`
);
break;
case "DestroyWeapon":
$("#enemy_weapon").css({ color: "red" });
GAME.TURN.enemy.weapon = drain(GAME.TURN.enemy.weapon);
CONSOLE.print(
`<span class="blue">${
HERO.name
}</span> magically drained <span class="red">${
GAME.TURN.enemy.type.title
}'s</span> weapons.`
);
break;
default:
console.log("Scroll action ERROR!");
break;
}
function drain(number) {
let N = RND(Math.floor(0.333 * number), Math.ceil(0.666 * number));
if (N === number) N = number - 1;
return N;
}
function inflate(number) {
let N = RND(Math.floor(1.2 * number), Math.ceil(1.5 * number));
if (N === number) N = number + 1;
return N;
}
}
}
var HERO = {
construct: function() {
//HERO.name = "HERO";
HERO.name = HERO.getName();
HERO.gold = 0;
//HERO.gold = 999; //DEBUG
HERO.redPotion = 0;
HERO.bluePotion = 0;
HERO.maxHealth = 8;
HERO.maxHealth = 58; //debug
HERO.health = HERO.maxHealth;
HERO.maxMana = 13;
HERO.maxMana = 999; //debug
HERO.mana = HERO.maxMana;
HERO.armor = 1;
HERO.armor = 6; //debug
HERO.weapon = 1;
HERO.weapon = 6; //debug
HERO.magic = 2;
HERO.magic = 20; //debug
HERO.agility = 1;
//HERO.agility = 25; //debug
HERO.magicResistance = 0;
HERO.inventory = new Set();
HERO.scrolls = new Inventory();
HERO.silverKey = false;
HERO.goldKey = false;
HERO.depth = 1;
//HERO.level = 1;
HERO.level = 0;
HERO.experience = 0;
//HERO.experience = 399; //debug
HERO.expBuffer = 0;
//HERO.expBuffer = 399; //debug
HERO.magicExpBuffer = 0;
//HERO.magicExpBuffer = 48; //debug
HERO.speed = 6;
HERO.visibility = 1;
HERO.dark = false;
HERO.dead = false;
HERO.cloak = false;
HERO.lamp = false;
HERO.points = 0;
HERO.canEnterTemple = true;
HERO.canLevelUp = false;
HERO.spriteClass = "Knight";
HERO.asset = ASSET[HERO.spriteClass];
HERO.actor = new ACTOR(HERO.spriteClass, 0, 0, "front", HERO.asset);
HERO.inFight = false;
HERO.maxDepth = 1;
},
getName: function() {
console.log("getting Hero name");
let name = $("#HeroName").val();
return name.toLowerCase().capitalize();
},
invisible: function() {
HERO.dark = true;
HERO.cloak = new SimpleTimer(INI.INVISIBILITY_TIME, HERO.visible);
HERO.spriteClass = "KnightInvisible";
HERO.setSpriteClass(HERO.spriteClass);
},
visible: function() {
HERO.dark = false;
HERO.cloak = false;
HERO.spriteClass = "Knight";
HERO.setSpriteClass(HERO.spriteClass);
},
setSpriteClass: function(spriteClass) {
HERO.asset = ASSET[spriteClass];
HERO.actor.class = spriteClass;
HERO.actor.asset = HERO.asset;
HERO.actor.resetIndexes();
HERO.actor.animateMove(HERO.actor.orientation);
},
init: function() {
GRID.gridToSprite(MAP[GAME.level].DUNGEON[GAME.location], HERO.actor);
HERO.MoveState = new MoveState(
MAP[GAME.level].DUNGEON[GAME.location],
DOWN
);
ENGINE.VIEWPORT.check(HERO.actor);
ENGINE.VIEWPORT.alignTo(HERO.actor);
},
draw: function() {
if (HERO.dead) return;
ENGINE.spriteDraw(
"actors",
HERO.actor.vx,
HERO.actor.vy,
HERO.actor.sprite()
);
ENGINE.layersToClear.add("actors");
},
move: function() {
if (HERO.dead) return;
if (HERO.MoveState.moving) {
GRID.translateMove(HERO, true, HeroOnFinish);
}
function HeroOnFinish() {
TITLE.change();
HERO.updView();
}
},
changeDirection: function(dir) {
if (HERO.MoveState.moving) return;
let x = HERO.MoveState.endGrid.x + dir.x;
let y = HERO.MoveState.endGrid.y + dir.y;
if (!GRID.isBlock(x, y)) {
HERO.MoveState.next(dir);
}
},
interactLists: function() {
let LIST = ["boosts", "chests", "gold", "potions", "lamps", "scrolls"];
for (let outer = 0, LN = LIST.length; outer < LN; outer++) {
let LL = MAP[GAME.level].DUNGEON[LIST[outer]].length;
for (let list_index = 0; list_index < LL; list_index++) {
let element = MAP[GAME.level].DUNGEON[LIST[outer]][list_index];
let hit = GRID.collision(HERO, element.grid);
if (hit) {
//console.log(hit, element);
if (element.static) HERO.MoveState.reverse();
element.exe();
ENGINE.VIEWPORT.changed = true;
MAP[GAME.level].DUNGEON[LIST[outer]].splice(list_index, 1);
GAME.PAINT.config();
return;
}
}
}
},
interactStatic: function() {
let LIST = [
"door",
"gate",
"entrance",
"exit",
"goldKey",
"silverKey",
"temple"
];
let element;
for (let q = 0, LN = LIST.length; q < LN; q++) {
element = check(LIST[q]);
if (element !== null) break;
}
if (element === null) return;
switch (element) {
case "door":
if (HERO.silverKey) {
HERO.silverKey = false;
HERO.incExp(getKeyExp());
SpritePOOL.pool.push(
new PartSprite(
GRID.gridToCoord(MAP[GAME.level].DUNGEON.door),
SPRITE.Door,
SPRITE.Door.height,
1
)
);
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.door)
] = 1;
MAP[GAME.level].DUNGEON.door = null;
MAP[GAME.level].DUNGEON.setObstacles(
MAP[GAME.level].DUNGEON.door,
MAP[GAME.level].DUNGEON.gate
);
HERO.inventory.delete(SPRITE.silverKey);
GAME.PAINT.config();
TITLE.change();
} else {
HERO.MoveState.reverse();
}
break;
case "gate":
if (HERO.goldKey) {
HERO.goldKey = false;
HERO.incExp(getKeyExp());
SpritePOOL.pool.push(
new PartSprite(
GRID.gridToCoord(MAP[GAME.level].DUNGEON.gate),
SPRITE.Gate,
SPRITE.Gate.height,
1
)
);
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.gate)
] = 1;
MAP[GAME.level].DUNGEON.gate = null;
MAP[GAME.level].DUNGEON.setObstacles(
MAP[GAME.level].DUNGEON.door,
MAP[GAME.level].DUNGEON.gate
);
HERO.inventory.delete(SPRITE.goldKey);
GAME.PAINT.config();
TITLE.change();
} else {
HERO.MoveState.reverse();
}
break;
case "entrance":
if (GAME.level > 1) {
console.log("Going up!");
if (ENGINE.GAME.keymap[ENGINE.KEY.map.tab]) {
HERO.usingStairs(-1);
}
}
break;
case "exit":
console.log("Going down");
if (ENGINE.GAME.keymap[ENGINE.KEY.map.tab]) {
HERO.usingStairs(1);
}
break;
case "goldKey":
case "silverKey":
HERO[element] = true;
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON[element])
] = 1;
MAP[GAME.level].DUNGEON[element] = null;
GAME.PAINT.config();
HERO.incExp(getKeyExp());
HERO.inventory.add(SPRITE[element]);
TITLE.change();
break;
case "temple":
if (HERO.canEnterTemple) GAME.visitTemple();
console.log("Temple");
break;
}
function check(instance) {
let hit = GRID.collision(HERO, MAP[GAME.level].DUNGEON[instance]);
if (hit) {
return instance;
} else return null;
}
function getKeyExp() {
let exp = INI.EXP_KEY;
for (let i = 1; i <= GAME.level - 1; i++) {
exp *= INI.EXP_FACTOR;
}
console.log("GATE, KEY exp awarded", exp);
return round10(exp);
}
},
useManaPotion: function() {
if (HERO.mana === HERO.maxMana) return;
if (HERO.bluePotion > 0) {
HERO.bluePotion--;
HERO.incMana();
}
},
useHealingPotion: function() {
if (HERO.health === HERO.maxHealth) return 0;
if (HERO.redPotion > 0) {
HERO.redPotion--;
return HERO.heal();
}
},
heal: function() {
let addHealth = Math.round(INI.Health_INC * HERO.maxHealth);
let realHealing = Math.min(addHealth, HERO.maxHealth - HERO.health);
HERO.health += addHealth;
let above = GRID.gridToCoord(HERO.MoveState.homeGrid.add(UP)).add(
new Vector(0, 24)
);
TEXTPOOL.pool.push(
new TextSprite(("+" + addHealth).toString(), above, "#DD0000", 50)
);
HERO.health = Math.min(HERO.health, HERO.maxHealth);
TITLE.change();
return realHealing;
},
incMana: function() {
let above = GRID.gridToCoord(HERO.MoveState.homeGrid.add(UP)).add(
new Vector(0, 24)
);
let addMana = Math.round(INI.Health_INC * HERO.maxMana);
HERO.mana += addMana;
TEXTPOOL.pool.push(
new TextSprite(("+" + addMana).toString(), above, "#0000EE", 50)
);
HERO.mana = Math.min(HERO.mana, HERO.maxMana);
TITLE.change();
},
decMana: function(dec) {
HERO.mana -= dec;
if (HERO.mana <= 0) HERO.mana = 0;
TITLE.change();
},
updView: function() {
ENGINE.VIEWPORT.changed = true;
TITLE.change();
let x = HERO.MoveState.endGrid.x - 1;
let y = HERO.MoveState.endGrid.y - 1;
let left = x * ENGINE.INI.GRIDPIX;
let top = y * ENGINE.INI.GRIDPIX;
ENGINE.cutManyGrids(LAYER.fog, new Point(left, top), 3);
for (let q = x; q < x + 3; q++) {
for (let w = y; w < y + 3; w++) {
let temp = new Grid(q, w);
MINIMAP.maps[GAME.level].map[GRID.gridToIndex(temp)] &= 127;
MINIMAP.maps[GAME.level].map[GRID.gridToIndex(temp)] |= 64; //set fog info
}
}
if (HERO.visibility !== 2) return;
for (let q = 0; q < ENGINE.directions.length; q++) {
let test = HERO.MoveState.endGrid.add(ENGINE.directions[q]);
if (!GRID.gridIsBlock(test)) {
test = test.add(ENGINE.directions[q]);
ENGINE.cutGrid(LAYER.fog, GRID.gridToCoord(test));
MINIMAP.maps[GAME.level].map[GRID.gridToIndex(test)] &= 127;
MINIMAP.maps[GAME.level].map[GRID.gridToIndex(test)] |= 64; //set fog info
}
}
var vectors = [new Vector(1, 1), new Vector(1, 0), new Vector(0, 1)];
for (let q = 0; q < ENGINE.corners.length; q++) {
let test = HERO.MoveState.endGrid.add(ENGINE.corners[q]);
if (!GRID.gridIsBlock(test)) {
for (let w = 0; w < vectors.length; w++) {
let tVector = ENGINE.corners[q].mul(vectors[w]);
let lightGrid = HERO.MoveState.endGrid.add(tVector);
ENGINE.cutGrid(LAYER.fog, GRID.gridToCoord(lightGrid));
MINIMAP.maps[GAME.level].map[GRID.gridToIndex(lightGrid)] &= 127;
MINIMAP.maps[GAME.level].map[GRID.gridToIndex(lightGrid)] |= 64; //set fog info
}
}
}
},
manage: function() {
HERO.move();
if (HERO.lamp) HERO.lamp.update();
if (HERO.cloak) HERO.cloak.update();
HERO.interactLists();
HERO.interactStatic();
HERO.collisionEnemy();
},
incExp: function(exp) {
HERO.experience += exp;
HERO.expBuffer += exp;
if (HERO.expBuffer >= GAME.EXP) {
HERO.expBuffer -= GAME.EXP;
HERO.canLevelUp = true;
GAME.EXP *= INI.EXP_FACTOR;
GAME.EXP = Math.round(GAME.EXP);
console.log("NEW EXP limit", GAME.EXP);
}
},
castMagic: function() {
const dir = HERO.MoveState.dir;
const cost = HERO.magic + INI.MAGIC_POWER_COST;
if (cost > HERO.mana) {
//sound failed magic
return;
}
if (success() && dir !== null) {
const power = setPower();
HERO.mana -= cost;
TITLE.change();
//sound casting magic
ORBS.pool.push(
new Orb(HERO.MoveState.homeGrid, dir, power, HERO.magic, "friendly")
);
} else {
HERO.decMana(1);
//sound failed magic
EXPLOSIONS.pool.push(
new AnimationSPRITE(HERO.actor.x, HERO.actor.y, "Fizzle_", 10)
);
}
function success() {
let prop = Math.round(HERO.magic / (HERO.magic + INI.MAGIC_FAIL) * 100);
return probable(prop);
}
function setPower() {
let bottom = Math.round(HERO.magic * 0.8);
if (bottom === 0) bottom = 1;
const power = RND(bottom, Math.round(HERO.magic * 1.2));
return power;
}
},
collisionEnemy: function() {
let POOL = MAP[GAME.level].DUNGEON.ENEMY;
for (let PL = POOL.length, q = PL - 1; q >= 0; q--) {
let enemy = POOL[q];
if (!enemy.visible) continue;
//let hitGrid = GRID.spriteToSpriteCollision(HERO, enemy);
let hitShape = ENGINE.collision(HERO.actor, enemy.actor);
//research hitGrid || hitShape, hitGrid && hitShape
if (hitShape) {
GAME.fight(enemy, true, q);
break;
}
}
},
turn: function(enemy) {
if (!GAME.TURN.fight_active) return;
let damage = GAME.TURN.damage(HERO, enemy);
if (damage > 0) {
CONSOLE.print(
`<span class="blue">${
HERO.name
}</span> hits and makes <span class="orange">${damage}</span> damage.`
);
enemy.health -= damage;
if (enemy.health <= 0) {
enemy.die();
CONSOLE.print(
`<span class="red">${enemy.type.title}</span> was killed.`
);
CONSOLE.print(
`<span class="blue">${HERO.name} gets ${enemy.type.exp} XP.</span>`
);
GAME.TURN.fight_active = false;
MAP[GAME.level].DUNGEON.ENEMY.splice(GAME.TURN.enemyIndex, 1);
GAME.CLICK.endFight();
LOG[enemy.type.title].kills++;
}
enemy.health = Math.max(0, enemy.health);
GAME.fightRefresh(enemy);
} else {
CONSOLE.print(`<span class="blue">${HERO.name}</span> misses.`);
}
},
death: function() {
console.log(`%cHERO died`, PRG.CSS);
HERO.dead = true;
HERO.dark = true;
ENGINE.spriteDraw("grave", HERO.actor.vx, HERO.actor.vy, SPRITE.Grave);
ENGINE.TEXT.setFS(60);
ENGINE.TEXT.centeredText("THE END", 300);
//Game still runs!!
GAME.over();
},
usingStairs: function(delta) {
GAME.prepareLevel = GAME.level + delta;
switch (delta) {
case 1:
GAME.location = "entrance";
HERO.maxDepth = Math.max(HERO.maxDepth, GAME.prepareLevel);
break;
case -1:
GAME.location = "exit";
break;
}
console.log(
delta,
"HERO using the stairs to level",
GAME.prepareLevel,
GAME.location
);
console.log("GAME waitin for all processes to complete");
ENGINE.GAME.ANIMATION.STACK.push(GAME.coolDown);
ENGINE.GAME.ANIMATION.stop();
}
};
class Orb {
constructor(grid, dir, power, range, type) {
this.grid = Grid.toClass(grid);
this.type = type;
this.power = power;
this.speed = 16;
this.range = Math.min(range, INI.ORB_MAX_RANGE);
let id;
if (type === "friendly") {
id = "MagicOrb";
} else id = "RedMagic";
this.actor = new ACTOR(id, 0, 0, "linear", ASSET[id]);
GRID.gridToSprite(this.grid, this.actor);
this.alignToViewport();
this.MoveState = new MoveState(this.grid, dir);
this.MoveState.next(dir);
}
alignToViewport() {
ENGINE.VIEWPORT.alignTo(this.actor);
}
draw() {
ENGINE.spriteDraw(
"orbs",
this.actor.vx,
this.actor.vy,
this.actor.sprite()
);
}
fizzle() {
EXPLOSIONS.pool.push(
new AnimationSPRITE(this.actor.x, this.actor.y, "Fizzle_", 10)
);
}
}
var ORBS = {
pool: [],
draw: function() {
let OPL = ORBS.pool.length;
if (OPL === 0) return;
ENGINE.layersToClear.add("orbs");
for (let q = OPL - 1; q >= 0; q--) {
ORBS.pool[q].draw();
}
},
manage: function() {
ORBS.move();
ORBS.collide();
},
move: function() {
let OPL = ORBS.pool.length;
if (OPL === 0) return;
for (let q = OPL - 1; q >= 0; q--) {
let orb = ORBS.pool[q];
if (orb.range === 0) {
orb.fizzle();
ORBS.pool.splice(q, 1);
continue;
}
if (orb.MoveState.moving) {
GRID.translateMove(orb);
} else {
orb.MoveState.next(orb.MoveState.dir);
orb.range--;
}
}
},
collide: function() {
//to background
let OPL = ORBS.pool.length;
if (OPL === 0) return;
for (let q = OPL - 1; q >= 0; q--) {
let orb = ORBS.pool[q];
if (GRID.gridIsBlock(orb.MoveState.homeGrid)) {
EXPLOSIONS.pool.push(
new AnimationSPRITE(orb.actor.x, orb.actor.y, "ShipExp", 8)
);
ORBS.pool.splice(q, 1);
continue; //next orb
}
}
//to enemy
OPL = ORBS.pool.length;
if (OPL === 0) return;
for (let q = OPL - 1; q >= 0; q--) {
let orb = ORBS.pool[q];
if (orb.type === "friendly") {
let ENML = MAP[GAME.level].DUNGEON.ENEMY.length;
for (let w = ENML - 1; w >= 0; w--) {
let enemy = MAP[GAME.level].DUNGEON.ENEMY[w];
//let hit = GRID.spriteToSpriteCollision(orb, enemy);
let hit = ENGINE.collision(orb.actor, enemy.actor);
if (hit) {
//
HERO.magicExpBuffer++;
//console.log("HERO.magicExpBuffer", HERO.magicExpBuffer);
if (HERO.magicExpBuffer >= GAME.MAGIC_EXP) {
HERO.magicExpBuffer -= GAME.MAGIC_EXP;
GAME.MAGIC_EXP *= INI.MAGIC_EXP_FACTOR;
GAME.MAGIC_EXP = round10(GAME.MAGIC_EXP);
HERO.magic++;
console.log(
"magic increased due to succesful use, new EXP limit",
GAME.MAGIC_EXP
);
TITLE.change();
}
//
let damage = orb.power - enemy.type.magicResistance;
enemy.health -= damage;
damage = Math.max(damage, 0);
let above = GRID.gridToCoord(enemy.MoveState.homeGrid.add(UP)).add(
new Vector(0, 24)
);
if (damage === 0) {
TEXTPOOL.pool.push(
new TextSprite("Resisted".toString(), above, "#AAA", 50)
);
EXPLOSIONS.pool.push(
new AnimationSPRITE(orb.actor.x, orb.actor.y, "Fizzle_", 10)
);
} else if (enemy.health > 0) {
TEXTPOOL.pool.push(
new TextSprite(("-" + damage).toString(), above, "#FF0000", 50)
);
EXPLOSIONS.pool.push(
new AnimationSPRITE(orb.actor.x, orb.actor.y, "AlienExp", 6)
);
} else {
EXPLOSIONS.pool.push(
new AnimationSPRITE(orb.actor.x, orb.actor.y, "ShipExp", 8)
);
enemy.die();
MAP[GAME.level].DUNGEON.ENEMY.splice(w, 1);
}
ORBS.pool.splice(q, 1);
break;
}
}
}
}
//to HERO
OPL = ORBS.pool.length;
if (OPL === 0) return;
for (let q = OPL - 1; q >= 0; q--) {
let orb = ORBS.pool[q];
if (orb.type !== "friendly") {
//let hit = GRID.spriteToSpriteCollision(orb, HERO);
let hit = ENGINE.collision(orb.actor, HERO.actor);
if (hit) {
let resistance = Math.floor(HERO.magic / 3);
let damage = orb.power - resistance;
HERO.health -= damage;
HERO.health = Math.max(HERO.health, 0);
TITLE.change();
damage = Math.max(damage, 0);
let above = GRID.gridToCoord(HERO.MoveState.homeGrid.add(UP)).add(
new Vector(0, 22)
);
if (damage === 0) {
TEXTPOOL.pool.push(
new TextSprite("Resisted".toString(), above, "#AAA", 50)
);
EXPLOSIONS.pool.push(
new AnimationSPRITE(orb.actor.x, orb.actor.y, "Fizzle_", 10)
);
} else if (HERO.health > 0) {
TEXTPOOL.pool.push(
new TextSprite(("-" + damage).toString(), above, "#FF0022", 50)
);
EXPLOSIONS.pool.push(
new AnimationSPRITE(orb.actor.x, orb.actor.y, "AlienExp", 6)
);
} else {
EXPLOSIONS.pool.push(
new AnimationSPRITE(orb.actor.x, orb.actor.y, "ShipExp", 8)
);
console.log("HERO died from Orb collision");
HERO.death();
}
ORBS.pool.splice(q, 1);
}
}
}
}
};
var ENEMY = {
draw: function() {
let POOL = MAP[GAME.level].DUNGEON.ENEMY;
for (let q = 0, PL = POOL.length; q < PL; q++) {
let enemy = POOL[q];
ENGINE.VIEWPORT.alignTo(enemy.actor); //pazi, v translateMove se ponovi!! samo za DEV, lahko isključiš ko riše samo visible
if (enemy.visible || DEBUG.viewMonsters) {
ENGINE.spriteDraw(
"actors",
enemy.actor.vx,
enemy.actor.vy,
enemy.actor.sprite()
);
}
ENGINE.layersToClear.add("actors");
}
},
manage: function() {
//DEBUG.frameCount++;
//console.log("**************** REVIEW cycle", DEBUG.frameCount);
//if (DEBUG.frameCount > 12) GAME.abort();
let POOL = MAP[GAME.level].DUNGEON.ENEMY;
//let t1 = performance.now();
for (let PL = POOL.length, q = PL - 1; q >= 0; q--) {
let enemy = POOL[q];
let distance = HERO.MoveState.endGrid.distanceDiagonal(
enemy.MoveState.endGrid
);
if (enemy.MoveState.moving) {
if (checkFreedom(q)) GRID.translateMove(enemy);
if (distance <= INI.TRIGGER_VISION) enemy.visible = enemy.isVisible();
continue;
}
let node;
let path = [];
if (enemy.awake) {
//AWAKE
//should go to sleep?
if (distance > INI.TRIGGER_WAKE && enemy.strategy !== "hunt") {
//console.log(enemy, "goes to sleep");
enemy.sleep();
continue;
}
//go to sleep if path too long
node = GRID.findDungeonPath(
enemy.MoveState.endGrid,
HERO.MoveState.endGrid,
MAP[GAME.level].DUNGEON.obstacles,
GAME.DISTANCE_WAKE
);
//hunters should be switched to wander in order to sleep!
if (node && node.dist !== 0 && node.priority > INI.TRIGGER_WAKE && enemy.strategy !== "hunt") {
enemy.sleep();
continue;
}
//sleep, awake evaluation end
//check vision, line of sight
//if in visible range, check if truly visible
if (distance <= INI.TRIGGER_VISION) enemy.visible = enemy.isVisible();
//end checking vision
//not moving
//SHOOT
if (!HERO.dark && enemy.magic > 0) {
//has ability to shoot
const cost = enemy.magic + INI.MAGIC_POWER_COST;
if (cost <= enemy.mana && distance <= enemy.magic) {
//has enough mana and range
if (distance <= INI.STALK_DISTANCE && enemy.visible && !enemy.canShoot){
//stalk while on cool down, or else shoot
enemy.strategy = "stalk";
} else enemy.strategy = "hunt";
if (enemy.canShoot) {
let direction = enemy.MoveState.endGrid.absDirection(
HERO.MoveState.endGrid
);
if (direction.isOrto() || direction.isDiagonal()) {
//has proper direction
if (
GRID.vision(enemy.MoveState.endGrid, HERO.MoveState.endGrid)
) {
enemy.mana -= cost;
ORBS.pool.push(
new Orb(
enemy.MoveState.homeGrid,
direction,
setPower(enemy),
enemy.magic,
"deadly"
)
);
enemy.casted();
continue;
}
}
}
} else {
//not enough mana to cast
//change strategy --> hunt
enemy.strategy = "hunt";
}
}
//SHOOT END
//has stack?
if (enemy.dirStack.length > 0) {
// has stack, but let's check first if it should hunt
if (!HERO.dark && distance <= enemy.type.triggers.hunt) {
//switch to hunt and clear stack
enemy.strategy = "hunt";
enemy.dirStack.clear();
continue;
} else {
//yes, move from stack
enemy.makeMove();
continue;
}
} else {
//no, stack is empty
//set direction stack from strategy
switch (enemy.strategy) {
case "wander":
//check first for status change
if (!HERO.dark && distance <= enemy.type.triggers.hunt) {
enemy.strategy = "hunt";
break;
}
//finished checking for status
path = GRID.AI.wanderer.hunt(enemy.MoveState).return;
break;
case "hunt":
//check first for status change
if (HERO.dark || distance >= enemy.type.triggers.wander) {
enemy.strategy = "wander";
break;
}
//finished checking for status
node = GRID.findDungeonPath(
enemy.MoveState.endGrid,
HERO.MoveState.endGrid,
MAP[GAME.level].DUNGEON.obstacles
);
//debug
if (enemy.type.name === "Wizard"){
console.log(q, enemy, "hunter-->", node);
}
//debug end
if (node === null) {
GAME.fight(enemy, false, q);
continue;
} else {
path = node.stack;
path.length = 1;
}
//end finding path
break;
case "stalk":
//console.log("Stalking!");
path = GRID.AI.keepTheDistance.hunt(enemy.MoveState, HERO.MoveState.endGrid, INI.STALK_DISTANCE).return
//console.log("Stalking!", path, path.length, path[0]);
break;
case "goto":
//console.log("goto");
break;
case "guard":
break;
case "seek":
break;
case "flee":
break;
default:
console.log("monster strategy ERROR");
break;
}
//end of switch, if not continue(d) break points to here:
if (path && path.length > 0) {
enemy.dirStack = path;
enemy.makeMove();
continue;
} else {
continue;
}
}
//stack end
} else {
//HYBERNATING
//should wake?
if (distance <= INI.TRIGGER_WAKE) {
//console.log("check if it should wake", enemy);
node = GRID.findDungeonPath(
enemy.MoveState.endGrid,
HERO.MoveState.endGrid,
MAP[GAME.level].DUNGEON.obstacles,
GAME.DISTANCE_WAKE
);
if (node.status === "NoSolution") continue;
if (
node === null ||
(node.dist === 0 && node.priority <= INI.TRIGGER_WAKE)
)
enemy.wake();
}
continue;
}
}
//GAME.abort()
//let t2 = performance.now() - t1;
//console.log("spent", t2, "ms");
function checkFreedom(q) {
for (let W = 0; W < q; W++) {
if (MAP[GAME.level].DUNGEON.ENEMY[W].awake) {
if (
GRID.same(
MAP[GAME.level].DUNGEON.ENEMY[q].MoveState.endGrid,
MAP[GAME.level].DUNGEON.ENEMY[W].MoveState.endGrid
)
)
return false;
}
}
return true;
}
function setPower(enemy) {
let bottom = Math.round(enemy.type.magic * 0.8);
if (bottom === 0) bottom = 1;
const power = RND(bottom, Math.round(enemy.type.magic * 1.2));
return power;
}
}
};
var GAME = {
CSS: "color: #0F0",
abort: function() {
ENGINE.GAME.stopAnimation = true;
console.error("..... aborting GAME, DEBUG info:");
console.log("scrolls", HERO.scrolls);
},
start: function() {
GAME.DISTANCE_WAKE = Math.floor(2 * Math.sqrt((INI.TRIGGER_WAKE ** 2) / 2));
console.log("GAME.DISTANCE_WAKE", GAME.DISTANCE_WAKE);
ENGINE.GAME.start(); //INIT game loop
ENGINE.KEY.on(); // keymapping active
CreateDungeon.init();
GAME.prepareForRestart(); //everything required for safe restart
GAME.level = 1;
GAME.prepareLevel = null;
GAME.location = "entrance";
GAME.score = 0;
ENGINE.INI.ANIMATION_INTERVAL = 16;
HERO.construct();
ENGINE.GAME.ANIMATION.waitThen(GAME.levelStart, 2);
GAME.EXP = INI.EXP;
GAME.MAGIC_EXP = INI.MAGIC_EXP;
GAME.time = new Timer();
},
prepareForRestart: function() {
console.log("preparing game for start or safe restart ...");
//everything required for safe restart
ENGINE.GAME.ANIMATION.stop();
//clear layers
ENGINE.clearLayer("text");
ENGINE.clearLayer("animation");
ENGINE.clearLayer("grave");
ENGINE.clearLayer("actors");
},
coolDown: function() {
ENGINE.GAME.ANIMATION.stop();
console.log("%c ...all processes completed", PRG.CSS);
GAME.nextLevel();
},
spawnNemesis: function() {
console.log("GAME.spawnNemesis");
CreateDungeon.spawnNemesis(GAME.level);
//just one for debug purpose! uncomment line below!!
GAME.levelTime = new SimpleTimer(INI.NEMESIS_RESPAWN, GAME.spawnNemesis);
//GAME.levelTime = new SimpleTimer(9999, GAME.spawnNemesis);
},
levelExecute: function() {
console.log(`%cLevel ${GAME.level} executes ...`, GAME.CSS);
ENGINE.VIEWPORT.reset();
HERO.init();
EXPLOSIONS.pool.clear();
GAME.firstFrameDraw(GAME.level);
GAME.levelTime = new SimpleTimer(INI.NEMESIS_RESPAWN, GAME.spawnNemesis);
if (HERO.level === 0) {
GAME.levelUp(INI.POINTS_ON_START);
} else GAME.levelContinue();
},
levelContinue: function() {
console.log("LEVEL", GAME.level, "continues ...");
ENGINE.GAME.ANIMATION.STACK.push(GAME.run);
ENGINE.GAME.ANIMATION.queue();
},
levelStart: function() {
console.log(`%cStarting level ${GAME.level}`, GAME.CSS);
GAME.initLevel(GAME.level);
GAME.levelExecute();
},
nextLevel: function() {
GAME.level = GAME.prepareLevel;
console.log("creating next level: ", GAME.level);
if (GAME.level > INI.LAST_LEVEL) {
console.log("Game have been won or last level has been played.");
//add end game stuff
//TITLE.gameWon();
//ENGINE.GAME.ANIMATION.stop();
//GAME.endAnimationStart();
//GAME.end();
} else {
console.log("Starting next level:", GAME.level);
ENGINE.GAME.ANIMATION.waitThen(GAME.levelStart, 2);
}
},
levelEnd: function() {
console.log("level", GAME.level, "ended.");
GAME.levelCompleted = true;
ENGINE.GAME.ANIMATION.STACK.push(
ENGINE.KEY.waitFor.bind(null, GAME.nextLevel)
);
TITLE.endLevel();
ENGINE.GAME.ANIMATION.stop();
},
initLevel: function(level) {
if (!MAP[level].dungeonExist) {
CreateDungeon.create(level);
MAP[level].pw = MAP[level].width * ENGINE.INI.GRIDPIX;
MAP[level].ph = MAP[level].height * ENGINE.INI.GRIDPIX;
} else MAP[level].returning = true;
//MAP[level].pw = MAP[level].width * ENGINE.INI.GRIDPIX;
//MAP[level].ph = MAP[level].height * ENGINE.INI.GRIDPIX;
ENGINE.VIEWPORT.setMax({ x: MAP[level].pw, y: MAP[level].ph });
MINIMAP.create(level);
//DEBUG
//let grid = MAP[GAME.level].DUNGEON.entrance.add(UP)
//MAP[level].DUNGEON.scrolls.push(new Scroll(grid, "Light", "explore"));
//grid = MAP[GAME.level].DUNGEON.entrance.add(DOWN)
//MAP[level].DUNGEON.scrolls.push(new Scroll(grid, "Light", "explore"));
DEBUG.addScrolls();
//
},
updateVieport: function() {
if (!ENGINE.VIEWPORT.changed) return;
// do required repaints
ENGINE.VIEWPORT.change("floor", "background");
ENGINE.VIEWPORT.change("config", "background");
ENGINE.clearLayer("fogview");
ENGINE.VIEWPORT.change("fog", "fogview");
if (DEBUG.coord) {
ENGINE.clearLayer("coordview");
ENGINE.VIEWPORT.change("coord", "coordview");
}
//
ENGINE.VIEWPORT.changed = false;
},
frameDraw: function() {
ENGINE.clearLayerStack();
GAME.updateVieport();
TEXTPOOL.draw("animation");
SpritePOOL.draw("animation");
EXPLOSIONS.draw();
HERO.draw();
ORBS.draw();
TITLE.time();
TITLE.status();
ENEMY.draw();
},
firstFrameDraw: function(level) {
ENGINE.resizeBOX("LEVEL", MAP[level].pw, MAP[level].ph);
GRID.repaint(
MAP[level].grid,
TEXTURE[MAP[level].floor],
TEXTURE[MAP[level].background]
);
ENGINE.flattenLayers("wall", "floor");
GAME.PAINT.config();
if (DEBUG.fog) {
ENGINE.fill(LAYER.fog, TEXTURE.Fog);
GAME.adjustFogToMap();
}
if (DEBUG.coord) GAME.PAINT.coord();
ENGINE.VIEWPORT.changed = true;
HERO.updView();
GAME.updateVieport();
TITLE.main();
TITLE.time();
TITLE.status();
ENGINE.clearLayer("actors");
ENGINE.clearLayer("explosion");
},
adjustFogToMap: function() {
console.log("Adjusting fog from MINIMAP");
for (let x = 0; x < MAP[GAME.level].width; x++) {
for (let y = 0; y < MAP[GAME.level].height; y++) {
let grid = new Grid(x, y);
if (MINIMAP.maps[GAME.level].map[GRID.gridToIndex(grid)] & 64) {
ENGINE.cutGrid(LAYER.fog, GRID.gridToCoord(grid));
}
}
}
},
run: function() {
//let t1 = performance.now();
GAME.frameCount++;
//GAME.run() template
if (ENGINE.GAME.stopAnimation) return;
//do all game loop stuff here
GAME.respond();
ORBS.manage();
ENEMY.manage();
HERO.manage();
GAME.levelTime.update();
//
GAME.frameDraw();
//let t2 = performance.now() - t1;
//console.log("Frame", t2);
//if (GAME.frameCount > 100) GAME.abort();
},
respond: function() {
//GAME.respond() template
if (HERO.dead) return;
var map = ENGINE.GAME.keymap;
//fall throught section
if (map[ENGINE.KEY.map.F9]) {
//GAME.abort();
//teleport to temple
/*
console.log("teleporting to temple room");
GRID.teleportToGrid(HERO, MAP[GAME.level].DUNGEON.temple, true);
HERO.updView();
*/
//cheats
/*
console.log("CHEATING");
HERO.armor += 10;
HERO.weapon += 10;
HERO.agility +=10;
HERO.heal();
HERO.incMana();
TITLE.change();
*/
//teleport to exit and purge enemies
console.log("teleport to exit");
//MAP[GAME.level].DUNGEON.ENEMY.clear();
GRID.teleportToGrid(HERO, MAP[GAME.level].DUNGEON.exit.add(UP), true);
HERO.updView();
}
if (map[ENGINE.KEY.map.F8]) {
console.log("instaDeath");
HERO.death();
}
if (map[ENGINE.KEY.map.tab]) {
if (!HERO.canLevelUp) return;
GAME.levelUp(INI.PTS_LVL);
ENGINE.GAME.keymap[ENGINE.KEY.map.tab] = false; //NO repeat
}
if (map[ENGINE.KEY.map.A]) {
TITLE.stack.scrollIndex--;
TITLE.stack.scrollIndex = Math.max(0, TITLE.stack.scrollIndex);
TITLE.change();
ENGINE.GAME.keymap[ENGINE.KEY.map.A] = false;
}
if (map[ENGINE.KEY.map.D]) {
TITLE.stack.scrollIndex++;
TITLE.stack.scrollIndex = Math.min(
HERO.scrolls.size() - 1,
TITLE.stack.scrollIndex
);
TITLE.change();
ENGINE.GAME.keymap[ENGINE.KEY.map.D] = false;
}
if (map[ENGINE.KEY.map.H]) {
HERO.useHealingPotion();
ENGINE.GAME.keymap[ENGINE.KEY.map.H] = false; //NO repeat
}
if (map[ENGINE.KEY.map.M]) {
HERO.useManaPotion();
ENGINE.GAME.keymap[ENGINE.KEY.map.M] = false; //NO repeat
}
if (map[ENGINE.KEY.map.ctrl]) {
HERO.castMagic();
ENGINE.GAME.keymap[ENGINE.KEY.map.ctrl] = false; //NO repeat
}
if (map[ENGINE.KEY.map.space]) {
console.log("SPACE");
ENGINE.GAME.keymap[ENGINE.KEY.map.space] = false; //NO repeat
}
if (map[ENGINE.KEY.map.enter]) {
if (HERO.scrolls.size() === 0) return;
let scroll = HERO.scrolls.remove(TITLE.stack.scrollIndex);
scroll.action();
TITLE.change();
ENGINE.GAME.keymap[ENGINE.KEY.map.enter] = false; //NO repeat
}
//single key section
if (map[ENGINE.KEY.map.left]) {
HERO.changeDirection(LEFT);
return;
}
if (map[ENGINE.KEY.map.right]) {
HERO.changeDirection(RIGHT);
return;
}
if (map[ENGINE.KEY.map.up]) {
HERO.changeDirection(UP);
return;
}
if (map[ENGINE.KEY.map.down]) {
HERO.changeDirection(DOWN);
return;
}
return;
},
setup: function() {
console.log("%cGAME SETUP started", PRG.CSS);
},
end: function() {
console.log("GAME ENDED");
GAME.checkScore();
},
PAINT: {
coord: function(layer = "coord") {
ENGINE.clearLayer(layer);
for (let x = 0; x < MAP[GAME.level].width; x++) {
for (let y = 0; y < MAP[GAME.level].height; y++) {
if (!GRID.isBlock(x, y)) {
let point = GRID.gridToCoord(new Grid(x, y));
let text = `${x},${y}`;
GRID.paintText(point, text, layer);
}
}
}
},
config: function() {
ENGINE.VIEWPORT.changed = true;
ENGINE.clearLayer("config");
let layer = "config";
ENGINE.spriteToGrid(
layer,
MAP[GAME.level].DUNGEON.entrance,
SPRITE.Entrance
);
ENGINE.spriteToGrid(layer, MAP[GAME.level].DUNGEON.exit, SPRITE.Exit);
ENGINE.spriteToGrid(layer, MAP[GAME.level].DUNGEON.temple, SPRITE.temple);
if (MAP[GAME.level].DUNGEON.gate)
ENGINE.spriteToGrid(layer, MAP[GAME.level].DUNGEON.gate, SPRITE.Gate);
if (MAP[GAME.level].DUNGEON.door)
ENGINE.spriteToGrid(layer, MAP[GAME.level].DUNGEON.door, SPRITE.Door);
if (MAP[GAME.level].DUNGEON.goldKey)
ENGINE.spriteToGrid(
layer,
MAP[GAME.level].DUNGEON.goldKey,
SPRITE.goldKey
);
if (MAP[GAME.level].DUNGEON.silverKey)
ENGINE.spriteToGrid(
layer,
MAP[GAME.level].DUNGEON.silverKey,
SPRITE.silverKey
);
GAME.PAINT.gold(layer);
GAME.PAINT.lamp(layer);
GAME.PAINT.potion(layer);
GAME.PAINT.boost(layer);
GAME.PAINT.chest(layer);
GAME.PAINT.scroll(layer);
},
gold: function(layer) {
for (let q = 0, GL = MAP[GAME.level].DUNGEON.gold.length; q < GL; q++) {
let gold = MAP[GAME.level].DUNGEON.gold[q];
ENGINE.spriteToGrid(layer, gold.grid, gold.sprite);
}
},
lamp: function(layer) {
for (let q = 0, GL = MAP[GAME.level].DUNGEON.lamps.length; q < GL; q++) {
let lamp = MAP[GAME.level].DUNGEON.lamps[q];
ENGINE.spriteToGrid(layer, lamp.grid, lamp.sprite);
}
},
potion: function(layer) {
for (
let q = 0, GL = MAP[GAME.level].DUNGEON.potions.length;
q < GL;
q++
) {
let potion = MAP[GAME.level].DUNGEON.potions[q];
ENGINE.spriteToGrid(layer, potion.grid, potion.sprite);
}
},
boost: function(layer) {
for (let q = 0, GL = MAP[GAME.level].DUNGEON.boosts.length; q < GL; q++) {
let boost = MAP[GAME.level].DUNGEON.boosts[q];
ENGINE.spriteToGrid(layer, boost.grid, boost.sprite);
}
},
chest: function(layer) {
for (let q = 0, GL = MAP[GAME.level].DUNGEON.chests.length; q < GL; q++) {
let chest = MAP[GAME.level].DUNGEON.chests[q];
ENGINE.spriteToGrid(layer, chest.grid, chest.sprite);
}
},
scroll: function(layer) {
for (
let q = 0, GL = MAP[GAME.level].DUNGEON.scrolls.length;
q < GL;
q++
) {
let scroll = MAP[GAME.level].DUNGEON.scrolls[q];
ENGINE.spriteToGrid(layer, scroll.grid, scroll.sprite);
}
}
},
CLICK: {
sacrificeGold: function() {
HERO.gold -= 1000;
HERO.points += 1;
GAME.templeRefresh();
},
manageTemple: function() {
GAME.CLICK.decPoint(this);
GAME.templeRefresh();
},
manageCharacter: function() {
GAME.CLICK.decPoint(this);
GAME.heroRefresh();
},
decPoint: function(that) {
HERO.points -= 1;
let id = that.id;
id = id.substr(id.lastIndexOf("_") + 1);
if (id === "maxHealth") {
HERO[id] += INI.LVL_HEALTH;
} else if (id === "maxMana") {
HERO[id] += INI.LVL_MANA;
} else HERO[id]++;
},
endFight: function() {
$("#form_continue").prop("disabled", false);
$("#form_make_turn").prop("disabled", true);
$("#form_flee").prop("disabled", true);
},
usePotion: function() {
let healed = HERO.useHealingPotion();
//console.log("using red potion for", healed, "HP");
GAME.fightRefresh();
if (healed === 0) {
CONSOLE.print(
`<span class="blue">${HERO.name}</span> has full health already.`
);
} else {
CONSOLE.print(`
<span class="blue">${HERO.name}</span> heals ${healed} points.
`);
}
},
useScroll: function() {
const regex = /_([a-zA-Z]+)/;
let type = regex.exec(this.id)[1];
let temp = new Scroll(null, type, null);
temp.action();
HERO.scrolls.remove(HERO.scrolls.find("type", type));
GAME.fightRefresh();
}
},
levelUp: function(points) {
console.log("%cCharacter screen", PRG.CSS);
ENGINE.GAME.ANIMATION.stop();
HERO.canLevelUp = false;
let x = ENGINE.gameWIDTH / 4;
let y = ENGINE.gameHEIGHT / 4;
let w = ENGINE.gameWIDTH / 2 + 16;
let h = ENGINE.gameHEIGHT / 2 - 50;
HERO.level++;
HERO.points = points;
HERO.maxHealth += INI.LVL_HEALTH;
HERO.maxMana += INI.LVL_MANA;
const temp = character();
function character() {
const temp = new Form(HERO.name, x, y, w, h, FORM_WEDGE.HERO);
FORM_WEDGE.hero();
GAME.heroRefresh();
return temp;
}
},
visitTemple: function() {
console.log("%cEntered temple", PRG.CSS);
ENGINE.GAME.ANIMATION.stop();
let x = ENGINE.gameWIDTH / 4;
let y = ENGINE.gameHEIGHT / 4;
let w = ENGINE.gameWIDTH / 2 + 16;
let h = ENGINE.gameHEIGHT / 2 + 32;
const temp = temple();
function temple() {
const temp = new Form("The Temple", x, y, w, h, FORM_WEDGE.TEMPLE);
FORM_WEDGE.temple();
GAME.templeRefresh();
return temp;
}
},
heroRefresh: function() {
if (HERO.points > 0) {
$("#form_done").prop("disabled", true);
$(".skill").prop("disabled", false);
} else {
$("#form_done").prop("disabled", false);
$(".skill").prop("disabled", true);
}
$("#hero_points").html(HERO.points);
$("#hero_sword").html(HERO.weapon.toString().padStart(2, "0"));
$("#hero_shield").html(HERO.armor.toString().padStart(2, "0"));
$("#hero_agility").html(HERO.agility.toString().padStart(2, "0"));
$("#hero_magic").html(HERO.magic.toString().padStart(2, "0"));
HERO.health = HERO.maxHealth;
HERO.mana = HERO.maxMana;
TITLE.change();
TITLE.status();
},
templeRefresh: function() {
$("#hero_gold").html(HERO.gold);
if (HERO.gold < 1000) {
$("#form_sacrifice_gold").prop("disabled", true);
} else $("#form_sacrifice_gold").prop("disabled", false);
if (HERO.points === 0) {
$(".skill").prop("disabled", true);
} else $(".skill").prop("disabled", false);
$("#hero_points").html(HERO.points);
$("#hero_vitality").html(HERO.maxHealth.toString().padStart(2, "0"));
$("#hero_mana").html(HERO.maxMana.toString().padStart(2, "0"));
$("#hero_sword").html(HERO.weapon.toString().padStart(2, "0"));
$("#hero_shield").html(HERO.armor.toString().padStart(2, "0"));
$("#hero_agility").html(HERO.agility.toString().padStart(2, "0"));
$("#hero_magic").html(HERO.magic.toString().padStart(2, "0"));
HERO.health = HERO.maxHealth;
HERO.mana = HERO.maxMana;
TITLE.change();
TITLE.status();
},
fightRefresh: function() {
let enemy = GAME.TURN.enemy;
$("#hero_sword").html(HERO.weapon.toString().padStart(2, "0"));
$("#hero_shield").html(HERO.armor.toString().padStart(2, "0"));
$("#hero_agility").html(HERO.agility.toString().padStart(2, "0"));
if (LOG[enemy.type.title].kills >= 1) {
$("#enemy_agility").html(enemy.agility.toString().padStart(2, "0"));
} else $("#enemy_agility").html("??");
if (LOG[enemy.type.title].kills >= 2) {
$("#enemy_armor").html(enemy.armor.toString().padStart(2, "0"));
} else $("#enemy_armor").html("??");
if (LOG[enemy.type.title].kills >= 3) {
$("#enemy_weapon").html(enemy.weapon.toString().padStart(2, "0"));
} else $("#enemy_weapon").html("??");
ENGINE.clearLayer("hero_health");
ENGINE.clearLayer("enemy_health");
ENGINE.statusBar(
LAYER.hero_health,
0,
0,
INI.FIGHT_PANEL_WIDTH / 2 - 10,
20,
HERO.health,
HERO.maxHealth,
"red"
);
ENGINE.statusBar(
LAYER.enemy_health,
0,
0,
INI.FIGHT_PANEL_WIDTH / 2 - 10,
20,
enemy.health,
enemy.maxHealth,
"orange"
);
$("#count_redPotion").html(HERO.redPotion.toString().padStart(2, "0"));
if (HERO.redPotion > 0) {
$("#redPotion").prop("disabled", false);
} else {
$("#redPotion").prop("disabled", true);
//delete if none
$("#redPotion").remove();
$("#count_redPotion").remove();
}
scrollPanel();
TITLE.forceChange();
return;
function scrollPanel() {
let scrolls = [
"BoostWeapon",
"BoostArmor",
"DestroyWeapon",
"DestroyArmor"
];
scrolls.forEach(scroll => {
let count = HERO.scrolls.getCount("type", scroll);
$(`#count_${scroll}`).html(count.toString().padStart(2, "0"));
if (count === 0) {
$(`#SCR_${scroll}`).prop("disabled", true);
//delete those not present
$(`#SCR_${scroll}`).remove();
$(`#count_${scroll}`).remove();
} else $(`#SCR_${scroll}`).prop("disabled", false);
});
}
},
leaveTemple: function() {
console.log("leaving temple ...");
HERO.canEnterTemple = false;
$("#FORM").remove();
setTimeout(() => {
console.log("HERO.canEnterTemple");
HERO.canEnterTemple = true;
}, INI.TEMPLE_TIMEOUT);
GAME.levelContinue();
},
endFight: function() {
console.log("fight ends ...");
$("#FORM").remove();
HERO.inFight = false;
GAME.TURN.fight_active = false;
HERO.weapon = GAME.TURN.HERO_weapon;
HERO.armor = GAME.TURN.HERO_armor;
GAME.levelContinue();
},
fleeFight: function() {
let enemy = GAME.TURN.enemy;
CONSOLE.print(`<span class="blue">${HERO.name}</span> tries to flee ...`);
let delta = HERO.agility - enemy.agility;
const top = 2 * INI.FLEE_AGILITY_DELTA;
let chance;
if (delta > top) {
chance = top;
} else {
chance = RND(-top + delta, top);
}
let selectedDir = safeSpot(); //bugfix for dead ends
if (
chance > 0 &&
GAME.TURN.agility_accumulator > INI.AGILITY_TURN * -1 &&
selectedDir !== null
) {
CONSOLE.print(` ... and succeeds.`);
//let selectedDir = safeSpot();
HERO.MoveState.dir = selectedDir;
GRID.blockMove(HERO, true);
HERO.updView();
GAME.CLICK.endFight();
} else {
CONSOLE.print(` ... and fails ...`);
GAME.TURN.agility_accumulator += chance;
enemy.turn();
GAME.fightRefresh(); //bugfix
}
function safeSpot() {
let obstacles = MAP[GAME.level].DUNGEON.obstacles.clone();
obstacles.push(enemy.MoveState.homeGrid);
let dirs = GRID.getDirections(HERO.MoveState.startGrid, obstacles);
if (dirs.length === 0) return null;
let selectedDir;
let enemyDirIndex = enemy.MoveState.dir.isInAt(dirs);
if (enemyDirIndex >= 0) {
selectedDir = dirs[enemyDirIndex];
} else {
selectedDir = dirs.chooseRandom();
}
return selectedDir;
}
},
charDone: function() {
console.log("levelUp done");
$("#FORM").remove();
GAME.levelContinue();
},
debug: function() {},
fight: function(enemy, initiative, index) {
if (HERO.inFight) return;
if (HERO.dead) return;
if (!HERO.MoveState.moving) initiative = false;
if (initiative) {
initiative = 1;
} else initiative = 0;
HERO.inFight = true;
ENGINE.GAME.ANIMATION.stop();
let x = ENGINE.gameWIDTH / 4;
let y = ENGINE.gameHEIGHT / 4 - 60;
let w = ENGINE.gameWIDTH / 2 + 16;
let h = ENGINE.gameHEIGHT / 2 + 32 + 168;
INI.FIGHT_PANEL_WIDTH = w;
GAME.TURN.enemy = enemy;
kills();
fight(enemy);
CONSOLE.set("Console");
GAME.TURN.agility_accumulator = 0;
GAME.TURN.counter = 1;
GAME.TURN.HERO_weapon = HERO.weapon;
GAME.TURN.HERO_armor = HERO.armor;
//GAME.TURN.enemy = enemy;
GAME.TURN.fight_active = true;
GAME.TURN.enemyIndex = index;
let delta = HERO.agility - enemy.agility;
if (delta + initiative * INI.INITIATIVE_BONUS >= 0) {
//hero starts
CONSOLE.print(
`<span class="blue">${HERO.name}</span> attacks <span class="red">${
enemy.type.title
}</span>.`
);
} else {
//enemy attacks
CONSOLE.print(
`<span class="red">${
enemy.type.title
}</span> attacks <span class="blue">${HERO.name}</span>.`
);
enemy.turn(enemy);
}
//GAME.fightRefresh(enemy);
GAME.fightRefresh();
return;
function fight(enemy) {
const temp = new Form("Fight!", x, y, w, h, FORM_WEDGE.FIGHT);
FORM_WEDGE.fight(enemy);
GAME.fightRefresh();
return temp;
}
function kills() {
if (LOG[enemy.type.title] === undefined) {
LOG[enemy.type.title] = new Log();
}
return LOG[enemy.type.title].kills;
}
},
turn: function() {
if (!GAME.TURN.fight_active) return;
GAME.TURN.setQuickResolve();
let enemy = GAME.TURN.enemy;
GAME.TURN.counter++;
let delta = HERO.agility - enemy.type.agility;
GAME.TURN.agility_accumulator += delta;
//console.log("GAME.TURN.agility_accumulator", GAME.TURN.agility_accumulator);
if (GAME.TURN.agility_accumulator > INI.AGILITY_TURN * -1) {
//hero turn
HERO.turn(enemy);
} else {
//hero skips turn
GAME.TURN.agility_accumulator += INI.AGILITY_TURN;
CONSOLE.print(
`<span class="blue">${HERO.name}</span> fell behind and missed turn.`
);
}
if (!GAME.TURN.fight_active) return;
if (GAME.TURN.agility_accumulator < INI.AGILITY_TURN) {
enemy.turn();
} else {
GAME.TURN.agility_accumulator -= INI.AGILITY_TURN;
CONSOLE.print(
`<span class="red">${
enemy.type.title
}</span> fell behind and missed turn.`
);
}
GAME.fightRefresh(enemy);
TITLE.forceChange();
if (GAME.TURN.fight_active && GAME.turn.quickResolve) {
return GAME.turn();
} else return;
},
TURN: {
counter: 0,
agility_accumulator: 0,
HERO_weapon: null,
HERO_armor: null,
fight_active: null,
enemy: null,
quickResolve: false,
setQuickResolve: function() {
if ($("#form_quick_resolve").prop("checked")) {
GAME.turn.quickResolve = true;
} else GAME.turn.quickResolve = false;
},
enemyIndex: null,
damage: function(attacker, defender) {
if (attacker.weapon === 0) return 0;
//let delta = Math.max(attacker.weapon - defender.armor, 1);
//let damage = RND(INI.ATTACk_OFFSET, delta);
let delta = attacker.weapon - defender.armor;
let damage = RND(
Math.min(INI.ATTACk_OFFSET, Math.floor(delta / 2)),
Math.max(delta, 1)
);
return damage;
}
},
over: function() {
//$("#Restart").remove();
$("#buttons").prepend(
`<input type='button' id='Restart' value='Restart game'>`
);
$("#Restart").on("click", GAME.restartGame);
},
restartGame: function() {
//$("#Restart").addClass("hidden");
$("#Restart").remove();
console.log("GAME RESTARTED");
GAME.discardMaps();
GAME.start();
},
discardMaps: function() {
console.log("discarding old maps");
MINIMAP.maps = {};
MAP.clear();
LOG = {};
}
};
var MINIMAP = {
/*
1 - wall, path (0),
2 - door/gate
4 - key
8 - entrance, exit
16 - temple
32 -
64 - fog info
128 - fog
*/
key: "#00F",
door: "#F00",
wall: "#8B4513",
path: "#000",
hero: "#0F0",
temple: "yellow",
exit: "#FFF",
maps: {},
create: function(level) {
console.log("MINIMAP creation - level:", level);
if (MINIMAP.maps[GAME.level] === undefined) {
MINIMAP.maps[GAME.level] = {};
MINIMAP.maps[GAME.level].buffer = new ArrayBuffer(
MAP[GAME.level].width * MAP[GAME.level].height
);
MINIMAP.maps[GAME.level].map = new Uint8Array(
MINIMAP.maps[GAME.level].buffer
);
let index;
for (let y = 0; y < MAP[GAME.level].height; y++) {
for (let x = 0; x < MAP[GAME.level].width; x++) {
index = x + y * MAP[GAME.level].width;
if (GRID.isBlock(x, y)) {
MINIMAP.maps[GAME.level].map[index] = 0b10000000;
} else MINIMAP.maps[GAME.level].map[index] = 0b10000001;
}
}
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.door)
] |= 2;
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.gate)
] |= 2;
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.silverKey)
] |= 4;
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.goldKey)
] |= 4;
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.entrance)
] |= 8;
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.exit)
] |= 8;
MINIMAP.maps[GAME.level].map[
GRID.gridToIndex(MAP[GAME.level].DUNGEON.temple)
] |= 16;
//reserve 64 for fog storage
} else console.log(`MINIMAP to ${GAME.level} already exists`);
//console.log("MINIMAP in MINIMAP", MINIMAP);
},
draw: function(x, y, CTX) {
MINIMAP.maps[GAME.level].map.forEach((value, index) => {
if (value < 128) {
if (value & 16) {
CTX.fillStyle = MINIMAP.temple;
} else if (value & 8) {
CTX.fillStyle = MINIMAP.exit;
} else if (value & 4) {
CTX.fillStyle = MINIMAP.key;
} else if (value & 2) {
CTX.fillStyle = MINIMAP.door;
} else if (value & 1) {
CTX.fillStyle = MINIMAP.path;
} else {
CTX.fillStyle = MINIMAP.wall;
}
let grid = GRID.indexToGrid(index);
CTX.pixelAt(
x + grid.x * INI.MINI_PIX,
y + grid.y * INI.MINI_PIX,
INI.MINI_PIX
);
}
CTX.fillStyle = MINIMAP.hero;
CTX.pixelAt(
x + HERO.MoveState.homeGrid.x * INI.MINI_PIX,
y + HERO.MoveState.homeGrid.y * INI.MINI_PIX,
INI.MINI_PIX
);
});
},
unveil: function(grid, r = INI.MAP_RADIUS) {
let startX = Math.max(grid.x - r, 0);
let startY = Math.max(grid.y - r, 0);
let maxX = Math.min(MAP[GAME.level].DUNGEON.maxX + 1, startX + 2 * r);
let maxY = Math.min(MAP[GAME.level].DUNGEON.maxY + 1, startY + 2 * r);
for (let y = startY; y <= maxY; y++) {
for (let x = startX; x <= maxX; x++) {
let index = x + y * MAP[GAME.level].width;
MINIMAP.maps[GAME.level].map[index] &= 0b01111111;
}
}
}
};
var TITLE = {
stack: {
scrollIndex: 0,
scrollInRow: 3,
scrollDelta: 72
},
change: function() {
TITLE.STATUS.changed = true;
},
STATUS: {
changed: true,
layer: "status"
},
main: function() {
TITLE.title();
TITLE.bottom();
TITLE.side();
},
title: function() {
var CTX = LAYER.title;
TITLE.background();
var fs = 42;
CTX.font = fs + "px Arcade";
CTX.textAlign = "center";
var txt = CTX.measureText(PRG.NAME);
var x = ENGINE.titleWIDTH / 2;
var y = fs + 10;
var gx = x - txt.width / 2;
var gy = y - fs;
var grad = CTX.createLinearGradient(gx, gy + 10, gx, gy + fs);
grad.addColorStop("0", "#0F0");
grad.addColorStop("0.1", "#0E0");
grad.addColorStop("0.2", "#0D0");
grad.addColorStop("0.3", "#0C0");
grad.addColorStop("0.4", "#0B0");
grad.addColorStop("0.5", "#0A0");
grad.addColorStop("0.6", "#090");
grad.addColorStop("0.7", "#080");
grad.addColorStop("0.8", "#070");
grad.addColorStop("0.9", "#060");
grad.addColorStop("1", "#050");
GAME.grad = grad;
CTX.fillStyle = grad;
CTX.shadowColor = "#040";
CTX.shadowOffsetX = 2;
CTX.shadowOffsetY = 2;
CTX.shadowBlur = 3;
CTX.fillText(PRG.NAME, x, y);
},
background: function() {
var CTX = LAYER.title;
CTX.fillStyle = "#000";
CTX.roundRect(
0,
0,
ENGINE.titleWIDTH,
ENGINE.titleHEIGHT,
{
upperLeft: 20,
upperRight: 20,
lowerLeft: 0,
lowerRight: 0
},
true,
true
);
},
bottom: function() {
var CTX = LAYER.bottom;
CTX.fillStyle = "#000";
CTX.roundRect(
0,
0,
ENGINE.bottomWIDTH,
ENGINE.bottomHEIGHT,
{
upperLeft: 0,
upperRight: 0,
lowerLeft: 20,
lowerRight: 20
},
true,
true
);
CTX.textAlign = "center";
var x = ENGINE.bottomWIDTH / 2;
var y = ENGINE.bottomHEIGHT / 2;
CTX.font = "10px Consolas";
CTX.fillStyle = GAME.grad;
CTX.shadowOffsetX = 2;
CTX.shadowOffsetY = 2;
CTX.shadowBlur = 5;
CTX.shadowColor = "#040";
CTX.fillText("Version " + PRG.VERSION + " by Lovro Selič", x, y);
},
side() {
ENGINE.clearLayer("sideback");
ENGINE.fillLayer("sideback", "#000");
},
forceChange: function() {
TITLE.change();
TITLE.status();
},
status: function() {
if (!TITLE.STATUS.changed) return;
TITLE.STATUS.changed = false;
ENGINE.clearLayer("status");
TITLE.hero();
TITLE.time();
TITLE.health();
TITLE.gold();
TITLE.inv();
TITLE.skills();
TITLE.scrolls();
TITLE.map();
},
scrolls: function() {
TITLE.stack.scrollIndex = Math.min(
TITLE.stack.scrollIndex,
HERO.scrolls.size() - 1
);
let scrollSpread = ENGINE.spreadAroundCenter(
TITLE.stack.scrollInRow,
ENGINE.sideWIDTH / 2 - 16,
TITLE.stack.scrollDelta
);
var CTX = LAYER[TITLE.STATUS.layer];
CTX.save();
ENGINE.resetShadow(CTX);
let x = 16;
let fs = 16;
let y = TITLE.stack.y + 4;
let LN = HERO.scrolls.size();
let start = Math.max(
0,
TITLE.stack.scrollIndex - TITLE.stack.scrollInRow + 1
);
start = Math.min(start, LN - TITLE.stack.scrollInRow);
if (start < 0) start = 0;
let max = start + Math.min(TITLE.stack.scrollInRow, LN);
for (let q = start; q < max; q++) {
let scroll = HERO.scrolls.list[q];
if (scroll.object.use === "explore") {
CTX.globalAlpha = 1;
CTX.strokeStyle = "#0F0";
} else {
CTX.globalAlpha = 0.3;
CTX.strokeStyle = "#F00";
}
x = scrollSpread.shift();
ENGINE.draw(TITLE.STATUS.layer, x, y, scroll.object.sprite);
CTX.font = "10px Consolas";
CTX.fillStyle = "#FFF";
CTX.fillText(
scroll.count.toString().padStart(2, "0"),
x + 32,
y + 18 + 4
);
if (q === TITLE.stack.scrollIndex) {
CTX.globalAlpha = 0.5;
CTX.lineWidth = "1";
CTX.beginPath();
CTX.rect(x - 14, y - 3, 60, 44);
CTX.closePath();
CTX.stroke();
}
}
CTX.globalAlpha = 1;
y += 2 * fs + 12;
x = (ENGINE.sideWIDTH - SPRITE.LineTop.width) / 2;
CTX.restore();
ENGINE.draw(TITLE.STATUS.layer, x, y, SPRITE.LineBottom);
y += SPRITE.LineBottom.height + 4;
TITLE.stack.y = y;
},
map: function() {
var CTX = LAYER.map;
ENGINE.clearLayer("map");
let y = TITLE.stack.y + 14;
let x = 28;
CTX.strokeStyle = "#FFF";
CTX.beginPath();
CTX.rect(x - 1, y - 1, 202, 202);
CTX.closePath();
CTX.stroke();
CTX.fillStyle = "#444";
CTX.fillRect(x, y, 200, 200);
MINIMAP.draw(x, y, CTX);
},
statusBar: function(x, y, value, max, color) {
var CTX = LAYER[TITLE.STATUS.layer];
let h = 16;
let w = 160;
ENGINE.statusBar(CTX, x, y, w, h, value, max, color);
},
healthBar: function(x, y) {
TITLE.statusBar(x, y, HERO.health, HERO.maxHealth, "#F00");
},
manaBar: function(x, y) {
TITLE.statusBar(x, y, HERO.mana, HERO.maxMana, "#00F");
},
inv: function() {
var CTX = LAYER[TITLE.STATUS.layer];
let fs = 16;
let y = TITLE.stack.y + fs + 4;
let x;
let NUM = 2;
let delta = 80;
let xS = ENGINE.spreadAroundCenter(NUM, ENGINE.sideWIDTH / 2, delta);
x = xS.shift();
ENGINE.spriteDraw(TITLE.STATUS.layer, x, y, SPRITE.RedPotion);
x += SPRITE.RedPotion.width / 2 + 5;
CTX.fillText(HERO.redPotion, x, y + 6);
x = xS.shift();
ENGINE.spriteDraw(TITLE.STATUS.layer, x, y, SPRITE.BluePotion);
x += SPRITE.RedPotion.width / 2 + 5;
CTX.fillText(HERO.bluePotion, x, y + 6);
y += fs + 4;
x = (ENGINE.sideWIDTH - SPRITE.LineTop.width) / 2;
ENGINE.draw(TITLE.STATUS.layer, x, y, SPRITE.LineBottom);
y += SPRITE.LineBottom.height;
TITLE.stack.y = y;
},
gold: function() {
var CTX = LAYER[TITLE.STATUS.layer];
CTX.save();
CTX.fillStyle = "#CFB53B";
CTX.shadowColor = "#DAA520";
let fs = 16;
let y = TITLE.stack.y + 1.5 * fs;
let x = 16;
CTX.fillText("Gold:", x, y);
CTX.fillText(HERO.gold.toString().padStart(6, "0"), TITLE.stack.tab, y);
CTX.restore();
y += fs;
x = (ENGINE.sideWIDTH - SPRITE.LineTop.width) / 2;
ENGINE.draw(TITLE.STATUS.layer, x, y, SPRITE.LineTop);
y += SPRITE.LineBottom.height;
TITLE.stack.y = y;
},
skills: function() {
var CTX = LAYER[TITLE.STATUS.layer];
let fs = 16;
let y = TITLE.stack.y + 1.5 * fs;
let x = 16;
let pad1 = 80;
let pad2 = 32;
CTX.fillText("Sword:", x, y);
x += pad1;
CTX.fillText(HERO.weapon.toString().padStart(2, "0"), x, y);
x += pad2;
CTX.fillText("Shield:", x, y);
x += pad1;
CTX.fillText(HERO.armor.toString().padStart(2, "0"), x, y);
y += 1.5 * fs;
x = 16;
CTX.fillText("Agility:", x, y);
x += pad1;
CTX.fillText(HERO.agility.toString().padStart(2, "0"), x, y);
x += pad2;
CTX.fillText("Magic:", x, y);
x += pad1;
CTX.fillText(HERO.magic.toString().padStart(2, "0"), x, y);
y += fs - 2;
x = (ENGINE.sideWIDTH - SPRITE.LineTop.width) / 2;
ENGINE.draw(TITLE.STATUS.layer, x, y, SPRITE.LineBottom);
y += SPRITE.LineBottom.height + 2;
TITLE.stack.y = y;
},
health: function() {
var CTX = LAYER[TITLE.STATUS.layer];
let fs = 16;
let y = TITLE.stack.y + 1.5 * fs;
let x = 16;
CTX.font = fs + "px Times";
CTX.fillStyle = "#AAA";
CTX.shadowColor = "#666";
CTX.shadowOffsetX = 1;
CTX.shadowOffsetY = 1;
CTX.shadowBlur = 1;
if (HERO.canLevelUp) {
CTX.fillStyle = "#0F0";
CTX.shadowColor = "#0D0";
}
CTX.fillText("Level:", x, y);
CTX.fillText(HERO.level.toString().padStart(2, "0"), 100, y);
y += 1.5 * fs;
CTX.fillText("Experience:", x, y);
CTX.fillText(HERO.experience.toString().padStart(6, "0"), 100, y);
if (HERO.canLevelUp) {
CTX.fillStyle = "#00F";
CTX.shadowColor = "#00D";
CTX.font = "10px Arial";
CTX.fillText("Press TAB for Level Up!", x + 116, y - 26);
}
CTX.font = fs + "px Times";
CTX.fillStyle = "#AAA";
CTX.shadowColor = "#666";
y += 1.5 * fs;
x = (ENGINE.sideWIDTH - SPRITE.LineTop.width) / 2;
ENGINE.draw(TITLE.STATUS.layer, x, y, SPRITE.LineBottom);
y += SPRITE.LineBottom.height + 1.5 * fs;
x = 16;
CTX.fillText("Health:", x, y);
var bx, by;
inc();
TITLE.healthBar(bx, by);
x = 16;
y += 1.5 * fs;
CTX.fillText("Mana:", x, y);
inc();
TITLE.manaBar(bx, by);
y += 1.5 * fs;
x = (ENGINE.sideWIDTH - SPRITE.LineTop.width) / 2;
ENGINE.draw(TITLE.STATUS.layer, x, y, SPRITE.LineBottom);
y += SPRITE.LineBottom.height;
TITLE.stack.y = y;
TITLE.stack.tab = bx;
function inc() {
const pad = 3;
bx = x + 64;
by = y - fs + pad;
}
},
time: function() {
var CTX = LAYER["time"];
ENGINE.clearLayer("time");
let fs = 14;
CTX.font = fs + "px Times";
CTX.fillStyle = "#0D0";
CTX.shadowColor = "#0F0";
CTX.shadowOffsetX = 0;
CTX.shadowOffsetY = 0;
CTX.shadowBlur = 0;
CTX.fillText(GAME.time.timeString(), 180, 28);
},
hero: function() {
let x = 48;
let y = 20;
let fs = 14;
ENGINE.spriteDraw(TITLE.STATUS.layer, x, y, SPRITE.Knight_front_0);
var CTX = LAYER[TITLE.STATUS.layer];
x = 102;
CTX.font = fs + "px Times";
CTX.fillStyle = "#0A0";
CTX.shadowColor = "#0F0";
CTX.shadowOffsetX = 0;
CTX.shadowOffsetY = 0;
CTX.shadowBlur = 0;
CTX.fillText(HERO.name, x, y);
y += fs + 4;
CTX.fillText(`Depth: ${GAME.level.toString().padStart(2, "0")}`, x, y);
//reset shadow
CTX.fillStyle = "#AAA";
CTX.shadowColor = "#666";
//
y = 26 + 32;
x = (ENGINE.sideWIDTH - SPRITE.LineTop.width) / 2;
ENGINE.draw(TITLE.STATUS.layer, x, y, SPRITE.LineTop);
let delta = 48;
let NUM = HERO.inventory.size;
let xS = ENGINE.spreadAroundCenter(NUM, ENGINE.sideWIDTH / 2, delta);
y += 32;
fs = 16;
HERO.inventory.forEach(sprite => {
ENGINE.spriteDraw(TITLE.STATUS.layer, xS.shift(), y, sprite);
});
y += fs;
ENGINE.draw(TITLE.STATUS.layer, x, y, SPRITE.LineBottom);
y += SPRITE.LineBottom.height;
TITLE.stack.y = y;
}
};
class Monster {
constructor(type, grid, go = false) {
this.enemy = true;
this.type = type;
this.grid = Grid.toClass(grid);
this.awake = false;
this.actor = new ACTOR(
this.type.name,
0,
0,
"front",
ASSET[this.type.name]
);
GRID.gridToSprite(this.grid, this.actor);
this.alignToViewport();
this.MoveState = new MoveState(this.grid, DOWN);
this.dirStack = [];
this.strategy = this.type.strategy;
this.speed = this.type.speed;
this.visible = false;
this.health = this.type.health;
this.weapon = this.type.weapon;
this.armor = this.type.armor;
this.maxHealth = this.type.health;
this.mana = this.type.mana;
this.magic = this.type.magic;
this.agility = this.type.agility;
//this.casted(2000);
this.casted(0);
if (go) this.wake();
/*
strategy:
wander, hunt
goto, guard (door, key)
seek
flee, wander
*/
}
alignToViewport() {
ENGINE.VIEWPORT.alignTo(this.actor);
}
makeMove() {
this.MoveState.dir = this.dirStack.shift();
this.MoveState.next(this.MoveState.dir);
}
wake() {
//console.log("waking", this);
this.awake = true;
}
sleep() {
this.awake = false;
this.hide();
}
show() {
this.visible = true;
}
hide() {
this.visible = false;
}
isVisible() {
let targetGrid = this.MoveState.closerGrid(HERO.MoveState);
return GRID.vision(Grid.toClass(HERO.MoveState.homeGrid), targetGrid);
}
casted(add = 0) {
this.canShoot = false;
this.shootCoolDown(add);
}
shootCoolDown(add) {
setTimeout(() => (this.canShoot = true), INI.SHOOT_TIMEOUT + add);
}
drop(grid) {
let drop;
if (this.type.inventory) {
switch (this.type.inventory.type) {
case "potion":
drop = new Potion(this.type.inventory.value.chooseRandom(), grid);
MAP[GAME.level].DUNGEON.potions.push(drop);
break;
case "boost":
drop = new Boost(this.type.inventory.value.chooseRandom(), grid);
MAP[GAME.level].DUNGEON.boosts.push(drop);
break;
default:
console.log("Monster dropping", this.type.inventory, "ERROR");
break;
}
} else {
let value = RND(Math.floor(this.type.gold / 2), this.type.gold);
drop = new Gold(value, grid);
MAP[GAME.level].DUNGEON.gold.push(drop);
}
ENGINE.VIEWPORT.changed = true;
GAME.PAINT.config();
}
turn() {
if (!GAME.TURN.fight_active) return;
let damage = GAME.TURN.damage(this, HERO);
if (damage > 0) {
CONSOLE.print(
`<span class="red">${
this.type.title
}</span> hits and makes <span class="red">${damage}</span> damage.`
);
HERO.health -= damage;
HERO.health = Math.max(HERO.health, 0);
if (HERO.health <= 0) {
console.log("HERO dies in fight");
GAME.TURN.fight_active = false;
CONSOLE.print(`<span class="blue">${HERO.name} was killed.`);
GAME.CLICK.endFight();
HERO.death();
}
} else {
CONSOLE.print(`<span class="red">${this.type.title}</span> misses.`);
}
}
die() {
HERO.incExp(this.type.exp);
//HERO.experience += this.type.exp;
TITLE.change();
this.drop(this.MoveState.homeGrid);
TEXTPOOL.pool.push(
new TextSprite(
(this.type.exp + "XP").toString(),
GRID.gridToCoord(this.MoveState.homeGrid),
"#00FF00",
100
)
);
}
}
var LOG = {};
class Log {
constructor() {
this.kills = 0;
}
}
/////////////////
$(function() {
PRG.INIT();
PRG.setup();
ENGINE.LOAD.preload();
SCORE.init("SC", "DDID", 10, 2500);
SCORE.loadHS();
SCORE.hiScore();
BACKUP_MAP = $.extend(true, {}, MAP);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://codepen.io/laughingskull/pen/MOJejy"></script>
<script src="https://codepen.io/laughingskull/pen/ooZWOB"></script>
<script src="https://codepen.io/laughingskull/pen/MOmNNE"></script>
<script src="https://codepen.io/laughingskull/pen/qwbKMK"></script>
<script src="https://codepen.io/laughingskull/pen/KEpzbV"></script>
<script src="https://codepen.io/laughingskull/pen/vMGjqe"></script>
@font-face {
font-family: "Emulogic";
src: url("https://www.c00lsch00l.eu/Fonts/emulogic.ttf");
}
@font-face {
font-family: "Arcade";
src: url("https://www.c00lsch00l.eu/Fonts/Arcade Classic.ttf");
}
:root {
background-color: orange;
background: -webkit-linear-gradient(90deg, orange, #ffc04d);
background: -o-linear-gradient(90deg, orange, #ffc04d);
background: -moz-linear-gradient(90deg, orange, #ffc04d);
background: -ms-linear-gradient(90deg, orange, #ffc04d);
background: linear-gradient(90deg, orange, #ffc04d);
font-family: "Source Sans Pro", sans-serif;
}
h1,
h2 {
text-align: left;
font-size: 25px;
text-shadow: 1px 1px #666;
font-family: "Arcade";
}
p {
color: #000;
font-size: 18px;
/*font-family: "Emulogic";*/
}
hr {
border: 0;
height: 1px;
background-image: -webkit-linear-gradient(
left,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0.75),
rgba(0, 0, 0, 0)
);
background-image: -moz-linear-gradient(
left,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0.75),
rgba(0, 0, 0, 0)
);
background-image: -ms-linear-gradient(
left,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0.75),
rgba(0, 0, 0, 0)
);
background-image: -o-linear-gradient(
left,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0.75),
rgba(0, 0, 0, 0)
);
}
.center {
text-align: center;
}
.big {
font-size: 22px;
}
.fr {
float: right;
}
.fl {
float: left;
}
.cb {
clear: both;
}
.cr {
clear: right;
}
.cl {
clear: left;
}
.win {
width: 960px;
margin: auto;
margin-top: 20px;
padding: 20px 32px 32px;
background-color: #ffd78e;
background: -webkit-linear-gradient(
to right,
#ffd78e,
#ffd382,
#ffe5b4,
#ffd78e,
#ffd78e
);
background: -o-linear-gradient(
to right,
#ffd78e,
#ffd382,
#ffe5b4,
#ffd78e,
#ffd78e
);
background: -moz-linear-gradient(
to right,
#ffd78e,
#ffd382,
#ffe5b4,
#ffd78e,
#ffd78e
);
background: -ms-linear-gradient(
to right,
#ffd78e,
#ffd382,
#ffe5b4,
#ffd78e,
#ffd78e
);
background: linear-gradient(
to right,
#ffd78e,
#ffd382,
#ffe5b4,
#ffd78e,
#ffd78e
);
border-radius: 13px;
box-shadow: 5px 5px 20px 7px #e69400;
}
/**NEW**/
.setup_container {
padding: 10px;
display: inline-block;
float: left;
margin: 12px;
}
input[type="button"] {
cursor: pointer;
}
input[type="text"] {
text-align: center;
}
.version {
font-family: Consolas;
font-size: 10px;
}
.hidden {
display: none;
}
.winTrans {
width: 960px;
margin: auto;
margin-top: 20px;
padding: 20px 32px 32px;
}
input[type="button"] {
-webkit-border-radius: 0px 5px 5px 5px;
border-radius: 0px 5px 5px 5px;
}
#startGame, #Restart {
font-weight: bold;
}
#setup2 input[type="text"] {
font-size: 12px;
font-family: Consolas;
}
.section {
display: none;
font-size: 12px;
font-family: Consolas;
}
.section fieldset {
border-radius: 7px;
width: 600px;
}
.section fieldset p {
font-size: 12px;
}
.layer {
position: absolute;
left: 0;
}
#hiscore {
border: 1px dotted #888;
border-radius: 13px;
padding: 20px;
box-shadow: 2px 3px 3px 3px #333;
font-family: "Courier New", Courier, monospace;
font-size: 16px;
white-space: pre;
width: 230px;
margin-top: 8px;
}
#SC {
float: right;
margin-left: 8px;
}
.gw {
float: left;
margin: 0px;
padding: 0px;
}
.gh {
float: none;
clear: both;
margin: 0px;
padding: 0px;
}
.bordered {
border: 1px solid black;
border-radius: 7px;
width: 240px;
height: 100px;
}
img.pic {
margin: 10px;
border: 1px solid #000;
padding: 2px;
max-width: 100%;
height: auto;
}
/*FORM*/
.form {
padding-left: 8px;
padding-right: 8px;
}
.form input[type="button"] {
margin: 4px;
-webkit-border-radius: 5px 5px 5px 5px;
border-radius: 5px;
background-color: #999;
border: 1px solid #666;
color: #111;
box-shadow: 3px 3px 3px 0px #222;
cursor: pointer;
}
.form input[type="button"]:active {
background-color: #111;
border: 1px solid #666;
color: #999;
cursor: pointer;
}
.form input[type="button"]:disabled {
background-color: #333;
border: 1px solid #222;
box-shadow: 3px 3px 3px 0px #111;
color: #222;
cursor: not-allowed;
}
#FORM {
position: absolute;
border: 1px solid #645454;
border-radius: 7px;
background: #000;
z-index: 999;
}
#FORM h1 {
font-family: "Garamond";
text-align: center;
font-size: 16px;
color: #999;
text-shadow: 1px 1px #666;
margin: 0px;
margin-top: 4px;
padding: 0px;
}
#FORM hr {
border: 0;
border-top: 1px solid #222;
}
#FORM img {
position: relative;
top: 4px;
}
.form span {
font-family: "Consolas";
color: #777;
padding: 16px;
}
.fightWindow {
padding: 0px;
float: left;
width: 100;
}
.fightWindow p {
color: #0d0;
font-size: 14px;
text-align: center;
padding: 0px;
margin: 0px;
}
.fightWindow p.attr {
font-family: "Garamond";
color: #999;
text-shadow: 1px 1px #666;
font-size: 16px;
text-align: left;
padding: 0px;
padding-left: 12px;
margin: 0px;
}
div .healthbar {
padding: 5px;
}
span.scroll_counter {
padding: 0px;
padding-right: 0px;
/*position: relative;
top: -10px;*/
padding-right: 20px;
font-size: 14px;
}
.form input[type="image"] {
}
.form input[type="image"]:disabled {
cursor: not-allowed;
opacity: 0.4
}
div#Console {}
div#Console p {
color: #0F0;
font-family: Consolas;
font-size: 11px;
padding-left: 8px;
margin: 0px;
}
.blue {
color: #00F !important;
padding: 0px !important;
}
.red {
color: #F00 !important;
padding: 0px !important;
}
.orange {
color: #FFA500 !important;
padding: 0px !important;
}
#scrollPanel {
clear: both;
overflow-x: scroll;
white-space: nowrap;
scroll-behavior: smooth;
scrollbar-width: thin;
scrollbar-color: #333 #000;
justify-content: safe center;
display: flex;
align-items: center;
height: 50px;
}
#scrollPanel input[type="image"]{
padding-right: 0px;
flex-shrink: 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment