mkendrick 2 years ago
parent ae14326fe8
commit 4c59532a9f
  1. BIN
      assets/ship-sheet.png
  2. 341
      factory.py
  3. 30
      game.py
  4. 6
      settings.py
  5. 172
      ship.py
  6. 34
      spritesheet.py

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,341 @@
import time, datetime, math
from copy import deepcopy
from enum import Enum
def now():
return datetime.datetime.now().timestamp()
class DroneState(Enum):
Grounded = 0
InTheAir = 1
WaitingForTakeOff = 2
class StopType(Enum):
City = 0
Factory = 1
class Position:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return(f"[{self.x}, {self.y}]")
class Drone:
def __init__(self, name, position, route, rate, capacity):
self.__name = name
self.__position = position
self.__route = route
self.__rate = rate # per second
self.__capacity = capacity
self.__cargo = {}
self.__state = DroneState.Grounded
self.__prevousStop = -1
self.__nextStop = 0
self.__lastDeparture = 0
self.__lastDeparturePosition = deepcopy(position)
def set_position(self, position):
self.__position = position
# returns how much was accepted from the load
def load_cargo(self, product, quantity):
cargoTotal = self.get_cargo_total()
if not product in self.__cargo:
self.__cargo[product] = 0
spaceRemaining = (self.__capacity - cargoTotal)
acceptedQuantity = quantity
# if the quantity is more than space remaining then just return the space remaining
if(quantity > spaceRemaining):
acceptedQuantity = spaceRemaining
self.__cargo[product] += acceptedQuantity
return acceptedQuantity
def get_cargo_total(self):
total = 0
for key in self.__cargo:
total += self.__cargo[key]
return total
def takeOff(self):
self.__state = DroneState.InTheAir
self.__lastDeparture = now()
self.__lastDeparturePosition = deepcopy(self.__position)
positionFound = False
for idx, stop in enumerate(self.__route.get_stops()):
stopPosition = stop.get_position()
if (stopPosition.x == self.__position.x) and (stopPosition.y == self.__position.y):
positionFound = True
self.__prevousStop = idx
# if the next stop is out of range, loop back home
if(idx + 1 == len(self.__route.get_stops())):
self.__nextStop = 0
else:
self.__nextStop = idx + 1
if(not positionFound):
self.__prevousStop = -1
self.__nextStop = 0
print(f"{self.__name}: Taking off from: {self.__position}")
else:
print(f"{self.__name}: Taking off from: {self.__route.get_stops()[self.__prevousStop]}")
def cargo_report(self):
str = ""
for product in self.__cargo:
str += f"{product}: {self.__cargo[product]}"
return
def update(self):
state = DroneState.Grounded.name
position = f"Position: {self.__position}"
prevousStop = self.__route.get_stops()[self.__prevousStop]
nextStop = self.__route.get_stops()[self.__nextStop]
# print cargo levels
print(f"{self.__name}: On Board {self.__cargo}")
# are we at a unknown location?
if(self.__prevousStop == -1):
location = self.__lastDeparturePosition
else:
location = prevousStop
# is the drone in the air?
if(self.__state == DroneState.Grounded):
print(f"{self.__name}: {state}: {location}")
elif(self.__state == DroneState.WaitingForTakeOff):
currentStop = prevousStop
print(f"{self.__name}: Waiting For Take Off: {location}")
# load cargo/receive_supplies if at a factory
if(currentStop.get_stop_type() == StopType.Factory):
print(f"{self.__name}: Loading Inventory")
products = currentStop.get_output_quantity()
for key in products:
acceptedQuantity = self.load_cargo(key, products[key])
# adjust inventory by accepted quantity
currentStop.update_inventory(-acceptedQuantity)
currentStop.receive_supplies(self.__cargo)
if(self.__nextTakeOff <= now()):
self.takeOff()
else:
print(f"{self.__name}: Traveling Between: {location} and {nextStop}")
# time in the air since take off
timeDifference = math.floor(now() - self.__lastDeparture)
x2 = float(nextStop.get_position().x)
y2 = float(nextStop.get_position().y)
if(self.__prevousStop == -1):
x1 = float(self.__lastDeparturePosition.x)
y1 = float(self.__lastDeparturePosition.y)
else:
x1 = prevousStop.get_position().x
y1 = prevousStop.get_position().y
#print(f"{self.__name}: {x1},{y1} -> {x2},{y2}")
# total distance: d=√((x2 – x1)² + (y2 – y1)²)
totalDistance = (math.sqrt(abs(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))))
# d = rt
distanceTraveled = self.__rate * (now() - self.__lastDeparture)
#print(f"{self.__name}: Distance Traveled: {distanceTraveled}, Total Distance: {totalDistance}")
# slope m = (y2 - y1) / (x2 - x1)
if(x2 == x1):
m = 0
else:
m = (y2 - y1) / (x2 - x1)
travelingEast = x2 > x1
travelingNorth = y2 > y1
# if traveled the distance, ground the drone and update stops
if(distanceTraveled >= totalDistance):
# force position
self.__position.x = nextStop.get_position().x
self.__position.y = nextStop.get_position().y
self.__state = DroneState.WaitingForTakeOff
self.__nextTakeOff = now() + 5
self.__prevousStop = self.__nextStop
self.__nextStop += 1
# if next stop out of range, sent back to first stop
if(self.__nextStop >= len(self.__route.get_stops())):
self.__nextStop = 0
else:
# what percentage of the journey has passed?
per = (distanceTraveled/totalDistance)
# compute X
if(travelingEast):
x = x1 + ((x2 - x1) * per)
else:
x = x1 - ((x1 - x2) * per)
# compute y
if(m > 0):
# if there is a slope
#(y – y1) = m(x – x1)
y = (m * (x - x1)) + y1
else:
if(travelingNorth):
y = y1 + ((y2 - y1) * per)
else:
y = y1 - ((y1 - y2) * per)
# if no slope, either x or y is constant
if(x1 == x2):
x = x1
if(y1 == y2):
y = y1
self.__position.x = x
self.__position.y = y
print(f"{self.__name} {position}")
def get_state(self):
return self.__state
class Route:
def __init__(self, name, stops):
self.__name = name
self.__stops = stops
def get_stops(self):
return self.__stops
class Product():
def __init__(self, name):
self.__name = name
def __str__(self):
return f"{self.__name}"
def get_name(self):
return self.__name
class Stop:
def __init__(self, stopType, name, position):
self.__stopType = stopType
self.__name = name
self.__position = position
def __str__(self):
return f"{self.__name} {self.__position}"
def get_position(self):
return self.__position
def get_name(self):
return self.__name
def get_stop_type(self):
return self.__stopType
class Factory(Stop):
def __init__(self, name, position, product, buildRate, acceptedSupplies):
super().__init__(StopType.Factory, name, position)
#product name
self.__product = product
#build rate (units per second)
self.__buildRate = buildRate
#units ready to ship
self.__inventory = {product: 0}
#last time inventory was updated
self.__lastCheck = 0
#acceptedSupplies = products accepted by this factory
self.__acceptedSupplies = acceptedSupplies
# returns a dictionary of the product and inventory level
def get_output_quantity(self):
# only return finished goods
return {self.__product: math.floor(self.__inventory[self.__product])}
# adds to inventory level
def update_inventory(self, quantity):
self.__inventory[self.__product] += quantity
def update_supply_inventory(self, product, quantity):
if product not in self.__inventory:
self.__inventory[product] = 0
self.__inventory[product] += quantity
def receive_supplies(self, supplies):
for supply in supplies:
if supply in self.__acceptedSupplies:
self.update_supply_inventory(supply, supplies[supply])
def inventory_report(self):
str = f"{self.get_name()}: "
for product in self.__inventory:
str += f"{product}: {self.__inventory[product]} "
return str
# runs on timer tick
def update(self):
now = datetime.datetime.now().timestamp()
if(self.__lastCheck > 0):
timeDifference = (now - self.__lastCheck)
self.update_inventory(timeDifference * self.__buildRate)
print(self.inventory_report())
self.__lastCheck = datetime.datetime.now().timestamp()
chicago = Stop(StopType.City, "Chicago", Position(1, 45))
miami = Stop(StopType.City, "Miami", Position(50, 1))
newYork = Stop(StopType.City, "New York", Position(50, 50))
widget = Product("Widget")
gizmo = Product("Gizmo")
widgetFactory = Factory("Widget Inc", Position(50, 25), widget, .25, [])
gizmoSupplies = [widget]
gizmoFactory = Factory("Gizmo Inc", Position(50, 30), gizmo, .25, gizmoSupplies)
factories = [widgetFactory, gizmoFactory]
route = Route(name="Express Route", stops=(widgetFactory, gizmoFactory))
routeRev = Route(name="Express Route Reverse", stops=(miami, newYork, chicago, widgetFactory))
alphaDrone = Drone("Alpha", Position(25, 25), route, 1, 10)
betaDrone = Drone("Beta", Position(35, 25), routeRev, 5, 10)
drones = [alphaDrone]
while(True):
for drone in drones:
if(drone.get_state() == DroneState.Grounded):
drone.takeOff()
else:
drone.update()
for factory in factories:
factory.update()
pass
time.sleep(1)

@ -0,0 +1,30 @@
import pygame
from settings import Settings
from spritesheet import SquareSpriteSheet
from ship import Drone
settings = Settings()
screen = pygame.display.set_mode((settings.width, settings.height))
pygame.display.set_caption(settings.caption)
pygame.init()
ship = Drone(screen, (0, 0))
run = True
while run:
#update background
screen.fill(settings.background_color)
ship.update()
#event handler
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()

@ -0,0 +1,6 @@
class Settings():
def __init__(self) -> None:
self.width = 500
self.height = 500
self.caption = "My Game"
self.background_color = (50, 50, 50)

@ -0,0 +1,172 @@
import pygame
from enum import Enum
import math
import random
from spritesheet import SquareSpriteSheet
class DroneStatus(Enum):
GROUNDED = 0
TAKINGOFF = 1
TURNING = 2
INTHEAIR = 3
LANDING = 4
class Drone:
def __init__(self, screen, pos = (0, 0)) -> None:
# image
self.screen = screen
self.sheet = sheet = SquareSpriteSheet('assets/ship-sheet.png', 96, 4)
self.scale = 1
self.angle = 0
# animation
self.frame = 0
self.animation_cooldown = 75
# tracking
self.scale = .25
self.last_update = 0
self.pos = pos
self.prev_index = 0
self.next_index = 1
self.route = [(400,400),(0,400),(400,0),(0,0),(400,0)]
# status
self.status = DroneStatus.TAKINGOFF
self.animate = False
self.last_departure = 0
self.rate = 100/1000 #(units per second)
def update(self):
current_time = pygame.time.get_ticks()
if(self.status == DroneStatus.GROUNDED):
self.prev_index = 0
self.next_index = 1
self.scale = .25
self.animate = False
elif(self.status == DroneStatus.TAKINGOFF):
self.scale += .001
if(self.scale >= 1):
self.scale = 1
self.status = DroneStatus.TURNING
self.animate = True
elif(self.status == DroneStatus.TURNING):
x1 = self.route[self.prev_index][0]
y1 = self.route[self.prev_index][1]
x2 = self.route[self.next_index][0]
y2 = self.route[self.next_index][1]
#https://replit.com/@Rabbid76/PyGame-RotateWithMouse#main.py
correction_angle = 90
dx = x2 - x1
dy = y2 - y1
new_angle = math.degrees(math.atan2(-dy, dx)) - correction_angle
if(int(self.angle) > int(new_angle)):
self.angle -= .1
elif(int(self.angle) < int(new_angle)):
self.angle += .1
if((int(self.angle) + int(new_angle)) == 0):
self.angle = new_angle
self.last_departure = current_time
self.status = DroneStatus.INTHEAIR
if((int(self.angle) - int(new_angle)) == 0):
self.angle = new_angle
self.last_departure = current_time
self.status = DroneStatus.INTHEAIR
self.animate = True
elif(self.status == DroneStatus.INTHEAIR):
x1 = self.route[self.prev_index][0]
y1 = self.route[self.prev_index][1]
x2 = self.route[self.next_index][0]
y2 = self.route[self.next_index][1]
going_north = y2 >= y1
going_east = x2 >= x1
slope = 0
if(x2 - x1 != 0):
slope = (y2 - y1) / (x2 - x1)
# total distance: d=√((x2 – x1)² + (y2 – y1)²)
total_distance = (math.sqrt(abs(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))))
# d = rt
distance_traveled = self.rate * (current_time - self.last_departure)
# if traveled the distance, ground the drone and update stops
if(distance_traveled >= total_distance):
# force position
self.pos = self.route[self.next_index]
self.status = DroneStatus.LANDING
else:
# what percentage of the journey has passed?
per = (distance_traveled/total_distance)
# compute X
if(going_east):
x = x1 + ((x2 - x1) * per)
else:
x = x1 - ((x1 - x2) * per)
# compute y
if(slope > 0):
# if there is a slope
#(y – y1) = m(x – x1)
y = (slope * (x - x1)) + y1
else:
if(going_north):
y = y1 + ((y2 - y1) * per)
else:
y = y1 - ((y1 - y2) * per)
# if no slope, either x or y is constant
if(x1 == x2):
x = x1
if(y1 == y2):
y = y1
self.pos = (x, y)
self.scale = 1
self.animate = True
elif(self.status == DroneStatus.LANDING):
self.scale -= .001
if(self.scale <= .25):
self.scale = .25
self.status = DroneStatus.TAKINGOFF
self.prev_index = self.next_index
self.next_index = self.prev_index + 1 if self.prev_index + 1 < len(self.route) else 0
self.animate = True
if(self.animate):
if current_time - self.last_update >= self.animation_cooldown:
self.frame += 1
self.frame = self.frame if self.frame < len(self.sheet.images) else 0
self.last_update = current_time
current_image = self.sheet.get_image_by_frame(self.frame, self.scale, self.angle)
self.screen.blit(current_image, (self.pos))

@ -0,0 +1,34 @@
import pygame
class SquareSpriteSheet():
def __init__(self, filename, size, numberOfImages, transparency_color=(0,0,0)) -> None:
self.sheet = pygame.image.load(filename).convert_alpha()
self.size = size
self.numberOfImages = numberOfImages
self.transparency_color = transparency_color
self.images = []
self.load_images()
def load_images(self):
for i in range(self.numberOfImages):
image = pygame.Surface((self.size, self.size)).convert_alpha()
image.blit(self.sheet, (0,0), ((i * self.size), 0, self.size, self.size))
image.set_colorkey(self.transparency_color)
self.images.append(image)
def get_image_by_frame(self, frame, scale=1, angle=0):
image = self.images[frame]
image = pygame.transform.scale(image, (self.size * scale, self.size * scale))
image = self.rot_center(image, angle)
image.set_colorkey(self.transparency_color)
return image
def rot_center(self, image, angle):
"""rotate an image while keeping its center and size"""
orig_rect = image.get_rect()
rot_image = pygame.transform.rotate(image, angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
Loading…
Cancel
Save

Powered by TurnKey Linux.