1
0
Fork 0

Fixed encounter generation matching star system level

This commit is contained in:
Michaël Lemaire 2017-04-26 01:27:42 +02:00
parent 8ba518270b
commit f8c443f3c9
18 changed files with 104 additions and 70 deletions

3
TODO
View file

@ -4,7 +4,7 @@
* Character sheet: improve eye-catching for shop and loot section * Character sheet: improve eye-catching for shop and loot section
* Character sheet: highlight allowed destinations during drag-and-drop * Character sheet: highlight allowed destinations during drag-and-drop
* Add permanent effects to ship models to ease balancing * Add permanent effects to ship models to ease balancing
* Ships should start battle in formation to force them to move * Find incentives to move from starting position
* Fix targetting not resetting when using action shortcuts * Fix targetting not resetting when using action shortcuts
* Add battle statistics and/or critics in outcome dialog * Add battle statistics and/or critics in outcome dialog
* Add battle experience * Add battle experience
@ -40,7 +40,6 @@
* TacticalAI: add pauses to not play too quickly * TacticalAI: add pauses to not play too quickly
* TacticalAI: replace BullyAI * TacticalAI: replace BullyAI
* Map: restore fog of war * Map: restore fog of war
* Map: a star system should have an average level
* Map: add information on current star/location + information on hovered location * Map: add information on current star/location + information on hovered location
* Map: generate random shops * Map: generate random shops
* Map: remove jump links that cross the radius of other systems * Map: remove jump links that cross the radius of other systems

View file

@ -50,9 +50,9 @@ module TS.SpaceTac {
} }
// Create a quick random battle, for testing purposes // Create a quick random battle, for testing purposes
static newQuickRandom(start = true): Battle { static newQuickRandom(start = true, level = 1): Battle {
var player1 = Player.newQuickRandom("John"); var player1 = Player.newQuickRandom("John", level, true);
var player2 = Player.newQuickRandom("Carl"); var player2 = Player.newQuickRandom("Carl", level, true);
var result = new Battle(player1.fleet, player2.fleet); var result = new Battle(player1.fleet, player2.fleet);
if (start) { if (start) {

View file

@ -15,32 +15,32 @@ module TS.SpaceTac.Specs {
var equipment = new Equipment(); var equipment = new Equipment();
var ship = new Ship(); var ship = new Ship();
expect(equipment.canBeEquipped(ship)).toBe(true); expect(equipment.canBeEquipped(ship.attributes)).toBe(true);
equipment.requirements["skill_time"] = 2; equipment.requirements["skill_time"] = 2;
expect(equipment.canBeEquipped(ship)).toBe(false); expect(equipment.canBeEquipped(ship.attributes)).toBe(false);
ship.attributes.skill_time.set(1); ship.attributes.skill_time.set(1);
expect(equipment.canBeEquipped(ship)).toBe(false); expect(equipment.canBeEquipped(ship.attributes)).toBe(false);
ship.attributes.skill_time.set(2); ship.attributes.skill_time.set(2);
expect(equipment.canBeEquipped(ship)).toBe(true); expect(equipment.canBeEquipped(ship.attributes)).toBe(true);
ship.attributes.skill_time.set(3); ship.attributes.skill_time.set(3);
expect(equipment.canBeEquipped(ship)).toBe(true); expect(equipment.canBeEquipped(ship.attributes)).toBe(true);
// Second requirement // Second requirement
equipment.requirements["skill_material"] = 3; equipment.requirements["skill_material"] = 3;
expect(equipment.canBeEquipped(ship)).toBe(false); expect(equipment.canBeEquipped(ship.attributes)).toBe(false);
ship.attributes.skill_material.set(4); ship.attributes.skill_material.set(4);
expect(equipment.canBeEquipped(ship)).toBe(true); expect(equipment.canBeEquipped(ship.attributes)).toBe(true);
}); });
it("generates a description of the effects", function () { it("generates a description of the effects", function () {

View file

@ -112,19 +112,19 @@ module TS.SpaceTac {
} }
/** /**
* Returns true if the equipment can be equipped on a ship. * Returns true if the equipment can be equipped on a ship with given skills.
* *
* This checks *requirements* against the ship skills. * This checks *requirements* against the skills.
* *
* This does not check where the equipment currently is (except if is it already attached and should be detached first). * This does not check where the equipment currently is (except if is it already attached and should be detached first).
*/ */
canBeEquipped(ship: Ship): boolean { canBeEquipped(skills: ShipAttributes): boolean {
if (this.attached_to) { if (this.attached_to) {
return false; return false;
} else { } else {
var able = true; var able = true;
iteritems(this.requirements, (attr, minvalue) => { iteritems(this.requirements, (attr, minvalue) => {
if (ship.getAttribute(<keyof ShipAttributes>attr) < minvalue) { if (skills[attr].get() < minvalue) {
able = false; able = false;
} }
}); });

View file

@ -9,13 +9,13 @@ module TS.SpaceTac {
} }
// Generate a fleet of a given level // Generate a fleet of a given level
generate(level: number, player?: Player, ship_count = 3): Fleet { generate(level: number, player?: Player, ship_count = 3, upgrade = false): Fleet {
var fleet = new Fleet(player); var fleet = new Fleet(player);
var ship_generator = new ShipGenerator(this.random); var ship_generator = new ShipGenerator(this.random);
while (ship_count--) { while (ship_count--) {
var ship = ship_generator.generate(level); var ship = ship_generator.generate(level, null, upgrade);
ship.name = "Ship " + ship_count.toString(); ship.name = `${fleet.player.name}'s Level ${ship.level.get()} ${ship.model.name}`;
fleet.addShip(ship); fleet.addShip(ship);
} }

View file

@ -31,8 +31,6 @@ module TS.SpaceTac {
this.templates = templates; this.templates = templates;
} }
// TODO Add generator from skills
// Generate a random equipment for a specific level // Generate a random equipment for a specific level
// If slot is specified, it will generate an equipment for this slot type specifically // If slot is specified, it will generate an equipment for this slot type specifically
// If no equipment could be generated from available templates, null is returned // If no equipment could be generated from available templates, null is returned
@ -48,5 +46,27 @@ module TS.SpaceTac {
// Pick a random equipment // Pick a random equipment
return this.random.choice(equipments); return this.random.choice(equipments);
} }
/**
* Generate a random equipment of highest level, from a given set of skills
*/
generateHighest(skills: ShipSkills, quality = EquipmentQuality.COMMON, slot: SlotType | null = null): Equipment | null {
let templates = this.templates.filter(template => slot == null || slot == template.slot);
let candidates: Equipment[] = [];
let level = 1;
templates.forEach(template => {
let equipment = template.generateHighest(skills, quality, this.random);
if (equipment && equipment.level >= level) {
if (equipment.level > level) {
candidates.splice(0);
level = equipment.level;
}
candidates.push(equipment);
}
});
return (candidates.length == 0) ? null : this.random.choice(candidates);
}
} }
} }

View file

@ -213,6 +213,26 @@ module TS.SpaceTac {
return result; return result;
} }
/**
* Generate the highest equipment level, for a given set of skills
*/
generateHighest(skills: ShipSkills, quality = EquipmentQuality.COMMON, random = RandomGenerator.global): Equipment | null {
let level = 1;
let equipment: Equipment | null = null;
let attributes = new ShipAttributes();
keys(skills).forEach(skill => attributes[skill].set(skills[skill].get()));
do {
let nequipment = this.generate(level, quality, random);
if (nequipment.canBeEquipped(attributes)) {
equipment = nequipment;
} else {
break;
}
level += 1;
} while (level < 100);
return equipment;
}
/** /**
* Set skill requirements that will be added to each level of equipment. * Set skill requirements that will be added to each level of equipment.
*/ */

View file

@ -1,6 +1,9 @@
module TS.SpaceTac { module TS.SpaceTac {
// One player (human or IA) // One player (human or IA)
export class Player { export class Player {
// Player's name
name: string;
// Universe in which we are playing // Universe in which we are playing
universe: Universe; universe: Universe;
@ -11,33 +14,17 @@ module TS.SpaceTac {
visited: StarLocation[] = []; visited: StarLocation[] = [];
// Create a player, with an empty fleet // Create a player, with an empty fleet
constructor(universe: Universe = new Universe()) { constructor(universe: Universe = new Universe(), name = "Player") {
this.universe = universe; this.universe = universe;
this.name = name;
this.fleet = new Fleet(this); this.fleet = new Fleet(this);
} }
// Create a quick random player, with a fleet, for testing purposes // Create a quick random player, with a fleet, for testing purposes
static newQuickRandom(name: String): Player { static newQuickRandom(name: string, level = 1, upgrade = false): Player {
var player = new Player(new Universe()); let player = new Player(new Universe(), name);
var ship: Ship; let generator = new FleetGenerator();
var ship_generator = new ShipGenerator(); player.fleet = generator.generate(level, player, 4, upgrade);
ship = ship_generator.generate(1);
ship.name = name + "'s First";
player.fleet.addShip(ship);
ship = ship_generator.generate(1);
ship.name = name + "'s Second";
player.fleet.addShip(ship);
ship = ship_generator.generate(1);
ship.name = name + "'s Third";
player.fleet.addShip(ship);
ship = ship_generator.generate(1);
ship.name = name + "'s Fourth";
player.fleet.addShip(ship);
return player; return player;
} }

View file

@ -65,7 +65,7 @@ module TS.SpaceTac {
name: string name: string
// Code of the ShipModel used to create it // Code of the ShipModel used to create it
model: string model: ShipModel
// Flag indicating if the ship is alive // Flag indicating if the ship is alive
alive: boolean alive: boolean
@ -100,10 +100,9 @@ module TS.SpaceTac {
play_priority = 0; play_priority = 0;
// Create a new ship inside a fleet // Create a new ship inside a fleet
constructor(fleet: Fleet | null = null, name = "Ship") { constructor(fleet: Fleet | null = null, name = "Ship", model = new ShipModel("default", "Default", 1, 0)) {
this.fleet = fleet || new Fleet(); this.fleet = fleet || new Fleet();
this.name = name; this.name = name;
this.model = "default";
this.alive = true; this.alive = true;
this.sticky_effects = []; this.sticky_effects = [];
this.slots = []; this.slots = [];
@ -115,6 +114,10 @@ module TS.SpaceTac {
if (fleet) { if (fleet) {
fleet.addShip(this); fleet.addShip(this);
} }
this.model = model;
this.setCargoSpace(model.cargo);
model.slots.forEach(slot => this.addSlot(slot));
} }
// Returns true if the ship is able to play // Returns true if the ship is able to play
@ -532,7 +535,7 @@ module TS.SpaceTac {
canEquip(item: Equipment): Slot | null { canEquip(item: Equipment): Slot | null {
let free_slot = first(this.slots, slot => slot.type == item.slot_type && !slot.attached); let free_slot = first(this.slots, slot => slot.type == item.slot_type && !slot.attached);
if (free_slot) { if (free_slot) {
if (item.canBeEquipped(this)) { if (item.canBeEquipped(this.attributes)) {
return free_slot; return free_slot;
} else { } else {
return null; return null;

View file

@ -2,9 +2,9 @@ module TS.SpaceTac.Specs {
describe("ShipGenerator", function () { describe("ShipGenerator", function () {
it("can use ship model", function () { it("can use ship model", function () {
var gen = new ShipGenerator(); var gen = new ShipGenerator();
var model = new ShipModel("test", 1, 2, SlotType.Shield, SlotType.Weapon, SlotType.Weapon); var model = new ShipModel("test", "Test", 1, 2, SlotType.Shield, SlotType.Weapon, SlotType.Weapon);
var ship = gen.generate(1, model); var ship = gen.generate(1, model);
expect(ship.model).toBe("test"); expect(ship.model).toBe(model);
expect(ship.cargo_space).toBe(2); expect(ship.cargo_space).toBe(2);
expect(ship.slots.length).toBe(3); expect(ship.slots.length).toBe(3);
expect(ship.slots[0].type).toBe(SlotType.Shield); expect(ship.slots[0].type).toBe(SlotType.Shield);

View file

@ -10,28 +10,29 @@ module TS.SpaceTac {
// Generate a ship of a given level // Generate a ship of a given level
// The ship will not be named, nor will be a member of any fleet // The ship will not be named, nor will be a member of any fleet
generate(level: number, model: ShipModel | null = null): Ship { generate(level: number, model: ShipModel | null = null, upgrade = false): Ship {
var result = new Ship();
var loot = new LootGenerator(this.random);
if (!model) { if (!model) {
// Get a random model // Get a random model
model = ShipModel.getRandomModel(level, this.random); model = ShipModel.getRandomModel(level, this.random);
} }
// Apply model var result = new Ship(null, undefined, model);
result.model = model.code; var loot = new LootGenerator(this.random);
result.setCargoSpace(model.cargo);
model.slots.forEach((slot: SlotType) => {
result.addSlot(slot);
});
// Set all skills to 1 (to be able to use at least basic equipment) // Set all skills to 1 (to be able to use at least basic equipment)
keys(result.skills).forEach(skill => result.upgradeSkill(skill)); keys(result.skills).forEach(skill => result.upgradeSkill(skill));
// Level upgrade
result.level.forceLevel(level);
if (upgrade) {
while (result.getAvailableUpgradePoints() > 0) {
result.upgradeSkill(this.random.choice(keys(SHIP_SKILLS)));
}
}
// Fill equipment slots // Fill equipment slots
result.slots.forEach((slot: Slot) => { result.slots.forEach((slot: Slot) => {
var equipment = loot.generate(level, EquipmentQuality.COMMON, slot.type); var equipment = loot.generateHighest(result.skills, EquipmentQuality.COMMON, slot.type);
if (equipment) { if (equipment) {
slot.attach(equipment) slot.attach(equipment)
if (slot.attached !== equipment) { if (slot.attached !== equipment) {

View file

@ -5,6 +5,9 @@ module TS.SpaceTac {
// Code to identify the model // Code to identify the model
code: string; code: string;
// Human-readable model name
name: string;
// Minimal level to use this model // Minimal level to use this model
level: number; level: number;
@ -14,8 +17,9 @@ module TS.SpaceTac {
// Available slots // Available slots
slots: SlotType[]; slots: SlotType[];
constructor(code: string, level: number, cargo: number, ...slots: SlotType[]) { constructor(code: string, name: string, level: number, cargo: number, ...slots: SlotType[]) {
this.code = code; this.code = code;
this.name = name;
this.level = level; this.level = level;
this.cargo = cargo; this.cargo = cargo;
this.slots = slots; this.slots = slots;
@ -26,12 +30,12 @@ module TS.SpaceTac {
// TODO Store in cache // TODO Store in cache
var result: ShipModel[] = []; var result: ShipModel[] = [];
result.push(new ShipModel("scout", 1, 2, SlotType.Hull, SlotType.Power, SlotType.Power, SlotType.Engine, SlotType.Weapon)); result.push(new ShipModel("scout", "Scout", 1, 2, SlotType.Hull, SlotType.Power, SlotType.Power, SlotType.Engine, SlotType.Weapon));
result.push(new ShipModel("whirlwind", 1, 4, SlotType.Hull, SlotType.Shield, SlotType.Power, SlotType.Engine, result.push(new ShipModel("whirlwind", "Whirlwind", 1, 4, SlotType.Hull, SlotType.Shield, SlotType.Power, SlotType.Engine,
SlotType.Weapon, SlotType.Weapon)); SlotType.Weapon, SlotType.Weapon));
result.push(new ShipModel("tomahawk", 1, 6, SlotType.Hull, SlotType.Shield, SlotType.Power, SlotType.Engine, SlotType.Engine, result.push(new ShipModel("tomahawk", "Tomahawk", 1, 6, SlotType.Hull, SlotType.Shield, SlotType.Power, SlotType.Engine, SlotType.Engine,
SlotType.Weapon)); SlotType.Weapon));
return result; return result;

View file

@ -28,7 +28,7 @@ module TS.SpaceTac {
// Attach an equipment in this slot // Attach an equipment in this slot
attach(equipment: Equipment): Equipment { attach(equipment: Equipment): Equipment {
if (this.type === equipment.slot_type && equipment.canBeEquipped(this.ship)) { if (this.type === equipment.slot_type && equipment.canBeEquipped(this.ship.attributes)) {
this.attached = equipment; this.attached = equipment;
equipment.attached_to = this; equipment.attached_to = this;

View file

@ -70,7 +70,7 @@ module TS.SpaceTac {
if (this.encounter_random.random() < 0.8) { if (this.encounter_random.random() < 0.8) {
var fleet_generator = new FleetGenerator(this.encounter_random); var fleet_generator = new FleetGenerator(this.encounter_random);
var ship_count = this.encounter_random.randInt(1, 5); var ship_count = this.encounter_random.randInt(1, 5);
this.encounter = fleet_generator.generate(this.star.level, undefined, ship_count); this.encounter = fleet_generator.generate(this.star.level, undefined, ship_count, true);
} }
} }

View file

@ -34,7 +34,7 @@ module TS.SpaceTac.UI {
this.enemy = this.ship.getPlayer() != this.battleview.player; this.enemy = this.ship.getPlayer() != this.battleview.player;
// Add ship sprite // Add ship sprite
this.sprite = new Phaser.Button(this.game, 0, 0, "ship-" + ship.model + "-sprite"); this.sprite = new Phaser.Button(this.game, 0, 0, "ship-" + ship.model.code + "-sprite");
this.sprite.rotation = ship.arena_angle; this.sprite.rotation = ship.arena_angle;
this.sprite.anchor.set(0.5, 0.5); this.sprite.anchor.set(0.5, 0.5);
this.sprite.scale.set(64 / this.sprite.width); this.sprite.scale.set(64 / this.sprite.width);

View file

@ -38,7 +38,7 @@ module TS.SpaceTac.UI {
this.active_effects = new Phaser.Group(this.game); this.active_effects = new Phaser.Group(this.game);
this.addChild(this.active_effects); this.addChild(this.active_effects);
this.layer_portrait = new Phaser.Image(this.game, 8, 8, "ship-" + ship.model + "-portrait", 0); this.layer_portrait = new Phaser.Image(this.game, 8, 8, "ship-" + ship.model.code + "-portrait", 0);
this.layer_portrait.scale.set(0.3, 0.3); this.layer_portrait.scale.set(0.3, 0.3);
this.addChild(this.layer_portrait); this.addChild(this.layer_portrait);

View file

@ -16,7 +16,7 @@ module TS.SpaceTac.UI {
this.sheet = sheet; this.sheet = sheet;
this.ship = ship; this.ship = ship;
let portrait_pic = new Phaser.Image(this.game, 0, 0, `ship-${ship.model}-portrait`); let portrait_pic = new Phaser.Image(this.game, 0, 0, `ship-${ship.model.code}-portrait`);
portrait_pic.anchor.set(0.5, 0.5); portrait_pic.anchor.set(0.5, 0.5);
this.addChild(portrait_pic); this.addChild(portrait_pic);

View file

@ -25,7 +25,7 @@ module TS.SpaceTac.UI {
fleet.ships.forEach((ship, index) => { fleet.ships.forEach((ship, index) => {
let offset = LOCATIONS[index]; let offset = LOCATIONS[index];
let sprite = this.game.add.image(offset[0], offset[1] + 150, `ship-${ship.model}-sprite`, 0, this); let sprite = this.game.add.image(offset[0], offset[1] + 150, `ship-${ship.model.code}-sprite`, 0, this);
sprite.scale.set(64 / sprite.width); sprite.scale.set(64 / sprite.width);
sprite.anchor.set(0.5, 0.5); sprite.anchor.set(0.5, 0.5);
}); });