diff --git a/binaries/data/mods/official/art/textures/skies/day1/back.dds b/binaries/data/mods/official/art/textures/skies/day1/back.dds new file mode 100644 index 0000000000..3b81c6acba --- /dev/null +++ b/binaries/data/mods/official/art/textures/skies/day1/back.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9eec24ba092990cb4a4c037133c28e58cb93f6c25f27d11c6c1f86d8154aba5 +size 262272 diff --git a/binaries/data/mods/official/art/textures/skies/day1/bottom.dds b/binaries/data/mods/official/art/textures/skies/day1/bottom.dds new file mode 100644 index 0000000000..9ed88eba28 --- /dev/null +++ b/binaries/data/mods/official/art/textures/skies/day1/bottom.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd7c7723c06eff72ac6fc81f2b0a8f3560ac68d72f83962e2ba2bcbfa71a7138 +size 262272 diff --git a/binaries/data/mods/official/art/textures/skies/day1/front.dds b/binaries/data/mods/official/art/textures/skies/day1/front.dds new file mode 100644 index 0000000000..6b524435c2 --- /dev/null +++ b/binaries/data/mods/official/art/textures/skies/day1/front.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:188cb207008478eaeb3800e105581c954524bcac7e0772f54976fbb45e64978f +size 262272 diff --git a/binaries/data/mods/official/art/textures/skies/day1/left.dds b/binaries/data/mods/official/art/textures/skies/day1/left.dds new file mode 100644 index 0000000000..d5b0999be3 --- /dev/null +++ b/binaries/data/mods/official/art/textures/skies/day1/left.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:427b224275e2c3f5e3571d7ba13093ed9e3642eefaf39e2c18917db0e07f5ca2 +size 262272 diff --git a/binaries/data/mods/official/art/textures/skies/day1/right.dds b/binaries/data/mods/official/art/textures/skies/day1/right.dds new file mode 100644 index 0000000000..5800f4bab9 --- /dev/null +++ b/binaries/data/mods/official/art/textures/skies/day1/right.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b712370bf15609d6d344ce6f44805a71f13874cf7746e8530ee4efae4bb1772 +size 262272 diff --git a/binaries/data/mods/official/art/textures/skies/day1/top.dds b/binaries/data/mods/official/art/textures/skies/day1/top.dds new file mode 100644 index 0000000000..0de761e500 --- /dev/null +++ b/binaries/data/mods/official/art/textures/skies/day1/top.dds @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf5d388552b1f7b165c1f073c1ed64d9460e1b02615a4cec2020858eb6550b71 +size 262272 diff --git a/binaries/data/mods/official/entities/template_entity_full.xml b/binaries/data/mods/official/entities/template_entity_full.xml index 9c01a7d8d6..e1be69ad71 100644 --- a/binaries/data/mods/official/entities/template_entity_full.xml +++ b/binaries/data/mods/official/entities/template_entity_full.xml @@ -9,9 +9,9 @@ - 2 - 1 - 3 + 15 + 10 + 5 diff --git a/binaries/data/mods/official/entities/template_entity_script.js b/binaries/data/mods/official/entities/template_entity_script.js index 6dadb7d086..ec7399321d 100644 --- a/binaries/data/mods/official/entities/template_entity_script.js +++ b/binaries/data/mods/official/entities/template_entity_script.js @@ -21,6 +21,7 @@ const ACTION_GATHER = 2; const ACTION_HEAL = 3; const ACTION_ATTACK_RANGED = 4; const ACTION_BUILD = 5; +const ACTION_REPAIR = 6; const PRODUCTION_TRAIN = 1; const PRODUCTION_RESEARCH = 2; @@ -36,7 +37,7 @@ function entityInit() // If this is a foundation, initialize traits from the building we're converting into if( this.building && this.building != "" ) { - var building = getEntityTemplate( this.building ); + var building = getEntityTemplate( this.building, this.player ); this.traits.id.generic = building.traits.id.generic; this.traits.id.specific = building.traits.id.specific; this.traits.id.civ = building.traits.id.civ; @@ -187,6 +188,10 @@ function entityInit() { this.setActionParams( ACTION_BUILD, 0.0, 2.0, this.actions.build.speed, "build" ); } + if( this.actions.repair ) + { + this.setActionParams( ACTION_REPAIR, 0.0, 2.0, this.actions.repair.speed, "build" ); + } } // Attach functions to ourselves @@ -196,6 +201,7 @@ function entityInit() this.performGather = performGather; this.performHeal = performHeal; this.performBuild = performBuild; + this.performRepair = performRepair; this.damage = damage; this.entityComplete = entityComplete; this.GotoInRange = GotoInRange; @@ -293,22 +299,32 @@ function attachAuras() if( this.traits.auras.courage ) { a = this.traits.auras.courage; - this.addAura ( "courage", a.radius, new DamageModifyAura( this, true, a.bonus ) ); + this.addAura ( "courage", a.radius, 0, new DamageModifyAura( this, true, a.bonus ) ); } - if( this.traits.auras.fear ) + if( this.traits.auras.fear ) { a = this.traits.auras.fear; - this.addAura ( "fear", a.radius, new DamageModifyAura( this, false, -a.bonus ) ); + this.addAura ( "fear", a.radius, 0, new DamageModifyAura( this, false, -a.bonus ) ); } - if( this.traits.auras.infidelity ) + if( this.traits.auras.infidelity ) { a = this.traits.auras.infidelity; - this.addAura ( "infidelity", a.radius, new InfidelityAura( this, a.time ) ); + this.addAura ( "infidelity", a.radius, 0, new InfidelityAura( this, a.time ) ); } - if( this.traits.auras.dropsite ) + if( this.traits.auras.dropsite ) { a = this.traits.auras.dropsite; - this.addAura ( "dropsite", a.radius, new DropsiteAura( this, a.types ) ); + this.addAura ( "dropsite", a.radius, 0, new DropsiteAura( this, a.types ) ); + } + if( this.traits.auras.heal ) + { + a = this.traits.auras.heal; + this.addAura ( "heal", a.radius, a.speed, new HealAura( this ) ); + } + if( this.traits.auras.trample ) + { + a = this.traits.auras.trample; + this.addAura ( "trample", a.radius, a.speed, new TrampleAura( this ) ); } } } @@ -324,7 +340,7 @@ function foundationDestroyed( evt ) var bp = this.build_points; var fractionToReturn = (bp.max - bp.curr) / bp.max; - var resources = getEntityTemplate( this.building ).traits.creation.resource; + var resources = getEntityTemplate( this.building, this.player ).traits.creation.resource; for( r in resources ) { amount = parseInt( fractionToReturn * parseInt(resources[r]) ); @@ -344,24 +360,31 @@ function performAttack( evt ) // Attack logic. var dmg = new DamageType(); - var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value; if ( this.getRunState() ) { console.write("" + this + " doing a charge attack!"); - dmg.crush = parseInt(this.actions.attack.charge.damage * this.actions.attack.charge.crush); - dmg.hack = parseInt(this.actions.attack.charge.damage * this.actions.attack.charge.hack); - dmg.pierce = parseInt(this.actions.attack.charge.damage * this.actions.attack.charge.pierce); + var a = this.actions.attack.charge; + dmg.crush = parseInt(a.damage * a.crush); + dmg.hack = parseInt(a.damage * a.hack); + dmg.pierce = parseInt(a.damage * a.pierce); this.setRun( false ); } else { - dmg.crush = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.crush); - dmg.hack = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.hack); - dmg.pierce = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.pierce); + var a = this.actions.attack.melee; + dmg.crush = parseInt(a.damage * a.crush); + dmg.hack = parseInt(a.damage * a.hack); + dmg.pierce = parseInt(a.damage * a.pierce); + } + + // Add flank bonus + if(evt.target.traits.flank_penalty) + { + var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value; + dmg.crush += dmg.crush * flank; + dmg.hack += dmg.hack * flank; + dmg.pierce += dmg.pierce * flank; } - dmg.crush += dmg.crush * flank; - dmg.hack += dmg.hack * flank; - dmg.pierce += dmg.pierce * flank; evt.target.damage( dmg, this ); @@ -375,14 +398,19 @@ function performAttackRanged( evt ) // Create a projectile from us, to the target, that will do some damage when it hits them. dmg = new DamageType(); - dmg.crush = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.crush); - dmg.hack = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.hack); - dmg.pierce = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.pierce); + var a = this.actions.attack.ranged; + dmg.crush = parseInt(a.damage * a.crush); + dmg.hack = parseInt(a.damage * a.hack); + dmg.pierce = parseInt(a.damage * a.pierce); - var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value; - dmg.crush += dmg.crush * flank; - dmg.hack += dmg.hack * flank; - dmg.pierce += dmg.pierce * flank; + // Add flank bonus + if(evt.target.traits.flank_penalty) + { + var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value; + dmg.crush += dmg.crush * flank; + dmg.hack += dmg.hack * flank; + dmg.pierce += dmg.pierce * flank; + } // The parameters for Projectile are: // 1 - The actor to use as the projectile. There are two ways of specifying this: @@ -544,6 +572,12 @@ function performHeal( evt ) function performBuild( evt ) { + if( !canBuild( this, evt.target ) ) + { + evt.preventDefault(); + return; + } + var t = evt.target; var b = this.actions.build; var bp = t.build_points; @@ -558,7 +592,7 @@ function performBuild( evt ) // We've finished building this object; convert the foundation to a building if( t.building != "" ) // Might be false if another unit finished building the thing during our last anim cycle { - t.template = getEntityTemplate( t.building ); + t.template = getEntityTemplate( t.building, t.player ); t.building = ""; t.attachAuras(); @@ -572,6 +606,48 @@ function performBuild( evt ) // ==================================================================== +function performRepair( evt ) +{ + if( !canRepair( this, evt.target ) ) + { + evt.preventDefault(); + return; + } + + var t = evt.target; + var b = this.actions.build; + var hp = t.traits.health; + + // Find the fraction of max health to repair by; this should be one build tick (i.e. longer for buildings with + // longer creation time) but also not so much that it causes the unit to have more than max HP + var fraction = Math.min( + parseFloat( b.rate ) / t.traits.creation.time, + ( hp.max - hp.curr ) / hp.max + ); + console.write("Repair fraction is " + fraction); + + // Check if we can afford to repair + var resources = t.traits.creation.resource; + for( r in resources ) + { + amount = parseInt( fraction * parseInt(resources[r]) ); + getGUIGlobal().deductResources( r.toString(), amount ); + } + + // Heal the building + hp.curr = Math.min( hp.max, hp.curr + fraction * hp.max ); + + // Deduct the resources + var resources = t.traits.creation.resource; + for( r in resources ) + { + amount = parseInt( fraction * parseInt(resources[r]) ); + getGUIGlobal().deductResources( r.toString(), amount ); + } +} + +// ==================================================================== + function damage( dmg, inflictor ) { if(!this.traits.armour) return; // corpses have no armour, everything else should @@ -636,7 +712,7 @@ function damage( dmg, inflictor ) inflictor.traits.id.icon_cell++; // Transmogrify him into his next rank. - inflictor.template = getEntityTemplate(inflictor.traits.promotion.newentity, inflictor.player); + inflictor.template = getEntityTemplate( inflictor.traits.promotion.newentity, inflictor.player ); } } break; @@ -706,6 +782,8 @@ function entityEventGeneric( evt ) this.performAttackRanged( evt ); break; case ACTION_BUILD: this.performBuild( evt ); break; + case ACTION_REPAIR: + this.performRepair( evt ); break; default: console.write( "Unknown generic action: " + evt.action ); @@ -880,6 +958,17 @@ function entityEventTargetChanged( evt ) evt.secondaryAction = ACTION_BUILD; evt.secondaryCursor = "action-build"; } + + if ( canRepair( this, evt.target ) ) + { + evt.defaultOrder = NMT_Generic; + evt.defaultAction = ACTION_REPAIR; + evt.defaultCursor = "action-build"; + + evt.secondaryOrder = NMT_Generic; + evt.secondaryAction = ACTION_REPAIR; + evt.secondaryCursor = "action-build"; + } } @@ -988,6 +1077,16 @@ function entityEventPrepareOrder( evt ) evt.notifyType = NOTIFY_NONE; this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); break; + + case ACTION_REPAIR: + if ( !this.actions.repair ) + { + evt.preventDefault(); + return; + } + evt.notifyType = NOTIFY_NONE; + this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this ); + break; } break; @@ -1018,7 +1117,7 @@ function entityStartProduction( evt ) if(evt.productionType == PRODUCTION_TRAIN) { - var template = getEntityTemplate( evt.name ); + var template = getEntityTemplate( evt.name, this.player ); var result = entityCheckQueueReq( this, template ); if (result == true) // If the entry meets requirements to be added to the queue (eg sufficient resources) @@ -1043,7 +1142,7 @@ function entityStartProduction( evt ) } // Set the amount of time it will take to complete production of the production object. - evt.time = getEntityTemplate(evt.name).traits.creation.time; + evt.time = getEntityTemplate( evt.name, this.player ).traits.creation.time; } else { @@ -1065,7 +1164,7 @@ function entityCancelProduction( evt ) if(evt.productionType == PRODUCTION_TRAIN) { // Give back all the resources spent on this entry. - var template = getEntityTemplate( evt.name ); + var template = getEntityTemplate( evt.name, this.player ); var pool = template.traits.creation.resource; for ( resource in pool ) { @@ -1092,7 +1191,7 @@ function entityFinishProduction( evt ) if(evt.productionType == PRODUCTION_TRAIN) { - var template = getEntityTemplate(evt.name); + var template = getEntityTemplate( evt.name, this.player ); // Code to find a free space around an object is tedious and slow, so // I wrote it in C. Takes the template object so it can determine how @@ -1220,7 +1319,7 @@ function entityCreateComplete() function attemptAddToBuildQueue( entity, create_tag, tab, list ) { - template = getEntityTemplate( create_tag ); + template = getEntityTemplate( create_tag, entity.player ); result = entityCheckQueueReq( entity, template ); if (result == true) // If the entry meets requirements to be added to the queue (eg sufficient resources) @@ -1342,6 +1441,19 @@ function canBuild( source, target ) // ==================================================================== +function canRepair( source, target ) +{ + // Checks whether we're allowed to gather from a target entity (this involves looking at both the type and subtype). + if( !source.actions ) + return false; + r = source.actions.repair; + return( r && target.traits.health.repairable && target.player.id == source.player.id + && target.traits.health.curr < target.traits.health.max + && target.building == "" ); +} + +// ==================================================================== + function DamageType() { this.typeless = 0.0; @@ -1524,6 +1636,75 @@ function InfidelityAura( source, time ) } // ==================================================================== + +function HealAura( source ) +{ + // Defines the effects of the Heal aura. Slowly heals nearby allies over time. + + this.source = source; + + this.affects = function( e ) + { + return ( e.player.id == this.source.player.id && e.traits.health + && e.traits.health.healable + && e.traits.health.curr < e.traits.health.max ); + } + + this.onTick = function( e ) + { + if( this.affects( e ) ) + { + var hp = e.traits.health; + var rate = this.source.traits.auras.heal.rate; + hp.curr = Math.min( hp.max, hp.curr + rate ); + } + }; +} + +// ==================================================================== + +function TrampleAura( source ) +{ + // Defines the effects of the Trample aura. Damages nearby enemies over time if the unit is charging or has recently charged. + + this.source = source; + + this.affects = function( e ) + { + // Check if the target is an enemy foot unit with health and if we were running in the last 3 seconds + var a = this.source.traits.auras.trample; + return ( e.player.id != this.source.player.id && e.traits.health && e.hasClass("Foot") + && (getGameTime() - this.source.last_run_time < a.duration) ); + } + + this.onTick = function( e ) + { + if( this.affects( e ) ) + { + // Set up the damage object + var dmg = new DamageType(); + var a = this.source.traits.auras.trample; + dmg.crush = parseInt(a.damage * a.crush); + dmg.hack = parseInt(a.damage * a.hack); + dmg.pierce = parseInt(a.damage * a.pierce); + + // Add flank bonus + if(e.traits.flank_penalty) + { + var flank = (e.getAttackDirections()-1)*e.traits.flank_penalty.value; + dmg.crush += dmg.crush * flank; + dmg.hack += dmg.hack * flank; + dmg.pierce += dmg.pierce * flank; + } + + // Perform the damage + e.damage( dmg, this.source ); + } + }; +} + +// ==================================================================== + function GotoInRange( x, y, run ) { if ( !this.actions || !this.actions.move ) @@ -1534,7 +1715,9 @@ function GotoInRange( x, y, run ) else this.order( ORDER_GOTO, x, y - this.actions.escort.distance, true); } + //===================================================================== + function entityEventFormation( evt ) { if ( evt.formationEvent == FORMATION_ENTER ) diff --git a/binaries/data/mods/official/entities/template_structure.xml b/binaries/data/mods/official/entities/template_structure.xml index 2705492bf8..b4fd5c469e 100644 --- a/binaries/data/mods/official/entities/template_structure.xml +++ b/binaries/data/mods/official/entities/template_structure.xml @@ -17,14 +17,23 @@ + + + + 0 + 100 + 50 + + + 12.0 + 9 -