Skip to content

Commit

Permalink
Update files to release 1.3
Browse files Browse the repository at this point in the history
- Added separate AI difficulty options for each player
- Added version name string to main menu
- Added -os parameter allowing spoofing current OS type
- Changed how the pause menu works internally
- Modified background tile rendering
- Modified README.md
- Fixed crash when loading save files with bigger grid size and AI players
- Fixed mobile version rendering graphics in the notch area
- Fixed Keyboard Mode issues in tutorial
- Removed unused graphics
  • Loading branch information
Nightwolf-47 committed Apr 3, 2022
1 parent 4737229 commit 355d8b8
Show file tree
Hide file tree
Showing 25 changed files with 215 additions and 148 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ A game where you have to take atoms from other players by blowing up yours.
![katoms4](https://user-images.githubusercontent.com/72660447/121074985-e68e1f00-c7d4-11eb-83bc-7d3b8512b8ef.png)

## Install instructions
#### If you're using Windows, download the win32 version and unzip it to an empty folder.
**If you're using Windows, download the win32 version and extract its contents to an empty folder.**
**If you're using 64-bit Linux, download and run the provided AppImage.**

Otherwise follow those steps:

Expand All @@ -15,7 +16,7 @@ Otherwise follow those steps:
Alternatively, you can play it online on [Newgrounds](https://www.newgrounds.com/portal/view/803623).

## How to play
#### Starting from version 1.2, the in-game tutorial explains the rules with examples.
**Starting from version 1.2, the in-game tutorial explains the rules with examples.**

The game starts with an empty grid. Every player can place 1 atom per turn on tiles without enemy atoms.
When a tile has too many* atoms, it becomes critical and explodes. Each surrounding tile gains an atom and all existing atoms on those tiles go to the player who started the explosion.
Expand All @@ -24,6 +25,17 @@ If a player loses all their atoms, they lose. The last standing player wins the

\*corner - 2 atoms, side - 3 atoms, otherwise 4 atoms (or more)

## Command line parameters
`-gw <number>` - change grid width (7-30)
`-gh <number>` - change grid height (4-20)
`-p1 <type>` - change player 1 type (0 - nothing, 1 - human, 2-4 - AI)
`-p2 <type>` - change player 2 type
`-p3 <type>` - change player 3 type
`-p4 <type>` - change player 4 type
`-kbmode` - enable keyboard-only mode (with keyboard-controlled cursor)
`-mobile` - simulate mobile/web mode (fullscreen, limited grid size etc.)
`-os <name>` - spoof the target OS (`-os Web` simulates HTML5 love.js version)

## Credits
**LOVE Development Team** - Löve, a Lua game engine this game runs on.
**DrPetter** - SFXR, a tool used to make sounds for this game.
Expand Down
2 changes: 1 addition & 1 deletion conf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ function love.conf(t)
t.identity = "kleleatoms"
t.version = "11.3"
t.window.vsync = 0
t.window.title = "KleleAtoms 1.2.1"
t.window.title = "KleleAtoms 1.3"
t.window.width = 640
t.window.height = 480
t.window.resizable = false
Expand Down
Binary file removed graphics/m_aicount.png
Binary file not shown.
Binary file modified graphics/m_ailevel1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphics/m_ailevel2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphics/m_ailevel3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/m_cpu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphics/m_player.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed graphics/m_playerai.png
Binary file not shown.
Binary file modified graphics/m_playerno.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed graphics/m_players.png
Binary file not shown.
Binary file modified graphics/m_sbutsel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphics/m_sbutton.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphics/playerai.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/playerai2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/tile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 17 additions & 15 deletions main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,52 +33,55 @@ local function checkSetValues() --Checks if values are in a valid range
_CAGridW = math.max(math.min(_CAGridW, 30), 7)
_CAGridH = math.max(math.min(_CAGridH, 20), 4)
_CAAILevel = math.max(math.min(_CAAILevel, 3), 1)
_CAPlayer1 = math.max(math.min(_CAPlayer1, 2), 0)
_CAPlayer2 = math.max(math.min(_CAPlayer2, 2), 0)
_CAPlayer3 = math.max(math.min(_CAPlayer3, 2), 0)
_CAPlayer4 = math.max(math.min(_CAPlayer4, 2), 0)
_CAPlayer1 = math.max(math.min(_CAPlayer1, 4), 0)
_CAPlayer2 = math.max(math.min(_CAPlayer2, 4), 0)
_CAPlayer3 = math.max(math.min(_CAPlayer3, 4), 0)
_CAPlayer4 = math.max(math.min(_CAPlayer4, 4), 0)
checkValidPlayers()
end

local function readArgs(args)
local valmode = 0 --0 - nothing, 1 - grid width, 2 - grid height, 3 - players, 4 - AI player count, 5 - AI difficulty
local valmode = 0 --0 - nothing, 1 - grid width, 2 - grid height, 3 - AI difficulty, 4-7 - Player types, 100 - OS type
for k,v in ipairs(args) do
if valmode == 0 then
if v == "-gridwidth" or v == "-gw" then
valmode = 1
elseif v == "-gridheight" or v == "-gh" then
valmode = 2
elseif v == "-ailevel" or v == "-al" then
valmode = 3
elseif (string.sub(v,1,-2) == "-player" and v ~= "-players") or string.sub(v,1,-2) == "-p" then
local pnum = tonumber(string.sub(v,-1,-1))
if pnum and pnum >= 1 and pnum <= 4 then
valmode = 3 + pnum
end
elseif v == "-mobilemode" or v == "-mobile" then
_CAIsMobile = true
elseif v == "-forceos" or v == "-os" then
valmode = 100
elseif v == "-kbmode" then --Keyboard mode (adds virtual mouse controlled by keyboard)
_CAKBMode = true
end
elseif valmode > 0 and valmode <= 5 then
elseif valmode > 0 and valmode <= 7 then
local index = settsvals[valmode]
_G[index] = tonumber(v) or _G[index]
valmode = 0
elseif valmode == 100 then --Force OS type
_CAOSType = v
_CAIsMobile = _CAIsMobile or (_CAOSType == "Android" or _CAOSType == "iOS" or _CAOSType == "Web")
end
end
end

local function loadSettings()
local inum = 1
if love.filesystem.getInfo("settings2.txt") then
if love.filesystem.getInfo("settings2.txt") then --Load a settings file (supports both 1.2 and 1.3 files)
for line in love.filesystem.lines("settings2.txt") do
if inum <= #settsvals then
local index = settsvals[inum]
_G[index] = tonumber(line) or _G[index]
inum = inum + 1
end
end
elseif love.filesystem.getInfo("settings.txt") then --Convert settings format from 1.1.2 and older versions to 1.2 format
elseif love.filesystem.getInfo("settings.txt") then --Convert settings format from 1.1.2 and older versions to 1.2 format (with 1.3 changes)
local pcount = nil
for line in love.filesystem.lines("settings.txt") do
if inum <= 2 then
Expand All @@ -101,7 +104,7 @@ local function loadSettings()
for i = pcount-aicount+1,pcount do
local index = settsvals[3+i]
if _G[index] == 0 then break end
_G[index] = 2
_G[index] = 3
end
end
elseif inum == 5 then
Expand All @@ -121,11 +124,11 @@ function love.load(args)
_CAFont32 = love.graphics.newFont(32) --Default font, size 32
_CAGridW = 10 --Grid width
_CAGridH = 6 --Grid Height
_CAPlayer1 = 1 --Player 1 type (0 - not present, 1 - player, 2 - AI, 3 - dummy/scripted)
_CAPlayer2 = 2 --Player 2 type
_CAPlayer1 = 1 --Player 1 type (0 - not present, 1 - player, 2 - AI easy, 3 - AI medium, 4 - AI hard, 9 - dummy/scripted)
_CAPlayer2 = 3 --Player 2 type
_CAPlayer3 = 0 --Player 3 type
_CAPlayer4 = 0 --Player 4 type
_CAAILevel = 2 --AI difficulty level (1 - easy, 2 - medium, 3 - hard)
_CAAILevel = 2 --Only used for settings/savefile compatibility
_CAOSType = love.system.getOS()
_CAIsMobile = (_CAOSType == "Android" or _CAOSType == "iOS" or _CAOSType == "Web") --If true, mobile mode will be enabled
_CAKBMode = false --Keyboard mode
Expand All @@ -134,7 +137,6 @@ function love.load(args)
checkSetValues() --Make sure the settings are within the acceptable range
_CAState.list["game"] = require("states.game.gamestate")
_CAState.list["menu"] = require("states.menu.menustate")
_CAState.list["pause"] = require("states.pause.pausestate")
_CAState.change("menu")
end

Expand Down
6 changes: 5 additions & 1 deletion mobile.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ local scale = 1 --Mobile scale

local res = {640,480} --Ingame resolution (units)

local sxo = 0 --Safe area X offset (avoids drawing on the notch)

local realx = 0 --Real screen width (in pixels)

local realy = 0 --Real screen height (in pixels)
Expand All @@ -21,15 +23,17 @@ function mobile.init() --Initialize mobile mode
realy = 600
love.window.updateMode(realx,realy)
else
sxo = love.window.getSafeArea()
realx, realy = love.window.getDesktopDimensions()
realx = realx - sxo*2
love.window.updateMode(realx,realy,{fullscreen=true})
end
isInit = true
end

function mobile.predraw() --Setup graphics scaling
scale = math.min(realx/res[1],realy/res[2])
movex = math.floor((realx/2)-((res[1]/2)*scale))
movex = math.floor((realx/2)-((res[1]/2)*scale))+sxo
love.graphics.push()
love.graphics.translate(movex,0)
love.graphics.scale(scale,scale)
Expand Down
4 changes: 2 additions & 2 deletions state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function state.mousepressed(x, y, button)
x,y = mobile.convertcoords(x,y)
end
if curstatedata and curstatedata.mousepressed then
state.list[curstate].mousepressed(x,y,button)
curstatedata.mousepressed(x,y,button)
end
end

Expand All @@ -78,7 +78,7 @@ function state.mousereleased(x, y, button)
x,y = mobile.convertcoords(x,y)
end
if curstatedata and curstatedata.mousereleased then
state.list[curstate].mousereleased(x,y,button)
curstatedata.mousereleased(x,y,button)
end
end

Expand Down
14 changes: 7 additions & 7 deletions states/game/gameai.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ local aitime = 0.0 --Timer for AI (it can only move after cAIDELAY)

local ai = {}

local logic = nil

local ccdchecktab = {} --Table of diagonally closest tiles to a corner, created every time AI code is initialized

local function aiIsTileEnemy(x,y) --Check if a tile belongs to another player
Expand Down Expand Up @@ -144,8 +146,8 @@ end
local function aiThinker() --Pick a tile and use the clickedTile() function
local tx = 0
local ty = 0
if ai.difficulty <= 3 then --Easy - just pick a random tile, Normal and Hard - more advanced strategies, not too advanced for optimization reasons
local sptiles, tiles = aiGetSpecialTiles(ai.difficulty)
if ai.difficulty[logic.curplayer] <= 3 then --Easy - just pick a random tile, Normal and Hard - more advanced strategies, not too advanced for optimization reasons
local sptiles, tiles = aiGetSpecialTiles(ai.difficulty[logic.curplayer])
if #sptiles > 0 then
local rindex = love.math.random(1,#sptiles)
tx = sptiles[rindex][1]
Expand All @@ -156,19 +158,17 @@ local function aiThinker() --Pick a tile and use the clickedTile() function
ty = tiles[rindex][2]
end
else
error("Incorrect AI difficulty - "..tostring(ai.difficulty))
error("Incorrect AI difficulty - "..tostring(ai.difficulty[logic.curplayer]))
end
logic.clickedTile(tx,ty,true) --Simulate clicking the chosen tile
end

ai.playertab = {false,false}

ai.difficulty = 2
ai.difficulty = {2,2,2,2}

function ai.init(logictab,ailevel) --Initialize AI
function ai.init(logictab) --Initialize AI
logic = logictab
ai.playertab = {}
ai.difficulty = ailevel
local gridw = #logic.grid
local gridh = #logic.grid[1]
ccdchecktab = {
Expand Down
56 changes: 34 additions & 22 deletions states/game/gamelogic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ local sndput = love.audio.newSource("sounds/put.wav","static")

local sndexplode = love.audio.newSource("sounds/explode.wav","static")

local ctile = love.graphics.newImage("graphics/tile.png")

local colornames = {"Red","Blue","Green","Yellow"}

local checktab = { --Order of chain reaction checks
Expand All @@ -20,7 +22,7 @@ local function getWinSize(w,h) --Get screen/window size based on grid size
return (20+w*cGRIDSIZE),(100+h*cGRIDSIZE)
end

local function nextplayer() --Set the current player to next player
local function nextPlayer() --Set the current player to next player
ai.resetTime()
if logic.players < 2 then return end
repeat
Expand Down Expand Up @@ -103,7 +105,7 @@ local function prepareNewAtoms(tx,ty) --Place atom on a tile, prepare for explos
logic.willexplode = {ttpos[1],ttpos[2],true}
table.insert(logic.atomstack,ttpos)
else
nextplayer()
nextPlayer()
end
end

Expand Down Expand Up @@ -170,6 +172,8 @@ logic.playerwon = 0 --0 if no player won yet

logic.expcount = 0 --Explosion count (resets when explosions stop)

logic.paused = false --Is the game paused?

logic.coltab = { --table of atom colors by player number
[0] = {0.2,0.2,0.2,1}, --only for testing
[1] = {1,0.2,0.2,1}, --red
Expand All @@ -178,7 +182,7 @@ logic.coltab = { --table of atom colors by player number
[4] = {1,1,0,1} --yellow
}

function logic.loadAll(gridWidth,gridHeight,aidifficulty,pttab) --Reset most of the game state with given values
function logic.loadAll(gridWidth,gridHeight,pttab) --Reset most of the game state with given values
logic.grid = {}
logic.critgrid = {}
for x = 1,gridWidth do
Expand All @@ -199,19 +203,23 @@ function logic.loadAll(gridWidth,gridHeight,aidifficulty,pttab) --Reset most of
logic.playeratoms = {}
logic.playermoved = {}
logic.players = 0
ai.init(logic,aidifficulty)
ai.playertab = {}
ai.difficulty = {}
ai.init(logic)
for i = 1,4 do
if pttab[i] > 0 then
if logic.curplayer == -1 then logic.curplayer = i end
logic.players = logic.players + 1
logic.playertab[i] = true
logic.playeratoms[i] = 0
logic.playermoved[i] = false
if pttab[i] == 3 then logic.playertab[i] = "dummy" end
if pttab[i] == 2 then
logic.ai.playertab[i] = true
if pttab[i] == 9 then
logic.playertab[i] = "dummy"
elseif pttab[i] > 1 then
ai.playertab[i] = true
ai.difficulty[i] = pttab[i] - 1
else
logic.ai.playertab[i] = false
ai.playertab[i] = false
end
end
end
Expand Down Expand Up @@ -286,7 +294,7 @@ function logic.tick(dt) --Game tick - disqualifies players, picks the winner, ex
else
table.remove(logic.atomstack)
if #logic.atomstack == 0 then
nextplayer()
nextPlayer()
break
end
end
Expand All @@ -300,14 +308,10 @@ end
function logic.generateGrid(gridWidth,gridHeight) --Generate the grid background
logic.bgimg = love.graphics.newCanvas(logic.winsize[1],logic.winsize[2])
logic.bgimg:renderTo(function()
love.graphics.setColor(0.5,0.5,0.5,1)
love.graphics.rectangle("fill",10,90,gridWidth*cGRIDSIZE,gridHeight*cGRIDSIZE)
love.graphics.setColor(1,1,1,1)
for i = 0,gridWidth do
love.graphics.rectangle("fill",10+(i*cGRIDSIZE),90,1,gridHeight*cGRIDSIZE)
end
for i = 0,gridHeight do
love.graphics.rectangle("fill",10,90+(i*cGRIDSIZE),gridWidth*cGRIDSIZE,1)
for y = 0,gridHeight-1 do
for x = 0,gridWidth-1 do
love.graphics.draw(ctile,10+(x*cGRIDSIZE),90+(y*cGRIDSIZE))
end
end
end)
end
Expand All @@ -327,11 +331,16 @@ function logic.drawVictoryWin(timestr) --Draw victory window and make background
love.graphics.printf("Victory!",_CAFont24,msgx+8,msgy+8,240,"center")
end

local function cai(num,val) --For loadGame: convert player AI value to boolean
local function cai(num,val,aidiff) --For loadGame: convert player AI value to boolean and AI difficulty
if val == 0 then
ai.playertab[num] = false
else
ai.playertab[num] = true
if val == 1 then --Default AI difficulty
ai.difficulty[num] = aidiff
elseif val <= 4 then
ai.difficulty[num] = val - 1
end
end
end

Expand Down Expand Up @@ -364,20 +373,22 @@ function logic.loadGame() --Loads the game, returns nil (on failure) or the new
logic.playeratoms = {}
logic.playermoved = {}
ai.playertab = {}
ai.difficulty = {}
logic.startplayers = math.min(string.byte(str,6),4)
logic.players = math.min(string.byte(str,7),4)
ai.difficulty = math.min(string.byte(str,8),3)
local aidifficulty = math.min(string.byte(str,8),3)
logic.curplayer = string.byte(str,9)
for i = 1,4 do
cpst(i,string.byte(str,9+i))
cai(i,string.byte(str,13+i))
cai(i,string.byte(str,13+i),aidifficulty)
end
if logic.curplayer == 0 or logic.curplayer > 4 or logic.playertab[logic.curplayer] == nil then
nextplayer()
nextPlayer()
end
local ttime = (string.byte(str,20)*3600)+(string.byte(str,19)*60)+string.byte(str,18)
local pos = 0
logic.grid = {}
logic.critgrid = {}
for x = 1,gridWidth do
logic.grid[x] = {}
logic.critgrid[x] = {}
Expand All @@ -390,10 +401,11 @@ function logic.loadGame() --Loads the game, returns nil (on failure) or the new
pos = pos + 2
end
end
ai.init(logic)
return ttime
end

logic.nextPlayer = nextplayer
logic.nextPlayer = nextPlayer

logic.setAtoms = setAtomsUnsafe

Expand Down
Loading

0 comments on commit 355d8b8

Please sign in to comment.