~harrison-rt/+junk/jaguar_company

« back to all changes in this revision

Viewing changes to tags/1.6/jaguar_company_1.6.oxp/Scripts/jaguar_company_ship.js

  • Committer: Richard Harrison
  • Date: 2013-01-19 22:39:57 UTC
  • Revision ID: harrison.rt@gmail.com-20130119223957-lcuzmbsrx6o2bk8o
Tags: 1.6.2
* Bug fix for the ship script.
* Bug fix to the waypoint AI. Never exited the stock AI.
* Cascade Weapon detection actually works for pre v1.77
* Took the opportunity to clean up some files and re-do the textures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*jslint indent: 4, maxerr: 50, white: true, browser: false, undef: true, nomen: true, plusplus: true, bitwise: true, regexp: true, newcap: true, sloppy: false */
 
2
 
 
3
/* Jaguar Company
 
4
**
 
5
** Copyright (C) 2012 Tricky
 
6
**
 
7
** This work is licensed under the Creative Commons
 
8
** Attribution-Noncommercial-Share Alike 3.0 Unported License.
 
9
**
 
10
** To view a copy of this license, visit
 
11
** http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter
 
12
** to Creative Commons, 171 Second Street, Suite 300, San Francisco,
 
13
** California, 94105, USA.
 
14
**
 
15
** Ship related functions for the patrol AI.
 
16
** Missile subentity loading based on tgGeneric_externalMissiles.js by Thargoid
 
17
*/
 
18
 
 
19
this.name = "jaguar_company_ship.js";
 
20
this.author = "Tricky";
 
21
this.copyright = "© 2012 Tricky";
 
22
this.license = "CC BY-NC-SA 3.0";
 
23
this.description = "Ship script for the Jaguar Company.";
 
24
this.version = "1.2";
 
25
 
 
26
(function () {
 
27
    "use strict";
 
28
 
 
29
    this.shipSpawned = function shipSpawned() {
 
30
        var shipNames,
 
31
        addCounter;
 
32
 
 
33
        /* OU's, GOU's, LOU's and (d)ROU's. Also some names I really like. */
 
34
        shipNames = ["Profit Margin", "Trade Surplus", "Limiting Factor", "Gunboat Diplomat", "Zealot", "Xenophobe", "God Told Me To Do It", "Just Another Victim Of The Ambient Morality", "Synchronize Your Dogmas", "Thank you And Goodnight", "Well I Was In The Neighbourhood", "You'll Thank Me Later", "Shoot Them Later", "Attitude Adjuster", "Killing Time", "I Blame Your Mother", "I Blame My Mother", "Heavy Messing", "Frank Exchange Of Views", "Nuisance Value", "All Through With This Niceness And Negotiation Stuff", "I Said, I've Got A Big Stick", "Hand Me The Gun And Ask Me Again", "But Who's Counting?", "Germane Riposte", "We Haven't Met But You're A Great Fan Of Mine", "All The Same, I Saw It First", "Ravished By The Sheer Implausibility Of That Last Statement", "Zero Credibility", "Charming But Irrational", "Demented But Determined", "You May Not Be The Coolest Person Here", "Lucid Nonsense", "Awkward Customer", "Conventional Wisdom", "Fine Till You Came Along", "I Blame The Parents", "Inappropriate Response", "A Momentary Lapse Of Sanity", "Lapsed Pacifist", "Reformed Nice Guy", "Pride Comes Before A Fall", "Injury Time", "Now Look What You've Made Me Do", "Kiss This Then", "Eight Rounds Rapid", "You'll Clean That Up Before You Leave", "Me, I'm Counting", "The Usual But Etymologically Unsatisfactory", "Falling Outside The Normal Moral Constraints", "Hylozoist", "No One Knows What The Dead Think", "Flick to Kick", "Your Egg's Broken But Mine Is Ok", "Shall I Be Mummy?", "Is This Galaxy Taken?", "Famous Last Words", "Road Rage", "Live A Little", "Not in My Back Yard", "Playing A Sweeper", "You're Going Home In A Fracking Ambulance", "Rear Entry", "Open Wide, Say Aaaarrgghhh", "Hope You Like Explosions", "I Haven't Seen One Of Those For Years", "Are You Religious?", "Not Now Dear", "Something Had To Be Done", "Hideously Indefensible Sense Of Humour", "Camouflage", "Come And Have A Go If You Think You're Hard Enough", "Throwing Toys Out The Crib", "Podex Perfectus Es", "Stercorem Pro Cerebro Habes", "Futue Te Ipsum Et Caballum Tuum", "Remember To Wash Your Hands", "One Out All Out", "Looking At Me, Pal?", "You Showed Me Yours, Now I'll Show You Mine", "Salt In Your Vaseline", "Cracking My Knuckles", "Break Glass In Case Of War", "My Turn", "No Pun Intended", "Look No Hands", "Very Sharp Stick", "Weapons Of Mass Deception", "...And Another Thing", "Clerical Error", "Silly Mid On", "You And Whose Army?", "This Sector Ain't Big Enough For The Both Of Us", "Diplomacy Was Never My Strong Suite", "Such A Pretty Big Red Button", "Synthetic Paragon Rubber Company", "Forget And Fire", "I Was Just Following Orders", "Weapon of Mass Distraction", "Forgive and Forget", "Innocence Is No Excuse", "Psychosis Is Only One State Of Mind", "Lets Dance", "AI Avenger", "Dead Man Walking", "A Little Less Conversation", "Here One Minute, Gone The Next", "Here, Let Me Escort You", "Killed With Superior Skill", "External Agitation", "Catch Me If You Can", "But What About The Children?", "Single Fingered Hand Gestures", "A World Of Hurt", "Looking Down The Gun Barrel", "Terminal Atomic Headache", "Know Thy Enemy", "Cold Steel For An Iron Age", "The Malevolent Creation", "Gamma Ray Goggles", "End Of Green", "Terrorwheel", "Sickening Sense Of Humour", "Mines Bigger", "Friendly Fire Isn't", "No Need For Stealth", "All Guns Blazin!", "Harmony Dies", "The Controlled Psychopath", "It Ends Now", "Forced To Be Nice", "Axis of Advance", "Acts of God", "The Feeling's Mutual", "The Beautiful Nightmare", "If You Can Read This...", "Are You Saved?", "Cunning Linguist", "Gay Abandon", "My Finger", "Got Legs", "Hose Job", "Protect And Sever", "Rebuttal", "Not In The Face", "I Have Right Of Way", "It Ran Into My Missile", "Have A Nice Rest Of Your Life", "Nose Job", "Get My Point?", "Grid Worker", "Eraserhead", "What Star?", "All This (And Brains)", "Random Acts Of Senseless Violence", "God Will Recognize His Own", "Would You Like A Quick Suppository With That?", "Pop Me A Couple More Of Those Happy Pills (Eccentric)", "Trouble Maker?", "Talk Is Cheap", "Tightly Strung", "Have You Kept The Receipt?", "It Was Broke When I Got Here", "Insanity Plea Rejected", "Thora Hird", "Barbara Cartland", "Freddy Starr Ate My Hamster", "And You Thought You Knew What Terror Means", "I'm A 'Shoot First, Ask Questions Later' Kinda Guy", "Duck You Suckers", "Trumpton Riots", "Dodgy Transformer"];
 
35
 
 
36
        if (worldScripts["Jaguar Company"].logAIMessages) {
 
37
            this.ship.reportAIMessages = true;
 
38
        }
 
39
 
 
40
        this.ship.displayName = "Jaguar Company: " + shipNames[Math.floor(Math.random() * shipNames.length)];
 
41
        this.route = "JAGUAR_COMPANY_BASE_TO_WP";
 
42
 
 
43
        /* just to ensure ship is fully loaded with selected missile type and nothing else. */
 
44
        if (this.ship.scriptInfo.missileRole) {
 
45
            /* missileRole should be defined in shipdata.plist */
 
46
            this.missileRole = this.ship.scriptInfo.missileRole;
 
47
        } else {
 
48
            /* default to standard missile if not. */
 
49
            this.missileRole = "EQ_HARDENED_MISSILE";
 
50
        }
 
51
 
 
52
        /* Thargoid's missile code. */
 
53
        if (this.ship.scriptInfo.initialMissiles) {
 
54
            this.initialMissiles = parseInt(this.ship.scriptInfo.initialMissiles, 10);
 
55
        } else {
 
56
            this.initialMissiles = this.ship.missileCapacity;
 
57
        }
 
58
 
 
59
        if (this.ship.missiles.length > 0) {
 
60
            /* remove all spawning missiles and restock with selected ones. */
 
61
            this.ship.awardEquipment("EQ_MISSILE_REMOVAL");
 
62
        }
 
63
 
 
64
        for (addCounter = 0; addCounter < this.initialMissiles; addCounter++) {
 
65
            this.ship.awardEquipment(this.missileRole);
 
66
        }
 
67
    };
 
68
 
 
69
    /* Thargoid's missile code. */
 
70
    this.shipFiredMissile = function shipFiredMissile(missile, target) {
 
71
        var subCounter;
 
72
 
 
73
        if (this.ship.subEntities.length === 0) {
 
74
            /* if we've run out of sub-ents before we run out of missiles. */
 
75
            return;
 
76
        }
 
77
 
 
78
        /* Set counter to number of sub-ents minus 1 (as entity array goes up from zero). */
 
79
        subCounter = this.ship.subEntities.length - 1;
 
80
 
 
81
        for (subCounter = this.ship.subEntities.length - 1; subCounter >= 0; subCounter--) {
 
82
            if (this.ship.subEntities[subCounter].hasRole(missile.primaryRole)) {
 
83
                /* if the sub-ent is the same as the missile being fired. */
 
84
                /* move the fired missile to the sub-ent position. */
 
85
                missile.position = this.$localToGlobal(this.ship.subEntities[subCounter].position);
 
86
                /* point the missile in the right direction. */
 
87
                missile.orientation = this.ship.subEntities[subCounter].orientation.multiply(this.ship.orientation);
 
88
                missile.desiredSpeed = missile.maxSpeed;
 
89
                /* remove the sub-ent version of the missile. */
 
90
                this.ship.subEntities[subCounter].remove();
 
91
 
 
92
                /* come out of the loop, as we've done our swap. */
 
93
                break;
 
94
            }
 
95
        }
 
96
    };
 
97
 
 
98
    /* Thargoid's missile code. */
 
99
    this.$localToGlobal = function $localToGlobal(position) {
 
100
        /* sub-ent position is relative to mother, but for swapping we need the absolute global position. */
 
101
        var orientation = this.ship.orientation;
 
102
 
 
103
        return this.ship.position.add(position.rotateBy(orientation));
 
104
    };
 
105
 
 
106
    /* Thargoid's missile code. */
 
107
    this.shipTakingDamage = function shipTakingDamage(amount, fromEntity, damageType) {
 
108
        var missileCounter,
 
109
        removeCounter,
 
110
        subCounter;
 
111
 
 
112
        if (this.ship.missiles.length === 0 && this.ship.subEntities.length === 0) {
 
113
            /* if we're all out of missiles and any sub-entities, bail out. */
 
114
            return;
 
115
        }
 
116
 
 
117
        this.missileSubs = 0;
 
118
 
 
119
        /* Initially set subCounter to number of sub-ents minus 1 (as entity array goes up from zero). */
 
120
        for (subCounter = this.ship.subEntities.length - 1; subCounter >= 0; subCounter--) {
 
121
            if (this.ship.subEntities[subCounter].hasRole(this.missileRole)) {
 
122
                /* if the sub-ent is a missile, count it. */
 
123
                this.missileSubs++;
 
124
            }
 
125
        }
 
126
 
 
127
        if (this.missileSubs === 0 && this.ship.subEntities.length === 0) {
 
128
            /* if we're all out of missiles and missile sub-entities, bail out. */
 
129
            return;
 
130
        }
 
131
 
 
132
        if (this.missileSubs < this.ship.missiles.length) {
 
133
            /* if we've got more missiles than sub-entity missiles. */
 
134
            /* get rid of all missiles. */
 
135
            this.ship.awardEquipment("EQ_MISSILE_REMOVAL");
 
136
 
 
137
            if (this.missileSubs > 0) {
 
138
                for (missileCounter = 0; missileCounter < this.missileSubs; missileCounter++) {
 
139
                    /* restock with the correct number of selected missile. */
 
140
                    this.ship.awardEquipment(this.missileRole);
 
141
                }
 
142
            }
 
143
 
 
144
            return;
 
145
        }
 
146
 
 
147
        if (this.missileSubs > this.ship.missiles.length) {
 
148
            /* if we've got less missiles than sub-entity missiles. */
 
149
            this.difference = this.missileSubs - this.ship.missiles.length;
 
150
 
 
151
            for (removeCounter = 0; removeCounter < this.difference; removeCounter++) {
 
152
                /* loop through however many subs we need to remove. */
 
153
                /* Initially set subCounter to number of sub-ents minus 1 (as entity array goes up from zero). */
 
154
                for (subCounter = this.ship.subEntities.length - 1; subCounter >= 0; subCounter--) {
 
155
                    if (this.ship.subEntities[subCounter].hasRole(this.missileRole)) {
 
156
                        /* if the sub-ent is a missile, remove it. */
 
157
                        this.ship.subEntities[subCounter].remove();
 
158
 
 
159
                        break;
 
160
                    }
 
161
                }
 
162
            }
 
163
 
 
164
            return;
 
165
        }
 
166
    };
 
167
 
 
168
    /* Not doing any exotic routes for now. */
 
169
    /* route: Base->WP, WP->PLANET, PLANET->WP, WP->Base */
 
170
    this.$checkCurrentRoute = function $checkCurrentRoute() {
 
171
        switch (this.route) {
 
172
        case "JAGUAR_COMPANY_BASE_TO_WP":
 
173
            this.ship.reactToAIMessage("GOTO_WITCHPOINT_FROM_BASE");
 
174
            break;
 
175
        case "JAGUAR_COMPANY_WP_TO_PLANET":
 
176
            this.ship.reactToAIMessage("GOTO_PLANET");
 
177
            break;
 
178
        case "JAGUAR_COMPANY_PLANET_TO_WP":
 
179
            this.ship.reactToAIMessage("GOTO_WITCHPOINT");
 
180
            break;
 
181
        case "JAGUAR_COMPANY_WP_TO_BASE":
 
182
            this.ship.reactToAIMessage("GOTO_BASE");
 
183
            break;
 
184
        default:
 
185
            this.route = "JAGUAR_COMPANY_BASE_TO_WP";
 
186
            this.ship.reactToAIMessage("INVALID_ROUTE");
 
187
        }
 
188
    };
 
189
 
 
190
    /* Find out our next route. */
 
191
    this.$findNextRoute = function $findNextRoute() {
 
192
        switch (this.route) {
 
193
        case "JAGUAR_COMPANY_BASE_TO_WP":
 
194
            this.route = "JAGUAR_COMPANY_WP_TO_PLANET";
 
195
            break;
 
196
        case "JAGUAR_COMPANY_WP_TO_PLANET":
 
197
            this.route = "JAGUAR_COMPANY_PLANET_TO_WP";
 
198
            break;
 
199
        case "JAGUAR_COMPANY_PLANET_TO_WP":
 
200
            this.route = "JAGUAR_COMPANY_WP_TO_BASE";
 
201
            break;
 
202
        case "JAGUAR_COMPANY_WP_TO_BASE":
 
203
            this.route = "JAGUAR_COMPANY_BASE_TO_WP";
 
204
            break;
 
205
        default:
 
206
            this.route = "JAGUAR_COMPANY_BASE_TO_WP";
 
207
        }
 
208
 
 
209
        this.$checkCurrentRoute();
 
210
    };
 
211
 
 
212
    /* Return the ship furthest away. */
 
213
    this.$queryMaxShip = function $queryMaxShip() {
 
214
        var ships = system.shipsWithRole("jaguar_company_patrol", this.ship);
 
215
 
 
216
        /* If there is no other ships then return null */
 
217
        if (ships.length === 1) {
 
218
            return null;
 
219
        }
 
220
 
 
221
        /* Even though the ship group can be set up, all the ships may not be present. */
 
222
        if (ships.length !== worldScripts["Jaguar Company"].shipGroup.ships.length) {
 
223
            return;
 
224
        }
 
225
 
 
226
        /* Return the last ship in the array which is the furthest away. */
 
227
        return ships[ships.length - 1];
 
228
    };
 
229
 
 
230
    /* Find the average distance to all the other ships. */
 
231
    this.$queryAverageDistance = function $queryAverageDistance() {
 
232
        var ships = system.shipsWithRole("jaguar_company_patrol", this.ship),
 
233
        distance,
 
234
        averageDistance,
 
235
        logStr,
 
236
        i,
 
237
        j;
 
238
 
 
239
        /* If there is no other ships then return null */
 
240
        if (ships.length === 1) {
 
241
            return null;
 
242
        }
 
243
 
 
244
        if (worldScripts["Jaguar Company"].logging && worldScripts["Jaguar Company"].logExtra) {
 
245
            logStr = this.ship.displayName + " => ";
 
246
        }
 
247
 
 
248
        averageDistance = 0.0;
 
249
 
 
250
        for (i = 0, j = 0; i < ships.length; i++) {
 
251
            /* Don't check our own ship. */
 
252
            if (this.ship !== ships[i]) {
 
253
                distance = this.ship.position.distanceTo(ships[i].position);
 
254
 
 
255
                if (worldScripts["Jaguar Company"].logging && worldScripts["Jaguar Company"].logExtra) {
 
256
                    logStr += ships[i].displayName + " = " + distance;
 
257
 
 
258
                    if (++j !== ships.length - 1) {
 
259
                        logStr += ", ";
 
260
                    }
 
261
                }
 
262
 
 
263
                /* Add up the distances. */
 
264
                averageDistance += distance;
 
265
            }
 
266
        }
 
267
 
 
268
        /* Average all the distances. */
 
269
        averageDistance /= (ships.length - 1);
 
270
 
 
271
        if (worldScripts["Jaguar Company"].logging && worldScripts["Jaguar Company"].logExtra) {
 
272
            log(this.name, "Avg: " + averageDistance + " (" + logStr + ")");
 
273
        }
 
274
 
 
275
        return averageDistance;
 
276
    };
 
277
 
 
278
    /* Find the others ships and set the target to the one furthest away. */
 
279
    this.$locateJaguarCompany = function $locateJaguarCompany() {
 
280
        var ships = system.shipsWithRole("jaguar_company_patrol", this.ship),
 
281
        maxShip;
 
282
 
 
283
        /* Even though the ship group can be set up, all the ships may not be present. */
 
284
        if (ships.length !== worldScripts["Jaguar Company"].shipGroup.ships.length) {
 
285
            return;
 
286
        }
 
287
 
 
288
        maxShip = this.$queryMaxShip();
 
289
 
 
290
        if (maxShip === null) {
 
291
            this.ship.reactToAIMessage("JAGUAR_COMPANY_NOT_FOUND");
 
292
 
 
293
            return;
 
294
        }
 
295
 
 
296
        this.ship.target = maxShip;
 
297
        this.ship.reactToAIMessage("JAGUAR_COMPANY_FOUND");
 
298
    };
 
299
 
 
300
    this.$checkJaguarCompanyDistance = function $checkJaguarCompanyDistance() {
 
301
        var ships = system.shipsWithRole("jaguar_company_patrol", this.ship),
 
302
        distance;
 
303
 
 
304
        /* Even though the ship group can be set up, all the ships may not be present. */
 
305
        if (ships.length !== worldScripts["Jaguar Company"].shipGroup.ships.length) {
 
306
            return;
 
307
        }
 
308
 
 
309
        /* Find the average distance to all the other ships. */
 
310
        distance = this.$queryAverageDistance();
 
311
 
 
312
        /* A 'null' distance means no other ships. */
 
313
        if (distance === null) {
 
314
            return;
 
315
        }
 
316
 
 
317
        /* I would love to create a fuzzy logic controller for this. */
 
318
        if (distance < 7500.0) {
 
319
            /* If the average distance is less than 7.5km then we have regrouped. */
 
320
            this.ship.reactToAIMessage("JAGUAR_COMPANY_REGROUPED");
 
321
        } else if (distance >= 7500.0 && distance < 15000.0) {
 
322
            /* If the average distance is between 7.5km and 15km then we are close. */
 
323
            this.ship.reactToAIMessage("JAGUAR_COMPANY_CLOSE");
 
324
        } else if (distance >= 15000.0 && distance < 22500.0) {
 
325
            /* If the average distance is between 15km and 22.5km then we are nearby. */
 
326
            this.ship.reactToAIMessage("JAGUAR_COMPANY_NEARBY");
 
327
        } else {
 
328
            /* If the average distance is more than 22.5km then we are far away. */
 
329
            this.ship.reactToAIMessage("JAGUAR_COMPANY_FAR_AWAY");
 
330
        }
 
331
    };
 
332
 
 
333
    /* Tell everyone to regroup if the average distance to all the other ships is too great. */
 
334
    this.$checkJaguarCompanyRegroup = function $checkJaguarCompanyRegroup(maxDistance) {
 
335
        var ships = system.shipsWithRole("jaguar_company_patrol", this.ship),
 
336
        distance,
 
337
        i;
 
338
 
 
339
        /* Even though the ship group can be set up, all the ships may not be present. */
 
340
        if (ships.length !== worldScripts["Jaguar Company"].shipGroup.ships.length) {
 
341
            return;
 
342
        }
 
343
 
 
344
        /* Find the average distance to all the other ships. */
 
345
        distance = this.$queryAverageDistance();
 
346
 
 
347
        /* A 'null' distance means no other ships. */
 
348
        if (distance === null) {
 
349
            return;
 
350
        }
 
351
 
 
352
        if (distance >= maxDistance) {
 
353
            for (i = 0; i < ships.length; i++) {
 
354
                /* Tell all ships, including ourself, to regroup. */
 
355
                ships[i].reactToAIMessage("JAGUAR_COMPANY_REGROUP");
 
356
            }
 
357
        }
 
358
    };
 
359
 
 
360
    /* This does something similar to the groupAttack AI command. */
 
361
    this.$jaguarCompanyAttackTarget = function $jaguarCompanyAttackTarget() {
 
362
        var ships = system.shipsWithRole("jaguar_company_patrol", this.ship),
 
363
        i;
 
364
 
 
365
        /* Even though the ship group can be set up, all the ships may not be present. */
 
366
        if (ships.length !== worldScripts["Jaguar Company"].shipGroup.ships.length) {
 
367
            return;
 
368
        }
 
369
 
 
370
        for (i = 0; i < ships.length; i++) {
 
371
            /* Other ships may be busy killing baddies, react 25% of the time. */
 
372
            if (this.ship !== ships[i] && Math.random() < 0.25) {
 
373
                log(this.name, "ship #" + i + " responding: " + ships[i].displayName);
 
374
                ships[i].target = this.ship.target;
 
375
                ships[i].reactToAIMessage("JAGUAR_COMPANY_ATTACK_TARGET");
 
376
            }
 
377
        }
 
378
 
 
379
        /* Respond to our own attack call. */
 
380
        this.ship.reactToAIMessage("JAGUAR_COMPANY_ATTACK_TARGET");
 
381
    };
 
382
 
 
383
    /* Locate the base. */
 
384
    this.$locateJaguarCompanyBase = function $locateJaguarCompanyBase() {
 
385
        var ships = system.shipsWithRole("jaguar_company_base", this.ship);
 
386
 
 
387
        if (ships.length === 0) {
 
388
            /* If it has gone, just patrol the witchpoint to the planet lane. */
 
389
            this.route = "JAGUAR_COMPANY_WP_TO_PLANET";
 
390
            this.ship.reactToAIMessage("JAGUAR_COMPANY_BASE_NOT_FOUND");
 
391
        } else {
 
392
            /* Set the target to the base. */
 
393
            this.ship.target = ships[0];
 
394
            this.ship.reactToAIMessage("JAGUAR_COMPANY_BASE_FOUND");
 
395
        }
 
396
    };
 
397
 
 
398
    /* Won't be needed when v1.78 comes out. */
 
399
    this.$detectQBomb = function $detectQBomb() {
 
400
        var qbomb = system.shipsWithRole("EQ_QC_MINE EQ_CASCADE_MISSILE", this.ship, 25600.0);
 
401
 
 
402
        /* Let v1.77+ handle it. */
 
403
        if (0 >= oolite.compareVersion("1.77")) {
 
404
            return;
 
405
        }
 
406
 
 
407
        if (qbomb.length !== 0) {
 
408
            /* First one is the closest. */
 
409
            this.ship.target = qbomb[0];
 
410
            this.ship.reactToAIMessage("CASCADE_WEAPON_DETECTED");
 
411
        }
 
412
    };
 
413
 
 
414
}).call(this);