Hurray! Another multiplayer clone project. :)

Discuss and distribute tools and methods for modding. Moderator - Grognak
Post Reply
jrb00001
Posts: 201
Joined: Fri Jan 15, 2016 2:22 pm

Re: Hurray! Another multiplayer clone project. :)

Post by jrb00001 »

kcd.Spektor wrote:Well I'm stuck. :|
With the oxygen algorithm.
I'm probably missing some small detail but sill - I'm stuck.
Anyone want's to help me with that?
How can we help if you do not tell us what the problem is?

Does it work in one direction but not in the opposite direction? That was my first problem with this type of algorithm. If it is something else, please describe what works and what not.
stylesrj wrote:Not quite true: an open door that goes directly into space instantly drains the room no matter how much air was in it before.
The other doors take time and so do hull breaches. Not sure how much time though.
I had to check it but yes, you are right. I think it should take some time but it should be faster than breaches.
kcd.Spektor
Posts: 586
Joined: Thu Nov 26, 2015 8:21 am

Re: Hurray! Another multiplayer clone project. :)

Post by kcd.Spektor »

Whiew :)
Think I've managed to replicate the oxygen mechanic.
I'm pretty sure that my algorithm is not the same as FTL has. (Because mine uses duct tape and some nails to work :mrgreen: )
But still I think it does the job.
Just need to balance it a bit, and during the next update I hope you will be satisfied by how it works. :D

But if anyone will be kind enough to draw his own oxygen algorithm - I would be very glad to try it out.

Don't ask how mine works (I'm too shamed to tell :? )
kcd.Spektor
Posts: 586
Joined: Thu Nov 26, 2015 8:21 am

Re: Hurray! Another multiplayer clone project. :)

Post by kcd.Spektor »

Finally it's starting to look good :)
Image
jrb00001
Posts: 201
Joined: Fri Jan 15, 2016 2:22 pm

Re: Hurray! Another multiplayer clone project. :)

Post by jrb00001 »

kcd.Spektor wrote:Whiew :)
Think I've managed to replicate the oxygen mechanic.
I'm pretty sure that my algorithm is not the same as FTL has. (Because mine uses duct tape and some nails to work :mrgreen: )
But still I think it does the job.
Just need to balance it a bit, and during the next update I hope you will be satisfied by how it works. :D

But if anyone will be kind enough to draw his own oxygen algorithm - I would be very glad to try it out.

Don't ask how mine works (I'm too shamed to tell :? )
You do not have to tell how yours works, just post the relevant source code :D
Mine would look something like this (pseudocode / not tested):

Code: Select all

OxygenCell[][] oxygen = new OxygenCell[cells.length][cells[0].length];
for (int x = 0; x < cells.length; x++) {
    for (int y = 0; y < cells[x].length; y++) {
        oxygen[x][y] = new OxygenCell();
    }
}

for (int x = 0; x < cells.length; x++) {
    for (int y = 0; y < cells[x].length; y++) {
        int walls = 0;
        double target = cells[x][y].oxygen;

        if (y > 0 && !cells[x][y].wallUp) {
            target += cells[x][y - 1].oxygen;
            walls++;
        }
        if (y < cells[x].length - 1 && !cells[x][y].wallDown) {
            target += cells[x][y + 1].oxygen;
            walls++;
        }
        if (x > 0 && !cells[x][y].wallLeft) {
            target += cells[x - 1][y].oxygen;
            walls++;
        }
        if (y < cells.length - 1 && !cells[x][y].wallRight) {
            target += cells[x + 1][y].oxygen;
            walls++;
        }

        target /= walls + 1;
        oxygen[x][y].diff = target - cells[x][y].oxygen;
    }
}

for (int x = 0; x < cells.length; x++) {
    for (int y = 0; y < cells[x].length; y++) {
        if (oxygen[x][y].diff != 0) {
            if (y > 0 && !cells[x][y].wallUp) {
                oxygen[x][y].up = oxygen[x][y - 1].diff;
            }
            if (y < cells[x].length - 1 && !cells[x][y].wallDown) {
                oxygen[x][y].down = oxygen[x][y + 1].diff;
            }
            if (x > 0 && !cells[x][y].wallLeft) {
                oxygen[x][y].left = oxygen[x - 1][y].diff;
            }
            if (y < cells.length - 1 && !cells[x][y].wallRight) {
                oxygen[x][y].right = oxygen[x + 1][y].diff;
            }

            double factor = oxygen[x][y].up + oxygen[x][y].down + oxygen[x][y].left + oxygen[x][y].right / -oxygen[x][y].diff;
            oxygen[x][y].up /= factor;
            oxygen[x][y].down /= factor;
            oxygen[x][y].left /= factor;
            oxygen[x][y].right /= factor;
        }
    }
}

for (int x = 0; x < cells.length - 1; x++) {
    for (int y = 0; y < cells[x].length - 1; y++) {
        if (y < cells.length - 1) {
            double move = Math.copySign(Math.min(Math.abs(oxygen[x][y].down), Math.abs(oxygen[x][y + 1].up)), oxygen[x][y].down) / DISTRIBUTION_RATE;
            cells[x][y].oxygen -= move;
            cells[x][y + 1].oxygen += move;
        }
    }
}
EDIT: Added the DISTRIBUTION_RATE constant. It should be less than 1 and says how fast the oxygen is distributed. Because open space has an oxygen level of 0, a DISTRIBUTION_RATE of 0.5d means that the oxygen level of an adjacent cell is halved each update.
Last edited by jrb00001 on Mon Feb 08, 2016 5:59 pm, edited 1 time in total.
User avatar
fdagpigj
Posts: 84
Joined: Sat Apr 25, 2015 3:14 pm

Re: Hurray! Another multiplayer clone project. :)

Post by fdagpigj »

kcd.Spektor wrote:But if anyone will be kind enough to draw his own oxygen algorithm - I would be very glad to try it out.
I don't know Java but I made an algo in Python, no idea how good it is but whatever, I tested it and seems to work alright. Just modify the constants to what feels good in your conditions. This basically assumes oxygen inside a ship goes from 0.0 to 100.0

Code: Select all

def distributeOxygen(roomA, roomB):
	rate = 40.0
	voidOxygen = -500.0
	minChange = 0.1
	minOxygen = 0.0
	difference = abs(roomA.oxygen - roomB.oxygen)
	if roomA.oxygen < roomB.oxygen:
		sign = -1
	else:
		sign = 1
	change = min(max(difference / rate, minChange), difference/2.0)
	roomA.oxygen -= sign*change
	roomB.oxygen += sign*change
	if roomA.isVoid():
		roomA.oxygen = voidOxygen
	elif roomA.oxygen < minOxygen:
		roomA.oxygen = minOxygen
	if roomB.isVoid():
		roomB.oxygen = voidOxygen
	elif roomB.oxygen < minOxygen:
		roomB.oxygen = minOxygen
Edit: Made a few improvements to the function, including support for when A has less oxygen than B :oops:
kcd.Spektor
Posts: 586
Joined: Thu Nov 26, 2015 8:21 am

Re: Hurray! Another multiplayer clone project. :)

Post by kcd.Spektor »

Don't forget that oxygen should also be replenished by all installed oxygen system.
And should be dispersed when there is no active O2 system :)

My main problem was caused by combination of 2 facts:
1. Oxygen is replenished every tick in each cell - by the amount gathered from all active O2 systems.
2. Every tick - adjacent room cells everage their o2 levels between them.

I was only able to solve this by changing the first fact - O2 is now replenished not every tick, but every 4th tick.
faggot
Posts: 1
Joined: Mon Feb 08, 2016 9:23 pm

Re: Hurray! Another multiplayer clone project. :)

Post by faggot »

(ignore my username, my friend is a real jokester)


Hey: There are tons of people that would love to help, have you considered uploading your code to github?
jrb00001
Posts: 201
Joined: Fri Jan 15, 2016 2:22 pm

Re: Hurray! Another multiplayer clone project. :)

Post by jrb00001 »

kcd.Spektor wrote:Don't forget that oxygen should also be replenished by all installed oxygen system.
And should be dispersed when there is no active O2 system :)

My main problem was caused by combination of 2 facts:
1. Oxygen is replenished every tick in each cell - by the amount gathered from all active O2 systems.
2. Every tick - adjacent room cells everage their o2 levels between them.

I was only able to solve this by changing the first fact - O2 is now replenished not every tick, but every 4th tick.
How much is replenished every tick? Is it more than the oxygen lost through the open doors?

I think the amount of replenished oxygen per cell should be divided by the amount of room cells. Bigger ships need more oxygen!
User avatar
fdagpigj
Posts: 84
Joined: Sat Apr 25, 2015 3:14 pm

Re: Hurray! Another multiplayer clone project. :)

Post by fdagpigj »

kcd.Spektor wrote:Don't forget that oxygen should also be replenished by all installed oxygen system.
And should be dispersed when there is no active O2 system :)

My main problem was caused by combination of 2 facts:
1. Oxygen is replenished every tick in each cell - by the amount gathered from all active O2 systems.
2. Every tick - adjacent room cells everage their o2 levels between them.

I was only able to solve this by changing the first fact - O2 is now replenished not every tick, but every 4th tick.
Well, I just made a simulation in pygame (python 2). No idea why. Because it's fun, I guess. I feel like I have something pretty close to the original now.

Code: Select all

import pygame
import math

pygame.init()

DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
FPS = 30

GRID_SIZE = 40
all_rooms = []
all_doors = []
GRID_WIDTH = 20
GRID_HEIGHT = 13
WALL_THICKNESS = 2


gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("Oxygen")

gameState = "playing"
oxygenPower = 1


class Door(object):
	def __init__(self, roomA, roomB=None, position=(0,0,0,1), startOpen=False):
		global all_doors
		super(Door, self).__init__()
		all_doors.append(self)
		self.roomA = roomA
		self.roomB = roomB
		self.position = position #(x, y, w, h) in relation to roomA x, y, dimensions
		self.open = startOpen

	def getRenderCoords(self):
		shape = (8, 30)
		if self.position[2] == 0: #vertical
			w, h = shape
			x = (self.roomA.x+self.position[0])*GRID_SIZE - w/2.0
			y = (self.roomA.y+self.position[1])*GRID_SIZE + (GRID_SIZE - h)/2.0
		else: #horizontal
			w, h = reversed(shape)
			x = (self.roomA.x+self.position[0])*GRID_SIZE + (GRID_SIZE - w)/2.0
			y = (self.roomA.y+self.position[1])*GRID_SIZE - h/2.0
		return (x, y, w, h)

	def getColour(self):
		if self.open:
			return (255, 200, 150)
		else:
			return (255, 100, 0)

	def tick(self):
		if self.open:
			if self.roomB != None:
				oxygenB = self.roomB.oxygen
			else:
				oxygenB = -500
			self.roomA.oxygen, oxygenB = distributeOxygen(self.roomA.oxygen, oxygenB)
			if self.roomB != None:
				self.roomB.oxygen = oxygenB



class Room(object):
	def __init__(self, x, y, dimensions=(1,1)):
		global all_rooms
		super(Room, self).__init__()
		all_rooms.append(self)
		self.x = x #coords on the grid, not screen
		self.y = y
		self.dimensions = dimensions
		self.oxygen = 100.0
		self.breaches = 0

	def getSize(self, dimension):
		return self.dimensions[dimension]

	def getColour(self):
		return (255,round(2.55*self.oxygen),round(2.55*self.oxygen))

	def getTileCount(self):
		return self.dimensions[0] * self.dimensions[1]
		
	def getBreachPos(self, n):
		return (n%self.getSize(0), n/self.getSize(1))

	def tick(self):
		self.oxygen -= 0.05
		for i in range(self.breaches):
			self.oxygen, _ = distributeOxygen(self.oxygen, 0.0)
		self.oxygen += (160 - self.oxygen) * oxygenPower / 700
		self.oxygen = max(min(self.oxygen, 100.0), 0.0)


def distributeOxygen(oxygenA, oxygenB):
	rate = 40.0
	minChange = 0.1
	minOxygen = 0.0
	difference = abs(oxygenA - oxygenB)
	if oxygenA < oxygenB:
		sign = -1
	else:
		sign = 1
	change = min(max(difference / rate, minChange), difference/2.0)
	oxygenA -= sign*change
	oxygenB += sign*change
	if oxygenA < minOxygen:
		oxygenA = minOxygen
	if oxygenB < minOxygen:
		oxygenB = minOxygen
	return oxygenA, oxygenB

def averageOxygen():
	totalOxygen = 0.0
	for room in all_rooms:
		totalOxygen += room.oxygen
	return totalOxygen / len(all_rooms)

def addBreach(x, y, amount):
	global all_rooms
	for i in xrange(len(all_rooms)):
		room = all_rooms[i]
		if x < room.x+room.getSize(0) and x >= room.x and y < room.y+room.getSize(1) and y >= room.y:
			all_rooms[i].breaches = max(min(all_rooms[i].breaches+amount, room.getTileCount()), 0)

def toggleDoor(x, y, newState=None):
	for door in all_doors:
		doorX, doorY, doorW, doorH = door.getRenderCoords()
		if x < doorX+doorW and x >= doorX and y < doorY+doorH and y >= doorY:
			if newState == None:
				door.open = not door.open
			else:
				door.open = newState
			return True
	return False

def toggleAllDoors(newState):
	if newState == True:
		openAirlocks = True
		for door in all_doors:
			if door.open == False and door.roomB != None:
				openAirlocks = False
				break
		for door in all_doors:
			if door.roomB != None or openAirlocks == True:
				door.open = True
	else:
		for door in all_doors:
			door.open = False

def canPlace(x, y, dimensions=(1,1)):
	w = dimensions[0]
	h = dimensions[1]
	if x+w > GRID_WIDTH or y+h > GRID_HEIGHT:
		print "Outside border"
		return False
	for room in all_rooms:
		if room.x+room.getSize(0) > x and x+w > room.x and room.y+room.getSize(1) > y and y+h > room.y:
			return False
	return True

def addOxygen(amount):
	global oxygenPower
	oxygenPower = max(oxygenPower + amount, 0)

def newGame():
	for room in ((4,4), (4,6), (4,8), (6,4), (6,6), (6,8)):
		new_room = Room(room[0],room[1], (2,2))
	Door(all_rooms[0])
	Door(all_rooms[1], all_rooms[0], (0,0,1,0))
	Door(all_rooms[1], all_rooms[0], (1,0,1,0))
	Door(all_rooms[2], all_rooms[1], (1,0,1,0))
	Door(all_rooms[3], all_rooms[0], (0,1,0,1))
	Door(all_rooms[3], all_rooms[0], (0,0,0,1))
	Door(all_rooms[4], all_rooms[3], (1,0,1,0))
	Door(all_rooms[4], all_rooms[1], (0,0,0,1))
	Door(all_rooms[5], all_rooms[2], (0,0,0,1))
	Door(all_rooms[5], all_rooms[4], (0,0,1,0))
	Door(all_rooms[5], all_rooms[4], (1,0,1,0))
	Door(all_rooms[4], position=(2,0,0,1))
	Door(all_rooms[4], position=(2,1,0,1))
	Door(all_rooms[5], position=(2,0,0,1))
	Door(all_rooms[3], position=(2,0,0,1))
	Door(all_rooms[2], position=(1,2,1,0))
	playGame()

def playGame():
	global gameState
	clock = pygame.time.Clock()
	iteration = 0
	while not gameState=="quitting":
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				gameState = "quitting"
			elif event.type == pygame.KEYDOWN:
				if event.key == pygame.K_z:
					toggleAllDoors(True)
				elif event.key == pygame.K_x:
					toggleAllDoors(False)
				elif event.key == pygame.K_a:
					addOxygen(-1)
				elif event.key == pygame.K_s:
					addOxygen(1)
				elif event.key == pygame.K_SPACE:
					if gameState == "playing":
						gameState = "paused"
					else:
						gameState = "playing"
			elif event.type == pygame.MOUSEBUTTONDOWN:
				x, y = event.pos
				if event.button == 3: #rightclick
					if x < GRID_SIZE*GRID_WIDTH and y < GRID_SIZE*GRID_HEIGHT:
						x = x/GRID_SIZE
						y = y/GRID_SIZE
						if canPlace(x, y, (2,2)):
							Room(x, y, (2,2))
						else:
							addBreach(x, y, 1)
				if event.button == 1: #leftclick
					if x < GRID_SIZE*GRID_WIDTH and y < GRID_SIZE*GRID_HEIGHT:
						if not toggleDoor(x, y):
							x = x/GRID_SIZE
							y = y/GRID_SIZE
							addBreach(x, y, -1)

		if gameState == "playing":
			for room in all_rooms:
				room.tick()
			for door in all_doors:
				door.tick()


		renderGame()
		clock.tick(FPS)

def renderGame():
	gameDisplay.fill((55,55,55))
	gameDisplay.fill((0,0,0), rect=[0, DISPLAY_HEIGHT-80, DISPLAY_WIDTH, 80])
	for room in all_rooms:
		gameDisplay.fill((0,0,0), rect=[room.x*GRID_SIZE, room.y*GRID_SIZE, room.getSize(0)*GRID_SIZE, room.getSize(1)*GRID_SIZE])
		gameDisplay.fill(room.getColour(), rect=[room.x*GRID_SIZE+WALL_THICKNESS, room.y*GRID_SIZE+WALL_THICKNESS, room.getSize(0)*GRID_SIZE-2*WALL_THICKNESS, room.getSize(1)*GRID_SIZE-2*WALL_THICKNESS])
		for breach in range(room.breaches):
			(breach_x, breach_y) = room.getBreachPos(breach)
			gameDisplay.fill((25,35,55), rect=[(room.x+0.25+breach_x)*GRID_SIZE, (room.y+0.25+breach_y)*GRID_SIZE, 0.5*GRID_SIZE, 0.5*GRID_SIZE])
	for door in all_doors:
		gameDisplay.fill(door.getColour(), rect=door.getRenderCoords())
	text(DISPLAY_WIDTH/2, DISPLAY_HEIGHT-40, "Oxygen Power: %i   Average Oxygen: %f"%(oxygenPower,averageOxygen()), colour=(0,255,0))
	if gameState == "paused":
		text(DISPLAY_WIDTH/2, DISPLAY_HEIGHT-60, "PAUSED", colour=(255,255,255))

	pygame.display.update()

def text(x, y, text, colour=(255,255,255), size=25, w=0, h=0):
	textSurface = get_msg(text, colour, size)
	textRect = textSurface.get_rect()
	textRect.center = x, y+h/4
	gameDisplay.blit(textSurface, textRect)

cache = {}
def get_msg(msg, colour, size):
	if not size in cache:
		cache[size] = {}
	if not colour in cache[size]:
		cache[size][colour] = {}
	if not msg in cache[size][colour]:
		fontobj = pygame.font.Font("arial.ttf", size)
		cache[size][colour][msg] = fontobj.render(msg, False , colour)
	return cache[size][colour][msg]

newGame()

pygame.quit()
quit()
Quick edit: Removed some unused bits of code from the program
kcd.Spektor
Posts: 586
Joined: Thu Nov 26, 2015 8:21 am

Re: Hurray! Another multiplayer clone project. :)

Post by kcd.Spektor »

faggot wrote: Hey: There are tons of people that would love to help, have you considered uploading your code to github?
It's not open source. ;)
Plus I can handle the coding part,
I need help with art and sound.

jrb00001 wrote: How much is replenished every tick? Is it more than the oxygen lost through the open doors?
The oxygen in the airlock is set to 0 every tick when the airlock is open.
Then every tick air of nearby cells is averaged between them, so the air is sucked out pretty fast :)
I think the amount of replenished oxygen per cell should be divided by the amount of room cells. Bigger ships need more oxygen!
This can lead to 0 oxygen replenishment on really big ships.
And on smaller ships it would be replenished really slow, but venting it out will still be fast, and that would be a disballance.
fdagpigj wrote: Well, I just made a simulation in pygame (python 2). No idea why. Because it's fun, I guess. I feel like I have something pretty close to the original now.
Well I'm not familiar with python, so that code doesn't help any bit :)
Can you draw a logic tree for your algorithm?
Post Reply